From ec55890c55a74bff65d4ebede611c0ed4bb1c4a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Fri, 28 Jul 2017 12:01:36 +0200 Subject: [PATCH 001/333] Check that .metadata.yaml actually contains a dict It might just be empty (= None) or a list or something. Thanks @Kunsi for the heads-up. --- src/octoprint/filemanager/storage.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/octoprint/filemanager/storage.py b/src/octoprint/filemanager/storage.py index 0f49ec88a4..582abaea42 100644 --- a/src/octoprint/filemanager/storage.py +++ b/src/octoprint/filemanager/storage.py @@ -1353,8 +1353,9 @@ def _get_metadata(self, path): except: self._logger.exception("Error while reading .metadata.yaml from {path}".format(**locals())) else: - self._metadata_cache[path] = deepcopy(metadata) - return metadata + if isinstance(metadata, dict): + self._metadata_cache[path] = deepcopy(metadata) + return metadata return dict() def _save_metadata(self, path, metadata): From 60be6349f575213dfee92246c96ae88aa2313f0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Fri, 28 Jul 2017 13:08:28 +0200 Subject: [PATCH 002/333] Allow cancelling file transfers Cancelling also deletes the incomplete file on the printer's SD. --- src/octoprint/events.py | 1 + src/octoprint/printer/standard.py | 23 ++++--- .../static/js/app/viewmodels/files.js | 17 ++++++ src/octoprint/util/comm.py | 60 ++++++++++++++----- 4 files changed, 78 insertions(+), 23 deletions(-) diff --git a/src/octoprint/events.py b/src/octoprint/events.py index 7f9b818fd7..03a7daf228 100644 --- a/src/octoprint/events.py +++ b/src/octoprint/events.py @@ -62,6 +62,7 @@ class Events(object): # SD Upload TRANSFER_STARTED = "TransferStarted" TRANSFER_DONE = "TransferDone" + TRANSFER_FAILED = "TransferFailed" # print job PRINT_STARTED = "PrintStarted" diff --git a/src/octoprint/printer/standard.py b/src/octoprint/printer/standard.py index 6f2cddab0a..873dfec2f8 100644 --- a/src/octoprint/printer/standard.py +++ b/src/octoprint/printer/standard.py @@ -71,6 +71,7 @@ def __init__(self, fileManager, analysisQueue, printerProfileManager): self._sdStreaming = False self._sdFilelistAvailable = threading.Event() self._streamingFinishedCallback = None + self._streamingFailedCallback = None self._selectedFileMutex = threading.RLock() self._selectedFile = None @@ -579,12 +580,13 @@ def get_sd_files(self): return [] return map(lambda x: (x[0][1:], x[1]), self._comm.getSdFiles()) - def add_sd_file(self, filename, absolutePath, streamingFinishedCallback): + def add_sd_file(self, filename, absolutePath, on_success=None, on_failure=None): if not self._comm or self._comm.isBusy() or not self._comm.isSdReady(): self._logger.error("No connection to printer or printer is busy") return - self._streamingFinishedCallback = streamingFinishedCallback + self._streamingFinishedCallback = on_success + self._streamingFailedCallback = on_failure self.refresh_sd_files(blocking=True) existingSdFiles = map(lambda x: x[0], self._comm.getSdFiles()) @@ -1171,19 +1173,26 @@ def on_comm_file_transfer_started(self, filename, filesize): self._updateProgressData(completion=0.0, filepos=0, printTime=0) self._stateMonitor.set_state({"text": self.get_state_string(), "flags": self._getStateFlags()}) - def on_comm_file_transfer_done(self, filename): + def on_comm_file_transfer_done(self, filename, failed=False): self._sdStreaming = False - if self._streamingFinishedCallback is not None: - # in case of SD files, both filename and absolutePath are the same, so we set the (remote) filename for - # both parameters - self._streamingFinishedCallback(filename, filename, FileDestinations.SDCARD) + # in case of SD files, both filename and absolutePath are the same, so we set the (remote) filename for + # both parameters + if failed: + if self._streamingFailedCallback is not None: + self._streamingFailedCallback(filename, filename, FileDestinations.SDCARD) + else: + if self._streamingFinishedCallback is not None: + self._streamingFinishedCallback(filename, filename, FileDestinations.SDCARD) self._setCurrentZ(None) self._setJobData(None, None, None) self._updateProgressData() self._stateMonitor.set_state({"text": self.get_state_string(), "flags": self._getStateFlags()}) + def on_comm_file_transfer_failed(self, filename): + self.on_comm_file_transfer_done(filename, failed=True) + def on_comm_force_disconnect(self): self.disconnect() diff --git a/src/octoprint/static/js/app/viewmodels/files.js b/src/octoprint/static/js/app/viewmodels/files.js index f74b93f475..159bfc3c66 100644 --- a/src/octoprint/static/js/app/viewmodels/files.js +++ b/src/octoprint/static/js/app/viewmodels/files.js @@ -954,6 +954,23 @@ $(function() { self.requestData({focus: {location: "sdcard", path: payload.remote}}); }; + self.onEventTransferFailed = function(payload) { + self.uploadProgress + .removeClass("progress-striped") + .removeClass("active"); + self.uploadProgressBar + .css("width", "0"); + self.uploadProgressText(""); + + new PNotify({ + title: gettext("Streaming failed"), + text: _.sprintf(gettext("Did not finish streaming %(local)s to %(remote)s on SD"), payload), + type: "error" + }); + + self.requestData(); + }; + self.onServerConnect = self.onServerReconnect = function(payload) { self._enableDragNDrop(true); self.requestData(); diff --git a/src/octoprint/util/comm.py b/src/octoprint/util/comm.py index 299efc19fd..1cadcd344b 100644 --- a/src/octoprint/util/comm.py +++ b/src/octoprint/util/comm.py @@ -877,7 +877,7 @@ def startPrint(self, pos=None): def startFileTransfer(self, filename, localFilename, remoteFilename): if not self.isOperational() or self.isBusy(): - logging.info("Printer is not operational or busy") + self._logger.info("Printer is not operational or busy") return with self._jobLock: @@ -890,6 +890,39 @@ def startFileTransfer(self, filename, localFilename, remoteFilename): eventManager().fire(Events.TRANSFER_STARTED, {"local": localFilename, "remote": remoteFilename}) self._callback.on_comm_file_transfer_started(remoteFilename, self._currentFile.getFilesize()) + def cancelFileTransfer(self): + if not self.isOperational() or not self.isStreaming(): + self._logger.info("Printer is not operational or not streaming") + return + + self._finishFileTransfer(failed=True) + + def _finishFileTransfer(self, failed=False): + with self._jobLock: + remote = self._currentFile.getRemoteFilename() + + self._sendCommand("M29") + if failed: + self.deleteSdFile(remote) + + payload = { + "local": self._currentFile.getLocalFilename(), + "remote": remote, + "time": self.getPrintTime() + } + + self._currentFile = None + self._changeState(self.STATE_OPERATIONAL) + + if failed: + self._callback.on_comm_file_transfer_failed(remote) + eventManager().fire(Events.TRANSFER_FAILED, payload) + else: + self._callback.on_comm_file_transfer_done(remote) + eventManager().fire(Events.TRANSFER_DONE, payload) + + self.refreshSdFiles() + def selectFile(self, filename, sd): if self.isBusy(): return @@ -920,13 +953,18 @@ def _cancel_preparation_done(self): self._callback.on_comm_print_job_cancelled() def cancelPrint(self, firmware_error=None): - if not self.isOperational() or self.isStreaming(): + if not self.isOperational(): return if not self.isBusy() or self._currentFile is None: # we aren't even printing, nothing to cancel... return + if self.isStreaming(): + # we are streaming, we handle cancelling that differently... + self.cancelFileTransfer() + return + def _on_M400_sent(): # we don't call on_print_job_cancelled on our callback here # because we do this only after our M114 has been answered @@ -1964,20 +2002,7 @@ def _getNext(self): line = self._currentFile.getNext() if line is None: if self.isStreaming(): - self._sendCommand("M29") - - remote = self._currentFile.getRemoteFilename() - payload = { - "local": self._currentFile.getLocalFilename(), - "remote": remote, - "time": self.getPrintTime() - } - - self._currentFile = None - self._changeState(self.STATE_OPERATIONAL) - self._callback.on_comm_file_transfer_done(remote) - eventManager().fire(Events.TRANSFER_DONE, payload) - self.refreshSdFiles() + self._finishFileTransfer() else: self._callback.on_comm_print_job_done() self._changeState(self.STATE_OPERATIONAL) @@ -2726,6 +2751,9 @@ def on_comm_file_transfer_started(self, filename, filesize): def on_comm_file_transfer_done(self, filename): pass + def on_comm_file_transfer_failed(self, filename): + pass + def on_comm_force_disconnect(self): pass From 7073c1244469314e738147d055edfe45db66b052 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Fri, 28 Jul 2017 13:34:10 +0200 Subject: [PATCH 003/333] Virtual printer: Support debug commands while streaming --- src/octoprint/plugins/virtual_printer/virtual.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/octoprint/plugins/virtual_printer/virtual.py b/src/octoprint/plugins/virtual_printer/virtual.py index a87e16a075..bf08fa03e9 100644 --- a/src/octoprint/plugins/virtual_printer/virtual.py +++ b/src/octoprint/plugins/virtual_printer/virtual.py @@ -259,6 +259,13 @@ def _processIncoming(self): data += "\n" + if data.startswith("!!DEBUG:") or data.strip() == "!!DEBUG": + debug_command = "" + if data.startswith("!!DEBUG:"): + debug_command = data[len("!!DEBUG:"):].strip() + self._debugTrigger(debug_command) + continue + # shortcut for writing to SD if self._writingToSdHandle is not None and not "M29" in data: self._writingToSdHandle.write(data) @@ -269,12 +276,6 @@ def _processIncoming(self): from octoprint._version import get_versions self._send("OctoPrint VirtualPrinter v" + get_versions()["version"]) continue - elif data.startswith("!!DEBUG:") or data.strip() == "!!DEBUG": - debug_command = "" - if data.startswith("!!DEBUG:"): - debug_command = data[len("!!DEBUG:"):].strip() - self._debugTrigger(debug_command) - continue # if we are sending oks before command output, send it now if len(data.strip()) > 0 and self._okBeforeCommandOutput: From 4d27de032d4f749a6c942bfd6d8fca0207df8827 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Fri, 28 Jul 2017 13:35:00 +0200 Subject: [PATCH 004/333] Improved handling of external reset while operational * Display message to user * Stop print/transfer (lost state) --- src/octoprint/events.py | 1 + src/octoprint/static/js/app/dataupdater.js | 8 +++++++- src/octoprint/util/comm.py | 21 ++++++++++++++++----- 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/octoprint/events.py b/src/octoprint/events.py index 03a7daf228..f8c355586a 100644 --- a/src/octoprint/events.py +++ b/src/octoprint/events.py @@ -40,6 +40,7 @@ class Events(object): # State changes PRINTER_STATE_CHANGED = "PrinterStateChanged" + PRINTER_RESET = "PrinterReset" # connect/disconnect by client CLIENT_OPENED = "ClientOpened" diff --git a/src/octoprint/static/js/app/dataupdater.js b/src/octoprint/static/js/app/dataupdater.js index 1083130326..06a15c1227 100644 --- a/src/octoprint/static/js/app/dataupdater.js +++ b/src/octoprint/static/js/app/dataupdater.js @@ -238,11 +238,17 @@ function DataUpdater(allViewModels, connectCallback, disconnectCallback) { if (payload.error && payload.error.indexOf("autodetect") == -1) { // ignore "failed to autodetect" new PNotify({ title: gettext("Unhandled communication error"), - text: _.sprintf(gettext("The was an unhandled error while talking to the printer. Due to that OctoPrint disconnected. Error: %(error)s"), payload), + text: _.sprintf(gettext("There was an unhandled error while talking to the printer. Due to that OctoPrint disconnected. Error: %(error)s"), payload), type: "error", hide: false }); } + } else if (type == "PrinterReset") { + new PNotify({ + title: gettext("Printer reset detected"), + text: gettext("It looks like the printer was reset while a connection was active. If this was intentional you may safely ignore this message. Otherwise you should investigate why your printer reset itself, since this will interrupt prints and also file transfers to your printer's SD."), + hide: false + }); } var legacyEventHandlers = { diff --git a/src/octoprint/util/comm.py b/src/octoprint/util/comm.py index 1cadcd344b..cdad51302b 100644 --- a/src/octoprint/util/comm.py +++ b/src/octoprint/util/comm.py @@ -1562,11 +1562,22 @@ def convert_line(line): self.STATE_PAUSED, self.STATE_TRANSFERING_FILE): if line == "start": # exact match, to be on the safe side - message = "Printer sent 'start' while already operational. External reset? " \ - "Resetting line numbers to be on the safe side" - self._log(message) - self._logger.warn(message) - self.resetLineNumbers() + if self._state in (self.STATE_OPERATIONAL,): + message = "Printer sent 'start' while already operational. External reset? " \ + "Resetting line numbers to be on the safe side" + self._log(message) + self._logger.warn(message) + self.resetLineNumbers() + + else: + verb = "streaming to SD" if self.isStreaming() else "printing" + message = "Printer sent 'start' while {}. External reset? " \ + "Aborting job since printer lost state.".format(verb) + self._log(message) + self._logger.warn(message) + self.cancelPrint() + + eventManager().fire(Events.PRINTER_RESET) except: self._logger.exception("Something crashed inside the serial connection loop, please report this in OctoPrint's bug tracker:") From 69dec6e83fccdded026c33c35a35aa1c5285ae00 Mon Sep 17 00:00:00 2001 From: Daniele Forsi Date: Sat, 29 Jul 2017 16:12:08 +0200 Subject: [PATCH 005/333] Fix typos in strings and comments Those typos were found with codespell which is available from https://github.com/lucasdemarchi/codespell.git --- docs/events/index.rst | 2 +- docs/jsclientlib/base.rst | 2 +- docs/jsclientlib/util.rst | 2 +- src/octoprint/printer/standard.py | 2 +- src/octoprint/static/js/app/main.js | 10 +++++----- src/octoprint/static/js/app/viewmodels/slicing.js | 2 +- src/octoprint/static/js/app/viewmodels/system.js | 2 +- src/octoprint/templates/tabs/terminal.jinja2 | 2 +- src/octoprint/util/__init__.py | 2 +- src/octoprint/util/comm.py | 4 ++-- src/octoprint_client/__init__.py | 2 +- 11 files changed, 16 insertions(+), 16 deletions(-) diff --git a/docs/events/index.rst b/docs/events/index.rst index 225331f5a8..4e675c8f8f 100644 --- a/docs/events/index.rst +++ b/docs/events/index.rst @@ -237,7 +237,7 @@ UpdatedFiles .. deprecated:: 1.2.0 - The ``gcode`` modification type has been superceeded by ``printables``. It is currently still available for + The ``gcode`` modification type has been superseded by ``printables``. It is currently still available for reasons of backwards compatibility and will also be sent on modification of ``printables``. It will however be removed with 1.4.0. diff --git a/docs/jsclientlib/base.rst b/docs/jsclientlib/base.rst index d393f27e2b..618299fd12 100644 --- a/docs/jsclientlib/base.rst +++ b/docs/jsclientlib/base.rst @@ -119,7 +119,7 @@ :param string method: The HTTP method to use for the request (optional) :param string url: The URL to perform the request against (optional) :param object data: The data to send in the request body (optional) - :param object opts: Additonal options to use for the request (optional) + :param object opts: Additional options to use for the request (optional) :returns Promise: A `jQuery Promise `_ for the request's response .. js:function:: OctoPrintClient.get(url, opts) diff --git a/docs/jsclientlib/util.rst b/docs/jsclientlib/util.rst index 6168e1af5a..74b10cbea6 100644 --- a/docs/jsclientlib/util.rst +++ b/docs/jsclientlib/util.rst @@ -21,7 +21,7 @@ .. js:function:: OctoPrintClient.util.testPath(path, additional, opts) - Test the provided ``path`` for existance. More test criteria supported by the :ref:`path test command ` + Test the provided ``path`` for existence. More test criteria supported by the :ref:`path test command ` can be provided via the ``additional`` object. **Example 1** diff --git a/src/octoprint/printer/standard.py b/src/octoprint/printer/standard.py index 873dfec2f8..184bb564e8 100644 --- a/src/octoprint/printer/standard.py +++ b/src/octoprint/printer/standard.py @@ -958,7 +958,7 @@ def _sendInitialStateUpdate(self, callback): }) callback.on_printer_send_initial_data(data) except: - self._logger.exception("Error while trying to send inital state update") + self._logger.exception("Error while trying to send initial state update") def _getStateFlags(self): return { diff --git a/src/octoprint/static/js/app/main.js b/src/octoprint/static/js/app/main.js index 5960bc7b91..be684ab63e 100644 --- a/src/octoprint/static/js/app/main.js +++ b/src/octoprint/static/js/app/main.js @@ -125,7 +125,7 @@ $(function() { return exports; })(); - log.debug("Browser enviroment:", OctoPrint.coreui.browser); + log.debug("Browser environment:", OctoPrint.coreui.browser); //~~ AJAX setup @@ -180,7 +180,7 @@ $(function() { gettext("Printing"), gettext("Paused"), gettext("Closed"), - gettext("Transfering file to SD") + gettext("Transferring file to SD") ]; //~~ Initialize PNotify @@ -340,7 +340,7 @@ $(function() { continue; } - // we could resolve the depdendencies and the view model is not defined yet => add it, it's now fully processed + // we could resolve the dependencies and the view model is not defined yet => add it, it's now fully processed var viewModelBindTargets = viewModel.elements; if (additionalBindings.hasOwnProperty(viewModel.name)) { @@ -672,7 +672,7 @@ $(function() { * onServerConnect below takes care of the passive login. Only once that's completed it tells * our DataUpdater that it's ok to trigger any callbacks in view models. On the initial * server connect (during first initialization) we also trigger the settings fetch and - * binding proceedure once that's done, but only then. + * binding procedure once that's done, but only then. * * Or, as a fancy diagram: https://gist.githubusercontent.com/foosel/0cdc3a03cf5311804271f12e87293c0c/raw/abc84fdc3b13030d70961539d9c132ae39c32085/octoprint_web_startup.txt */ @@ -686,7 +686,7 @@ $(function() { // passive login request. // // This is to ensure that we have no concurrent requests triggered by socket events - // overriding each other's session during app intialization + // overriding each other's session during app initialization dataUpdater.initialized(); }); }; diff --git a/src/octoprint/static/js/app/viewmodels/slicing.js b/src/octoprint/static/js/app/viewmodels/slicing.js index d2d47098a2..a02a6a5eba 100644 --- a/src/octoprint/static/js/app/viewmodels/slicing.js +++ b/src/octoprint/static/js/app/viewmodels/slicing.js @@ -15,7 +15,7 @@ $(function() { self.defaultProfile = undefined; self.destinationFilename = ko.observable(); - self.gcodeFilename = self.destinationFilename; // TODO: for backwards compatiblity, mark deprecated ASAP + self.gcodeFilename = self.destinationFilename; // TODO: for backwards compatibility, mark deprecated ASAP self.title = ko.observable(); self.slicer = ko.observable(); diff --git a/src/octoprint/static/js/app/viewmodels/system.js b/src/octoprint/static/js/app/viewmodels/system.js index 6b3002c0f0..7637c04ac7 100644 --- a/src/octoprint/static/js/app/viewmodels/system.js +++ b/src/octoprint/static/js/app/viewmodels/system.js @@ -51,7 +51,7 @@ $(function() { if (commandSpec.async) { text = gettext("The command \"%(command)s\" executed successfully"); } else { - text = gettext("The command \"%(command)s\" was triggered asychronously"); + text = gettext("The command \"%(command)s\" was triggered asynchronously"); } new PNotify({ diff --git a/src/octoprint/templates/tabs/terminal.jinja2 b/src/octoprint/templates/tabs/terminal.jinja2 index d86b18f84c..ea351e5c29 100644 --- a/src/octoprint/templates/tabs/terminal.jinja2 +++ b/src/octoprint/templates/tabs/terminal.jinja2 @@ -27,7 +27,7 @@

- {{ _("If acknowledgements (\"ok\"s) sent by the firmware get lost due to issues with the serial communication to your printer, OctoPrint's communication with it can become stuck. If that happens, this can help. Please be advised that such occurences hint at general communication issues with your printer which will probably negatively influence your printing results and which you should therefore try to resolve!") }} + {{ _("If acknowledgements (\"ok\"s) sent by the firmware get lost due to issues with the serial communication to your printer, OctoPrint's communication with it can become stuck. If that happens, this can help. Please be advised that such occurrences hint at general communication issues with your printer which will probably negatively influence your printing results and which you should therefore try to resolve!") }}

diff --git a/src/octoprint/util/__init__.py b/src/octoprint/util/__init__.py index 950946cf83..862347c705 100644 --- a/src/octoprint/util/__init__.py +++ b/src/octoprint/util/__init__.py @@ -2,7 +2,7 @@ from __future__ import absolute_import """ -This module bundles commonly used utility methods or helper classes that are used in multiple places withing +This module bundles commonly used utility methods or helper classes that are used in multiple places within OctoPrint's source code. """ from __future__ import absolute_import, division, print_function diff --git a/src/octoprint/util/comm.py b/src/octoprint/util/comm.py index cdad51302b..b1782f6805 100644 --- a/src/octoprint/util/comm.py +++ b/src/octoprint/util/comm.py @@ -578,7 +578,7 @@ def getStateString(self, state=None): if state == self.STATE_CLOSED_WITH_ERROR: return "Offline: %s" % (self.getErrorString()) if state == self.STATE_TRANSFERING_FILE: - return "Transfering file to SD" + return "Transferring file to SD" return "Unknown State (%d)" % (self._state) def getErrorString(self): @@ -1953,7 +1953,7 @@ def _handleErrors(self, line): #Also skip errors with the SD card pass elif 'unknown command' in lower_line: - #Ignore unkown command errors, it could be a typo or some missing feature + #Ignore unknown command errors, it could be a typo or some missing feature pass elif not self.isError(): error_text = line[6:] if lower_line.startswith("error:") else line[2:] diff --git a/src/octoprint_client/__init__.py b/src/octoprint_client/__init__.py index 33afec8626..49adb56188 100644 --- a/src/octoprint_client/__init__.py +++ b/src/octoprint_client/__init__.py @@ -164,7 +164,7 @@ def reconnect(self, timeout=None, disconnect=True): If no timeout is provided, the method will block until the connection could be re-established. - If disconnect is set to ``True`` will disconnect the socket explictly + If disconnect is set to ``True`` will disconnect the socket explicitly first if it is currently connected. Arguments: From edb4d0fe04e874d74c661bc230f4b674e4fe6033 Mon Sep 17 00:00:00 2001 From: Daniele Forsi Date: Sun, 30 Jul 2017 13:23:31 +0200 Subject: [PATCH 006/333] Fix warnings about duplicated names in rst files Replacing a single underscore (which creates a named target) with a double underscore creates an anonymous target. Fixes: /home/daniele/OctoPrint/docs/bundledplugins/cura.rst:4: WARNING: Duplicate explicit target name: "here". /home/daniele/OctoPrint/docs/plugins/hooks.rst:370: WARNING: Invalid caption: /home/daniele/OctoPrint/docs/plugins/hooks.rst:4: (INFO/1) Duplicate explicit target name: "custom_action_command.py". --- docs/bundledplugins/cura.rst | 4 ++-- docs/plugins/hooks.rst | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/bundledplugins/cura.rst b/docs/bundledplugins/cura.rst index 30370e5bee..aac96397f2 100644 --- a/docs/bundledplugins/cura.rst +++ b/docs/bundledplugins/cura.rst @@ -45,7 +45,7 @@ Installing CuraEngine You'll need a build of ``legacy`` branch of `CuraEngine `_ in order to be able to use the Cura OctoPrint plugin. You can find the ``legacy`` branch -`here `_. +`here `__. If you previously used the `old variant of the Cura integration `_, you probably still have a fully functional binary lying around in the @@ -60,7 +60,7 @@ Compiling for Raspbian .. note:: A binary of CuraEngine 15.04.06 precompiled on Raspbian Jessie Lite 2016-03-18 is available - `here `_. Don't forget to make it + `here `__. Don't forget to make it executable after copying it to your preferred destination on your Pi (suggestion: ``/usr/local/bin``) with ``chmod +x cura_engine``. Use at your own risk. diff --git a/docs/plugins/hooks.rst b/docs/plugins/hooks.rst index e1009b4153..5e5b9c8f0e 100644 --- a/docs/plugins/hooks.rst +++ b/docs/plugins/hooks.rst @@ -79,7 +79,7 @@ property instead, manually instantiate your implementation instance and then add .. onlineinclude:: https://raw.githubusercontent.com/OctoPrint/Plugin-Examples/master/custom_action_command.py :linenos: :tab-width: 4 - :caption: `custom_action_command.py `_ + :caption: `custom_action_command.py `__ :name: sec-plugin-concepts-hooks-example .. _sec-plugins-hooks-ordering: @@ -370,7 +370,7 @@ octoprint.comm.protocol.action .. onlineinclude:: https://raw.githubusercontent.com/OctoPrint/Plugin-Examples/master/custom_action_command.py :linenos: :tab-width: 4 - :caption: `custom_action_command.py `_ + :caption: `custom_action_command.py `__ :param object comm_instance: The :class:`~octoprint.util.comm.MachineCom` instance which triggered the hook. :param str line: The complete line as received from the printer, format ``// action:`` From 0c2169dd9c24f3534f83ded771f532e11a9b041e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Mon, 21 Aug 2017 11:29:43 +0200 Subject: [PATCH 007/333] Removed leftover Thanks @dforsi for the heads-up. --- .../templates/dialogs/settings/serialconnection.jinja2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/octoprint/templates/dialogs/settings/serialconnection.jinja2 b/src/octoprint/templates/dialogs/settings/serialconnection.jinja2 index 6860d50bd2..5f367894d3 100644 --- a/src/octoprint/templates/dialogs/settings/serialconnection.jinja2 +++ b/src/octoprint/templates/dialogs/settings/serialconnection.jinja2 @@ -105,7 +105,7 @@

From fbcbb3f5f792a4104fdc9b33991448d10a9b5b1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Mon, 21 Aug 2017 16:52:38 +0200 Subject: [PATCH 008/333] Virtual printer: Support configurable ambient temperature --- .../plugins/virtual_printer/virtual.py | 23 ++++++++++++------- src/octoprint/settings.py | 3 ++- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/octoprint/plugins/virtual_printer/virtual.py b/src/octoprint/plugins/virtual_printer/virtual.py index bf08fa03e9..8afd317712 100644 --- a/src/octoprint/plugins/virtual_printer/virtual.py +++ b/src/octoprint/plugins/virtual_printer/virtual.py @@ -28,6 +28,7 @@ class VirtualPrinter(object): custom_action_regex = re.compile("action_custom ([a-zA-Z0-9_]+)(\s+.*)?") prepare_ok_regex = re.compile("prepare_ok (.*)") send_regex = re.compile("send (.*)") + set_ambient_regex = re.compile("set_ambient ([-+]?[0-9]*\.?[0-9]+)") def __init__(self, seriallog_handler=None, read_timeout=5.0, write_timeout=10.0): import logging @@ -71,11 +72,13 @@ def __init__(self, seriallog_handler=None, read_timeout=5.0, write_timeout=10.0) self.sharedNozzle = settings().getBoolean(["devel", "virtualPrinter", "sharedNozzle"]) self.temperatureCount = (1 if self.sharedNozzle else self.extruderCount) - self.temp = [0.0] * self.temperatureCount + self._ambient_temperature = settings().getFloat(["devel", "virtualPrinter", "ambientTemperature"]) + + self.temp = [self._ambient_temperature] * self.temperatureCount self.targetTemp = [0.0] * self.temperatureCount + self.bedTemp = self._ambient_temperature + self.bedTargetTemp = 0.0 self.lastTempAt = time.time() - self.bedTemp = 1.0 - self.bedTargetTemp = 1.0 self._relative = True self._lastX = 0.0 @@ -621,6 +624,7 @@ def _debugTrigger(self, data): custom_action_match = VirtualPrinter.custom_action_regex.match(data) prepare_ok_match = VirtualPrinter.prepare_ok_regex.match(data) send_match = VirtualPrinter.send_regex.match(data) + set_ambient_match = VirtualPrinter.set_ambient_regex.match(data) if sleep_match is not None: interval = int(sleep_match.group(1)) @@ -646,6 +650,9 @@ def _debugTrigger(self, data): self._prepared_oks.append(ok) elif send_match is not None: self._send(send_match.group(1)) + elif set_ambient_match is not None: + self._ambient_temperature = float(set_ambient_match.group(1)) + self._send("// set ambient temperature to {}".format(self._ambient_temperature)) except: pass @@ -720,7 +727,7 @@ def _generateTemperatureOutput(self): if settings().getBoolean(["devel", "virtualPrinter", "includeCurrentToolInTemps"]): if includeTarget: - output = "T:%.2f /%.2f %s" % (self.temp[self.currentExtruder], self.targetTemp[self.currentExtruder] + 1, allTempsString) + output = "T:%.2f /%.2f %s" % (self.temp[self.currentExtruder], self.targetTemp[self.currentExtruder], allTempsString) else: output = "T:%.2f %s" % (self.temp[self.currentExtruder], allTempsString) else: @@ -1014,15 +1021,15 @@ def _simulateTemps(self, delta=1): self.temp[i] += math.copysign(timeDiff * 10, self.targetTemp[i] - self.temp[i]) if math.copysign(1, self.targetTemp[i] - oldVal) != math.copysign(1, self.targetTemp[i] - self.temp[i]): self.temp[i] = self.targetTemp[i] - if self.temp[i] < 0: - self.temp[i] = 0 + if self.temp[i] < self._ambient_temperature: + self.temp[i] = self._ambient_temperature if abs(self.bedTemp - self.bedTargetTemp) > delta: oldVal = self.bedTemp self.bedTemp += math.copysign(timeDiff * 10, self.bedTargetTemp - self.bedTemp) if math.copysign(1, self.bedTargetTemp - oldVal) != math.copysign(1, self.bedTargetTemp - self.bedTemp): self.bedTemp = self.bedTargetTemp - if self.bedTemp < 0: - self.bedTemp = 0 + if self.bedTemp < self._ambient_temperature: + self.bedTemp = self._ambient_temperature def _processBuffer(self): while self.buffered is not None: diff --git a/src/octoprint/settings.py b/src/octoprint/settings.py index 5e9615213d..d28d352bc5 100644 --- a/src/octoprint/settings.py +++ b/src/octoprint/settings.py @@ -380,7 +380,8 @@ def settings(init=False, basedir=None, configfile=None): "m115ReportCapabilities": False, "capabilities": { "AUTOREPORT_TEMP": True - } + }, + "ambientTemperature": 21.3 } } } From d7298cee061e2c852d166e2f7847d638826f28c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Tue, 22 Aug 2017 11:21:04 +0200 Subject: [PATCH 009/333] Virtual printer: Less code duplication in temp simulation --- .../plugins/virtual_printer/virtual.py | 38 +++++++++++-------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/src/octoprint/plugins/virtual_printer/virtual.py b/src/octoprint/plugins/virtual_printer/virtual.py index 8afd317712..2434393e91 100644 --- a/src/octoprint/plugins/virtual_printer/virtual.py +++ b/src/octoprint/plugins/virtual_printer/virtual.py @@ -1008,28 +1008,34 @@ def _deleteSdFile(self, filename): if os.path.exists(f) and os.path.isfile(f): os.remove(f) - def _simulateTemps(self, delta=1): + def _simulateTemps(self, delta=0.5): timeDiff = self.lastTempAt - time.time() self.lastTempAt = time.time() + + def simulate(actual, target, ambient): + if target > 0 and abs(actual - target) > delta: + goal = target + factor = 10 + elif not target and abs(actual - ambient) > delta: + goal = ambient + factor = 2 + else: + return actual + + old = actual + actual += math.copysign(timeDiff * factor, goal - actual) + + if math.copysign(1, goal - old) != math.copysign(1, goal - actual): + actual = goal + + return actual + for i in range(len(self.temp)): if i in self.pinnedExtruders: self.temp[i] = self.pinnedExtruders[i] continue - - if abs(self.temp[i] - self.targetTemp[i]) > delta: - oldVal = self.temp[i] - self.temp[i] += math.copysign(timeDiff * 10, self.targetTemp[i] - self.temp[i]) - if math.copysign(1, self.targetTemp[i] - oldVal) != math.copysign(1, self.targetTemp[i] - self.temp[i]): - self.temp[i] = self.targetTemp[i] - if self.temp[i] < self._ambient_temperature: - self.temp[i] = self._ambient_temperature - if abs(self.bedTemp - self.bedTargetTemp) > delta: - oldVal = self.bedTemp - self.bedTemp += math.copysign(timeDiff * 10, self.bedTargetTemp - self.bedTemp) - if math.copysign(1, self.bedTargetTemp - oldVal) != math.copysign(1, self.bedTargetTemp - self.bedTemp): - self.bedTemp = self.bedTargetTemp - if self.bedTemp < self._ambient_temperature: - self.bedTemp = self._ambient_temperature + self.temp[i] = simulate(self.temp[i], self.targetTemp[i], self._ambient_temperature) + self.bedTemp = simulate(self.bedTemp, self.bedTargetTemp, self._ambient_temperature) def _processBuffer(self): while self.buffered is not None: From bfe5bc179c726b6363167842897eebb739a76fe7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Tue, 22 Aug 2017 11:32:47 +0200 Subject: [PATCH 010/333] New hook octoprint.comm.protocol.temperatures.received Allows preprocessing/sanitizing temperatures as received from the printer. Workaround for printers that occasionally report garbage temperature data, e.g. #2050 --- docs/plugins/hooks.rst | 26 ++++++++++++++++++++++++++ src/octoprint/util/comm.py | 10 ++++++++++ 2 files changed, 36 insertions(+) diff --git a/docs/plugins/hooks.rst b/docs/plugins/hooks.rst index 5e5b9c8f0e..a53a47815e 100644 --- a/docs/plugins/hooks.rst +++ b/docs/plugins/hooks.rst @@ -555,6 +555,32 @@ octoprint.comm.protocol.scripts :return: A 2-tuple in the form ``(prefix, postfix)`` or None :rtype: tuple or None +.. _sec-plugins-hook-comm-protocol-temperatures-received: + +octoprint.comm.protocol.temperatures.received +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. py:function:: protocol_temperatures_received_hook(comm_instance, parsed_temperatures, *args, **kwargs) + + Get the parsed temperatures returned by the printer, allowing handlers to modify them prior to handing them off + to the system. Handlers are expected to either return ``parsed_temperatures`` as-is or a modified copy thereof. + + ``parsed_temperatures`` is a dictionary mapping from tool/bed identifier (``B``, ``T0``, ``T1``) to a 2-tuple of + actual and target temperature, e.g. ``{'B': (45.2, 50.0), 'T0': (178.9, 210.0), 'T1': (21.3, 0.0)}``. + + This hook can be useful in cases where a printer e.g. is prone to returning garbage data from time to time, allowing + additional sanity checking to be applied and invalid values to be filtered out. If a handler returns an empty + dictionary or ``None``, no further processing will take place. + + **Example** + + The following example shows how to filter out actual temperatures that are outside a sane range of 1°C to 300°C. + + .. onlineinclude:: https://raw.githubusercontent.com/OctoPrint/Plugin-Examples/master/sanitize_temperatures.py + :linenos: + :tab-width: 4 + :caption: `sanitize_temperatures.py `_ + .. _sec-plugins-hook-comm-transport-serial-factory: octoprint.comm.transport.serial.factory diff --git a/src/octoprint/util/comm.py b/src/octoprint/util/comm.py index cdad51302b..ecd0a74e04 100644 --- a/src/octoprint/util/comm.py +++ b/src/octoprint/util/comm.py @@ -441,6 +441,8 @@ def __init__(self, port = None, baudrate=None, callbackObject=None, printerProfi self._gcodescript_hooks = self._pluginManager.get_hooks("octoprint.comm.protocol.scripts") self._serial_factory_hooks = self._pluginManager.get_hooks("octoprint.comm.transport.serial.factory") + self._temperature_hooks = self._pluginManager.get_hooks("octoprint.comm.protocol.temperatures.received") + # SD status data self._sdEnabled = settings().getBoolean(["feature", "sdSupport"]) self._sdAvailable = False @@ -1129,6 +1131,14 @@ def _processTemperatures(self, line): current_tool = self._currentTool if self._currentTool is not None else 0 maxToolNum, parsedTemps = parse_temperature_line(line, current_tool) + for name, hook in self._temperature_hooks.items(): + try: + parsedTemps = hook(self, parsedTemps) + if parsedTemps is None or not parsedTemps: + return + except: + self._logger.exception("Error while processing temperatures in {}, skipping".format(name)) + if "T0" in parsedTemps.keys(): for n in range(maxToolNum + 1): tool = "T%d" % n From 76c2113ad415ddbc2d7baabbd62c5d17edd1a1b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Tue, 22 Aug 2017 18:05:00 +0200 Subject: [PATCH 011/333] Fix block-level input-append/input-prepend We still had double borders going on for included add-ons. --- src/octoprint/static/css/octoprint.css | 2 +- src/octoprint/static/less/octoprint.less | 12 ++++-------- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/src/octoprint/static/css/octoprint.css b/src/octoprint/static/css/octoprint.css index f9f749d98a..bcbfc921fc 100644 --- a/src/octoprint/static/css/octoprint.css +++ b/src/octoprint/static/css/octoprint.css @@ -1 +1 @@ -.btn{display:inline-block;*display:inline;*zoom:1;padding:4px 12px;margin-bottom:0;font-size:14px;line-height:20px;text-align:center;vertical-align:middle;cursor:pointer;color:#333;text-shadow:0 1px 1px rgba(255,255,255,.75);background-color:#f5f5f5;background-image:-moz-linear-gradient(top,#fff,#e6e6e6);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#e6e6e6));background-image:-webkit-linear-gradient(top,#fff,#e6e6e6);background-image:-o-linear-gradient(top,#fff,#e6e6e6);background-image:linear-gradient(to bottom,#fff,#e6e6e6);background-repeat:repeat-x;*background-color:#e6e6e6;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);border:1px solid #ccc;*border:0;border-bottom-color:#b3b3b3;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;*margin-left:.3em;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05)}.btn.active,.btn.disabled,.btn:active,.btn:focus,.btn:hover,.btn[disabled]{color:#333;background-color:#e6e6e6;*background-color:#d9d9d9}.btn.active,.btn:active{background-color:#ccc \9}.btn:first-child{*margin-left:0}.btn:focus,.btn:hover{color:#333;text-decoration:none;background-position:0 -15px;-webkit-transition:background-position .1s linear;-moz-transition:background-position .1s linear;-o-transition:background-position .1s linear;transition:background-position .1s linear}.btn:focus{outline:#333 dotted thin;outline:-webkit-focus-ring-color auto 5px;outline-offset:-2px}.btn.active,.btn:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,.15),0 1px 2px rgba(0,0,0,.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,.15),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 2px 4px rgba(0,0,0,.15),0 1px 2px rgba(0,0,0,.05)}.btn.disabled,.btn[disabled]{cursor:default;background-image:none;opacity:.65;filter:alpha(opacity=65);-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.btn-large{padding:11px 19px;font-size:17.5px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.btn-large [class*=" icon-"],.btn-large [class^=icon-]{margin-top:4px}.btn-small{padding:2px 10px;font-size:11.9px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.btn-small [class*=" icon-"],.btn-small [class^=icon-]{margin-top:0}.btn-mini [class*=" icon-"],.btn-mini [class^=icon-]{margin-top:-1px}.btn-mini{padding:0 6px;font-size:10.5px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.btn-block{display:block;width:100%;padding-left:0;padding-right:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.btn-block+.btn-block{margin-top:5px}input[type=submit].btn-block,input[type=reset].btn-block,input[type=button].btn-block{width:100%}.btn-primary{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,.25);background-color:#006dcc;background-image:-moz-linear-gradient(top,#08c,#04c);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#04c));background-image:-webkit-linear-gradient(top,#08c,#04c);background-image:-o-linear-gradient(top,#08c,#04c);background-image:linear-gradient(to bottom,#08c,#04c);background-repeat:repeat-x;border-color:#04c #04c #002a80;border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);*background-color:#04c;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-primary.active,.btn-primary.disabled,.btn-primary:active,.btn-primary:focus,.btn-primary:hover,.btn-primary[disabled]{color:#fff;background-color:#04c;*background-color:#003bb3}.btn-primary.active,.btn-primary:active{background-color:#039 \9}.btn-warning{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,.25);background-color:#faa732;background-image:-moz-linear-gradient(top,#fbb450,#f89406);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fbb450),to(#f89406));background-image:-webkit-linear-gradient(top,#fbb450,#f89406);background-image:-o-linear-gradient(top,#fbb450,#f89406);background-image:linear-gradient(to bottom,#fbb450,#f89406);background-repeat:repeat-x;border-color:#f89406 #f89406 #ad6704;border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);*background-color:#f89406;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-warning.active,.btn-warning.disabled,.btn-warning:active,.btn-warning:focus,.btn-warning:hover,.btn-warning[disabled]{color:#fff;background-color:#f89406;*background-color:#df8505}.btn-warning.active,.btn-warning:active{background-color:#c67605 \9}.btn-danger{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,.25);background-color:#da4f49;background-image:-moz-linear-gradient(top,#ee5f5b,#bd362f);background-image:-webkit-gradient(linear,0 0,0 100%,from(#ee5f5b),to(#bd362f));background-image:-webkit-linear-gradient(top,#ee5f5b,#bd362f);background-image:-o-linear-gradient(top,#ee5f5b,#bd362f);background-image:linear-gradient(to bottom,#ee5f5b,#bd362f);background-repeat:repeat-x;border-color:#bd362f #bd362f #802420;border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);*background-color:#bd362f;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-danger.active,.btn-danger.disabled,.btn-danger:active,.btn-danger:focus,.btn-danger:hover,.btn-danger[disabled]{color:#fff;background-color:#bd362f;*background-color:#a9302a}.btn-danger.active,.btn-danger:active{background-color:#942a25 \9}.btn-success{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,.25);background-color:#5bb75b;background-image:-moz-linear-gradient(top,#62c462,#51a351);background-image:-webkit-gradient(linear,0 0,0 100%,from(#62c462),to(#51a351));background-image:-webkit-linear-gradient(top,#62c462,#51a351);background-image:-o-linear-gradient(top,#62c462,#51a351);background-image:linear-gradient(to bottom,#62c462,#51a351);background-repeat:repeat-x;border-color:#51a351 #51a351 #387038;border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);*background-color:#51a351;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-success.active,.btn-success.disabled,.btn-success:active,.btn-success:focus,.btn-success:hover,.btn-success[disabled]{color:#fff;background-color:#51a351;*background-color:#499249}.btn-success.active,.btn-success:active{background-color:#408140 \9}.btn-info{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,.25);background-color:#49afcd;background-image:-moz-linear-gradient(top,#5bc0de,#2f96b4);background-image:-webkit-gradient(linear,0 0,0 100%,from(#5bc0de),to(#2f96b4));background-image:-webkit-linear-gradient(top,#5bc0de,#2f96b4);background-image:-o-linear-gradient(top,#5bc0de,#2f96b4);background-image:linear-gradient(to bottom,#5bc0de,#2f96b4);background-repeat:repeat-x;border-color:#2f96b4 #2f96b4 #1f6377;border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);*background-color:#2f96b4;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-info.active,.btn-info.disabled,.btn-info:active,.btn-info:focus,.btn-info:hover,.btn-info[disabled]{color:#fff;background-color:#2f96b4;*background-color:#2a85a0}.btn-info.active,.btn-info:active{background-color:#24748c \9}.btn-inverse{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,.25);background-color:#363636;background-image:-moz-linear-gradient(top,#444,#222);background-image:-webkit-gradient(linear,0 0,0 100%,from(#444),to(#222));background-image:-webkit-linear-gradient(top,#444,#222);background-image:-o-linear-gradient(top,#444,#222);background-image:linear-gradient(to bottom,#444,#222);background-repeat:repeat-x;border-color:#222 #222 #000;border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);*background-color:#222;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-inverse.active,.btn-inverse.disabled,.btn-inverse:active,.btn-inverse:focus,.btn-inverse:hover,.btn-inverse[disabled]{color:#fff;background-color:#222;*background-color:#151515}.btn-inverse.active,.btn-inverse:active{background-color:#080808 \9}button.btn,input[type=submit].btn{*padding-top:3px;*padding-bottom:3px}button.btn::-moz-focus-inner,input[type=submit].btn::-moz-focus-inner{padding:0;border:0}button.btn.btn-large,input[type=submit].btn.btn-large{*padding-top:7px;*padding-bottom:7px}button.btn.btn-small,input[type=submit].btn.btn-small{*padding-top:3px;*padding-bottom:3px}button.btn.btn-mini,input[type=submit].btn.btn-mini{*padding-top:1px;*padding-bottom:1px}.btn-link,.btn-link:active,.btn-link[disabled]{background-color:transparent;background-image:none;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.btn-link{border-color:transparent;cursor:pointer;color:#08c;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.btn-link:focus,.btn-link:hover{color:#005580;text-decoration:underline;background-color:transparent}.btn-link[disabled]:focus,.btn-link[disabled]:hover{color:#333;text-decoration:none}.clearfix{*zoom:1}.clearfix:after,.clearfix:before{display:table;content:"";line-height:0}.clearfix:after{clear:both}.hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.input-block-level{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.nowrap{white-space:nowrap}.actioncol{text-align:center;white-space:nowrap}.actioncol a{text-decoration:none;color:#000}.actioncol a.disabled{color:#ccc;cursor:default}#navbar .navbar-inner{background-color:#ebebeb;background-image:-moz-linear-gradient(top,#fff,#ccc);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#ccc));background-image:-webkit-linear-gradient(top,#fff,#ccc);background-image:-o-linear-gradient(top,#fff,#ccc);background-image:linear-gradient(to bottom,#fff,#ccc);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffcccccc', GradientType=0)}#navbar .navbar-inner .brand,#navbar .navbar-inner .nav>li>a{text-shadow:0 1px 0 #ccc;color:#333}#navbar .navbar-inner .brand .caret,#navbar .navbar-inner .nav>li>a .caret{border-bottom-color:#939393;border-top-color:#939393}#navbar .navbar-inner .brand:focus .caret,#navbar .navbar-inner .brand:hover .caret,#navbar .navbar-inner .nav>li>a:focus .caret,#navbar .navbar-inner .nav>li>a:hover .caret{border-bottom-color:#636363;border-top-color:#636363}#navbar .navbar-inner .brand span{background-image:url(../img/tentacle-20x20.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner .brand span{background-image:url(../img/tentacle-20x20@2x.png)}}#navbar .navbar-inner .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner .nav>li.dropdown.open>.dropdown-toggle{background-color:#e0e0e0;background-image:-moz-linear-gradient(top,#ccc,#fff);background-image:-webkit-gradient(linear,0 0,0 100%,from(#ccc),to(#fff));background-image:-webkit-linear-gradient(top,#ccc,#fff);background-image:-o-linear-gradient(top,#ccc,#fff);background-image:linear-gradient(to bottom,#ccc,#fff);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffcccccc', endColorstr='#ffffffff', GradientType=0)}#navbar .navbar-inner .nav>li>a:hover{background-color:#dedede;background-image:-moz-linear-gradient(top,#f2f2f2,#bfbfbf);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f2f2f2),to(#bfbfbf));background-image:-webkit-linear-gradient(top,#f2f2f2,#bfbfbf);background-image:-o-linear-gradient(top,#f2f2f2,#bfbfbf);background-image:linear-gradient(to bottom,#f2f2f2,#bfbfbf);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2f2f2', endColorstr='#ffbfbfbf', GradientType=0)}#navbar .navbar-inner.transparent{background-color:rgba(235,235,235,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(255,255,255,.6),rgba(204,204,204,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(255,255,255,.6)),to(rgba(204,204,204,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(255,255,255,.6),rgba(204,204,204,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(255,255,255,.6),rgba(204,204,204,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(255,255,255,.6),rgba(204,204,204,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99ffffff', endColorstr='#99cccccc', GradientType=0)}#navbar .navbar-inner.transparent .brand,#navbar .navbar-inner.transparent .nav>li>a{text-shadow:0 1px 0 #ccc;color:#333}#navbar .navbar-inner.transparent .brand .caret,#navbar .navbar-inner.transparent .nav>li>a .caret{border-bottom-color:#939393;border-top-color:#939393}#navbar .navbar-inner.transparent .brand:focus .caret,#navbar .navbar-inner.transparent .brand:hover .caret,#navbar .navbar-inner.transparent .nav>li>a:focus .caret,#navbar .navbar-inner.transparent .nav>li>a:hover .caret{border-bottom-color:#636363;border-top-color:#636363}#navbar .navbar-inner.transparent .brand span{background-image:url(../img/tentacle-20x20.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.transparent .brand span{background-image:url(../img/tentacle-20x20@2x.png)}}#navbar .navbar-inner.transparent .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.transparent .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.transparent .nav>li.dropdown.open>.dropdown-toggle{background-color:rgba(224,224,224,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(204,204,204,.6),rgba(255,255,255,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(204,204,204,.6)),to(rgba(255,255,255,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(204,204,204,.6),rgba(255,255,255,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(204,204,204,.6),rgba(255,255,255,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(204,204,204,.6),rgba(255,255,255,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99cccccc', endColorstr='#99ffffff', GradientType=0)}#navbar .navbar-inner.transparent .nav>li>a:hover{background-color:rgba(222,222,222,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(242,242,242,.6),rgba(191,191,191,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(242,242,242,.6)),to(rgba(191,191,191,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(242,242,242,.6),rgba(191,191,191,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(242,242,242,.6),rgba(191,191,191,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(242,242,242,.6),rgba(191,191,191,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99f2f2f2', endColorstr='#99bfbfbf', GradientType=0)}#navbar .navbar-inner.red{background-color:#bb645f;background-image:-moz-linear-gradient(top,#e28e8a,#802420);background-image:-webkit-gradient(linear,0 0,0 100%,from(#e28e8a),to(#802420));background-image:-webkit-linear-gradient(top,#e28e8a,#802420);background-image:-o-linear-gradient(top,#e28e8a,#802420);background-image:linear-gradient(to bottom,#e28e8a,#802420);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe28e8a', endColorstr='#ff802420', GradientType=0)}#navbar .navbar-inner.red .brand,#navbar .navbar-inner.red .nav>li>a{text-shadow:0 1px 0 #d86761;color:#f2f2f2}#navbar .navbar-inner.red .brand .caret,#navbar .navbar-inner.red .nav>li>a .caret{border-bottom-color:#d89491;border-top-color:#d89491}#navbar .navbar-inner.red .brand:focus .caret,#navbar .navbar-inner.red .brand:hover .caret,#navbar .navbar-inner.red .nav>li>a:focus .caret,#navbar .navbar-inner.red .nav>li>a:hover .caret{border-bottom-color:#e5c3c1;border-top-color:#e5c3c1}#navbar .navbar-inner.red .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.red .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.red .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.red .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.red .nav>li.dropdown.open>.dropdown-toggle{background-color:#a74f4a;background-image:-moz-linear-gradient(top,#802420,#e28e8a);background-image:-webkit-gradient(linear,0 0,0 100%,from(#802420),to(#e28e8a));background-image:-webkit-linear-gradient(top,#802420,#e28e8a);background-image:-o-linear-gradient(top,#802420,#e28e8a);background-image:linear-gradient(to bottom,#802420,#e28e8a);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff802420', endColorstr='#ffe28e8a', GradientType=0)}#navbar .navbar-inner.red .nav>li>a:hover{background-color:#af5651;background-image:-moz-linear-gradient(top,#dd7a75,#6b1f1b);background-image:-webkit-gradient(linear,0 0,0 100%,from(#dd7a75),to(#6b1f1b));background-image:-webkit-linear-gradient(top,#dd7a75,#6b1f1b);background-image:-o-linear-gradient(top,#dd7a75,#6b1f1b);background-image:linear-gradient(to bottom,#dd7a75,#6b1f1b);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdd7a75', endColorstr='#ff6b1f1b', GradientType=0)}#navbar .navbar-inner.red.transparent{background-color:rgba(187,100,95,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(226,142,138,.6),rgba(128,36,32,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(226,142,138,.6)),to(rgba(128,36,32,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(226,142,138,.6),rgba(128,36,32,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(226,142,138,.6),rgba(128,36,32,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(226,142,138,.6),rgba(128,36,32,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99e28e8a', endColorstr='#99802420', GradientType=0)}#navbar .navbar-inner.red.transparent .brand,#navbar .navbar-inner.red.transparent .nav>li>a{text-shadow:0 1px 0 #d86761;color:#f2f2f2}#navbar .navbar-inner.red.transparent .brand .caret,#navbar .navbar-inner.red.transparent .nav>li>a .caret{border-bottom-color:#d89491;border-top-color:#d89491}#navbar .navbar-inner.red.transparent .brand:focus .caret,#navbar .navbar-inner.red.transparent .brand:hover .caret,#navbar .navbar-inner.red.transparent .nav>li>a:focus .caret,#navbar .navbar-inner.red.transparent .nav>li>a:hover .caret{border-bottom-color:#e5c3c1;border-top-color:#e5c3c1}#navbar .navbar-inner.red.transparent .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.red.transparent .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.red.transparent .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.red.transparent .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.red.transparent .nav>li.dropdown.open>.dropdown-toggle{background-color:rgba(167,79,74,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(128,36,32,.6),rgba(226,142,138,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(128,36,32,.6)),to(rgba(226,142,138,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(128,36,32,.6),rgba(226,142,138,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(128,36,32,.6),rgba(226,142,138,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(128,36,32,.6),rgba(226,142,138,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99802420', endColorstr='#99e28e8a', GradientType=0)}#navbar .navbar-inner.red.transparent .nav>li>a:hover{background-color:rgba(175,86,81,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(221,122,117,.6),rgba(107,31,27,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(221,122,117,.6)),to(rgba(107,31,27,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(221,122,117,.6),rgba(107,31,27,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(221,122,117,.6),rgba(107,31,27,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(221,122,117,.6),rgba(107,31,27,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99dd7a75', endColorstr='#996b1f1b', GradientType=0)}#navbar .navbar-inner.orange{background-color:#e39665;background-image:-moz-linear-gradient(top,#f9c3a0,#c2530c);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f9c3a0),to(#c2530c));background-image:-webkit-linear-gradient(top,#f9c3a0,#c2530c);background-image:-o-linear-gradient(top,#f9c3a0,#c2530c);background-image:linear-gradient(to bottom,#f9c3a0,#c2530c);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff9c3a0', endColorstr='#ffc2530c', GradientType=0)}#navbar .navbar-inner.orange .brand,#navbar .navbar-inner.orange .nav>li>a{text-shadow:0 1px 0 #f6a570;color:#f2f2f2}#navbar .navbar-inner.orange .brand .caret,#navbar .navbar-inner.orange .nav>li>a .caret{border-bottom-color:#f2b58d;border-top-color:#f2b58d}#navbar .navbar-inner.orange .brand:focus .caret,#navbar .navbar-inner.orange .brand:hover .caret,#navbar .navbar-inner.orange .nav>li>a:focus .caret,#navbar .navbar-inner.orange .nav>li>a:hover .caret{border-bottom-color:#f2d3bf;border-top-color:#f2d3bf}#navbar .navbar-inner.orange .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.orange .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.orange .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.orange .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.orange .nav>li.dropdown.open>.dropdown-toggle{background-color:#d88047;background-image:-moz-linear-gradient(top,#c2530c,#f9c3a0);background-image:-webkit-gradient(linear,0 0,0 100%,from(#c2530c),to(#f9c3a0));background-image:-webkit-linear-gradient(top,#c2530c,#f9c3a0);background-image:-o-linear-gradient(top,#c2530c,#f9c3a0);background-image:linear-gradient(to bottom,#c2530c,#f9c3a0);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffc2530c', endColorstr='#fff9c3a0', GradientType=0)}#navbar .navbar-inner.orange .nav>li>a:hover{background-color:#d98956;background-image:-moz-linear-gradient(top,#f8b488,#aa490a);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f8b488),to(#aa490a));background-image:-webkit-linear-gradient(top,#f8b488,#aa490a);background-image:-o-linear-gradient(top,#f8b488,#aa490a);background-image:linear-gradient(to bottom,#f8b488,#aa490a);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff8b488', endColorstr='#ffaa490a', GradientType=0)}#navbar .navbar-inner.orange.transparent{background-color:rgba(227,150,101,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(249,195,160,.6),rgba(194,83,12,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(249,195,160,.6)),to(rgba(194,83,12,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(249,195,160,.6),rgba(194,83,12,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(249,195,160,.6),rgba(194,83,12,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(249,195,160,.6),rgba(194,83,12,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99f9c3a0', endColorstr='#99c2530c', GradientType=0)}#navbar .navbar-inner.orange.transparent .brand,#navbar .navbar-inner.orange.transparent .nav>li>a{text-shadow:0 1px 0 #f6a570;color:#f2f2f2}#navbar .navbar-inner.orange.transparent .brand .caret,#navbar .navbar-inner.orange.transparent .nav>li>a .caret{border-bottom-color:#f2b58d;border-top-color:#f2b58d}#navbar .navbar-inner.orange.transparent .brand:focus .caret,#navbar .navbar-inner.orange.transparent .brand:hover .caret,#navbar .navbar-inner.orange.transparent .nav>li>a:focus .caret,#navbar .navbar-inner.orange.transparent .nav>li>a:hover .caret{border-bottom-color:#f2d3bf;border-top-color:#f2d3bf}#navbar .navbar-inner.orange.transparent .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.orange.transparent .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.orange.transparent .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.orange.transparent .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.orange.transparent .nav>li.dropdown.open>.dropdown-toggle{background-color:rgba(216,128,71,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(194,83,12,.6),rgba(249,195,160,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(194,83,12,.6)),to(rgba(249,195,160,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(194,83,12,.6),rgba(249,195,160,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(194,83,12,.6),rgba(249,195,160,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(194,83,12,.6),rgba(249,195,160,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99c2530c', endColorstr='#99f9c3a0', GradientType=0)}#navbar .navbar-inner.orange.transparent .nav>li>a:hover{background-color:rgba(217,137,86,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(248,180,136,.6),rgba(170,73,10,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(248,180,136,.6)),to(rgba(170,73,10,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(248,180,136,.6),rgba(170,73,10,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(248,180,136,.6),rgba(170,73,10,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(248,180,136,.6),rgba(170,73,10,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99f8b488', endColorstr='#99aa490a', GradientType=0)}#navbar .navbar-inner.yellow{background-color:#e3d765;background-image:-moz-linear-gradient(top,#f9f0a0,#c2b00c);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f9f0a0),to(#c2b00c));background-image:-webkit-linear-gradient(top,#f9f0a0,#c2b00c);background-image:-o-linear-gradient(top,#f9f0a0,#c2b00c);background-image:linear-gradient(to bottom,#f9f0a0,#c2b00c);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff9f0a0', endColorstr='#ffc2b00c', GradientType=0)}#navbar .navbar-inner.yellow .brand,#navbar .navbar-inner.yellow .nav>li>a{text-shadow:0 1px 0 #c2b00c;color:#f2f2f2}#navbar .navbar-inner.yellow .brand .caret,#navbar .navbar-inner.yellow .nav>li>a .caret{border-bottom-color:#f2e88d;border-top-color:#f2e88d}#navbar .navbar-inner.yellow .brand:focus .caret,#navbar .navbar-inner.yellow .brand:hover .caret,#navbar .navbar-inner.yellow .nav>li>a:focus .caret,#navbar .navbar-inner.yellow .nav>li>a:hover .caret{border-bottom-color:#f2edbf;border-top-color:#f2edbf}#navbar .navbar-inner.yellow .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.yellow .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.yellow .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.yellow .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.yellow .nav>li.dropdown.open>.dropdown-toggle{background-color:#d8ca47;background-image:-moz-linear-gradient(top,#c2b00c,#f9f0a0);background-image:-webkit-gradient(linear,0 0,0 100%,from(#c2b00c),to(#f9f0a0));background-image:-webkit-linear-gradient(top,#c2b00c,#f9f0a0);background-image:-o-linear-gradient(top,#c2b00c,#f9f0a0);background-image:linear-gradient(to bottom,#c2b00c,#f9f0a0);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffc2b00c', endColorstr='#fff9f0a0', GradientType=0)}#navbar .navbar-inner.yellow .nav>li>a:hover{background-color:#d9cc56;background-image:-moz-linear-gradient(top,#f8ed88,#aa9a0a);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f8ed88),to(#aa9a0a));background-image:-webkit-linear-gradient(top,#f8ed88,#aa9a0a);background-image:-o-linear-gradient(top,#f8ed88,#aa9a0a);background-image:linear-gradient(to bottom,#f8ed88,#aa9a0a);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff8ed88', endColorstr='#ffaa9a0a', GradientType=0)}#navbar .navbar-inner.yellow.transparent{background-color:rgba(227,215,101,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(249,240,160,.6),rgba(194,176,12,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(249,240,160,.6)),to(rgba(194,176,12,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(249,240,160,.6),rgba(194,176,12,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(249,240,160,.6),rgba(194,176,12,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(249,240,160,.6),rgba(194,176,12,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99f9f0a0', endColorstr='#99c2b00c', GradientType=0)}#navbar .navbar-inner.yellow.transparent .brand,#navbar .navbar-inner.yellow.transparent .nav>li>a{text-shadow:0 1px 0 #c2b00c;color:#f2f2f2}#navbar .navbar-inner.yellow.transparent .brand .caret,#navbar .navbar-inner.yellow.transparent .nav>li>a .caret{border-bottom-color:#f2e88d;border-top-color:#f2e88d}#navbar .navbar-inner.yellow.transparent .brand:focus .caret,#navbar .navbar-inner.yellow.transparent .brand:hover .caret,#navbar .navbar-inner.yellow.transparent .nav>li>a:focus .caret,#navbar .navbar-inner.yellow.transparent .nav>li>a:hover .caret{border-bottom-color:#f2edbf;border-top-color:#f2edbf}#navbar .navbar-inner.yellow.transparent .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.yellow.transparent .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.yellow.transparent .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.yellow.transparent .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.yellow.transparent .nav>li.dropdown.open>.dropdown-toggle{background-color:rgba(216,202,71,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(194,176,12,.6),rgba(249,240,160,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(194,176,12,.6)),to(rgba(249,240,160,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(194,176,12,.6),rgba(249,240,160,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(194,176,12,.6),rgba(249,240,160,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(194,176,12,.6),rgba(249,240,160,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99c2b00c', endColorstr='#99f9f0a0', GradientType=0)}#navbar .navbar-inner.yellow.transparent .nav>li>a:hover{background-color:rgba(217,204,86,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(248,237,136,.6),rgba(170,154,10,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(248,237,136,.6)),to(rgba(170,154,10,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(248,237,136,.6),rgba(170,154,10,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(248,237,136,.6),rgba(170,154,10,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(248,237,136,.6),rgba(170,154,10,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99f8ed88', endColorstr='#99aa9a0a', GradientType=0)}#navbar .navbar-inner.green{background-color:#98f064;background-image:-moz-linear-gradient(top,#c8ffa7,#50da00);background-image:-webkit-gradient(linear,0 0,0 100%,from(#c8ffa7),to(#50da00));background-image:-webkit-linear-gradient(top,#c8ffa7,#50da00);background-image:-o-linear-gradient(top,#c8ffa7,#50da00);background-image:linear-gradient(to bottom,#c8ffa7,#50da00);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffc8ffa7', endColorstr='#ff50da00', GradientType=0)}#navbar .navbar-inner.green .brand,#navbar .navbar-inner.green .nav>li>a{text-shadow:0 1px 0 #50da00;color:#333}#navbar .navbar-inner.green .brand .caret,#navbar .navbar-inner.green .nav>li>a .caret{border-bottom-color:#55992e;border-top-color:#55992e}#navbar .navbar-inner.green .brand:focus .caret,#navbar .navbar-inner.green .brand:hover .caret,#navbar .navbar-inner.green .nav>li>a:focus .caret,#navbar .navbar-inner.green .nav>li>a:hover .caret{border-bottom-color:#446630;border-top-color:#446630}#navbar .navbar-inner.green .brand span{background-image:url(../img/tentacle-20x20.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.green .brand span{background-image:url(../img/tentacle-20x20@2x.png)}}#navbar .navbar-inner.green .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.green .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.green .nav>li.dropdown.open>.dropdown-toggle{background-color:#80e943;background-image:-moz-linear-gradient(top,#50da00,#c8ffa7);background-image:-webkit-gradient(linear,0 0,0 100%,from(#50da00),to(#c8ffa7));background-image:-webkit-linear-gradient(top,#50da00,#c8ffa7);background-image:-o-linear-gradient(top,#50da00,#c8ffa7);background-image:linear-gradient(to bottom,#50da00,#c8ffa7);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff50da00', endColorstr='#ffc8ffa7', GradientType=0)}#navbar .navbar-inner.green .nav>li>a:hover{background-color:#8ae655;background-image:-moz-linear-gradient(top,#b8ff8e,#47c100);background-image:-webkit-gradient(linear,0 0,0 100%,from(#b8ff8e),to(#47c100));background-image:-webkit-linear-gradient(top,#b8ff8e,#47c100);background-image:-o-linear-gradient(top,#b8ff8e,#47c100);background-image:linear-gradient(to bottom,#b8ff8e,#47c100);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffb8ff8e', endColorstr='#ff47c100', GradientType=0)}#navbar .navbar-inner.green.transparent{background-color:rgba(152,240,100,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(200,255,167,.6),rgba(80,218,0,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(200,255,167,.6)),to(rgba(80,218,0,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(200,255,167,.6),rgba(80,218,0,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(200,255,167,.6),rgba(80,218,0,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(200,255,167,.6),rgba(80,218,0,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99c8ffa7', endColorstr='#9950da00', GradientType=0)}#navbar .navbar-inner.green.transparent .brand,#navbar .navbar-inner.green.transparent .nav>li>a{text-shadow:0 1px 0 #50da00;color:#333}#navbar .navbar-inner.green.transparent .brand .caret,#navbar .navbar-inner.green.transparent .nav>li>a .caret{border-bottom-color:#55992e;border-top-color:#55992e}#navbar .navbar-inner.green.transparent .brand:focus .caret,#navbar .navbar-inner.green.transparent .brand:hover .caret,#navbar .navbar-inner.green.transparent .nav>li>a:focus .caret,#navbar .navbar-inner.green.transparent .nav>li>a:hover .caret{border-bottom-color:#446630;border-top-color:#446630}#navbar .navbar-inner.green.transparent .brand span{background-image:url(../img/tentacle-20x20.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.green.transparent .brand span{background-image:url(../img/tentacle-20x20@2x.png)}}#navbar .navbar-inner.green.transparent .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.green.transparent .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.green.transparent .nav>li.dropdown.open>.dropdown-toggle{background-color:rgba(128,233,67,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(80,218,0,.6),rgba(200,255,167,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(80,218,0,.6)),to(rgba(200,255,167,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(80,218,0,.6),rgba(200,255,167,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(80,218,0,.6),rgba(200,255,167,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(80,218,0,.6),rgba(200,255,167,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#9950da00', endColorstr='#99c8ffa7', GradientType=0)}#navbar .navbar-inner.green.transparent .nav>li>a:hover{background-color:rgba(138,230,85,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(184,255,142,.6),rgba(71,193,0,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(184,255,142,.6)),to(rgba(71,193,0,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(184,255,142,.6),rgba(71,193,0,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(184,255,142,.6),rgba(71,193,0,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(184,255,142,.6),rgba(71,193,0,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99b8ff8e', endColorstr='#9947c100', GradientType=0)}#navbar .navbar-inner.blue{background-color:#2e63cc;background-image:-moz-linear-gradient(top,#4d88ff,#002b80);background-image:-webkit-gradient(linear,0 0,0 100%,from(#4d88ff),to(#002b80));background-image:-webkit-linear-gradient(top,#4d88ff,#002b80);background-image:-o-linear-gradient(top,#4d88ff,#002b80);background-image:linear-gradient(to bottom,#4d88ff,#002b80);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff4d88ff', endColorstr='#ff002b80', GradientType=0)}#navbar .navbar-inner.blue .brand,#navbar .navbar-inner.blue .nav>li>a{text-shadow:0 1px 0 #1a66ff;color:#f2f2f2}#navbar .navbar-inner.blue .brand .caret,#navbar .navbar-inner.blue .nav>li>a .caret{border-bottom-color:#799bdf;border-top-color:#799bdf}#navbar .navbar-inner.blue .brand:focus .caret,#navbar .navbar-inner.blue .brand:hover .caret,#navbar .navbar-inner.blue .nav>li>a:focus .caret,#navbar .navbar-inner.blue .nav>li>a:hover .caret{border-bottom-color:#b6c7e9;border-top-color:#b6c7e9}#navbar .navbar-inner.blue .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.blue .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.blue .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.blue .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.blue .nav>li.dropdown.open>.dropdown-toggle{background-color:#1f50b3;background-image:-moz-linear-gradient(top,#002b80,#4d88ff);background-image:-webkit-gradient(linear,0 0,0 100%,from(#002b80),to(#4d88ff));background-image:-webkit-linear-gradient(top,#002b80,#4d88ff);background-image:-o-linear-gradient(top,#002b80,#4d88ff);background-image:linear-gradient(to bottom,#002b80,#4d88ff);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff002b80', endColorstr='#ff4d88ff', GradientType=0)}#navbar .navbar-inner.blue .nav>li>a:hover{background-color:#1f55c2;background-image:-moz-linear-gradient(top,#37f,#026);background-image:-webkit-gradient(linear,0 0,0 100%,from(#37f),to(#026));background-image:-webkit-linear-gradient(top,#37f,#026);background-image:-o-linear-gradient(top,#37f,#026);background-image:linear-gradient(to bottom,#37f,#026);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3377ff', endColorstr='#ff002266', GradientType=0)}#navbar .navbar-inner.blue.transparent{background-color:rgba(46,99,204,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(77,136,255,.6),rgba(0,43,128,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(77,136,255,.6)),to(rgba(0,43,128,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(77,136,255,.6),rgba(0,43,128,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(77,136,255,.6),rgba(0,43,128,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(77,136,255,.6),rgba(0,43,128,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#994d88ff', endColorstr='#99002b80', GradientType=0)}#navbar .navbar-inner.blue.transparent .brand,#navbar .navbar-inner.blue.transparent .nav>li>a{text-shadow:0 1px 0 #1a66ff;color:#f2f2f2}#navbar .navbar-inner.blue.transparent .brand .caret,#navbar .navbar-inner.blue.transparent .nav>li>a .caret{border-bottom-color:#799bdf;border-top-color:#799bdf}#navbar .navbar-inner.blue.transparent .brand:focus .caret,#navbar .navbar-inner.blue.transparent .brand:hover .caret,#navbar .navbar-inner.blue.transparent .nav>li>a:focus .caret,#navbar .navbar-inner.blue.transparent .nav>li>a:hover .caret{border-bottom-color:#b6c7e9;border-top-color:#b6c7e9}#navbar .navbar-inner.blue.transparent .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.blue.transparent .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.blue.transparent .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.blue.transparent .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.blue.transparent .nav>li.dropdown.open>.dropdown-toggle{background-color:rgba(31,80,179,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(0,43,128,.6),rgba(77,136,255,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(0,43,128,.6)),to(rgba(77,136,255,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(0,43,128,.6),rgba(77,136,255,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(0,43,128,.6),rgba(77,136,255,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(0,43,128,.6),rgba(77,136,255,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99002b80', endColorstr='#994d88ff', GradientType=0)}#navbar .navbar-inner.blue.transparent .nav>li>a:hover{background-color:rgba(31,85,194,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(51,119,255,.6),rgba(0,34,102,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(51,119,255,.6)),to(rgba(0,34,102,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(51,119,255,.6),rgba(0,34,102,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(51,119,255,.6),rgba(0,34,102,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(51,119,255,.6),rgba(0,34,102,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#993377ff', endColorstr='#99002266', GradientType=0)}#navbar .navbar-inner.violet{background-color:#9864f0;background-image:-moz-linear-gradient(top,#c8a7ff,#5000da);background-image:-webkit-gradient(linear,0 0,0 100%,from(#c8a7ff),to(#5000da));background-image:-webkit-linear-gradient(top,#c8a7ff,#5000da);background-image:-o-linear-gradient(top,#c8a7ff,#5000da);background-image:linear-gradient(to bottom,#c8a7ff,#5000da);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffc8a7ff', endColorstr='#ff5000da', GradientType=0)}#navbar .navbar-inner.violet .brand,#navbar .navbar-inner.violet .nav>li>a{text-shadow:0 1px 0 #a774ff;color:#f2f2f2}#navbar .navbar-inner.violet .brand .caret,#navbar .navbar-inner.violet .nav>li>a .caret{border-bottom-color:#b58df9;border-top-color:#b58df9}#navbar .navbar-inner.violet .brand:focus .caret,#navbar .navbar-inner.violet .brand:hover .caret,#navbar .navbar-inner.violet .nav>li>a:focus .caret,#navbar .navbar-inner.violet .nav>li>a:hover .caret{border-bottom-color:#d3bff5;border-top-color:#d3bff5}#navbar .navbar-inner.violet .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.violet .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.violet .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.violet .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.violet .nav>li.dropdown.open>.dropdown-toggle{background-color:#8043e9;background-image:-moz-linear-gradient(top,#5000da,#c8a7ff);background-image:-webkit-gradient(linear,0 0,0 100%,from(#5000da),to(#c8a7ff));background-image:-webkit-linear-gradient(top,#5000da,#c8a7ff);background-image:-o-linear-gradient(top,#5000da,#c8a7ff);background-image:linear-gradient(to bottom,#5000da,#c8a7ff);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5000da', endColorstr='#ffc8a7ff', GradientType=0)}#navbar .navbar-inner.violet .nav>li>a:hover{background-color:#8a55e6;background-image:-moz-linear-gradient(top,#b88eff,#4700c1);background-image:-webkit-gradient(linear,0 0,0 100%,from(#b88eff),to(#4700c1));background-image:-webkit-linear-gradient(top,#b88eff,#4700c1);background-image:-o-linear-gradient(top,#b88eff,#4700c1);background-image:linear-gradient(to bottom,#b88eff,#4700c1);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffb88eff', endColorstr='#ff4700c1', GradientType=0)}#navbar .navbar-inner.violet.transparent{background-color:rgba(152,100,240,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(200,167,255,.6),rgba(80,0,218,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(200,167,255,.6)),to(rgba(80,0,218,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(200,167,255,.6),rgba(80,0,218,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(200,167,255,.6),rgba(80,0,218,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(200,167,255,.6),rgba(80,0,218,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99c8a7ff', endColorstr='#995000da', GradientType=0)}#navbar .navbar-inner.violet.transparent .brand,#navbar .navbar-inner.violet.transparent .nav>li>a{text-shadow:0 1px 0 #a774ff;color:#f2f2f2}#navbar .navbar-inner.violet.transparent .brand .caret,#navbar .navbar-inner.violet.transparent .nav>li>a .caret{border-bottom-color:#b58df9;border-top-color:#b58df9}#navbar .navbar-inner.violet.transparent .brand:focus .caret,#navbar .navbar-inner.violet.transparent .brand:hover .caret,#navbar .navbar-inner.violet.transparent .nav>li>a:focus .caret,#navbar .navbar-inner.violet.transparent .nav>li>a:hover .caret{border-bottom-color:#d3bff5;border-top-color:#d3bff5}#navbar .navbar-inner.violet.transparent .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.violet.transparent .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.violet.transparent .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.violet.transparent .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.violet.transparent .nav>li.dropdown.open>.dropdown-toggle{background-color:rgba(128,67,233,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(80,0,218,.6),rgba(200,167,255,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(80,0,218,.6)),to(rgba(200,167,255,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(80,0,218,.6),rgba(200,167,255,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(80,0,218,.6),rgba(200,167,255,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(80,0,218,.6),rgba(200,167,255,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#995000da', endColorstr='#99c8a7ff', GradientType=0)}#navbar .navbar-inner.violet.transparent .nav>li>a:hover{background-color:rgba(138,85,230,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(184,142,255,.6),rgba(71,0,193,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(184,142,255,.6)),to(rgba(71,0,193,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(184,142,255,.6),rgba(71,0,193,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(184,142,255,.6),rgba(71,0,193,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(184,142,255,.6),rgba(71,0,193,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99b88eff', endColorstr='#994700c1', GradientType=0)}#navbar .navbar-inner.black{background-color:#4f4f4f;background-image:-moz-linear-gradient(top,#787878,#121212);background-image:-webkit-gradient(linear,0 0,0 100%,from(#787878),to(#121212));background-image:-webkit-linear-gradient(top,#787878,#121212);background-image:-o-linear-gradient(top,#787878,#121212);background-image:linear-gradient(to bottom,#787878,#121212);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff787878', endColorstr='#ff121212', GradientType=0)}#navbar .navbar-inner.black .brand,#navbar .navbar-inner.black .nav>li>a{text-shadow:0 1px 0 #5e5e5e;color:#f2f2f2}#navbar .navbar-inner.black .brand .caret,#navbar .navbar-inner.black .nav>li>a .caret{border-bottom-color:#959595;border-top-color:#959595}#navbar .navbar-inner.black .brand:focus .caret,#navbar .navbar-inner.black .brand:hover .caret,#navbar .navbar-inner.black .nav>li>a:focus .caret,#navbar .navbar-inner.black .nav>li>a:hover .caret{border-bottom-color:#c4c4c4;border-top-color:#c4c4c4}#navbar .navbar-inner.black .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.black .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.black .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.black .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.black .nav>li.dropdown.open>.dropdown-toggle{background-color:#3b3b3b;background-image:-moz-linear-gradient(top,#121212,#787878);background-image:-webkit-gradient(linear,0 0,0 100%,from(#121212),to(#787878));background-image:-webkit-linear-gradient(top,#121212,#787878);background-image:-o-linear-gradient(top,#121212,#787878);background-image:linear-gradient(to bottom,#121212,#787878);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff121212', endColorstr='#ff787878', GradientType=0)}#navbar .navbar-inner.black .nav>li>a:hover{background-color:#424242;background-image:-moz-linear-gradient(top,#6b6b6b,#050505);background-image:-webkit-gradient(linear,0 0,0 100%,from(#6b6b6b),to(#050505));background-image:-webkit-linear-gradient(top,#6b6b6b,#050505);background-image:-o-linear-gradient(top,#6b6b6b,#050505);background-image:linear-gradient(to bottom,#6b6b6b,#050505);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff6b6b6b', endColorstr='#ff050505', GradientType=0)}#navbar .navbar-inner.black.transparent{background-color:rgba(79,79,79,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(120,120,120,.6),rgba(18,18,18,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(120,120,120,.6)),to(rgba(18,18,18,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(120,120,120,.6),rgba(18,18,18,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(120,120,120,.6),rgba(18,18,18,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(120,120,120,.6),rgba(18,18,18,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99787878', endColorstr='#99121212', GradientType=0)}#navbar .navbar-inner.black.transparent .brand,#navbar .navbar-inner.black.transparent .nav>li>a{text-shadow:0 1px 0 #5e5e5e;color:#f2f2f2}#navbar .navbar-inner.black.transparent .brand .caret,#navbar .navbar-inner.black.transparent .nav>li>a .caret{border-bottom-color:#959595;border-top-color:#959595}#navbar .navbar-inner.black.transparent .brand:focus .caret,#navbar .navbar-inner.black.transparent .brand:hover .caret,#navbar .navbar-inner.black.transparent .nav>li>a:focus .caret,#navbar .navbar-inner.black.transparent .nav>li>a:hover .caret{border-bottom-color:#c4c4c4;border-top-color:#c4c4c4}#navbar .navbar-inner.black.transparent .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.black.transparent .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.black.transparent .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.black.transparent .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.black.transparent .nav>li.dropdown.open>.dropdown-toggle{background-color:rgba(59,59,59,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(18,18,18,.6),rgba(120,120,120,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(18,18,18,.6)),to(rgba(120,120,120,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(18,18,18,.6),rgba(120,120,120,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(18,18,18,.6),rgba(120,120,120,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(18,18,18,.6),rgba(120,120,120,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99121212', endColorstr='#99787878', GradientType=0)}#navbar .navbar-inner.black.transparent .nav>li>a:hover{background-color:rgba(66,66,66,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(107,107,107,.6),rgba(5,5,5,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(107,107,107,.6)),to(rgba(5,5,5,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(107,107,107,.6),rgba(5,5,5,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(107,107,107,.6),rgba(5,5,5,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(107,107,107,.6),rgba(5,5,5,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#996b6b6b', endColorstr='#99050505', GradientType=0)}#navbar .navbar-inner.white{background-color:#e9e9e9;background-image:-moz-linear-gradient(top,#fff,#c8c8c8);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#c8c8c8));background-image:-webkit-linear-gradient(top,#fff,#c8c8c8);background-image:-o-linear-gradient(top,#fff,#c8c8c8);background-image:linear-gradient(to bottom,#fff,#c8c8c8);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffc8c8c8', GradientType=0)}#navbar .navbar-inner.white .brand,#navbar .navbar-inner.white .nav>li>a{text-shadow:0 1px 0 #c8c8c8;color:#333}#navbar .navbar-inner.white .brand .caret,#navbar .navbar-inner.white .nav>li>a .caret{border-bottom-color:#919191;border-top-color:#919191}#navbar .navbar-inner.white .brand:focus .caret,#navbar .navbar-inner.white .brand:hover .caret,#navbar .navbar-inner.white .nav>li>a:focus .caret,#navbar .navbar-inner.white .nav>li>a:hover .caret{border-bottom-color:#626262;border-top-color:#626262}#navbar .navbar-inner.white .brand span{background-image:url(../img/tentacle-20x20.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.white .brand span{background-image:url(../img/tentacle-20x20@2x.png)}}#navbar .navbar-inner.white .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.white .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.white .nav>li.dropdown.open>.dropdown-toggle{background-color:#dedede;background-image:-moz-linear-gradient(top,#c8c8c8,#fff);background-image:-webkit-gradient(linear,0 0,0 100%,from(#c8c8c8),to(#fff));background-image:-webkit-linear-gradient(top,#c8c8c8,#fff);background-image:-o-linear-gradient(top,#c8c8c8,#fff);background-image:linear-gradient(to bottom,#c8c8c8,#fff);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffc8c8c8', endColorstr='#ffffffff', GradientType=0)}#navbar .navbar-inner.white .nav>li>a:hover{background-color:#dcdcdc;background-image:-moz-linear-gradient(top,#f2f2f2,#bbb);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f2f2f2),to(#bbb));background-image:-webkit-linear-gradient(top,#f2f2f2,#bbb);background-image:-o-linear-gradient(top,#f2f2f2,#bbb);background-image:linear-gradient(to bottom,#f2f2f2,#bbb);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2f2f2', endColorstr='#ffbbbbbb', GradientType=0)}#navbar .navbar-inner.white.transparent{background-color:rgba(233,233,233,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(255,255,255,.6),rgba(200,200,200,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(255,255,255,.6)),to(rgba(200,200,200,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(255,255,255,.6),rgba(200,200,200,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(255,255,255,.6),rgba(200,200,200,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(255,255,255,.6),rgba(200,200,200,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99ffffff', endColorstr='#99c8c8c8', GradientType=0)}#navbar .navbar-inner.white.transparent .brand,#navbar .navbar-inner.white.transparent .nav>li>a{text-shadow:0 1px 0 #c8c8c8;color:#333}#navbar .navbar-inner.white.transparent .brand .caret,#navbar .navbar-inner.white.transparent .nav>li>a .caret{border-bottom-color:#919191;border-top-color:#919191}#navbar .navbar-inner.white.transparent .brand:focus .caret,#navbar .navbar-inner.white.transparent .brand:hover .caret,#navbar .navbar-inner.white.transparent .nav>li>a:focus .caret,#navbar .navbar-inner.white.transparent .nav>li>a:hover .caret{border-bottom-color:#626262;border-top-color:#626262}#navbar .navbar-inner.white.transparent .brand span{background-image:url(../img/tentacle-20x20.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.white.transparent .brand span{background-image:url(../img/tentacle-20x20@2x.png)}}#navbar .navbar-inner.white.transparent .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.white.transparent .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.white.transparent .nav>li.dropdown.open>.dropdown-toggle{background-color:rgba(222,222,222,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(200,200,200,.6),rgba(255,255,255,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(200,200,200,.6)),to(rgba(255,255,255,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(200,200,200,.6),rgba(255,255,255,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(200,200,200,.6),rgba(255,255,255,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(200,200,200,.6),rgba(255,255,255,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99c8c8c8', endColorstr='#99ffffff', GradientType=0)}#navbar .navbar-inner.white.transparent .nav>li>a:hover{background-color:rgba(220,220,220,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(242,242,242,.6),rgba(187,187,187,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(242,242,242,.6)),to(rgba(187,187,187,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(242,242,242,.6),rgba(187,187,187,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(242,242,242,.6),rgba(187,187,187,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(242,242,242,.6),rgba(187,187,187,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99f2f2f2', endColorstr='#99bbbbbb', GradientType=0)}#navbar .navbar-inner .brand{padding:10px 20px 6px}#navbar .navbar-inner .brand span{padding-left:26px;background-size:20px 20px;background-repeat:no-repeat;display:inline-block;max-width:250px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;vertical-align:top;line-height:20px;height:24px}#navbar_login a.dropdown-toggle span{display:inline-block;max-width:100px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;vertical-align:top}.octoprint-container{margin-top:20px}.octoprint-container .tab-content{padding:9px 15px;border-left:1px solid #DDD;border-right:1px solid #DDD;border-bottom:1px solid #DDD;-webkit-border-bottom-right-radius:4px;-moz-border-radius-bottomright:4px;border-bottom-right-radius:4px;-webkit-border-bottom-left-radius:4px;-moz-border-radius-bottomleft:4px;border-bottom-left-radius:4px}.octoprint-container .nav{margin-bottom:0}.octoprint-container .tab-content h1{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:40px;color:#333;border:0;border-bottom:1px solid #E5E5E5;font-weight:400}.octoprint-container .accordion-heading .accordion-heading-button{float:right}.octoprint-container .accordion-heading .accordion-heading-button a{display:inline-block;padding:8px 15px;font-size:14px;line-height:20px;color:#000;text-decoration:none;background:0 0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.octoprint-container .accordion-heading a.accordion-toggle{display:inline-block}.octoprint-container .accordion-heading [class*=" icon-"],.octoprint-container .accordion-heading [class^=icon-]{color:#000}.print-control .btn{padding-left:4px;padding-right:4px}.upload-buttons .btn{margin-right:0}table{table-layout:fixed}table .popover-title{text-overflow:ellipsis;word-break:break-all}table td,table th{overflow:hidden}table td.gcode_files_name,table th.gcode_files_name{text-overflow:ellipsis;text-align:left;white-space:nowrap}table td.gcode_files_action,table th.gcode_files_action{width:90px;text-align:center;white-space:nowrap}table td.gcode_files_action a,table th.gcode_files_action a{text-decoration:none;color:#000}table td.gcode_files_action a.disabled,table th.gcode_files_action a.disabled{color:#ccc;cursor:default}table td.timelapse_files_checkbox,table td.timelapse_unrendered_checkbox,table th.timelapse_files_checkbox,table th.timelapse_unrendered_checkbox{text-align:center;width:10px}table td.timelapse_files_checkbox input[type=checkbox],table td.timelapse_unrendered_checkbox input[type=checkbox],table th.timelapse_files_checkbox input[type=checkbox],table th.timelapse_unrendered_checkbox input[type=checkbox]{margin-top:0}table td.timelapse_files_name,table td.timelapse_unrendered_name,table th.timelapse_files_name,table th.timelapse_unrendered_name{text-overflow:ellipsis;text-align:left}table td.timelapse_files_size,table td.timelapse_unrendered_size,table th.timelapse_files_size,table th.timelapse_unrendered_size{text-align:right;width:55px}table td.timelapse_unrendered_count,table th.timelapse_unrendered_count{text-align:right;width:45px}table td.timelapse_files_action,table td.timelapse_unrendered_action,table th.timelapse_files_action,table th.timelapse_unrendered_action{width:45px;text-align:center;white-space:nowrap}table td.timelapse_files_action a,table td.timelapse_unrendered_action a,table th.timelapse_files_action a,table th.timelapse_unrendered_action a{text-decoration:none;color:#000}table td.timelapse_files_action a.disabled,table td.timelapse_unrendered_action a.disabled,table th.timelapse_files_action a.disabled,table th.timelapse_unrendered_action a.disabled{color:#ccc;cursor:default}table td.settings_users_name,table th.settings_users_name{text-overflow:ellipsis;text-align:left}table td.settings_users_active,table td.settings_users_admin,table th.settings_users_active,table th.settings_users_admin{text-align:center;width:55px}table td.settings_users_actions,table th.settings_users_actions{width:60px;text-align:center;white-space:nowrap}table td.settings_users_actions a,table th.settings_users_actions a{text-decoration:none;color:#000}table td.settings_users_actions a.disabled,table th.settings_users_actions a.disabled{color:#ccc;cursor:default}table td.settings_logs_name,table th.settings_logs_name{text-overflow:ellipsis;text-align:left}table td.settings_logs_size,table th.settings_logs_size{text-align:right;width:70px}table td.settings_logs_date,table th.settings_logs_date{text-align:left;width:130px}table td.settings_logs_action,table th.settings_logs_action{width:70px;text-align:center;white-space:nowrap}table td.settings_logs_action a,table th.settings_logs_action a{text-decoration:none;color:#000}table td.settings_logs_action a.disabled,table th.settings_logs_action a.disabled{color:#ccc;cursor:default}table td.settings_printerProfiles_profiles_name,table th.settings_printerProfiles_profiles_name{text-overflow:ellipsis;text-align:left}table td.settings_printerProfiles_profiles_model,table th.settings_printerProfiles_profiles_model{text-align:left;width:250px}table td.settings_printerProfiles_profiles_action,table th.settings_printerProfiles_profiles_action{width:80px;text-align:center;white-space:nowrap}table td.settings_printerProfiles_profiles_action a,table th.settings_printerProfiles_profiles_action a{text-decoration:none;color:#000}table td.settings_printerProfiles_profiles_action a.disabled,table th.settings_printerProfiles_profiles_action a.disabled{color:#ccc;cursor:default}#temperature-graph{height:350px;width:100%;background:url(../img/graph-background.png) center no-repeat}#temperature-table{table-layout:fixed;width:100%;margin-top:20px}#temperature-table td.temperature_actual,#temperature-table td.temperature_offset,#temperature-table td.temperature_target,#temperature-table td.temperature_tool,#temperature-table th.temperature_actual,#temperature-table th.temperature_offset,#temperature-table th.temperature_target,#temperature-table th.temperature_tool{vertical-align:middle;text-align:center}#temperature-table td.temperature_actual form,#temperature-table td.temperature_offset form,#temperature-table td.temperature_target form,#temperature-table td.temperature_tool form,#temperature-table th.temperature_actual form,#temperature-table th.temperature_offset form,#temperature-table th.temperature_target form,#temperature-table th.temperature_tool form{margin:0}#temperature-table td.temperature_actual .dropdown-menu,#temperature-table td.temperature_offset .dropdown-menu,#temperature-table td.temperature_target .dropdown-menu,#temperature-table td.temperature_tool .dropdown-menu,#temperature-table th.temperature_actual .dropdown-menu,#temperature-table th.temperature_offset .dropdown-menu,#temperature-table th.temperature_target .dropdown-menu,#temperature-table th.temperature_tool .dropdown-menu{text-align:left}#temperature-table td.temperature_tool,#temperature-table th.temperature_tool{width:16%;text-align:left;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}#temperature-table td.temperature_actual,#temperature-table th.temperature_actual{width:12%}#temperature-table td.temperature_target,#temperature-table th.temperature_target{width:42%;overflow:visible}#temperature-table td.temperature_offset,#temperature-table th.temperature_offset{width:30%}.tab-content,.tab-pane{overflow:visible}#speed_fill,#speed_innerWall,#speed_outerWall,#speed_support,#temp_newBedTemp,#temp_newTemp,#webcam_timelapse_fps,#webcam_timelapse_interval,#webcam_timelapse_postRoll,#webcam_timelapse_retractionZHop{text-align:right}ul.dropdown-menu li a{cursor:pointer}#connection_baudrates,#connection_ports,#connection_printers{width:100%}#offline_overlay,#reloadui_overlay{position:fixed;top:0;left:0;width:100%;height:100%;display:none}#offline_overlay{z-index:10002}#reloadui_overlay{z-index:10001}#offline_overlay_background,#reloadui_overlay_background{position:fixed;top:0;left:0;width:100%;height:100%;background-color:#000;filter:alpha(opacity=50);-moz-opacity:.5;-khtml-opacity:.5;opacity:.5}#offline_overlay_wrapper,#reloadui_overlay_wrapper{position:absolute;top:0;bottom:0;left:0;right:0;padding-top:60px}#offline_overlay_wrapper .container,#reloadui_overlay_wrapper .container{margin:auto}#webcam_container{width:100%;position:relative;outline:0;background-color:#000}#webcam_container .keycontrol_overlay{position:absolute;left:10px;right:10px;bottom:10px;background:rgba(0,0,0,.5);font-size:85%;color:#fff;padding:0}#webcam_container .keycontrol_overlay kbd{border:1px solid #eee;border-radius:3px;margin-left:2px;margin-right:2px;font-size:90%;padding:2px;min-width:1em}#webcam_container .keycontrol_overlay .keycontrol_overlay_heading{position:relative;padding:10px;font-weight:700}#webcam_container .keycontrol_overlay .keycontrol_overlay_column{position:relative;width:45%;padding:10px;float:left}#webcam_container .nowebcam{position:absolute;top:0;left:0;right:0;bottom:0}#webcam_container .nowebcam .text{color:#fff;text-align:center;position:relative;margin:auto;width:80%;top:50%;transform:translateY(-50%);display:block}#webcam_container .nowebcam .text.webcam_loading{animation:pulsate 3s ease-out;animation-iteration-count:infinite}#webcam_container .webcam_rotated{position:relative;width:100%;padding-bottom:100%;pointer-events:none}#webcam_container .webcam_rotated .webcam_fixed_ratio{position:absolute;transform:rotate(-90deg);top:0;bottom:0;pointer-events:none}#webcam_container .webcam_rotated .webcam_fixed_ratio .webcam_fixed_ratio_inner{width:100%;height:100%;pointer-events:none}#webcam_container .webcam_unrotated .webcam_fixed_ratio{width:100%;pointer-events:none;padding-bottom:100%;position:relative}#webcam_container .webcam_unrotated .webcam_fixed_ratio.ratio43{padding-bottom:75%}#webcam_container .webcam_unrotated .webcam_fixed_ratio.ratio169{padding-bottom:56.25%}#webcam_container .webcam_unrotated .webcam_fixed_ratio.ratio1610{padding-bottom:62.5%}#webcam_container .webcam_unrotated .webcam_fixed_ratio .webcam_fixed_ratio_inner{position:absolute;top:0;bottom:0;left:0;right:0;pointer-events:none}#webcam_container img{width:100%;height:100%;object-fit:contain}#state_wrapper hr{margin:5px 0}#files .gcode_files{padding-right:7px}#files .gcode_files .entry{padding:5px;line-height:20px;border-bottom:1px solid #ddd;position:relative}#files .gcode_files .entry:hover{background-color:#f5f5f5}#files .gcode_files .entry .title{text-overflow:ellipsis;word-break:break-all}#files .gcode_files .entry .additionalInfo,#files .gcode_files .entry .size,#files .gcode_files .entry .uploaded{font-size:85%;color:#999}#files .gcode_files .entry .action-buttons{position:absolute;bottom:5px;right:5px}#files .gcode_files .entry .additionalInfo{padding-bottom:22px}@keyframes highlightframes{0%{background:#ff0}100%{background:0 0}}#files .gcode_files .entry.highlight{animation:highlightframes 2s}#files .gcode_files .back .back-path{white-space:nowrap}#files .gcode_files .back .back-path span{word-wrap:break-word;white-space:pre-line}#files .upload-buttons{margin-top:10px}#files .form-search{text-align:center;margin-bottom:5px!important}#control{overflow:hidden}#control .jog-panel{float:left;margin-right:19px}#control h1{text-align:left}#control .jog-panel>div{text-align:center}#control .jog-panel>div.distance{text-align:left}#control .jog-panel .slider{margin-bottom:10px}#control .box{width:30px;height:30px;margin-right:10px;margin-bottom:10px;padding-left:8px}#control .control-box{display:block;height:30px;margin-bottom:10px}#control .btn-group{margin-bottom:10px}#control .btn-group.distance>.btn{width:43px;padding:3px 0;height:30px}#control .slider-handle{width:14px;height:14px;margin-left:-7px;margin-top:-3px}#control .custom_section h1{cursor:pointer}#control .custom_section_horizontal>.custom_control{display:inline-block}#control .custom_section_vertical>.custom_control{display:block}#control .custom_control .slider{margin-left:10px;margin-right:10px;margin-bottom:2px}#gcode .progress{width:588px}#gcode .progress .bar{-webkit-transition:width 0s linear;-moz-transition:width 0s linear;-o-transition:width 0s linear;transition:width 0s linear}#gcode .canvas_container{position:relative}#gcode .canvas_container:active,#gcode .canvas_container:hover{outline:0}#gcode .layer-buttons{padding-top:5px;padding-bottom:7px}#gcode #gcode_layer_slider{position:absolute;right:0;top:0;height:568px;float:right}#gcode #gcode_layer_slider .slider-handle{width:14px;height:14px;margin-left:-3px;margin-top:-7px}#gcode #gcode_command_slider .slider-handle{width:14px;height:14px;margin-left:-7px;margin-top:-3px}#term .terminal{margin-bottom:30px}#term .terminal #terminal-output,#term .terminal #terminal-output-lowfi{min-height:340px;margin-bottom:5px}#settings_dialog .aboutlink{float:left}#settings_dialog_menu,#wizard_dialog_menu{margin-left:0}#wizard_firstrun_acl .acl_decision{margin-top:1em}#settings_appearance_managelanguagesdialog_emptylist{overflow:hidden;width:100%;height:300px;text-align:center;display:table}#settings_appearance_managelanguagesdialog_emptylist div{display:table-cell;vertical-align:middle}.footer ul{margin:0}.footer ul li{display:inline;margin-left:1em;font-size:85%}.footer ul li:first-child{margin-left:0}.footer ul li a{color:#555}.ui-pnotify .alert a{color:#c09853}.ui-pnotify .alert-danger a,.ui-pnotify .alert-error a{color:#b94a48}.ui-pnotify .alert-success a{color:#468847}.ui-pnotify .alert-info a{color:#3a87ad}.pnotify_additional_info .pnotify_more{font-size:85%}.text-right{text-align:right}.text-center{text-align:center}.overflow_visible{overflow:visible!important}.clickable{cursor:pointer}.border_box{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.hidden{display:none}textarea.block{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;width:100%}@keyframes pulsate{0%{opacity:.5}50%{opacity:1}100%{opacity:.5}}#drop_overlay{position:fixed;top:0;left:0;width:100%;height:100%;z-index:10000;display:none}#drop_overlay.in{display:block}#drop_overlay #drop_overlay_background{position:fixed;top:0;left:0;width:100%;height:100%;background-color:#000;filter:alpha(opacity=50);-moz-opacity:.5;-khtml-opacity:.5;opacity:.5}#drop_overlay #drop_overlay_wrapper{position:absolute;top:0;bottom:0;left:0;right:0;padding-top:60px}#drop_overlay #drop_overlay_wrapper #drop,#drop_overlay #drop_overlay_wrapper #drop_background{position:absolute;top:0;left:0;margin-left:0;width:100%}#drop_overlay #drop_overlay_wrapper #drop_locally,#drop_overlay #drop_overlay_wrapper #drop_locally_background{position:absolute;top:0;left:50%;margin-left:-50%;width:50%;border-right:2px dashed #ccc}#drop_overlay #drop_overlay_wrapper #drop_sd,#drop_overlay #drop_overlay_wrapper #drop_sd_background{position:absolute;top:0;left:50%;margin-left:0;width:50%;border-left:2px dashed #ccc}#drop_overlay #drop_overlay_wrapper .dropzone{height:100%;z-index:10001;color:#fff;font-size:30px}#drop_overlay #drop_overlay_wrapper .dropzone i{font-size:50px}#drop_overlay #drop_overlay_wrapper .dropzone .text{display:block;text-align:center;line-height:40px;position:absolute;width:100%;bottom:5%;filter:alpha(opacity=100);-moz-opacity:1;-khtml-opacity:1;opacity:1}#drop_overlay #drop_overlay_wrapper .dropzone_background{width:50%;height:100%;background-color:#000;filter:alpha(opacity=25);-moz-opacity:.25;-khtml-opacity:.25;opacity:.25}#drop_overlay #drop_overlay_wrapper .dropzone_background.hover{background-color:#000;filter:alpha(opacity=50);-moz-opacity:.5;-khtml-opacity:.5;opacity:.5}#drop_overlay #drop_overlay_wrapper .dropzone_background.fade{-webkit-transition:all .3s ease-out;-moz-transition:all .3s ease-out;-ms-transition:all .3s ease-out;-o-transition:all .3s ease-out;transition:all .3s ease-out;opacity:1}.icon-sd-black-14{background:url(../img/icon-sd-black-14.png) 0 3px no-repeat;width:11px;height:17px;display:inline-block!important}.center{float:none;margin-left:auto;margin-right:auto}.flipH{-webkit-transform:scaleX(-1);-moz-transform:scaleX(-1);-ms-transform:scaleX(-1);transform:scaleX(-1)}.flipV{-webkit-transform:scaleY(-1);-moz-transform:scaleY(-1);-ms-transform:scaleY(-1);transform:scaleY(-1)}.flipH.flipV{-webkit-transform:scaleX(-1) scaleY(-1);-moz-transform:scaleX(-1) scaleY(-1);-ms-transform:scaleX(-1) scaleY(-1);transform:scaleX(-1) scaleY(-1)}.rotate90{-webkit-transform:rotate(-90deg);transform:rotate(-90deg)}.ui-pnotify a{text-decoration:underline}.btn-mini .caret,.btn-small .caret{margin-top:8px}.dropdown-menu-right{right:0;left:auto}.slider .slider-selection{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,.25);background-color:#006dcc;background-image:-moz-linear-gradient(top,#08c,#04c);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#04c));background-image:-webkit-linear-gradient(top,#08c,#04c);background-image:-o-linear-gradient(top,#08c,#04c);background-image:linear-gradient(to bottom,#08c,#04c);background-repeat:repeat-x;border-color:#04c #04c #002a80;border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);*background-color:#04c;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.slider .slider-selection.active,.slider .slider-selection.disabled,.slider .slider-selection:active,.slider .slider-selection:focus,.slider .slider-selection:hover,.slider .slider-selection[disabled]{color:#fff;background-color:#04c;*background-color:#003bb3}.slider .slider-selection.active,.slider .slider-selection:active{background-color:#039 \9}.slider.slider-disabled .slider-selection{background-image:none;opacity:.65;filter:alpha(opacity=65);-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.slider .slider-track{background-color:#f5f5f5;border:1px solid #e3e3e3;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05)}.slider.slider-disabled .slider-track{background-image:none;opacity:.65;filter:alpha(opacity=65);-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.slider .slider-handle{display:inline-block;*display:inline;*zoom:1;font-size:14px;line-height:20px;text-align:center;vertical-align:middle;cursor:pointer;color:#333;text-shadow:0 1px 1px rgba(255,255,255,.75);background-color:#f5f5f5;background-image:-moz-linear-gradient(top,#fff,#e6e6e6);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#e6e6e6));background-image:-webkit-linear-gradient(top,#fff,#e6e6e6);background-image:-o-linear-gradient(top,#fff,#e6e6e6);background-image:linear-gradient(to bottom,#fff,#e6e6e6);background-repeat:repeat-x;*background-color:#e6e6e6;border:1px solid #ccc;*border:0;border-bottom-color:#b3b3b3;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;*margin-left:.3em;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05);padding:0;margin-bottom:0;opacity:1;filter:alpha(opacity=100)}.slider .slider-handle.active,.slider .slider-handle.disabled,.slider .slider-handle:active,.slider .slider-handle:focus,.slider .slider-handle:hover,.slider .slider-handle[disabled]{color:#333;background-color:#e6e6e6;*background-color:#d9d9d9}.slider .slider-handle.active,.slider .slider-handle:active{background-color:#ccc \9}.slider .slider-handle:first-child{*margin-left:0}.slider .slider-handle:focus,.slider .slider-handle:hover{color:#333;text-decoration:none;background-position:0 -15px;-webkit-transition:background-position .1s linear;-moz-transition:background-position .1s linear;-o-transition:background-position .1s linear;transition:background-position .1s linear}.slider .slider-handle:focus{outline:#333 dotted thin;outline:-webkit-focus-ring-color auto 5px;outline-offset:-2px}.slider .slider-handle.active,.slider .slider-handle:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,.15),0 1px 2px rgba(0,0,0,.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,.15),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 2px 4px rgba(0,0,0,.15),0 1px 2px rgba(0,0,0,.05)}.slider .slider-handle.disabled,.slider .slider-handle[disabled]{cursor:default;background-image:none;opacity:.65;filter:alpha(opacity=65);-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.slider .slider-handle.hide{display:none}.slider .slider-handle.round{-webkit-border-radius:50%;-moz-border-radius:50%;border-radius:50%}.modal.large{width:975px;margin-left:-487px}.full-sized-box{position:absolute;bottom:0;left:0;right:0;top:0;padding:15px}.full-sized-box .row-fluid{height:100%}@media (max-width:979px){.full-sized-box{position:static}}:root .full-sized-box,_::-webkit-full-page-media,_:future{position:static}.scrollable{height:100%;overflow:auto;-webkit-overflow-scrolling:touch}.pre-output span{display:block}.input-append .add-on.add-on-limited,.input-prepend .add-on.add-on-limited{overflow-x:hidden;text-overflow:ellipsis;width:inherit}.input-append .btn-group:first-child .btn:first-child,.input-prepend .btn-group:first-child .btn:first-child{-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.input-append .btn-group .btn:first-child,.input-prepend .btn-group .btn:first-child{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.input-append.input-block-level,.input-prepend.input-block-level{display:table}.input-append.input-block-level .add-on,.input-prepend.input-block-level .add-on{display:table-cell;width:1%}.input-append.input-block-level>input,.input-prepend.input-block-level>input{box-sizing:border-box;display:table;min-height:inherit;width:100%}.input-append.input-block-level>input{border-right:0}.input-prepend.input-block-level>input{border-left:0}.control-group.error .input-append .fileinput-button,.control-group.error .input-prepend .fileinput-button{border-color:#b94a48}.control-text{padding-top:5px;cursor:default}input[type=number]{text-align:right}input[type=number].input-nospin::-webkit-inner-spin-button,input[type=number].input-nospin::-webkit-outer-spin-button{-webkit-appearance:none;margin:0}input[type=number].input-nospin{-moz-appearance:textfield}.progress-text,.progress-text-centered{position:relative}.progress-text .progress-text-back,.progress-text .progress-text-front,.progress-text-centered .progress-text-back,.progress-text-centered .progress-text-front{white-space:nowrap}.progress-text .progress-text-front,.progress-text-centered .progress-text-front{box-sizing:border-box;padding:0 10px;width:100%;display:block}.progress-text .progress-text-back,.progress-text-centered .progress-text-back{position:absolute;font-size:12px;line-height:20px;display:block;box-sizing:border-box;text-align:center;padding:0 10px}.progress-text .bar,.progress-text-centered .bar{position:absolute;overflow:hidden}.progress-text-centered .progress-text-front{position:absolute;font-size:12px;line-height:20px;display:block;text-align:center;color:#fff}.progress-text-centered .progress-text-back{width:100%}#navbar_login:not(.open) #login_dropdown_loggedout{display:block;z-index:-1;height:0;width:0;padding:0!important;overflow:hidden;border:0;box-shadow:none;left:-9999px}#navbar_login:not(.open) #login_dropdown_loggedout.hide{display:none}#loginForm{margin:0}#loginForm button{margin-top:20px} \ No newline at end of file +.btn{display:inline-block;*display:inline;*zoom:1;padding:4px 12px;margin-bottom:0;font-size:14px;line-height:20px;text-align:center;vertical-align:middle;cursor:pointer;color:#333;text-shadow:0 1px 1px rgba(255,255,255,.75);background-color:#f5f5f5;background-image:-moz-linear-gradient(top,#fff,#e6e6e6);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#e6e6e6));background-image:-webkit-linear-gradient(top,#fff,#e6e6e6);background-image:-o-linear-gradient(top,#fff,#e6e6e6);background-image:linear-gradient(to bottom,#fff,#e6e6e6);background-repeat:repeat-x;*background-color:#e6e6e6;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);border:1px solid #ccc;*border:0;border-bottom-color:#b3b3b3;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;*margin-left:.3em;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05)}.btn.active,.btn.disabled,.btn:active,.btn:focus,.btn:hover,.btn[disabled]{color:#333;background-color:#e6e6e6;*background-color:#d9d9d9}.btn.active,.btn:active{background-color:#ccc \9}.btn:first-child{*margin-left:0}.btn:focus,.btn:hover{color:#333;text-decoration:none;background-position:0 -15px;-webkit-transition:background-position .1s linear;-moz-transition:background-position .1s linear;-o-transition:background-position .1s linear;transition:background-position .1s linear}.btn:focus{outline:#333 dotted thin;outline:-webkit-focus-ring-color auto 5px;outline-offset:-2px}.btn.active,.btn:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,.15),0 1px 2px rgba(0,0,0,.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,.15),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 2px 4px rgba(0,0,0,.15),0 1px 2px rgba(0,0,0,.05)}.btn.disabled,.btn[disabled]{cursor:default;background-image:none;opacity:.65;filter:alpha(opacity=65);-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.btn-large{padding:11px 19px;font-size:17.5px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.btn-large [class*=" icon-"],.btn-large [class^=icon-]{margin-top:4px}.btn-small{padding:2px 10px;font-size:11.9px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.btn-small [class*=" icon-"],.btn-small [class^=icon-]{margin-top:0}.btn-mini [class*=" icon-"],.btn-mini [class^=icon-]{margin-top:-1px}.btn-mini{padding:0 6px;font-size:10.5px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.btn-block{display:block;width:100%;padding-left:0;padding-right:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.btn-block+.btn-block{margin-top:5px}input[type=submit].btn-block,input[type=reset].btn-block,input[type=button].btn-block{width:100%}.btn-primary{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,.25);background-color:#006dcc;background-image:-moz-linear-gradient(top,#08c,#04c);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#04c));background-image:-webkit-linear-gradient(top,#08c,#04c);background-image:-o-linear-gradient(top,#08c,#04c);background-image:linear-gradient(to bottom,#08c,#04c);background-repeat:repeat-x;border-color:#04c #04c #002a80;border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);*background-color:#04c;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-primary.active,.btn-primary.disabled,.btn-primary:active,.btn-primary:focus,.btn-primary:hover,.btn-primary[disabled]{color:#fff;background-color:#04c;*background-color:#003bb3}.btn-primary.active,.btn-primary:active{background-color:#039 \9}.btn-warning{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,.25);background-color:#faa732;background-image:-moz-linear-gradient(top,#fbb450,#f89406);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fbb450),to(#f89406));background-image:-webkit-linear-gradient(top,#fbb450,#f89406);background-image:-o-linear-gradient(top,#fbb450,#f89406);background-image:linear-gradient(to bottom,#fbb450,#f89406);background-repeat:repeat-x;border-color:#f89406 #f89406 #ad6704;border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);*background-color:#f89406;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-warning.active,.btn-warning.disabled,.btn-warning:active,.btn-warning:focus,.btn-warning:hover,.btn-warning[disabled]{color:#fff;background-color:#f89406;*background-color:#df8505}.btn-warning.active,.btn-warning:active{background-color:#c67605 \9}.btn-danger{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,.25);background-color:#da4f49;background-image:-moz-linear-gradient(top,#ee5f5b,#bd362f);background-image:-webkit-gradient(linear,0 0,0 100%,from(#ee5f5b),to(#bd362f));background-image:-webkit-linear-gradient(top,#ee5f5b,#bd362f);background-image:-o-linear-gradient(top,#ee5f5b,#bd362f);background-image:linear-gradient(to bottom,#ee5f5b,#bd362f);background-repeat:repeat-x;border-color:#bd362f #bd362f #802420;border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);*background-color:#bd362f;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-danger.active,.btn-danger.disabled,.btn-danger:active,.btn-danger:focus,.btn-danger:hover,.btn-danger[disabled]{color:#fff;background-color:#bd362f;*background-color:#a9302a}.btn-danger.active,.btn-danger:active{background-color:#942a25 \9}.btn-success{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,.25);background-color:#5bb75b;background-image:-moz-linear-gradient(top,#62c462,#51a351);background-image:-webkit-gradient(linear,0 0,0 100%,from(#62c462),to(#51a351));background-image:-webkit-linear-gradient(top,#62c462,#51a351);background-image:-o-linear-gradient(top,#62c462,#51a351);background-image:linear-gradient(to bottom,#62c462,#51a351);background-repeat:repeat-x;border-color:#51a351 #51a351 #387038;border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);*background-color:#51a351;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-success.active,.btn-success.disabled,.btn-success:active,.btn-success:focus,.btn-success:hover,.btn-success[disabled]{color:#fff;background-color:#51a351;*background-color:#499249}.btn-success.active,.btn-success:active{background-color:#408140 \9}.btn-info{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,.25);background-color:#49afcd;background-image:-moz-linear-gradient(top,#5bc0de,#2f96b4);background-image:-webkit-gradient(linear,0 0,0 100%,from(#5bc0de),to(#2f96b4));background-image:-webkit-linear-gradient(top,#5bc0de,#2f96b4);background-image:-o-linear-gradient(top,#5bc0de,#2f96b4);background-image:linear-gradient(to bottom,#5bc0de,#2f96b4);background-repeat:repeat-x;border-color:#2f96b4 #2f96b4 #1f6377;border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);*background-color:#2f96b4;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-info.active,.btn-info.disabled,.btn-info:active,.btn-info:focus,.btn-info:hover,.btn-info[disabled]{color:#fff;background-color:#2f96b4;*background-color:#2a85a0}.btn-info.active,.btn-info:active{background-color:#24748c \9}.btn-inverse{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,.25);background-color:#363636;background-image:-moz-linear-gradient(top,#444,#222);background-image:-webkit-gradient(linear,0 0,0 100%,from(#444),to(#222));background-image:-webkit-linear-gradient(top,#444,#222);background-image:-o-linear-gradient(top,#444,#222);background-image:linear-gradient(to bottom,#444,#222);background-repeat:repeat-x;border-color:#222 #222 #000;border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);*background-color:#222;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-inverse.active,.btn-inverse.disabled,.btn-inverse:active,.btn-inverse:focus,.btn-inverse:hover,.btn-inverse[disabled]{color:#fff;background-color:#222;*background-color:#151515}.btn-inverse.active,.btn-inverse:active{background-color:#080808 \9}button.btn,input[type=submit].btn{*padding-top:3px;*padding-bottom:3px}button.btn::-moz-focus-inner,input[type=submit].btn::-moz-focus-inner{padding:0;border:0}button.btn.btn-large,input[type=submit].btn.btn-large{*padding-top:7px;*padding-bottom:7px}button.btn.btn-small,input[type=submit].btn.btn-small{*padding-top:3px;*padding-bottom:3px}button.btn.btn-mini,input[type=submit].btn.btn-mini{*padding-top:1px;*padding-bottom:1px}.btn-link,.btn-link:active,.btn-link[disabled]{background-color:transparent;background-image:none;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.btn-link{border-color:transparent;cursor:pointer;color:#08c;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.btn-link:focus,.btn-link:hover{color:#005580;text-decoration:underline;background-color:transparent}.btn-link[disabled]:focus,.btn-link[disabled]:hover{color:#333;text-decoration:none}.clearfix{*zoom:1}.clearfix:after,.clearfix:before{display:table;content:"";line-height:0}.clearfix:after{clear:both}.hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.input-block-level{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.nowrap{white-space:nowrap}.actioncol{text-align:center;white-space:nowrap}.actioncol a{text-decoration:none;color:#000}.actioncol a.disabled{color:#ccc;cursor:default}#navbar .navbar-inner{background-color:#ebebeb;background-image:-moz-linear-gradient(top,#fff,#ccc);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#ccc));background-image:-webkit-linear-gradient(top,#fff,#ccc);background-image:-o-linear-gradient(top,#fff,#ccc);background-image:linear-gradient(to bottom,#fff,#ccc);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffcccccc', GradientType=0)}#navbar .navbar-inner .brand,#navbar .navbar-inner .nav>li>a{text-shadow:0 1px 0 #ccc;color:#333}#navbar .navbar-inner .brand .caret,#navbar .navbar-inner .nav>li>a .caret{border-bottom-color:#939393;border-top-color:#939393}#navbar .navbar-inner .brand:focus .caret,#navbar .navbar-inner .brand:hover .caret,#navbar .navbar-inner .nav>li>a:focus .caret,#navbar .navbar-inner .nav>li>a:hover .caret{border-bottom-color:#636363;border-top-color:#636363}#navbar .navbar-inner .brand span{background-image:url(../img/tentacle-20x20.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner .brand span{background-image:url(../img/tentacle-20x20@2x.png)}}#navbar .navbar-inner .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner .nav>li.dropdown.open>.dropdown-toggle{background-color:#e0e0e0;background-image:-moz-linear-gradient(top,#ccc,#fff);background-image:-webkit-gradient(linear,0 0,0 100%,from(#ccc),to(#fff));background-image:-webkit-linear-gradient(top,#ccc,#fff);background-image:-o-linear-gradient(top,#ccc,#fff);background-image:linear-gradient(to bottom,#ccc,#fff);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffcccccc', endColorstr='#ffffffff', GradientType=0)}#navbar .navbar-inner .nav>li>a:hover{background-color:#dedede;background-image:-moz-linear-gradient(top,#f2f2f2,#bfbfbf);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f2f2f2),to(#bfbfbf));background-image:-webkit-linear-gradient(top,#f2f2f2,#bfbfbf);background-image:-o-linear-gradient(top,#f2f2f2,#bfbfbf);background-image:linear-gradient(to bottom,#f2f2f2,#bfbfbf);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2f2f2', endColorstr='#ffbfbfbf', GradientType=0)}#navbar .navbar-inner.transparent{background-color:rgba(235,235,235,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(255,255,255,.6),rgba(204,204,204,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(255,255,255,.6)),to(rgba(204,204,204,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(255,255,255,.6),rgba(204,204,204,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(255,255,255,.6),rgba(204,204,204,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(255,255,255,.6),rgba(204,204,204,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99ffffff', endColorstr='#99cccccc', GradientType=0)}#navbar .navbar-inner.transparent .brand,#navbar .navbar-inner.transparent .nav>li>a{text-shadow:0 1px 0 #ccc;color:#333}#navbar .navbar-inner.transparent .brand .caret,#navbar .navbar-inner.transparent .nav>li>a .caret{border-bottom-color:#939393;border-top-color:#939393}#navbar .navbar-inner.transparent .brand:focus .caret,#navbar .navbar-inner.transparent .brand:hover .caret,#navbar .navbar-inner.transparent .nav>li>a:focus .caret,#navbar .navbar-inner.transparent .nav>li>a:hover .caret{border-bottom-color:#636363;border-top-color:#636363}#navbar .navbar-inner.transparent .brand span{background-image:url(../img/tentacle-20x20.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.transparent .brand span{background-image:url(../img/tentacle-20x20@2x.png)}}#navbar .navbar-inner.transparent .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.transparent .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.transparent .nav>li.dropdown.open>.dropdown-toggle{background-color:rgba(224,224,224,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(204,204,204,.6),rgba(255,255,255,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(204,204,204,.6)),to(rgba(255,255,255,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(204,204,204,.6),rgba(255,255,255,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(204,204,204,.6),rgba(255,255,255,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(204,204,204,.6),rgba(255,255,255,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99cccccc', endColorstr='#99ffffff', GradientType=0)}#navbar .navbar-inner.transparent .nav>li>a:hover{background-color:rgba(222,222,222,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(242,242,242,.6),rgba(191,191,191,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(242,242,242,.6)),to(rgba(191,191,191,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(242,242,242,.6),rgba(191,191,191,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(242,242,242,.6),rgba(191,191,191,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(242,242,242,.6),rgba(191,191,191,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99f2f2f2', endColorstr='#99bfbfbf', GradientType=0)}#navbar .navbar-inner.red{background-color:#bb645f;background-image:-moz-linear-gradient(top,#e28e8a,#802420);background-image:-webkit-gradient(linear,0 0,0 100%,from(#e28e8a),to(#802420));background-image:-webkit-linear-gradient(top,#e28e8a,#802420);background-image:-o-linear-gradient(top,#e28e8a,#802420);background-image:linear-gradient(to bottom,#e28e8a,#802420);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe28e8a', endColorstr='#ff802420', GradientType=0)}#navbar .navbar-inner.red .brand,#navbar .navbar-inner.red .nav>li>a{text-shadow:0 1px 0 #d86761;color:#f2f2f2}#navbar .navbar-inner.red .brand .caret,#navbar .navbar-inner.red .nav>li>a .caret{border-bottom-color:#d89491;border-top-color:#d89491}#navbar .navbar-inner.red .brand:focus .caret,#navbar .navbar-inner.red .brand:hover .caret,#navbar .navbar-inner.red .nav>li>a:focus .caret,#navbar .navbar-inner.red .nav>li>a:hover .caret{border-bottom-color:#e5c3c1;border-top-color:#e5c3c1}#navbar .navbar-inner.red .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.red .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.red .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.red .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.red .nav>li.dropdown.open>.dropdown-toggle{background-color:#a74f4a;background-image:-moz-linear-gradient(top,#802420,#e28e8a);background-image:-webkit-gradient(linear,0 0,0 100%,from(#802420),to(#e28e8a));background-image:-webkit-linear-gradient(top,#802420,#e28e8a);background-image:-o-linear-gradient(top,#802420,#e28e8a);background-image:linear-gradient(to bottom,#802420,#e28e8a);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff802420', endColorstr='#ffe28e8a', GradientType=0)}#navbar .navbar-inner.red .nav>li>a:hover{background-color:#af5651;background-image:-moz-linear-gradient(top,#dd7a75,#6b1f1b);background-image:-webkit-gradient(linear,0 0,0 100%,from(#dd7a75),to(#6b1f1b));background-image:-webkit-linear-gradient(top,#dd7a75,#6b1f1b);background-image:-o-linear-gradient(top,#dd7a75,#6b1f1b);background-image:linear-gradient(to bottom,#dd7a75,#6b1f1b);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdd7a75', endColorstr='#ff6b1f1b', GradientType=0)}#navbar .navbar-inner.red.transparent{background-color:rgba(187,100,95,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(226,142,138,.6),rgba(128,36,32,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(226,142,138,.6)),to(rgba(128,36,32,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(226,142,138,.6),rgba(128,36,32,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(226,142,138,.6),rgba(128,36,32,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(226,142,138,.6),rgba(128,36,32,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99e28e8a', endColorstr='#99802420', GradientType=0)}#navbar .navbar-inner.red.transparent .brand,#navbar .navbar-inner.red.transparent .nav>li>a{text-shadow:0 1px 0 #d86761;color:#f2f2f2}#navbar .navbar-inner.red.transparent .brand .caret,#navbar .navbar-inner.red.transparent .nav>li>a .caret{border-bottom-color:#d89491;border-top-color:#d89491}#navbar .navbar-inner.red.transparent .brand:focus .caret,#navbar .navbar-inner.red.transparent .brand:hover .caret,#navbar .navbar-inner.red.transparent .nav>li>a:focus .caret,#navbar .navbar-inner.red.transparent .nav>li>a:hover .caret{border-bottom-color:#e5c3c1;border-top-color:#e5c3c1}#navbar .navbar-inner.red.transparent .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.red.transparent .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.red.transparent .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.red.transparent .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.red.transparent .nav>li.dropdown.open>.dropdown-toggle{background-color:rgba(167,79,74,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(128,36,32,.6),rgba(226,142,138,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(128,36,32,.6)),to(rgba(226,142,138,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(128,36,32,.6),rgba(226,142,138,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(128,36,32,.6),rgba(226,142,138,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(128,36,32,.6),rgba(226,142,138,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99802420', endColorstr='#99e28e8a', GradientType=0)}#navbar .navbar-inner.red.transparent .nav>li>a:hover{background-color:rgba(175,86,81,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(221,122,117,.6),rgba(107,31,27,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(221,122,117,.6)),to(rgba(107,31,27,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(221,122,117,.6),rgba(107,31,27,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(221,122,117,.6),rgba(107,31,27,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(221,122,117,.6),rgba(107,31,27,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99dd7a75', endColorstr='#996b1f1b', GradientType=0)}#navbar .navbar-inner.orange{background-color:#e39665;background-image:-moz-linear-gradient(top,#f9c3a0,#c2530c);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f9c3a0),to(#c2530c));background-image:-webkit-linear-gradient(top,#f9c3a0,#c2530c);background-image:-o-linear-gradient(top,#f9c3a0,#c2530c);background-image:linear-gradient(to bottom,#f9c3a0,#c2530c);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff9c3a0', endColorstr='#ffc2530c', GradientType=0)}#navbar .navbar-inner.orange .brand,#navbar .navbar-inner.orange .nav>li>a{text-shadow:0 1px 0 #f6a570;color:#f2f2f2}#navbar .navbar-inner.orange .brand .caret,#navbar .navbar-inner.orange .nav>li>a .caret{border-bottom-color:#f2b58d;border-top-color:#f2b58d}#navbar .navbar-inner.orange .brand:focus .caret,#navbar .navbar-inner.orange .brand:hover .caret,#navbar .navbar-inner.orange .nav>li>a:focus .caret,#navbar .navbar-inner.orange .nav>li>a:hover .caret{border-bottom-color:#f2d3bf;border-top-color:#f2d3bf}#navbar .navbar-inner.orange .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.orange .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.orange .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.orange .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.orange .nav>li.dropdown.open>.dropdown-toggle{background-color:#d88047;background-image:-moz-linear-gradient(top,#c2530c,#f9c3a0);background-image:-webkit-gradient(linear,0 0,0 100%,from(#c2530c),to(#f9c3a0));background-image:-webkit-linear-gradient(top,#c2530c,#f9c3a0);background-image:-o-linear-gradient(top,#c2530c,#f9c3a0);background-image:linear-gradient(to bottom,#c2530c,#f9c3a0);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffc2530c', endColorstr='#fff9c3a0', GradientType=0)}#navbar .navbar-inner.orange .nav>li>a:hover{background-color:#d98956;background-image:-moz-linear-gradient(top,#f8b488,#aa490a);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f8b488),to(#aa490a));background-image:-webkit-linear-gradient(top,#f8b488,#aa490a);background-image:-o-linear-gradient(top,#f8b488,#aa490a);background-image:linear-gradient(to bottom,#f8b488,#aa490a);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff8b488', endColorstr='#ffaa490a', GradientType=0)}#navbar .navbar-inner.orange.transparent{background-color:rgba(227,150,101,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(249,195,160,.6),rgba(194,83,12,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(249,195,160,.6)),to(rgba(194,83,12,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(249,195,160,.6),rgba(194,83,12,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(249,195,160,.6),rgba(194,83,12,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(249,195,160,.6),rgba(194,83,12,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99f9c3a0', endColorstr='#99c2530c', GradientType=0)}#navbar .navbar-inner.orange.transparent .brand,#navbar .navbar-inner.orange.transparent .nav>li>a{text-shadow:0 1px 0 #f6a570;color:#f2f2f2}#navbar .navbar-inner.orange.transparent .brand .caret,#navbar .navbar-inner.orange.transparent .nav>li>a .caret{border-bottom-color:#f2b58d;border-top-color:#f2b58d}#navbar .navbar-inner.orange.transparent .brand:focus .caret,#navbar .navbar-inner.orange.transparent .brand:hover .caret,#navbar .navbar-inner.orange.transparent .nav>li>a:focus .caret,#navbar .navbar-inner.orange.transparent .nav>li>a:hover .caret{border-bottom-color:#f2d3bf;border-top-color:#f2d3bf}#navbar .navbar-inner.orange.transparent .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.orange.transparent .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.orange.transparent .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.orange.transparent .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.orange.transparent .nav>li.dropdown.open>.dropdown-toggle{background-color:rgba(216,128,71,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(194,83,12,.6),rgba(249,195,160,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(194,83,12,.6)),to(rgba(249,195,160,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(194,83,12,.6),rgba(249,195,160,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(194,83,12,.6),rgba(249,195,160,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(194,83,12,.6),rgba(249,195,160,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99c2530c', endColorstr='#99f9c3a0', GradientType=0)}#navbar .navbar-inner.orange.transparent .nav>li>a:hover{background-color:rgba(217,137,86,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(248,180,136,.6),rgba(170,73,10,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(248,180,136,.6)),to(rgba(170,73,10,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(248,180,136,.6),rgba(170,73,10,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(248,180,136,.6),rgba(170,73,10,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(248,180,136,.6),rgba(170,73,10,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99f8b488', endColorstr='#99aa490a', GradientType=0)}#navbar .navbar-inner.yellow{background-color:#e3d765;background-image:-moz-linear-gradient(top,#f9f0a0,#c2b00c);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f9f0a0),to(#c2b00c));background-image:-webkit-linear-gradient(top,#f9f0a0,#c2b00c);background-image:-o-linear-gradient(top,#f9f0a0,#c2b00c);background-image:linear-gradient(to bottom,#f9f0a0,#c2b00c);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff9f0a0', endColorstr='#ffc2b00c', GradientType=0)}#navbar .navbar-inner.yellow .brand,#navbar .navbar-inner.yellow .nav>li>a{text-shadow:0 1px 0 #c2b00c;color:#f2f2f2}#navbar .navbar-inner.yellow .brand .caret,#navbar .navbar-inner.yellow .nav>li>a .caret{border-bottom-color:#f2e88d;border-top-color:#f2e88d}#navbar .navbar-inner.yellow .brand:focus .caret,#navbar .navbar-inner.yellow .brand:hover .caret,#navbar .navbar-inner.yellow .nav>li>a:focus .caret,#navbar .navbar-inner.yellow .nav>li>a:hover .caret{border-bottom-color:#f2edbf;border-top-color:#f2edbf}#navbar .navbar-inner.yellow .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.yellow .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.yellow .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.yellow .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.yellow .nav>li.dropdown.open>.dropdown-toggle{background-color:#d8ca47;background-image:-moz-linear-gradient(top,#c2b00c,#f9f0a0);background-image:-webkit-gradient(linear,0 0,0 100%,from(#c2b00c),to(#f9f0a0));background-image:-webkit-linear-gradient(top,#c2b00c,#f9f0a0);background-image:-o-linear-gradient(top,#c2b00c,#f9f0a0);background-image:linear-gradient(to bottom,#c2b00c,#f9f0a0);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffc2b00c', endColorstr='#fff9f0a0', GradientType=0)}#navbar .navbar-inner.yellow .nav>li>a:hover{background-color:#d9cc56;background-image:-moz-linear-gradient(top,#f8ed88,#aa9a0a);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f8ed88),to(#aa9a0a));background-image:-webkit-linear-gradient(top,#f8ed88,#aa9a0a);background-image:-o-linear-gradient(top,#f8ed88,#aa9a0a);background-image:linear-gradient(to bottom,#f8ed88,#aa9a0a);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff8ed88', endColorstr='#ffaa9a0a', GradientType=0)}#navbar .navbar-inner.yellow.transparent{background-color:rgba(227,215,101,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(249,240,160,.6),rgba(194,176,12,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(249,240,160,.6)),to(rgba(194,176,12,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(249,240,160,.6),rgba(194,176,12,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(249,240,160,.6),rgba(194,176,12,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(249,240,160,.6),rgba(194,176,12,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99f9f0a0', endColorstr='#99c2b00c', GradientType=0)}#navbar .navbar-inner.yellow.transparent .brand,#navbar .navbar-inner.yellow.transparent .nav>li>a{text-shadow:0 1px 0 #c2b00c;color:#f2f2f2}#navbar .navbar-inner.yellow.transparent .brand .caret,#navbar .navbar-inner.yellow.transparent .nav>li>a .caret{border-bottom-color:#f2e88d;border-top-color:#f2e88d}#navbar .navbar-inner.yellow.transparent .brand:focus .caret,#navbar .navbar-inner.yellow.transparent .brand:hover .caret,#navbar .navbar-inner.yellow.transparent .nav>li>a:focus .caret,#navbar .navbar-inner.yellow.transparent .nav>li>a:hover .caret{border-bottom-color:#f2edbf;border-top-color:#f2edbf}#navbar .navbar-inner.yellow.transparent .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.yellow.transparent .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.yellow.transparent .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.yellow.transparent .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.yellow.transparent .nav>li.dropdown.open>.dropdown-toggle{background-color:rgba(216,202,71,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(194,176,12,.6),rgba(249,240,160,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(194,176,12,.6)),to(rgba(249,240,160,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(194,176,12,.6),rgba(249,240,160,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(194,176,12,.6),rgba(249,240,160,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(194,176,12,.6),rgba(249,240,160,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99c2b00c', endColorstr='#99f9f0a0', GradientType=0)}#navbar .navbar-inner.yellow.transparent .nav>li>a:hover{background-color:rgba(217,204,86,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(248,237,136,.6),rgba(170,154,10,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(248,237,136,.6)),to(rgba(170,154,10,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(248,237,136,.6),rgba(170,154,10,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(248,237,136,.6),rgba(170,154,10,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(248,237,136,.6),rgba(170,154,10,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99f8ed88', endColorstr='#99aa9a0a', GradientType=0)}#navbar .navbar-inner.green{background-color:#98f064;background-image:-moz-linear-gradient(top,#c8ffa7,#50da00);background-image:-webkit-gradient(linear,0 0,0 100%,from(#c8ffa7),to(#50da00));background-image:-webkit-linear-gradient(top,#c8ffa7,#50da00);background-image:-o-linear-gradient(top,#c8ffa7,#50da00);background-image:linear-gradient(to bottom,#c8ffa7,#50da00);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffc8ffa7', endColorstr='#ff50da00', GradientType=0)}#navbar .navbar-inner.green .brand,#navbar .navbar-inner.green .nav>li>a{text-shadow:0 1px 0 #50da00;color:#333}#navbar .navbar-inner.green .brand .caret,#navbar .navbar-inner.green .nav>li>a .caret{border-bottom-color:#55992e;border-top-color:#55992e}#navbar .navbar-inner.green .brand:focus .caret,#navbar .navbar-inner.green .brand:hover .caret,#navbar .navbar-inner.green .nav>li>a:focus .caret,#navbar .navbar-inner.green .nav>li>a:hover .caret{border-bottom-color:#446630;border-top-color:#446630}#navbar .navbar-inner.green .brand span{background-image:url(../img/tentacle-20x20.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.green .brand span{background-image:url(../img/tentacle-20x20@2x.png)}}#navbar .navbar-inner.green .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.green .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.green .nav>li.dropdown.open>.dropdown-toggle{background-color:#80e943;background-image:-moz-linear-gradient(top,#50da00,#c8ffa7);background-image:-webkit-gradient(linear,0 0,0 100%,from(#50da00),to(#c8ffa7));background-image:-webkit-linear-gradient(top,#50da00,#c8ffa7);background-image:-o-linear-gradient(top,#50da00,#c8ffa7);background-image:linear-gradient(to bottom,#50da00,#c8ffa7);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff50da00', endColorstr='#ffc8ffa7', GradientType=0)}#navbar .navbar-inner.green .nav>li>a:hover{background-color:#8ae655;background-image:-moz-linear-gradient(top,#b8ff8e,#47c100);background-image:-webkit-gradient(linear,0 0,0 100%,from(#b8ff8e),to(#47c100));background-image:-webkit-linear-gradient(top,#b8ff8e,#47c100);background-image:-o-linear-gradient(top,#b8ff8e,#47c100);background-image:linear-gradient(to bottom,#b8ff8e,#47c100);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffb8ff8e', endColorstr='#ff47c100', GradientType=0)}#navbar .navbar-inner.green.transparent{background-color:rgba(152,240,100,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(200,255,167,.6),rgba(80,218,0,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(200,255,167,.6)),to(rgba(80,218,0,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(200,255,167,.6),rgba(80,218,0,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(200,255,167,.6),rgba(80,218,0,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(200,255,167,.6),rgba(80,218,0,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99c8ffa7', endColorstr='#9950da00', GradientType=0)}#navbar .navbar-inner.green.transparent .brand,#navbar .navbar-inner.green.transparent .nav>li>a{text-shadow:0 1px 0 #50da00;color:#333}#navbar .navbar-inner.green.transparent .brand .caret,#navbar .navbar-inner.green.transparent .nav>li>a .caret{border-bottom-color:#55992e;border-top-color:#55992e}#navbar .navbar-inner.green.transparent .brand:focus .caret,#navbar .navbar-inner.green.transparent .brand:hover .caret,#navbar .navbar-inner.green.transparent .nav>li>a:focus .caret,#navbar .navbar-inner.green.transparent .nav>li>a:hover .caret{border-bottom-color:#446630;border-top-color:#446630}#navbar .navbar-inner.green.transparent .brand span{background-image:url(../img/tentacle-20x20.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.green.transparent .brand span{background-image:url(../img/tentacle-20x20@2x.png)}}#navbar .navbar-inner.green.transparent .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.green.transparent .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.green.transparent .nav>li.dropdown.open>.dropdown-toggle{background-color:rgba(128,233,67,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(80,218,0,.6),rgba(200,255,167,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(80,218,0,.6)),to(rgba(200,255,167,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(80,218,0,.6),rgba(200,255,167,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(80,218,0,.6),rgba(200,255,167,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(80,218,0,.6),rgba(200,255,167,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#9950da00', endColorstr='#99c8ffa7', GradientType=0)}#navbar .navbar-inner.green.transparent .nav>li>a:hover{background-color:rgba(138,230,85,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(184,255,142,.6),rgba(71,193,0,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(184,255,142,.6)),to(rgba(71,193,0,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(184,255,142,.6),rgba(71,193,0,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(184,255,142,.6),rgba(71,193,0,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(184,255,142,.6),rgba(71,193,0,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99b8ff8e', endColorstr='#9947c100', GradientType=0)}#navbar .navbar-inner.blue{background-color:#2e63cc;background-image:-moz-linear-gradient(top,#4d88ff,#002b80);background-image:-webkit-gradient(linear,0 0,0 100%,from(#4d88ff),to(#002b80));background-image:-webkit-linear-gradient(top,#4d88ff,#002b80);background-image:-o-linear-gradient(top,#4d88ff,#002b80);background-image:linear-gradient(to bottom,#4d88ff,#002b80);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff4d88ff', endColorstr='#ff002b80', GradientType=0)}#navbar .navbar-inner.blue .brand,#navbar .navbar-inner.blue .nav>li>a{text-shadow:0 1px 0 #1a66ff;color:#f2f2f2}#navbar .navbar-inner.blue .brand .caret,#navbar .navbar-inner.blue .nav>li>a .caret{border-bottom-color:#799bdf;border-top-color:#799bdf}#navbar .navbar-inner.blue .brand:focus .caret,#navbar .navbar-inner.blue .brand:hover .caret,#navbar .navbar-inner.blue .nav>li>a:focus .caret,#navbar .navbar-inner.blue .nav>li>a:hover .caret{border-bottom-color:#b6c7e9;border-top-color:#b6c7e9}#navbar .navbar-inner.blue .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.blue .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.blue .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.blue .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.blue .nav>li.dropdown.open>.dropdown-toggle{background-color:#1f50b3;background-image:-moz-linear-gradient(top,#002b80,#4d88ff);background-image:-webkit-gradient(linear,0 0,0 100%,from(#002b80),to(#4d88ff));background-image:-webkit-linear-gradient(top,#002b80,#4d88ff);background-image:-o-linear-gradient(top,#002b80,#4d88ff);background-image:linear-gradient(to bottom,#002b80,#4d88ff);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff002b80', endColorstr='#ff4d88ff', GradientType=0)}#navbar .navbar-inner.blue .nav>li>a:hover{background-color:#1f55c2;background-image:-moz-linear-gradient(top,#37f,#026);background-image:-webkit-gradient(linear,0 0,0 100%,from(#37f),to(#026));background-image:-webkit-linear-gradient(top,#37f,#026);background-image:-o-linear-gradient(top,#37f,#026);background-image:linear-gradient(to bottom,#37f,#026);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3377ff', endColorstr='#ff002266', GradientType=0)}#navbar .navbar-inner.blue.transparent{background-color:rgba(46,99,204,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(77,136,255,.6),rgba(0,43,128,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(77,136,255,.6)),to(rgba(0,43,128,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(77,136,255,.6),rgba(0,43,128,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(77,136,255,.6),rgba(0,43,128,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(77,136,255,.6),rgba(0,43,128,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#994d88ff', endColorstr='#99002b80', GradientType=0)}#navbar .navbar-inner.blue.transparent .brand,#navbar .navbar-inner.blue.transparent .nav>li>a{text-shadow:0 1px 0 #1a66ff;color:#f2f2f2}#navbar .navbar-inner.blue.transparent .brand .caret,#navbar .navbar-inner.blue.transparent .nav>li>a .caret{border-bottom-color:#799bdf;border-top-color:#799bdf}#navbar .navbar-inner.blue.transparent .brand:focus .caret,#navbar .navbar-inner.blue.transparent .brand:hover .caret,#navbar .navbar-inner.blue.transparent .nav>li>a:focus .caret,#navbar .navbar-inner.blue.transparent .nav>li>a:hover .caret{border-bottom-color:#b6c7e9;border-top-color:#b6c7e9}#navbar .navbar-inner.blue.transparent .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.blue.transparent .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.blue.transparent .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.blue.transparent .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.blue.transparent .nav>li.dropdown.open>.dropdown-toggle{background-color:rgba(31,80,179,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(0,43,128,.6),rgba(77,136,255,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(0,43,128,.6)),to(rgba(77,136,255,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(0,43,128,.6),rgba(77,136,255,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(0,43,128,.6),rgba(77,136,255,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(0,43,128,.6),rgba(77,136,255,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99002b80', endColorstr='#994d88ff', GradientType=0)}#navbar .navbar-inner.blue.transparent .nav>li>a:hover{background-color:rgba(31,85,194,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(51,119,255,.6),rgba(0,34,102,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(51,119,255,.6)),to(rgba(0,34,102,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(51,119,255,.6),rgba(0,34,102,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(51,119,255,.6),rgba(0,34,102,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(51,119,255,.6),rgba(0,34,102,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#993377ff', endColorstr='#99002266', GradientType=0)}#navbar .navbar-inner.violet{background-color:#9864f0;background-image:-moz-linear-gradient(top,#c8a7ff,#5000da);background-image:-webkit-gradient(linear,0 0,0 100%,from(#c8a7ff),to(#5000da));background-image:-webkit-linear-gradient(top,#c8a7ff,#5000da);background-image:-o-linear-gradient(top,#c8a7ff,#5000da);background-image:linear-gradient(to bottom,#c8a7ff,#5000da);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffc8a7ff', endColorstr='#ff5000da', GradientType=0)}#navbar .navbar-inner.violet .brand,#navbar .navbar-inner.violet .nav>li>a{text-shadow:0 1px 0 #a774ff;color:#f2f2f2}#navbar .navbar-inner.violet .brand .caret,#navbar .navbar-inner.violet .nav>li>a .caret{border-bottom-color:#b58df9;border-top-color:#b58df9}#navbar .navbar-inner.violet .brand:focus .caret,#navbar .navbar-inner.violet .brand:hover .caret,#navbar .navbar-inner.violet .nav>li>a:focus .caret,#navbar .navbar-inner.violet .nav>li>a:hover .caret{border-bottom-color:#d3bff5;border-top-color:#d3bff5}#navbar .navbar-inner.violet .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.violet .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.violet .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.violet .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.violet .nav>li.dropdown.open>.dropdown-toggle{background-color:#8043e9;background-image:-moz-linear-gradient(top,#5000da,#c8a7ff);background-image:-webkit-gradient(linear,0 0,0 100%,from(#5000da),to(#c8a7ff));background-image:-webkit-linear-gradient(top,#5000da,#c8a7ff);background-image:-o-linear-gradient(top,#5000da,#c8a7ff);background-image:linear-gradient(to bottom,#5000da,#c8a7ff);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5000da', endColorstr='#ffc8a7ff', GradientType=0)}#navbar .navbar-inner.violet .nav>li>a:hover{background-color:#8a55e6;background-image:-moz-linear-gradient(top,#b88eff,#4700c1);background-image:-webkit-gradient(linear,0 0,0 100%,from(#b88eff),to(#4700c1));background-image:-webkit-linear-gradient(top,#b88eff,#4700c1);background-image:-o-linear-gradient(top,#b88eff,#4700c1);background-image:linear-gradient(to bottom,#b88eff,#4700c1);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffb88eff', endColorstr='#ff4700c1', GradientType=0)}#navbar .navbar-inner.violet.transparent{background-color:rgba(152,100,240,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(200,167,255,.6),rgba(80,0,218,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(200,167,255,.6)),to(rgba(80,0,218,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(200,167,255,.6),rgba(80,0,218,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(200,167,255,.6),rgba(80,0,218,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(200,167,255,.6),rgba(80,0,218,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99c8a7ff', endColorstr='#995000da', GradientType=0)}#navbar .navbar-inner.violet.transparent .brand,#navbar .navbar-inner.violet.transparent .nav>li>a{text-shadow:0 1px 0 #a774ff;color:#f2f2f2}#navbar .navbar-inner.violet.transparent .brand .caret,#navbar .navbar-inner.violet.transparent .nav>li>a .caret{border-bottom-color:#b58df9;border-top-color:#b58df9}#navbar .navbar-inner.violet.transparent .brand:focus .caret,#navbar .navbar-inner.violet.transparent .brand:hover .caret,#navbar .navbar-inner.violet.transparent .nav>li>a:focus .caret,#navbar .navbar-inner.violet.transparent .nav>li>a:hover .caret{border-bottom-color:#d3bff5;border-top-color:#d3bff5}#navbar .navbar-inner.violet.transparent .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.violet.transparent .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.violet.transparent .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.violet.transparent .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.violet.transparent .nav>li.dropdown.open>.dropdown-toggle{background-color:rgba(128,67,233,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(80,0,218,.6),rgba(200,167,255,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(80,0,218,.6)),to(rgba(200,167,255,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(80,0,218,.6),rgba(200,167,255,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(80,0,218,.6),rgba(200,167,255,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(80,0,218,.6),rgba(200,167,255,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#995000da', endColorstr='#99c8a7ff', GradientType=0)}#navbar .navbar-inner.violet.transparent .nav>li>a:hover{background-color:rgba(138,85,230,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(184,142,255,.6),rgba(71,0,193,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(184,142,255,.6)),to(rgba(71,0,193,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(184,142,255,.6),rgba(71,0,193,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(184,142,255,.6),rgba(71,0,193,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(184,142,255,.6),rgba(71,0,193,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99b88eff', endColorstr='#994700c1', GradientType=0)}#navbar .navbar-inner.black{background-color:#4f4f4f;background-image:-moz-linear-gradient(top,#787878,#121212);background-image:-webkit-gradient(linear,0 0,0 100%,from(#787878),to(#121212));background-image:-webkit-linear-gradient(top,#787878,#121212);background-image:-o-linear-gradient(top,#787878,#121212);background-image:linear-gradient(to bottom,#787878,#121212);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff787878', endColorstr='#ff121212', GradientType=0)}#navbar .navbar-inner.black .brand,#navbar .navbar-inner.black .nav>li>a{text-shadow:0 1px 0 #5e5e5e;color:#f2f2f2}#navbar .navbar-inner.black .brand .caret,#navbar .navbar-inner.black .nav>li>a .caret{border-bottom-color:#959595;border-top-color:#959595}#navbar .navbar-inner.black .brand:focus .caret,#navbar .navbar-inner.black .brand:hover .caret,#navbar .navbar-inner.black .nav>li>a:focus .caret,#navbar .navbar-inner.black .nav>li>a:hover .caret{border-bottom-color:#c4c4c4;border-top-color:#c4c4c4}#navbar .navbar-inner.black .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.black .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.black .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.black .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.black .nav>li.dropdown.open>.dropdown-toggle{background-color:#3b3b3b;background-image:-moz-linear-gradient(top,#121212,#787878);background-image:-webkit-gradient(linear,0 0,0 100%,from(#121212),to(#787878));background-image:-webkit-linear-gradient(top,#121212,#787878);background-image:-o-linear-gradient(top,#121212,#787878);background-image:linear-gradient(to bottom,#121212,#787878);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff121212', endColorstr='#ff787878', GradientType=0)}#navbar .navbar-inner.black .nav>li>a:hover{background-color:#424242;background-image:-moz-linear-gradient(top,#6b6b6b,#050505);background-image:-webkit-gradient(linear,0 0,0 100%,from(#6b6b6b),to(#050505));background-image:-webkit-linear-gradient(top,#6b6b6b,#050505);background-image:-o-linear-gradient(top,#6b6b6b,#050505);background-image:linear-gradient(to bottom,#6b6b6b,#050505);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff6b6b6b', endColorstr='#ff050505', GradientType=0)}#navbar .navbar-inner.black.transparent{background-color:rgba(79,79,79,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(120,120,120,.6),rgba(18,18,18,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(120,120,120,.6)),to(rgba(18,18,18,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(120,120,120,.6),rgba(18,18,18,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(120,120,120,.6),rgba(18,18,18,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(120,120,120,.6),rgba(18,18,18,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99787878', endColorstr='#99121212', GradientType=0)}#navbar .navbar-inner.black.transparent .brand,#navbar .navbar-inner.black.transparent .nav>li>a{text-shadow:0 1px 0 #5e5e5e;color:#f2f2f2}#navbar .navbar-inner.black.transparent .brand .caret,#navbar .navbar-inner.black.transparent .nav>li>a .caret{border-bottom-color:#959595;border-top-color:#959595}#navbar .navbar-inner.black.transparent .brand:focus .caret,#navbar .navbar-inner.black.transparent .brand:hover .caret,#navbar .navbar-inner.black.transparent .nav>li>a:focus .caret,#navbar .navbar-inner.black.transparent .nav>li>a:hover .caret{border-bottom-color:#c4c4c4;border-top-color:#c4c4c4}#navbar .navbar-inner.black.transparent .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.black.transparent .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.black.transparent .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.black.transparent .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.black.transparent .nav>li.dropdown.open>.dropdown-toggle{background-color:rgba(59,59,59,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(18,18,18,.6),rgba(120,120,120,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(18,18,18,.6)),to(rgba(120,120,120,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(18,18,18,.6),rgba(120,120,120,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(18,18,18,.6),rgba(120,120,120,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(18,18,18,.6),rgba(120,120,120,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99121212', endColorstr='#99787878', GradientType=0)}#navbar .navbar-inner.black.transparent .nav>li>a:hover{background-color:rgba(66,66,66,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(107,107,107,.6),rgba(5,5,5,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(107,107,107,.6)),to(rgba(5,5,5,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(107,107,107,.6),rgba(5,5,5,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(107,107,107,.6),rgba(5,5,5,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(107,107,107,.6),rgba(5,5,5,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#996b6b6b', endColorstr='#99050505', GradientType=0)}#navbar .navbar-inner.white{background-color:#e9e9e9;background-image:-moz-linear-gradient(top,#fff,#c8c8c8);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#c8c8c8));background-image:-webkit-linear-gradient(top,#fff,#c8c8c8);background-image:-o-linear-gradient(top,#fff,#c8c8c8);background-image:linear-gradient(to bottom,#fff,#c8c8c8);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffc8c8c8', GradientType=0)}#navbar .navbar-inner.white .brand,#navbar .navbar-inner.white .nav>li>a{text-shadow:0 1px 0 #c8c8c8;color:#333}#navbar .navbar-inner.white .brand .caret,#navbar .navbar-inner.white .nav>li>a .caret{border-bottom-color:#919191;border-top-color:#919191}#navbar .navbar-inner.white .brand:focus .caret,#navbar .navbar-inner.white .brand:hover .caret,#navbar .navbar-inner.white .nav>li>a:focus .caret,#navbar .navbar-inner.white .nav>li>a:hover .caret{border-bottom-color:#626262;border-top-color:#626262}#navbar .navbar-inner.white .brand span{background-image:url(../img/tentacle-20x20.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.white .brand span{background-image:url(../img/tentacle-20x20@2x.png)}}#navbar .navbar-inner.white .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.white .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.white .nav>li.dropdown.open>.dropdown-toggle{background-color:#dedede;background-image:-moz-linear-gradient(top,#c8c8c8,#fff);background-image:-webkit-gradient(linear,0 0,0 100%,from(#c8c8c8),to(#fff));background-image:-webkit-linear-gradient(top,#c8c8c8,#fff);background-image:-o-linear-gradient(top,#c8c8c8,#fff);background-image:linear-gradient(to bottom,#c8c8c8,#fff);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffc8c8c8', endColorstr='#ffffffff', GradientType=0)}#navbar .navbar-inner.white .nav>li>a:hover{background-color:#dcdcdc;background-image:-moz-linear-gradient(top,#f2f2f2,#bbb);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f2f2f2),to(#bbb));background-image:-webkit-linear-gradient(top,#f2f2f2,#bbb);background-image:-o-linear-gradient(top,#f2f2f2,#bbb);background-image:linear-gradient(to bottom,#f2f2f2,#bbb);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2f2f2', endColorstr='#ffbbbbbb', GradientType=0)}#navbar .navbar-inner.white.transparent{background-color:rgba(233,233,233,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(255,255,255,.6),rgba(200,200,200,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(255,255,255,.6)),to(rgba(200,200,200,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(255,255,255,.6),rgba(200,200,200,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(255,255,255,.6),rgba(200,200,200,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(255,255,255,.6),rgba(200,200,200,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99ffffff', endColorstr='#99c8c8c8', GradientType=0)}#navbar .navbar-inner.white.transparent .brand,#navbar .navbar-inner.white.transparent .nav>li>a{text-shadow:0 1px 0 #c8c8c8;color:#333}#navbar .navbar-inner.white.transparent .brand .caret,#navbar .navbar-inner.white.transparent .nav>li>a .caret{border-bottom-color:#919191;border-top-color:#919191}#navbar .navbar-inner.white.transparent .brand:focus .caret,#navbar .navbar-inner.white.transparent .brand:hover .caret,#navbar .navbar-inner.white.transparent .nav>li>a:focus .caret,#navbar .navbar-inner.white.transparent .nav>li>a:hover .caret{border-bottom-color:#626262;border-top-color:#626262}#navbar .navbar-inner.white.transparent .brand span{background-image:url(../img/tentacle-20x20.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.white.transparent .brand span{background-image:url(../img/tentacle-20x20@2x.png)}}#navbar .navbar-inner.white.transparent .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.white.transparent .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.white.transparent .nav>li.dropdown.open>.dropdown-toggle{background-color:rgba(222,222,222,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(200,200,200,.6),rgba(255,255,255,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(200,200,200,.6)),to(rgba(255,255,255,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(200,200,200,.6),rgba(255,255,255,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(200,200,200,.6),rgba(255,255,255,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(200,200,200,.6),rgba(255,255,255,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99c8c8c8', endColorstr='#99ffffff', GradientType=0)}#navbar .navbar-inner.white.transparent .nav>li>a:hover{background-color:rgba(220,220,220,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(242,242,242,.6),rgba(187,187,187,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(242,242,242,.6)),to(rgba(187,187,187,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(242,242,242,.6),rgba(187,187,187,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(242,242,242,.6),rgba(187,187,187,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(242,242,242,.6),rgba(187,187,187,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99f2f2f2', endColorstr='#99bbbbbb', GradientType=0)}#navbar .navbar-inner .brand{padding:10px 20px 6px}#navbar .navbar-inner .brand span{padding-left:26px;background-size:20px 20px;background-repeat:no-repeat;display:inline-block;max-width:250px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;vertical-align:top;line-height:20px;height:24px}#navbar_login a.dropdown-toggle span{display:inline-block;max-width:100px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;vertical-align:top}.octoprint-container{margin-top:20px}.octoprint-container .tab-content{padding:9px 15px;border-left:1px solid #DDD;border-right:1px solid #DDD;border-bottom:1px solid #DDD;-webkit-border-bottom-right-radius:4px;-moz-border-radius-bottomright:4px;border-bottom-right-radius:4px;-webkit-border-bottom-left-radius:4px;-moz-border-radius-bottomleft:4px;border-bottom-left-radius:4px}.octoprint-container .nav{margin-bottom:0}.octoprint-container .tab-content h1{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:40px;color:#333;border:0;border-bottom:1px solid #E5E5E5;font-weight:400}.octoprint-container .accordion-heading .accordion-heading-button{float:right}.octoprint-container .accordion-heading .accordion-heading-button a{display:inline-block;padding:8px 15px;font-size:14px;line-height:20px;color:#000;text-decoration:none;background:0 0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.octoprint-container .accordion-heading a.accordion-toggle{display:inline-block}.octoprint-container .accordion-heading [class*=" icon-"],.octoprint-container .accordion-heading [class^=icon-]{color:#000}.print-control .btn{padding-left:4px;padding-right:4px}.upload-buttons .btn{margin-right:0}table{table-layout:fixed}table .popover-title{text-overflow:ellipsis;word-break:break-all}table td,table th{overflow:hidden}table td.gcode_files_name,table th.gcode_files_name{text-overflow:ellipsis;text-align:left;white-space:nowrap}table td.gcode_files_action,table th.gcode_files_action{width:90px;text-align:center;white-space:nowrap}table td.gcode_files_action a,table th.gcode_files_action a{text-decoration:none;color:#000}table td.gcode_files_action a.disabled,table th.gcode_files_action a.disabled{color:#ccc;cursor:default}table td.timelapse_files_checkbox,table td.timelapse_unrendered_checkbox,table th.timelapse_files_checkbox,table th.timelapse_unrendered_checkbox{text-align:center;width:10px}table td.timelapse_files_checkbox input[type=checkbox],table td.timelapse_unrendered_checkbox input[type=checkbox],table th.timelapse_files_checkbox input[type=checkbox],table th.timelapse_unrendered_checkbox input[type=checkbox]{margin-top:0}table td.timelapse_files_name,table td.timelapse_unrendered_name,table th.timelapse_files_name,table th.timelapse_unrendered_name{text-overflow:ellipsis;text-align:left}table td.timelapse_files_size,table td.timelapse_unrendered_size,table th.timelapse_files_size,table th.timelapse_unrendered_size{text-align:right;width:55px}table td.timelapse_unrendered_count,table th.timelapse_unrendered_count{text-align:right;width:45px}table td.timelapse_files_action,table td.timelapse_unrendered_action,table th.timelapse_files_action,table th.timelapse_unrendered_action{width:45px;text-align:center;white-space:nowrap}table td.timelapse_files_action a,table td.timelapse_unrendered_action a,table th.timelapse_files_action a,table th.timelapse_unrendered_action a{text-decoration:none;color:#000}table td.timelapse_files_action a.disabled,table td.timelapse_unrendered_action a.disabled,table th.timelapse_files_action a.disabled,table th.timelapse_unrendered_action a.disabled{color:#ccc;cursor:default}table td.settings_users_name,table th.settings_users_name{text-overflow:ellipsis;text-align:left}table td.settings_users_active,table td.settings_users_admin,table th.settings_users_active,table th.settings_users_admin{text-align:center;width:55px}table td.settings_users_actions,table th.settings_users_actions{width:60px;text-align:center;white-space:nowrap}table td.settings_users_actions a,table th.settings_users_actions a{text-decoration:none;color:#000}table td.settings_users_actions a.disabled,table th.settings_users_actions a.disabled{color:#ccc;cursor:default}table td.settings_logs_name,table th.settings_logs_name{text-overflow:ellipsis;text-align:left}table td.settings_logs_size,table th.settings_logs_size{text-align:right;width:70px}table td.settings_logs_date,table th.settings_logs_date{text-align:left;width:130px}table td.settings_logs_action,table th.settings_logs_action{width:70px;text-align:center;white-space:nowrap}table td.settings_logs_action a,table th.settings_logs_action a{text-decoration:none;color:#000}table td.settings_logs_action a.disabled,table th.settings_logs_action a.disabled{color:#ccc;cursor:default}table td.settings_printerProfiles_profiles_name,table th.settings_printerProfiles_profiles_name{text-overflow:ellipsis;text-align:left}table td.settings_printerProfiles_profiles_model,table th.settings_printerProfiles_profiles_model{text-align:left;width:250px}table td.settings_printerProfiles_profiles_action,table th.settings_printerProfiles_profiles_action{width:80px;text-align:center;white-space:nowrap}table td.settings_printerProfiles_profiles_action a,table th.settings_printerProfiles_profiles_action a{text-decoration:none;color:#000}table td.settings_printerProfiles_profiles_action a.disabled,table th.settings_printerProfiles_profiles_action a.disabled{color:#ccc;cursor:default}#temperature-graph{height:350px;width:100%;background:url(../img/graph-background.png) center no-repeat}#temperature-table{table-layout:fixed;width:100%;margin-top:20px}#temperature-table td.temperature_actual,#temperature-table td.temperature_offset,#temperature-table td.temperature_target,#temperature-table td.temperature_tool,#temperature-table th.temperature_actual,#temperature-table th.temperature_offset,#temperature-table th.temperature_target,#temperature-table th.temperature_tool{vertical-align:middle;text-align:center}#temperature-table td.temperature_actual form,#temperature-table td.temperature_offset form,#temperature-table td.temperature_target form,#temperature-table td.temperature_tool form,#temperature-table th.temperature_actual form,#temperature-table th.temperature_offset form,#temperature-table th.temperature_target form,#temperature-table th.temperature_tool form{margin:0}#temperature-table td.temperature_actual .dropdown-menu,#temperature-table td.temperature_offset .dropdown-menu,#temperature-table td.temperature_target .dropdown-menu,#temperature-table td.temperature_tool .dropdown-menu,#temperature-table th.temperature_actual .dropdown-menu,#temperature-table th.temperature_offset .dropdown-menu,#temperature-table th.temperature_target .dropdown-menu,#temperature-table th.temperature_tool .dropdown-menu{text-align:left}#temperature-table td.temperature_tool,#temperature-table th.temperature_tool{width:16%;text-align:left;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}#temperature-table td.temperature_actual,#temperature-table th.temperature_actual{width:12%}#temperature-table td.temperature_target,#temperature-table th.temperature_target{width:42%;overflow:visible}#temperature-table td.temperature_offset,#temperature-table th.temperature_offset{width:30%}.tab-content,.tab-pane{overflow:visible}#speed_fill,#speed_innerWall,#speed_outerWall,#speed_support,#temp_newBedTemp,#temp_newTemp,#webcam_timelapse_fps,#webcam_timelapse_interval,#webcam_timelapse_postRoll,#webcam_timelapse_retractionZHop{text-align:right}ul.dropdown-menu li a{cursor:pointer}#connection_baudrates,#connection_ports,#connection_printers{width:100%}#offline_overlay,#reloadui_overlay{position:fixed;top:0;left:0;width:100%;height:100%;display:none}#offline_overlay{z-index:10002}#reloadui_overlay{z-index:10001}#offline_overlay_background,#reloadui_overlay_background{position:fixed;top:0;left:0;width:100%;height:100%;background-color:#000;filter:alpha(opacity=50);-moz-opacity:.5;-khtml-opacity:.5;opacity:.5}#offline_overlay_wrapper,#reloadui_overlay_wrapper{position:absolute;top:0;bottom:0;left:0;right:0;padding-top:60px}#offline_overlay_wrapper .container,#reloadui_overlay_wrapper .container{margin:auto}#webcam_container{width:100%;position:relative;outline:0;background-color:#000}#webcam_container .keycontrol_overlay{position:absolute;left:10px;right:10px;bottom:10px;background:rgba(0,0,0,.5);font-size:85%;color:#fff;padding:0}#webcam_container .keycontrol_overlay kbd{border:1px solid #eee;border-radius:3px;margin-left:2px;margin-right:2px;font-size:90%;padding:2px;min-width:1em}#webcam_container .keycontrol_overlay .keycontrol_overlay_heading{position:relative;padding:10px;font-weight:700}#webcam_container .keycontrol_overlay .keycontrol_overlay_column{position:relative;width:45%;padding:10px;float:left}#webcam_container .nowebcam{position:absolute;top:0;left:0;right:0;bottom:0}#webcam_container .nowebcam .text{color:#fff;text-align:center;position:relative;margin:auto;width:80%;top:50%;transform:translateY(-50%);display:block}#webcam_container .nowebcam .text.webcam_loading{animation:pulsate 3s ease-out;animation-iteration-count:infinite}#webcam_container .webcam_rotated{position:relative;width:100%;padding-bottom:100%;pointer-events:none}#webcam_container .webcam_rotated .webcam_fixed_ratio{position:absolute;transform:rotate(-90deg);top:0;bottom:0;pointer-events:none}#webcam_container .webcam_rotated .webcam_fixed_ratio .webcam_fixed_ratio_inner{width:100%;height:100%;pointer-events:none}#webcam_container .webcam_unrotated .webcam_fixed_ratio{width:100%;pointer-events:none;padding-bottom:100%;position:relative}#webcam_container .webcam_unrotated .webcam_fixed_ratio.ratio43{padding-bottom:75%}#webcam_container .webcam_unrotated .webcam_fixed_ratio.ratio169{padding-bottom:56.25%}#webcam_container .webcam_unrotated .webcam_fixed_ratio.ratio1610{padding-bottom:62.5%}#webcam_container .webcam_unrotated .webcam_fixed_ratio .webcam_fixed_ratio_inner{position:absolute;top:0;bottom:0;left:0;right:0;pointer-events:none}#webcam_container img{width:100%;height:100%;object-fit:contain}#state_wrapper hr{margin:5px 0}#files .gcode_files{padding-right:7px}#files .gcode_files .entry{padding:5px;line-height:20px;border-bottom:1px solid #ddd;position:relative}#files .gcode_files .entry:hover{background-color:#f5f5f5}#files .gcode_files .entry .title{text-overflow:ellipsis;word-break:break-all}#files .gcode_files .entry .additionalInfo,#files .gcode_files .entry .size,#files .gcode_files .entry .uploaded{font-size:85%;color:#999}#files .gcode_files .entry .action-buttons{position:absolute;bottom:5px;right:5px}#files .gcode_files .entry .additionalInfo{padding-bottom:22px}@keyframes highlightframes{0%{background:#ff0}100%{background:0 0}}#files .gcode_files .entry.highlight{animation:highlightframes 2s}#files .gcode_files .back .back-path{white-space:nowrap}#files .gcode_files .back .back-path span{word-wrap:break-word;white-space:pre-line}#files .upload-buttons{margin-top:10px}#files .form-search{text-align:center;margin-bottom:5px!important}#control{overflow:hidden}#control .jog-panel{float:left;margin-right:19px}#control h1{text-align:left}#control .jog-panel>div{text-align:center}#control .jog-panel>div.distance{text-align:left}#control .jog-panel .slider{margin-bottom:10px}#control .box{width:30px;height:30px;margin-right:10px;margin-bottom:10px;padding-left:8px}#control .control-box{display:block;height:30px;margin-bottom:10px}#control .btn-group{margin-bottom:10px}#control .btn-group.distance>.btn{width:43px;padding:3px 0;height:30px}#control .slider-handle{width:14px;height:14px;margin-left:-7px;margin-top:-3px}#control .custom_section h1{cursor:pointer}#control .custom_section_horizontal>.custom_control{display:inline-block}#control .custom_section_vertical>.custom_control{display:block}#control .custom_control .slider{margin-left:10px;margin-right:10px;margin-bottom:2px}#gcode .progress{width:588px}#gcode .progress .bar{-webkit-transition:width 0s linear;-moz-transition:width 0s linear;-o-transition:width 0s linear;transition:width 0s linear}#gcode .canvas_container{position:relative}#gcode .canvas_container:active,#gcode .canvas_container:hover{outline:0}#gcode .layer-buttons{padding-top:5px;padding-bottom:7px}#gcode #gcode_layer_slider{position:absolute;right:0;top:0;height:568px;float:right}#gcode #gcode_layer_slider .slider-handle{width:14px;height:14px;margin-left:-3px;margin-top:-7px}#gcode #gcode_command_slider .slider-handle{width:14px;height:14px;margin-left:-7px;margin-top:-3px}#term .terminal{margin-bottom:30px}#term .terminal #terminal-output,#term .terminal #terminal-output-lowfi{min-height:340px;margin-bottom:5px}#settings_dialog .aboutlink{float:left}#settings_dialog_menu,#wizard_dialog_menu{margin-left:0}#wizard_firstrun_acl .acl_decision{margin-top:1em}#settings_appearance_managelanguagesdialog_emptylist{overflow:hidden;width:100%;height:300px;text-align:center;display:table}#settings_appearance_managelanguagesdialog_emptylist div{display:table-cell;vertical-align:middle}.footer ul{margin:0}.footer ul li{display:inline;margin-left:1em;font-size:85%}.footer ul li:first-child{margin-left:0}.footer ul li a{color:#555}.ui-pnotify .alert a{color:#c09853}.ui-pnotify .alert-danger a,.ui-pnotify .alert-error a{color:#b94a48}.ui-pnotify .alert-success a{color:#468847}.ui-pnotify .alert-info a{color:#3a87ad}.pnotify_additional_info .pnotify_more{font-size:85%}.text-right{text-align:right}.text-center{text-align:center}.overflow_visible{overflow:visible!important}.clickable{cursor:pointer}.border_box{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.hidden{display:none}textarea.block{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;width:100%}@keyframes pulsate{0%{opacity:.5}50%{opacity:1}100%{opacity:.5}}#drop_overlay{position:fixed;top:0;left:0;width:100%;height:100%;z-index:10000;display:none}#drop_overlay.in{display:block}#drop_overlay #drop_overlay_background{position:fixed;top:0;left:0;width:100%;height:100%;background-color:#000;filter:alpha(opacity=50);-moz-opacity:.5;-khtml-opacity:.5;opacity:.5}#drop_overlay #drop_overlay_wrapper{position:absolute;top:0;bottom:0;left:0;right:0;padding-top:60px}#drop_overlay #drop_overlay_wrapper #drop,#drop_overlay #drop_overlay_wrapper #drop_background{position:absolute;top:0;left:0;margin-left:0;width:100%}#drop_overlay #drop_overlay_wrapper #drop_locally,#drop_overlay #drop_overlay_wrapper #drop_locally_background{position:absolute;top:0;left:50%;margin-left:-50%;width:50%;border-right:2px dashed #ccc}#drop_overlay #drop_overlay_wrapper #drop_sd,#drop_overlay #drop_overlay_wrapper #drop_sd_background{position:absolute;top:0;left:50%;margin-left:0;width:50%;border-left:2px dashed #ccc}#drop_overlay #drop_overlay_wrapper .dropzone{height:100%;z-index:10001;color:#fff;font-size:30px}#drop_overlay #drop_overlay_wrapper .dropzone i{font-size:50px}#drop_overlay #drop_overlay_wrapper .dropzone .text{display:block;text-align:center;line-height:40px;position:absolute;width:100%;bottom:5%;filter:alpha(opacity=100);-moz-opacity:1;-khtml-opacity:1;opacity:1}#drop_overlay #drop_overlay_wrapper .dropzone_background{width:50%;height:100%;background-color:#000;filter:alpha(opacity=25);-moz-opacity:.25;-khtml-opacity:.25;opacity:.25}#drop_overlay #drop_overlay_wrapper .dropzone_background.hover{background-color:#000;filter:alpha(opacity=50);-moz-opacity:.5;-khtml-opacity:.5;opacity:.5}#drop_overlay #drop_overlay_wrapper .dropzone_background.fade{-webkit-transition:all .3s ease-out;-moz-transition:all .3s ease-out;-ms-transition:all .3s ease-out;-o-transition:all .3s ease-out;transition:all .3s ease-out;opacity:1}.icon-sd-black-14{background:url(../img/icon-sd-black-14.png) 0 3px no-repeat;width:11px;height:17px;display:inline-block!important}.center{float:none;margin-left:auto;margin-right:auto}.flipH{-webkit-transform:scaleX(-1);-moz-transform:scaleX(-1);-ms-transform:scaleX(-1);transform:scaleX(-1)}.flipV{-webkit-transform:scaleY(-1);-moz-transform:scaleY(-1);-ms-transform:scaleY(-1);transform:scaleY(-1)}.flipH.flipV{-webkit-transform:scaleX(-1) scaleY(-1);-moz-transform:scaleX(-1) scaleY(-1);-ms-transform:scaleX(-1) scaleY(-1);transform:scaleX(-1) scaleY(-1)}.rotate90{-webkit-transform:rotate(-90deg);transform:rotate(-90deg)}.ui-pnotify a{text-decoration:underline}.btn-mini .caret,.btn-small .caret{margin-top:8px}.dropdown-menu-right{right:0;left:auto}.slider .slider-selection{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,.25);background-color:#006dcc;background-image:-moz-linear-gradient(top,#08c,#04c);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#04c));background-image:-webkit-linear-gradient(top,#08c,#04c);background-image:-o-linear-gradient(top,#08c,#04c);background-image:linear-gradient(to bottom,#08c,#04c);background-repeat:repeat-x;border-color:#04c #04c #002a80;border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);*background-color:#04c;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.slider .slider-selection.active,.slider .slider-selection.disabled,.slider .slider-selection:active,.slider .slider-selection:focus,.slider .slider-selection:hover,.slider .slider-selection[disabled]{color:#fff;background-color:#04c;*background-color:#003bb3}.slider .slider-selection.active,.slider .slider-selection:active{background-color:#039 \9}.slider.slider-disabled .slider-selection{background-image:none;opacity:.65;filter:alpha(opacity=65);-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.slider .slider-track{background-color:#f5f5f5;border:1px solid #e3e3e3;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05)}.slider.slider-disabled .slider-track{background-image:none;opacity:.65;filter:alpha(opacity=65);-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.slider .slider-handle{display:inline-block;*display:inline;*zoom:1;font-size:14px;line-height:20px;text-align:center;vertical-align:middle;cursor:pointer;color:#333;text-shadow:0 1px 1px rgba(255,255,255,.75);background-color:#f5f5f5;background-image:-moz-linear-gradient(top,#fff,#e6e6e6);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#e6e6e6));background-image:-webkit-linear-gradient(top,#fff,#e6e6e6);background-image:-o-linear-gradient(top,#fff,#e6e6e6);background-image:linear-gradient(to bottom,#fff,#e6e6e6);background-repeat:repeat-x;*background-color:#e6e6e6;border:1px solid #ccc;*border:0;border-bottom-color:#b3b3b3;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;*margin-left:.3em;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05);padding:0;margin-bottom:0;opacity:1;filter:alpha(opacity=100)}.slider .slider-handle.active,.slider .slider-handle.disabled,.slider .slider-handle:active,.slider .slider-handle:focus,.slider .slider-handle:hover,.slider .slider-handle[disabled]{color:#333;background-color:#e6e6e6;*background-color:#d9d9d9}.slider .slider-handle.active,.slider .slider-handle:active{background-color:#ccc \9}.slider .slider-handle:first-child{*margin-left:0}.slider .slider-handle:focus,.slider .slider-handle:hover{color:#333;text-decoration:none;background-position:0 -15px;-webkit-transition:background-position .1s linear;-moz-transition:background-position .1s linear;-o-transition:background-position .1s linear;transition:background-position .1s linear}.slider .slider-handle:focus{outline:#333 dotted thin;outline:-webkit-focus-ring-color auto 5px;outline-offset:-2px}.slider .slider-handle.active,.slider .slider-handle:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,.15),0 1px 2px rgba(0,0,0,.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,.15),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 2px 4px rgba(0,0,0,.15),0 1px 2px rgba(0,0,0,.05)}.slider .slider-handle.disabled,.slider .slider-handle[disabled]{cursor:default;background-image:none;opacity:.65;filter:alpha(opacity=65);-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.slider .slider-handle.hide{display:none}.slider .slider-handle.round{-webkit-border-radius:50%;-moz-border-radius:50%;border-radius:50%}.modal.large{width:975px;margin-left:-487px}.full-sized-box{position:absolute;bottom:0;left:0;right:0;top:0;padding:15px}.full-sized-box .row-fluid{height:100%}@media (max-width:979px){.full-sized-box{position:static}}:root .full-sized-box,_::-webkit-full-page-media,_:future{position:static}.scrollable{height:100%;overflow:auto;-webkit-overflow-scrolling:touch}.pre-output span{display:block}.input-append .add-on.add-on-limited,.input-prepend .add-on.add-on-limited{overflow-x:hidden;text-overflow:ellipsis;width:inherit}.input-append .btn-group:first-child .btn:first-child,.input-prepend .btn-group:first-child .btn:first-child{-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.input-append .btn-group .btn:first-child,.input-prepend .btn-group .btn:first-child{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.input-append.input-block-level,.input-prepend.input-block-level{display:table}.input-append.input-block-level .add-on,.input-prepend.input-block-level .add-on{display:table-cell;width:1%}.input-append.input-block-level>input,.input-prepend.input-block-level>input{box-sizing:border-box;display:table;min-height:inherit;width:100%}.input-append.input-block-level :not(:last-child),.input-prepend.input-block-level :not(:last-child){border-right:0}.control-group.error .input-append .fileinput-button,.control-group.error .input-prepend .fileinput-button{border-color:#b94a48}.control-text{padding-top:5px;cursor:default}input[type=number]{text-align:right}input[type=number].input-nospin::-webkit-inner-spin-button,input[type=number].input-nospin::-webkit-outer-spin-button{-webkit-appearance:none;margin:0}input[type=number].input-nospin{-moz-appearance:textfield}.progress-text,.progress-text-centered{position:relative}.progress-text .progress-text-back,.progress-text .progress-text-front,.progress-text-centered .progress-text-back,.progress-text-centered .progress-text-front{white-space:nowrap}.progress-text .progress-text-front,.progress-text-centered .progress-text-front{box-sizing:border-box;padding:0 10px;width:100%;display:block}.progress-text .progress-text-back,.progress-text-centered .progress-text-back{position:absolute;font-size:12px;line-height:20px;display:block;box-sizing:border-box;text-align:center;padding:0 10px}.progress-text .bar,.progress-text-centered .bar{position:absolute;overflow:hidden}.progress-text-centered .progress-text-front{position:absolute;font-size:12px;line-height:20px;display:block;text-align:center;color:#fff}.progress-text-centered .progress-text-back{width:100%}#navbar_login:not(.open) #login_dropdown_loggedout{display:block;z-index:-1;height:0;width:0;padding:0!important;overflow:hidden;border:0;box-shadow:none;left:-9999px}#navbar_login:not(.open) #login_dropdown_loggedout.hide{display:none}#loginForm{margin:0}#loginForm button{margin-top:20px} \ No newline at end of file diff --git a/src/octoprint/static/less/octoprint.less b/src/octoprint/static/less/octoprint.less index e439260dc9..aae4afce15 100644 --- a/src/octoprint/static/less/octoprint.less +++ b/src/octoprint/static/less/octoprint.less @@ -1313,15 +1313,11 @@ _::-webkit-full-page-media, _:future, :root .full-sized-box { min-height: inherit; width: 100%; } - } -} - -.input-append.input-block-level > input { - border-right: 0; -} -.input-prepend.input-block-level > input { - border-left: 0; + :not(:last-child) { + border-right: 0; + } + } } .btn-input-dec, From b138c2bccfbd931626da53bcf92cb973d0c660ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Tue, 22 Aug 2017 18:05:53 +0200 Subject: [PATCH 012/333] New global JS helper copyToClipboard --- src/octoprint/static/js/app/helpers.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/octoprint/static/js/app/helpers.js b/src/octoprint/static/js/app/helpers.js index abd8043661..c229ac2a74 100644 --- a/src/octoprint/static/js/app/helpers.js +++ b/src/octoprint/static/js/app/helpers.js @@ -1120,3 +1120,11 @@ var escapeUnprintableCharacters = function(str) { } return result; }; + +var copyToClipboard = function(text) { + var temp = $(""); + $("body").append(temp); + temp.val(text).select(); + document.execCommand("copy"); + temp.remove(); +}; From e9401b7090b5f3a1ef5173e94fc30ad6c71d910f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Tue, 22 Aug 2017 18:06:04 +0200 Subject: [PATCH 013/333] Easier copying of API keys There's now a dedicated button for that. This should get around issues like #2056. Also increased size of API key display field (where possible). Fixes #2055 --- src/octoprint/static/js/app/viewmodels/settings.js | 4 ++++ src/octoprint/static/js/app/viewmodels/users.js | 4 ++++ .../static/js/app/viewmodels/usersettings.js | 4 ++++ .../templates/dialogs/settings/accesscontrol.jinja2 | 11 ++++++----- src/octoprint/templates/dialogs/settings/api.jinja2 | 7 ++++--- .../templates/dialogs/usersettings/access.jinja2 | 9 +++++---- 6 files changed, 27 insertions(+), 12 deletions(-) diff --git a/src/octoprint/static/js/app/viewmodels/settings.js b/src/octoprint/static/js/app/viewmodels/settings.js index 1f5e6f3245..17c45437cb 100644 --- a/src/octoprint/static/js/app/viewmodels/settings.js +++ b/src/octoprint/static/js/app/viewmodels/settings.js @@ -475,6 +475,10 @@ $(function() { }); }; + self.copyApiKey = function() { + copyToClipboard(self.api_key()); + }; + self.showTranslationManager = function() { self.translationManagerDialog.modal(); return false; diff --git a/src/octoprint/static/js/app/viewmodels/users.js b/src/octoprint/static/js/app/viewmodels/users.js index 06b47be461..adb733d161 100644 --- a/src/octoprint/static/js/app/viewmodels/users.js +++ b/src/octoprint/static/js/app/viewmodels/users.js @@ -152,6 +152,10 @@ $(function() { }); }; + self.copyApikey = function() { + copyToClipboard(self.editorApikey()); + }; + self._updateApikey = function(apikey) { self.editorApikey(apikey); self.requestData(); diff --git a/src/octoprint/static/js/app/viewmodels/usersettings.js b/src/octoprint/static/js/app/viewmodels/usersettings.js index 27bfbae97a..7f7374c677 100644 --- a/src/octoprint/static/js/app/viewmodels/usersettings.js +++ b/src/octoprint/static/js/app/viewmodels/usersettings.js @@ -69,6 +69,10 @@ $(function() { }); }; + self.copyApikey = function() { + copyToClipboard(self.access_apikey()); + }; + self.generateApikey = function() { if (!CONFIG_ACCESS_CONTROL) return; diff --git a/src/octoprint/templates/dialogs/settings/accesscontrol.jinja2 b/src/octoprint/templates/dialogs/settings/accesscontrol.jinja2 index 735f0f2f30..2b7d780846 100644 --- a/src/octoprint/templates/dialogs/settings/accesscontrol.jinja2 +++ b/src/octoprint/templates/dialogs/settings/accesscontrol.jinja2 @@ -9,7 +9,7 @@ -
{{ _('API Key') }}:
+
{{ _('API Key') }}:
@@ -136,10 +136,11 @@
-
- - - +
+ + + +
diff --git a/src/octoprint/templates/dialogs/settings/api.jinja2 b/src/octoprint/templates/dialogs/settings/api.jinja2 index 1ca294fcbe..77df62960d 100644 --- a/src/octoprint/templates/dialogs/settings/api.jinja2 +++ b/src/octoprint/templates/dialogs/settings/api.jinja2 @@ -16,9 +16,10 @@
-
- - +
+ + +
{{ _('Please note that changes to the API key are applied immediately, without having to "Save" first.') }}
diff --git a/src/octoprint/templates/dialogs/usersettings/access.jinja2 b/src/octoprint/templates/dialogs/usersettings/access.jinja2 index 2fa52053d5..9b17779c2e 100644 --- a/src/octoprint/templates/dialogs/usersettings/access.jinja2 +++ b/src/octoprint/templates/dialogs/usersettings/access.jinja2 @@ -23,10 +23,11 @@
-
- - - +
+ + + +
{{ _('Please note that changes to the API key are applied immediately, without having to "Confirm" first.') }}
From 56ad1841d4b2645a053cc01a65a02fde80059346 Mon Sep 17 00:00:00 2001 From: Teja Date: Fri, 8 Sep 2017 16:20:37 +0200 Subject: [PATCH 014/333] added decorator for before firstrun only calls. --- src/octoprint/server/util/flask.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/octoprint/server/util/flask.py b/src/octoprint/server/util/flask.py index 682b905384..2d0a76f8b0 100644 --- a/src/octoprint/server/util/flask.py +++ b/src/octoprint/server/util/flask.py @@ -1110,6 +1110,23 @@ def decorated_view(*args, **kwargs): return decorated_view +def firstrun_only_access(func): + """ + If you decorate a view with this, it will ensure that first setup has _not_ been + done for OctoPrint's Access Control. Otherwise it + will cause a HTTP 403 status code to be returned by the decorated resource. + """ + @functools.wraps(func) + def decorated_view(*args, **kwargs): + # if OctoPrint has been set up yet, abort + if settings().getBoolean(["server", "firstRun"]) and (octoprint.server.userManager is None or not octoprint.server.userManager.hasBeenCustomized()): + return func(*args, **kwargs) + else: + return flask.make_response("OctoPrint is already setup, this resource is not longer available.", 403) + + return decorated_view + + class AppSessionManager(object): VALIDITY_UNVERIFIED = 1 * 60 # 1 minute From 791f0457fb007c2ba8ab9b15f8d9be26ff6b2230 Mon Sep 17 00:00:00 2001 From: Luke McKechnie Date: Thu, 21 Sep 2017 09:30:10 -0400 Subject: [PATCH 015/333] Fix for SharedNozzle, replicating temp from current tool to all tools. Issue # 2077 --- AUTHORS.md | 1 + src/octoprint/util/comm.py | 14 ++++++++++---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/AUTHORS.md b/AUTHORS.md index a56725a755..c7f585f86a 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -81,6 +81,7 @@ date of first contribution): * [Shawn Bruce](https://github.com/kantlivelong) * [Claudiu Ceia] (https://github.com/ClaudiuCeia) * [Goswin von Brederlow](https://github.com/mrvn) + * [Luke McKechnie](https://github.com/galamdring) OctoPrint started off as a fork of [Cura](https://github.com/daid/Cura) by [Daid Braam](https://github.com/daid). Parts of its communication layer and diff --git a/src/octoprint/util/comm.py b/src/octoprint/util/comm.py index ecd0a74e04..dbfe7029c0 100644 --- a/src/octoprint/util/comm.py +++ b/src/octoprint/util/comm.py @@ -1130,7 +1130,7 @@ def _recordFilePosition(self): def _processTemperatures(self, line): current_tool = self._currentTool if self._currentTool is not None else 0 maxToolNum, parsedTemps = parse_temperature_line(line, current_tool) - +# self._logger.info("current_tool: %d, maxToolNum: %d, parsedTemps: %s"%(current_tool,maxToolNum,parsedTemps)) for name, hook in self._temperature_hooks.items(): try: parsedTemps = hook(self, parsedTemps) @@ -1139,13 +1139,19 @@ def _processTemperatures(self, line): except: self._logger.exception("Error while processing temperatures in {}, skipping".format(name)) - if "T0" in parsedTemps.keys(): + printer_profile=self._printerProfileManager.get_current_or_default() + shared_nozzle = printer_profile["extruder"]["sharedNozzle"] + + if "T%d"%current_tool in parsedTemps.keys(): for n in range(maxToolNum + 1): tool = "T%d" % n - if not tool in parsedTemps.keys(): + if not tool in parsedTemps.keys() and not shared_nozzle: continue + elif not tool in parsedTemps.keys() and shared_nozzle: + actual, target = parsedTemps["T%d"%current_tool] - actual, target = parsedTemps[tool] + else: + actual, target = parsedTemps[tool] self.last_temperature.set_tool(n, actual=actual, target=target) # bed temperature From 0f325b95c1c84c9fdc387d07fc5b3f50ef18f50b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Fri, 29 Sep 2017 16:18:52 +0200 Subject: [PATCH 016/333] Minor changes to #2123 for better readability --- src/octoprint/util/comm.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/octoprint/util/comm.py b/src/octoprint/util/comm.py index dbfe7029c0..8078495a2e 100644 --- a/src/octoprint/util/comm.py +++ b/src/octoprint/util/comm.py @@ -1129,8 +1129,9 @@ def _recordFilePosition(self): def _processTemperatures(self, line): current_tool = self._currentTool if self._currentTool is not None else 0 + current_tool_key = "T%d" % current_tool maxToolNum, parsedTemps = parse_temperature_line(line, current_tool) -# self._logger.info("current_tool: %d, maxToolNum: %d, parsedTemps: %s"%(current_tool,maxToolNum,parsedTemps)) + for name, hook in self._temperature_hooks.items(): try: parsedTemps = hook(self, parsedTemps) @@ -1139,17 +1140,15 @@ def _processTemperatures(self, line): except: self._logger.exception("Error while processing temperatures in {}, skipping".format(name)) - printer_profile=self._printerProfileManager.get_current_or_default() - shared_nozzle = printer_profile["extruder"]["sharedNozzle"] - - if "T%d"%current_tool in parsedTemps.keys(): + if current_tool_key in parsedTemps.keys(): + shared_nozzle = self._printerProfileManager.get_current_or_default()["extruder"]["sharedNozzle"] for n in range(maxToolNum + 1): tool = "T%d" % n - if not tool in parsedTemps.keys() and not shared_nozzle: - continue - elif not tool in parsedTemps.keys() and shared_nozzle: - actual, target = parsedTemps["T%d"%current_tool] - + if not tool in parsedTemps: + if shared_nozzle: + actual, target = parsedTemps[current_tool_key] + else: + continue else: actual, target = parsedTemps[tool] self.last_temperature.set_tool(n, actual=actual, target=target) From 0f8f4579d6bf5afdaaa1217975fd02bbb2da49ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Mon, 2 Oct 2017 16:39:57 +0200 Subject: [PATCH 017/333] Add no-op default action to login form That way people clicking too fast on login before the view models are bound won't be able to make the form submit username and password as GET parameter to the base URL. Closes #2108 --- src/octoprint/templates/navbar/login.jinja2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/octoprint/templates/navbar/login.jinja2 b/src/octoprint/templates/navbar/login.jinja2 index bc215b9040..370ed299bc 100644 --- a/src/octoprint/templates/navbar/login.jinja2 +++ b/src/octoprint/templates/navbar/login.jinja2 @@ -3,7 +3,7 @@
- {{ _('This does not look like a valid "http://" or "https://" URL.') }} + {{ _('This does not look like a valid URL. Expected http, https or any of the supported VCS URLs.', url='https://pip.pypa.io/en/stable/reference/pip_install/#vcs-support') }}

{{ _('... from an uploaded archive') }}

From f4e7ab424b20fe1e754b578bd7e5feb49c788218 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Wed, 4 Oct 2017 15:02:07 +0200 Subject: [PATCH 019/333] staging is 1.3.5rc5.dev --- .versioneer-lookup | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.versioneer-lookup b/.versioneer-lookup index dd99c54303..0289bad383 100644 --- a/.versioneer-lookup +++ b/.versioneer-lookup @@ -22,10 +22,10 @@ maintenance 1.3.6 1a6dbb3f4a5bef857cdeb13c031b9deca2cf30a2 pep440-dev fix/.* 1.3.6 1a6dbb3f4a5bef857cdeb13c031b9deca2cf30a2 pep440-dev improve/.* 1.3.6 1a6dbb3f4a5bef857cdeb13c031b9deca2cf30a2 pep440-dev -# staging/maintenance is currently the branch for preparation of 1.3.5rc4 +# staging/maintenance is currently the branch for preparation of 1.3.5rc5 # so is regressionfix/... -staging/maintenance 1.3.5rc4 679674df2282af0c4500367fa93864c6defa3802 pep440-dev -regressionfix/.* 1.3.5rc4 679674df2282af0c4500367fa93864c6defa3802 pep440-dev +staging/maintenance 1.3.5rc5 e88e6ba29203757b07a3237a51f1346ab1e5aaae pep440-dev +regressionfix/.* 1.3.5rc5 e88e6ba29203757b07a3237a51f1346ab1e5aaae pep440-dev # every other branch is a development branch and thus gets resolved to 1.4.0-dev for now .* 1.4.0 7f5d03d0549bcbd26f40e7e4a3297ea5204fb1cc pep440-dev From 452ee8e15626dd48ef65c5a327a2ff893951a998 Mon Sep 17 00:00:00 2001 From: Peter Backx Date: Wed, 4 Oct 2017 21:17:16 +0200 Subject: [PATCH 020/333] Adding documentation for the virtual printer plugin. --- docs/development/environment.rst | 177 ++++++++++++++++++++++++++ docs/development/index.rst | 183 +-------------------------- docs/development/virtual_printer.rst | 116 +++++++++++++++++ 3 files changed, 297 insertions(+), 179 deletions(-) create mode 100644 docs/development/environment.rst create mode 100644 docs/development/virtual_printer.rst diff --git a/docs/development/environment.rst b/docs/development/environment.rst new file mode 100644 index 0000000000..785e90fda0 --- /dev/null +++ b/docs/development/environment.rst @@ -0,0 +1,177 @@ +.. _sec-development-environment: + +Setting up a Development environment +==================================== + +.. _sec-development-environment-source: + +Obtaining, building and running the source +------------------------------------------ + +This describes the general steps in obtaining, building and running. OS specific instructions can be found +below. + + * Prerequisites: + + * `Python 2.7 `_ including ``pip``, ``setuptools`` and ``virtualenv`` + * `Git `_ + + * Checkout the OctoPrint sources from their Git repository: ``git clone https://github.com/foosel/OctoPrint.git`` + * Create a virtual environment in the checkout folder to use for installing and running OctoPrint and its + dependencies (this avoids potential versioning issues for the dependencies with system wide installed + instances): ``virtualenv venv`` + * Activate the virtual environment: ``source venv/bin/activate`` (might differ per your platform/OS) + * Update ``pip`` in the virtual environment: ``pip install --upgrade pip`` + * Install OctoPrint in `"editable" mode `_, + including its regular *and* development dependencies: ``pip install -e .[develop]`` + +.. _sec-development-environment-source-linux: + +Linux +..... + +This assumes you'll host your OctoPrint development checkout at ``~/devel/OctoPrint``. If you want to use a different +location, please substitute accordingly. + +First make sure you have python including its header files, pip, setuptools, virtualenv, git and some build requirements +installed: + + * On apt based distributions (e.g. Debian, Ubuntu, ...): + + .. code-block:: none + + sudo apt-get install python python-pip python-dev python-setuptools python-virtualenv git libyaml-dev build-essential + +.. todo:: + + Using a Linux distribution that doesn't use ``apt``? Please send a + `Pull Request `_ to get the necessary + steps into this guide! + +Then: + +.. code-block:: none + + cd ~/devel + git clone https://github.com/foosel/OctoPrint.git + cd OctoPrint + virtualenv venv + source ./venv/bin/activate + pip install --upgrade pip + pip install -e .[develop] + +You can then start OctoPrint via ``~/devel/OctoPrint/venv/bin/octoprint`` or just ``octoprint`` if you activated the virtual +environment. + +.. _sec-development-environment-windows: + +Windows +....... + +This assumes you'll host your OctoPrint development checkout at ``C:\Devel\OctoPrint``. If you want to use a different +location, please substitute accordingly. + +First download & install: + + * `Python 2.7.12 Windows x86 MSI installer `_ + + * make sure to have the installer add Python to the ``PATH`` and have it install ``pip`` too + + * `Microsoft Visual C++ Compiler for Python 2.7 `_ + * `Git for Windows `_ + +Open the Git Bash you just installed and in that: + +.. code-block:: none + + pip install virtualenv + cd /c/Devel + git clone https://github.com/foosel/OctoPrint.git + cd OctoPrint + virtualenv venv + source ./venv/bin/activate + pip install --upgrade pip + pip install -e .[develop] + +You can then start OctoPrint via ``/c/Devel/OctoPrint/venv/bin/octoprint`` or just ``octoprint`` if you activated the virtual +environment. + +.. _sec-development-environment-mac: + +Mac OS X +........ + +.. note:: + + This guide is based on the `Setup Guide for Mac OS X on OctoPrint's wiki `_. + Please report back if it works for you, due to lack of access to a Mac I cannot test it myself. Thanks. + +This assumes you'll host your OctoPrint development checkout at ``~/devel/OctoPrint``. If you want to use a different +location, please substitute accordingly. + +You'll need a user account with administrator privileges. + + * Install the latest version of Xcode suitable for your OS. For example, OS X 10.11 (El Capitan) requires Xcode 7. + * Install Xcode's command line tools: + + * ``xcode-select --install`` + * ``sudo xcodebuild`` (ensure the license was accepted) + * If you have more than one Xcode installed: ``sudo xcode-select -s /Applications/Xcode.app/Contents/Developer`` + + * Install Homebrew and use that to install Python: + + * ``ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"`` + * ``brew install python`` + + * Install `virtualenv `_ + + * ``pip install virtualenv`` + + * Install OctoPrint + + .. code-block:: none + + cd ~/devel + git clone https://github.com/foosel/OctoPrint.git + cd OctoPrint + virtualenv venv + source venv/bin/activate + pip install -e .[develop] + +You can then start OctoPrint via ``~/devel/OctoPrint/venv/bin/octoprint`` or just ``octoprint`` if you activated the virtual +environment. + +.. _sec-development-environment-ides: + +IDE Setup +--------- + +.. todo:: + + Using another IDE than the ones below? Please send a + `Pull Request `_ to get the necessary + steps into this guide! + +.. _sec-development-environment-ides-pycharm: + +PyCharm +....... + + - "File" > "Open ...", select OctoPrint checkout folder (e.g. ``~/devel/OctoPrint`` or ``C:\Devel\OctoPrint``) + - "File" > "Settings ..." > "Project: OctoPrint" > "Project Interpreter" > "Add local ...", select OctoPrint venv + folder (e.g. ``~/devel/OctoPrint/venv`` or ``C:\Devel\OctoPrint\venv``) + - Right click "src" in project tree, mark as source folder + - Add Run/Debug Configuration, select "Python": + + * Name: OctoPrint server + * Script: path to ``run`` in the OctoPrint checkout folder (e.g. ``~/devel/OctoPrint/run`` or ``C:\Devel\OctoPrint\run``) + * Script parameters: ``--debug`` + * Project: ``OctoPrint`` + * Python interpreter: the ``venv`` local virtual environment + * Working directory: the OctoPrint checkout folder (e.g. ``~/devel/OctoPrint`` or ``C:\Devel\OctoPrint``) + +.. note:: + + Make sure you are running a PyCharm version of 2016.1 or later, or manually fix + `a debugger bug contained in earlier versions `_ or plugin management + will not work in your developer install when running OctoPrint from PyCharm in debug mode. diff --git a/docs/development/index.rst b/docs/development/index.rst index 7d24a843e2..8b30385920 100644 --- a/docs/development/index.rst +++ b/docs/development/index.rst @@ -4,183 +4,8 @@ Development ########### -.. contents:: - :local: +.. toctree:: + :maxdepth: 3 -.. _sec-development-environment: - -Setting up a Development environment -==================================== - -.. _sec-development-environment-source: - -Obtaining, building and running the source ------------------------------------------- - -This describes the general steps in obtaining, building and running. OS specific instructions can be found -below. - - * Prerequisites: - - * `Python 2.7 `_ including ``pip``, ``setuptools`` and ``virtualenv`` - * `Git `_ - - * Checkout the OctoPrint sources from their Git repository: ``git clone https://github.com/foosel/OctoPrint.git`` - * Create a virtual environment in the checkout folder to use for installing and running OctoPrint and its - dependencies (this avoids potential versioning issues for the dependencies with system wide installed - instances): ``virtualenv venv`` - * Activate the virtual environment: ``source venv/bin/activate`` (might differ per your platform/OS) - * Update ``pip`` in the virtual environment: ``pip install --upgrade pip`` - * Install OctoPrint in `"editable" mode `_, - including its regular *and* development dependencies: ``pip install -e .[develop]`` - -.. _sec-development-environment-source-linux: - -Linux -..... - -This assumes you'll host your OctoPrint development checkout at ``~/devel/OctoPrint``. If you want to use a different -location, please substitute accordingly. - -First make sure you have python including its header files, pip, setuptools, virtualenv, git and some build requirements -installed: - - * On apt based distributions (e.g. Debian, Ubuntu, ...): - - .. code-block:: none - - sudo apt-get install python python-pip python-dev python-setuptools python-virtualenv git libyaml-dev build-essential - -.. todo:: - - Using a Linux distribution that doesn't use ``apt``? Please send a - `Pull Request `_ to get the necessary - steps into this guide! - -Then: - -.. code-block:: none - - cd ~/devel - git clone https://github.com/foosel/OctoPrint.git - cd OctoPrint - virtualenv venv - source ./venv/bin/activate - pip install --upgrade pip - pip install -e .[develop] - -You can then start OctoPrint via ``~/devel/OctoPrint/venv/bin/octoprint`` or just ``octoprint`` if you activated the virtual -environment. - -.. _sec-development-environment-windows: - -Windows -....... - -This assumes you'll host your OctoPrint development checkout at ``C:\Devel\OctoPrint``. If you want to use a different -location, please substitute accordingly. - -First download & install: - - * `Python 2.7.12 Windows x86 MSI installer `_ - - * make sure to have the installer add Python to the ``PATH`` and have it install ``pip`` too - - * `Microsoft Visual C++ Compiler for Python 2.7 `_ - * `Git for Windows `_ - -Open the Git Bash you just installed and in that: - -.. code-block:: none - - pip install virtualenv - cd /c/Devel - git clone https://github.com/foosel/OctoPrint.git - cd OctoPrint - virtualenv venv - source ./venv/bin/activate - pip install --upgrade pip - pip install -e .[develop] - -You can then start OctoPrint via ``/c/Devel/OctoPrint/venv/bin/octoprint`` or just ``octoprint`` if you activated the virtual -environment. - -.. _sec-development-environment-mac: - -Mac OS X -........ - -.. note:: - - This guide is based on the `Setup Guide for Mac OS X on OctoPrint's wiki `_. - Please report back if it works for you, due to lack of access to a Mac I cannot test it myself. Thanks. - -This assumes you'll host your OctoPrint development checkout at ``~/devel/OctoPrint``. If you want to use a different -location, please substitute accordingly. - -You'll need a user account with administrator privileges. - - * Install the latest version of Xcode suitable for your OS. For example, OS X 10.11 (El Capitan) requires Xcode 7. - * Install Xcode's command line tools: - - * ``xcode-select --install`` - * ``sudo xcodebuild`` (ensure the license was accepted) - * If you have more than one Xcode installed: ``sudo xcode-select -s /Applications/Xcode.app/Contents/Developer`` - - * Install Homebrew and use that to install Python: - - * ``ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"`` - * ``brew install python`` - - * Install `virtualenv `_ - - * ``pip install virtualenv`` - - * Install OctoPrint - - .. code-block:: none - - cd ~/devel - git clone https://github.com/foosel/OctoPrint.git - cd OctoPrint - virtualenv venv - source venv/bin/activate - pip install -e .[develop] - -You can then start OctoPrint via ``~/devel/OctoPrint/venv/bin/octoprint`` or just ``octoprint`` if you activated the virtual -environment. - -.. _sec-development-environment-ides: - -IDE Setup ---------- - -.. todo:: - - Using another IDE than the ones below? Please send a - `Pull Request `_ to get the necessary - steps into this guide! - -.. _sec-development-environment-ides-pycharm: - -PyCharm -....... - - - "File" > "Open ...", select OctoPrint checkout folder (e.g. ``~/devel/OctoPrint`` or ``C:\Devel\OctoPrint``) - - "File" > "Settings ..." > "Project: OctoPrint" > "Project Interpreter" > "Add local ...", select OctoPrint venv - folder (e.g. ``~/devel/OctoPrint/venv`` or ``C:\Devel\OctoPrint\venv``) - - Right click "src" in project tree, mark as source folder - - Add Run/Debug Configuration, select "Python": - - * Name: OctoPrint server - * Script: path to ``run`` in the OctoPrint checkout folder (e.g. ``~/devel/OctoPrint/run`` or ``C:\Devel\OctoPrint\run``) - * Script parameters: ``--debug`` - * Project: ``OctoPrint`` - * Python interpreter: the ``venv`` local virtual environment - * Working directory: the OctoPrint checkout folder (e.g. ``~/devel/OctoPrint`` or ``C:\Devel\OctoPrint``) - -.. note:: - - Make sure you are running a PyCharm version of 2016.1 or later, or manually fix - `a debugger bug contained in earlier versions `_ or plugin management - will not work in your developer install when running OctoPrint from PyCharm in debug mode. + environment.rst + virtual_printer.rst diff --git a/docs/development/virtual_printer.rst b/docs/development/virtual_printer.rst new file mode 100644 index 0000000000..479cdc198a --- /dev/null +++ b/docs/development/virtual_printer.rst @@ -0,0 +1,116 @@ +.. _sec-development-virtual-printer: + +Setting up the virtual printer for debugging +============================================ + +OctoPrint includes, by default, a virtual printer plugin. This plugin allows you to debug OctoPrint's serial +communication without connecting to an actual printer. Furthermore, it is possible to create certain edge conditions +that may be hard to reproduce with a real printer. + +.. _sec-development-virtual-printer-enable: + +Enabling the virtual printer +---------------------------- + +The virtual printer is enabled by editing OctoPrint's config.yaml file. Details on the configuration file can +be found in the full :ref:`config.yaml documentation `. + +The steps to take are as follows: + +* Find config.yaml in the OctoPrint settings folder. Usually in ``~/.octoprint`` on Linux, in ``%APPDATA%/OctoPrint`` on Windows and in ``~/Library/Application Support/OctoPrint`` on MacOS. +* Add or extend the ``devel`` section with: + +.. code-block:: yaml + + devel: + virtualPrinter: + enabled: true + +* Restart OctoPrint. +* In the connection panel, a new option will appear in the Serial Port dropdown labeled ``VIRTUAL``. +* Select this option and click ``connect``. +* The virtual printer is now active. + +.. _sec-development-virtual-printer-config: + +Virtual printer configuration options +------------------------------------- + +The config.yaml file has many configuration options for the virtual printer that allow you to fine-tune its behavior. + +Please see the relevant :ref:`config.yaml section ` for the full details. + +.. _sec-development-virtual-printer-log: + +Log file +-------- + +Once activated, the virtual printer will log all serial communication in the ``plugin_virtual_printer_serial.log`` file +that can be found in the OctoPrint settings folder. + +.. _sec-development-virtual-printer-debug: + +Debug commands +-------------- + +You can simulate certain conditions and communications through the terminal tab in OctoPrint's interface. + +All commands start with ``!!DEBUG:`` and are followed by the command you want to execute. For instance, sending +``!!DEBUG:action_disconnect`` will disconnect the printer. Sending ``!!DEBUG`` without command will show a help +message with all the available commands. + +Action Triggers +............... + +``action_pause`` +Sends a "// action:pause" action trigger to the host. + +``action_resume`` +Sends a "// action:resume" action trigger to the host. + +``action_disconnect`` +Sends a "// action:disconnect" action trigger to the host. + +``action_custom [ ]`` +Sends a custom "// action: " action trigger to the host. + +Communication Errors +.................... + +``dont_answer`` +Will not acknowledge the next command. + +``go_awol`` +Will completely stop replying. + +``trigger_resend_lineno`` +Triggers a resend error with a line number mismatch + +``trigger_resend_checksum`` +Triggers a resend error with a checksum mismatch + +``drop_connection`` +Drops the serial connection + +``prepare_ok `` +Will cause to be enqueued for use, will be used instead of actual "ok" + +Reply Timing / Sleeping +....................... +``sleep `` +Sleep s + +``sleep_after `` +Sleeps s after each execution of + +``sleep_after_next `` +Sleeps s after execution of next + +Misc +.... + +``help`` +Show the available commands. + +``send `` +Sends back From 178131945ea379327bd2785926b341060451ca60 Mon Sep 17 00:00:00 2001 From: ntoff Date: Fri, 6 Oct 2017 09:32:49 +1000 Subject: [PATCH 021/333] Add possible print distruption warning to shutdown dialog --- docs/api/system.rst | 12 ++++++------ .../plugins/pluginmanager/static/js/pluginmanager.js | 2 +- src/octoprint/server/api/system.py | 8 ++++---- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/docs/api/system.rst b/docs/api/system.rst index 78ea128ff6..439373a2b0 100644 --- a/docs/api/system.rst +++ b/docs/api/system.rst @@ -39,7 +39,7 @@ List all registered system commands "action": "shutdown", "name": "Shutdown", "command": "sudo shutdown -h now", - "confirm": "You are about to shutdown the system.", + "confirm": "You are about to shutdown the system.

This action may disrupt any ongoing print jobs (depending on your printer's controller and general setup that might also apply to prints run directly from your printer's internal storage).", "async": true, "ignore": true, "source": "core", @@ -49,7 +49,7 @@ List all registered system commands "action": "reboot", "name": "Reboot", "command": "sudo reboot", - "confirm": "You are about to reboot the system.", + "confirm": "You are about to reboot the system.

This action may disrupt any ongoing print jobs (depending on your printer's controller and general setup that might also apply to prints run directly from your printer's internal storage).", "async": true, "ignore": true, "source": "core", @@ -59,7 +59,7 @@ List all registered system commands "action": "restart", "name": "Restart OctoPrint", "command": "sudo service octoprint restart", - "confirm": "You are about to restart the OctoPrint server.", + "confirm": "You are about to restart the OctoPrint server.

This action may disrupt any ongoing print jobs (depending on your printer's controller and general setup that might also apply to prints run directly from your printer's internal storage).", "async": true, "ignore": true, "source": "core", @@ -100,7 +100,7 @@ List all registered system commands for a source "action": "shutdown", "name": "Shutdown", "command": "sudo shutdown -h now", - "confirm": "You are about to shutdown the system.", + "confirm": "You are about to shutdown the system.

This action may disrupt any ongoing print jobs (depending on your printer's controller and general setup that might also apply to prints run directly from your printer's internal storage).", "async": true, "ignore": true, "source": "core", @@ -110,7 +110,7 @@ List all registered system commands for a source "action": "reboot", "name": "Reboot", "command": "sudo reboot", - "confirm": "You are about to reboot the system.", + "confirm": "You are about to reboot the system.

This action may disrupt any ongoing print jobs (depending on your printer's controller and general setup that might also apply to prints run directly from your printer's internal storage).", "async": true, "ignore": true, "source": "core", @@ -120,7 +120,7 @@ List all registered system commands for a source "action": "restart", "name": "Restart OctoPrint", "command": "sudo service octoprint restart", - "confirm": "You are about to restart the OctoPrint server.", + "confirm": "You are about to restart the OctoPrint server.

This action may disrupt any ongoing print jobs (depending on your printer's controller and general setup that might also apply to prints run directly from your printer's internal storage).", "async": true, "ignore": true, "source": "core", diff --git a/src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js b/src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js index 9663cc6195..6984644fe9 100644 --- a/src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js +++ b/src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js @@ -731,7 +731,7 @@ $(function() { if (restartClicked) return; restartClicked = true; showConfirmationDialog({ - message: gettext("This will restart your OctoPrint server."), + message: gettext("This will restart your OctoPrint server.

This action may disrupt any ongoing print jobs (depending on your printer's controller and general setup that might also apply to prints run directly from your printer's internal storage)."), onproceed: function() { OctoPrint.system.executeCommand("core", "restart") .done(function() { diff --git a/src/octoprint/server/api/system.py b/src/octoprint/server/api/system.py index e17bdb7813..8be2fec25c 100644 --- a/src/octoprint/server/api/system.py +++ b/src/octoprint/server/api/system.py @@ -167,19 +167,19 @@ def enable_safe_mode(): shutdown=dict( command=s().get(["server", "commands", "systemShutdownCommand"]), name=gettext("Shutdown system"), - confirm=gettext("You are about to shutdown the system.")), + confirm=gettext("You are about to shutdown the system.

This action may disrupt any ongoing print jobs (depending on your printer's controller and general setup that might also apply to prints run directly from your printer's internal storage).")), reboot=dict( command=s().get(["server", "commands", "systemRestartCommand"]), name=gettext("Reboot system"), - confirm=gettext("You are about to reboot the system.")), + confirm=gettext("You are about to reboot the system.

This action may disrupt any ongoing print jobs (depending on your printer's controller and general setup that might also apply to prints run directly from your printer's internal storage).")), restart=dict( command=s().get(["server", "commands", "serverRestartCommand"]), name=gettext("Restart OctoPrint"), - confirm=gettext("You are about to restart the OctoPrint server.")), + confirm=gettext("You are about to restart the OctoPrint server.

This action may disrupt any ongoing print jobs (depending on your printer's controller and general setup that might also apply to prints run directly from your printer's internal storage).")), restart_safe=dict( command=s().get(["server", "commands", "serverRestartCommand"]), name=gettext("Restart OctoPrint in safe mode"), - confirm=gettext("You are about to restart the OctoPrint server in safe mode."), + confirm=gettext("You are about to restart the OctoPrint server in safe mode.

This action may disrupt any ongoing print jobs (depending on your printer's controller and general setup that might also apply to prints run directly from your printer's internal storage)."), before=enable_safe_mode) ) From b89b6970c6d545b096b28042cbc10a6b715564b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Mon, 9 Oct 2017 15:48:31 +0200 Subject: [PATCH 022/333] Fix temperature plot init if it's not the 1st tab If the temperature graph was not on the first tab, the plot would be initialized without being visible, causing some sizing issues and alignment issues with the Y axis ticks. Forcing the plot to only get initialized once visible solves these problems. Fixes #2147 --- .../static/js/app/viewmodels/temperature.js | 187 ++++++++++-------- 1 file changed, 102 insertions(+), 85 deletions(-) diff --git a/src/octoprint/static/js/app/viewmodels/temperature.js b/src/octoprint/static/js/app/viewmodels/temperature.js index 9c4b6ef64e..6e70610bc6 100644 --- a/src/octoprint/static/js/app/viewmodels/temperature.js +++ b/src/octoprint/static/js/app/viewmodels/temperature.js @@ -142,7 +142,7 @@ $(function() { if (!self._printerProfileInitialized) { self._triggerBacklog(); } - self.updatePlot(false); + self.updatePlot(); }; self.settingsViewModel.printerProfiles.currentProfileData.subscribe(function() { self._printerProfileUpdated(); @@ -221,12 +221,12 @@ $(function() { if (!CONFIG_TEMPERATURE_GRAPH) return; self.temperatures = self._processTemperatureData(serverTime, data, self.temperatures); - self.updatePlot(false); + self.updatePlot(); }; self._processTemperatureHistoryData = function(serverTime, data) { self.temperatures = self._processTemperatureData(serverTime, data); - self.updatePlot(false); + self.updatePlot(); }; self._processOffsetData = function(data) { @@ -285,100 +285,112 @@ $(function() { return result; }; - self.updatePlot = function(force) { - force = force == undefined ? true : force; - + self.updatePlot = function() { var graph = $("#temperature-graph"); - if (graph.length) { - var data = []; - var heaterOptions = self.heaterOptions(); - if (!heaterOptions) return; + if (!graph.length) return; // no graph + if (!self.plot) return; // plot not yet initialized - var maxTemps = [310/1.1]; + var plotInfo = self._getPlotInfo(); + if (plotInfo === undefined) return; - var showFahrenheit = self._shallShowFahrenheit(); + // update the data + self.plot.setData(plotInfo.data); + self.plot.getAxes().yaxis.max = Math.max(Math.max.apply(null, plotInfo.max) * 1.1, 310); + self.updateLegend(self._replaceLegendLabel); + self.plot.draw(); + }; - _.each(_.keys(heaterOptions), function(type) { - if (type == "bed" && !self.hasBed()) { - return; + self._initializePlot = function(force) { + var graph = $("#temperature-graph"); + if (!graph.length) return; // no graph + if (self.plot && !force) return; // already initialized + + var plotInfo = self._getPlotInfo(); + if (plotInfo === undefined) return; + + // we don't have a plot yet, we need to set stuff up + var options = { + yaxis: { + min: 0, + max: Math.max(Math.max.apply(null, plotInfo.max) * 1.1, 310), + ticks: 10 + }, + xaxis: { + mode: "time", + minTickSize: [2, "minute"], + tickFormatter: function(val, axis) { + if (val === undefined || val === 0) + return ""; // we don't want to display the minutes since the epoch if not connected yet ;) + + // current time in milliseconds in UTC + var timestampUtc = Date.now(); + + // calculate difference in milliseconds + var diff = timestampUtc - val; + + // convert to minutes + var diffInMins = Math.round(diff / (60 * 1000)); + if (diffInMins === 0) + return gettext("just now"); + else + return "- " + diffInMins + " " + gettext("min"); } + }, + legend: { + position: "sw", + noColumns: 2, + backgroundOpacity: 0 + } + }; - var actuals = []; - var targets = []; - - if (self.temperatures[type]) { - actuals = self.temperatures[type].actual; - targets = self.temperatures[type].target; - } + if (!OctoPrint.coreui.browser.mobile) { + options["crosshair"] = { mode: "x" }; + options["grid"] = { hoverable: true, autoHighlight: false }; + } - var actualTemp = actuals && actuals.length ? formatTemperature(actuals[actuals.length - 1][1], showFahrenheit) : "-"; - var targetTemp = targets && targets.length ? formatTemperature(targets[targets.length - 1][1], showFahrenheit) : "-"; + self.plot = $.plot(graph, plotInfo.data, options); + }; - data.push({ - label: gettext("Actual") + " " + heaterOptions[type].name + ": " + actualTemp, - color: heaterOptions[type].color, - data: actuals - }); - data.push({ - label: gettext("Target") + " " + heaterOptions[type].name + ": " + targetTemp, - color: pusher.color(heaterOptions[type].color).tint(0.5).html(), - data: targets - }); + self._getPlotInfo = function() { + var data = []; + var heaterOptions = self.heaterOptions(); + if (!heaterOptions) return undefined; - maxTemps.push(self.getMaxTemp(actuals, targets)); - }); + var maxTemps = [310/1.1]; - if (!self.plot || force) { - // we don't have a plot yet, we need to set stuff up - var options = { - yaxis: { - min: 0, - max: Math.max(Math.max.apply(null, maxTemps) * 1.1, 310), - ticks: 10 - }, - xaxis: { - mode: "time", - minTickSize: [2, "minute"], - tickFormatter: function(val, axis) { - if (val == undefined || val == 0) - return ""; // we don't want to display the minutes since the epoch if not connected yet ;) - - // current time in milliseconds in UTC - var timestampUtc = Date.now(); - - // calculate difference in milliseconds - var diff = timestampUtc - val; - - // convert to minutes - var diffInMins = Math.round(diff / (60 * 1000)); - if (diffInMins == 0) - return gettext("just now"); - else - return "- " + diffInMins + " " + gettext("min"); - } - }, - legend: { - position: "sw", - noColumns: 2, - backgroundOpacity: 0 - } - }; + var showFahrenheit = self._shallShowFahrenheit(); - if (!OctoPrint.coreui.browser.mobile) { - options["crosshair"] = { mode: "x" }; - options["grid"] = { hoverable: true, autoHighlight: false }; - } + _.each(_.keys(heaterOptions), function(type) { + if (type === "bed" && !self.hasBed()) { + return; + } - self.plot = $.plot(graph, data, options); + var actuals = []; + var targets = []; - } else { - // graph already active, let's just update the data - self.plot.setData(data); - self.plot.getAxes().yaxis.max = Math.max(Math.max.apply(null, maxTemps) * 1.1, 310); - self.updateLegend(self._replaceLegendLabel); - self.plot.draw(); + if (self.temperatures[type]) { + actuals = self.temperatures[type].actual; + targets = self.temperatures[type].target; } - } + + var actualTemp = actuals && actuals.length ? formatTemperature(actuals[actuals.length - 1][1], showFahrenheit) : "-"; + var targetTemp = targets && targets.length ? formatTemperature(targets[targets.length - 1][1], showFahrenheit) : "-"; + + data.push({ + label: gettext("Actual") + " " + heaterOptions[type].name + ": " + actualTemp, + color: heaterOptions[type].color, + data: actuals + }); + data.push({ + label: gettext("Target") + " " + heaterOptions[type].name + ": " + targetTemp, + color: pusher.color(heaterOptions[type].color).tint(0.5).html(), + data: targets + }); + + maxTemps.push(self.getMaxTemp(actuals, targets)); + }); + + return {max: maxTemps, data: data}; }; self.updateLegend = function(replaceLegendLabel) { @@ -704,10 +716,15 @@ $(function() { }; self.onAfterTabChange = function(current, previous) { - if (current != "#temp") { + if (current !== "#temp") { return; } - self.updatePlot(false); + + if (!self.plot) { + self._initializePlot(); + } else { + self.updatePlot(); + } }; self.onStartup = function() { From abfcc6e5aa0c252741b261163826e6209aa286bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Tue, 10 Oct 2017 12:41:47 +0200 Subject: [PATCH 023/333] Fix deletion of unrendered timelapses If the filename contained a [] pair, the file would not match the glob pattern used for selecting for deletion. Backporting the glob.escape function from Python 3.4 and using it here should fix that. We only use our own ported version if glob.escape doesn't exist - that should reduce redundant code once we become Python 3 compatible. --- src/octoprint/timelapse.py | 4 +++- src/octoprint/util/__init__.py | 23 ++++++++++++++++++ tests/util/test_file_helpers.py | 42 +++++++++++++++++++++++++++++++++ 3 files changed, 68 insertions(+), 1 deletion(-) diff --git a/src/octoprint/timelapse.py b/src/octoprint/timelapse.py index 58de381f91..a3cff03eea 100644 --- a/src/octoprint/timelapse.py +++ b/src/octoprint/timelapse.py @@ -139,12 +139,14 @@ def finalize_fields(prefix, job): def delete_unrendered_timelapse(name): global _cleanup_lock + + pattern = "{}*.jpg".format(util.glob_escape(name)) basedir = settings().getBaseFolder("timelapse_tmp") with _cleanup_lock: for entry in scandir(basedir): try: - if fnmatch.fnmatch(entry.name, "{}*.jpg".format(name)): + if fnmatch.fnmatch(entry.name, pattern): os.remove(entry.path) except: if logging.getLogger(__name__).isEnabledFor(logging.DEBUG): diff --git a/src/octoprint/util/__init__.py b/src/octoprint/util/__init__.py index 862347c705..cc8a4a91e8 100644 --- a/src/octoprint/util/__init__.py +++ b/src/octoprint/util/__init__.py @@ -866,6 +866,29 @@ def is_hidden_path(path): return False +try: + from glob import escape + glob_escape = escape +except ImportError: + # no glob.escape - we need to implement our own + _glob_escape_check = re.compile("([*?[])") + _glob_escape_check_bytes = re.compile(b"([*?[])") + + def glob_escape(pathname): + """ + Ported from Python 3.4 + + See https://github.com/python/cpython/commit/fd32fffa5ada8b8be8a65bd51b001d989f99a3d3 + """ + + drive, pathname = os.path.splitdrive(pathname) + if isinstance(pathname, bytes): + pathname = _glob_escape_check_bytes.sub(br"[\1]", pathname) + else: + pathname = _glob_escape_check.sub(r"[\1]", pathname) + return drive + pathname + + class RepeatedTimer(threading.Thread): """ This class represents an action that should be run repeatedly in an interval. It is similar to python's diff --git a/tests/util/test_file_helpers.py b/tests/util/test_file_helpers.py index 2f0add32ca..36bf525daa 100644 --- a/tests/util/test_file_helpers.py +++ b/tests/util/test_file_helpers.py @@ -358,3 +358,45 @@ def tearDown(self): def test_is_hidden_path(self, path_id, expected): path = getattr(self, path_id) if path_id is not None else None self.assertEqual(octoprint.util.is_hidden_path(path), expected) + + +try: + from glob import escape + +except ImportError: + # no glob.escape - tests for our ported implementation + + @ddt.ddt + class GlobEscapeTest(unittest.TestCase): + """ + Ported from Python 3.4 + + See https://github.com/python/cpython/commit/fd32fffa5ada8b8be8a65bd51b001d989f99a3d3 + """ + + @ddt.data( + ("abc", "abc"), + ("[", "[[]"), + ("?", "[?]"), + ("*", "[*]"), + ("[[_/*?*/_]]", "[[][[]_/[*][?][*]/_]]"), + ("/[[_/*?*/_]]/", "/[[][[]_/[*][?][*]/_]]/") + ) + @ddt.unpack + def test_glob_escape(self, text, expected): + actual = octoprint.util.glob_escape(text) + self.assertEqual(actual, expected) + + @ddt.data( + ("?:?", "?:[?]"), + ("*:*", "*:[*]"), + (r"\\?\c:\?", r"\\?\c:\[?]"), + (r"\\*\*\*", r"\\*\*\[*]"), + ("//?/c:/?", "//?/c:/[?]"), + ("//*/*/*", "//*/*/[*]") + ) + @ddt.unpack + @unittest.skipUnless(sys.platform == "win32", "Win32 specific test") + def test_glob_escape_windows(self, text, expected): + actual = octoprint.util.glob_escape(text) + self.assertEqual(actual, expected) From d1fdbd5080a19fe0bc9d273466bd5e65532644a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Tue, 10 Oct 2017 12:51:43 +0200 Subject: [PATCH 024/333] More lenient check for unrendered timelapses Only delete automatically if modification AND creation date are older than the calculated cutoff. --- src/octoprint/timelapse.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/octoprint/timelapse.py b/src/octoprint/timelapse.py index a3cff03eea..7d203c4c51 100644 --- a/src/octoprint/timelapse.py +++ b/src/octoprint/timelapse.py @@ -194,7 +194,8 @@ def delete_old_unrendered_timelapses(): if prefix in prefixes_to_clean: continue - if entry.stat().st_mtime < cutoff: + # delete if both creation and modification time are older than the cutoff + if max(entry.stat().st_ctime, entry.stat().st_mtime) < cutoff: prefixes_to_clean.append(prefix) except: if logging.getLogger(__name__).isEnabledFor(logging.DEBUG): From 4093995ba09024882acd2d0ebf05d5068f044fbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Wed, 11 Oct 2017 16:19:37 +0200 Subject: [PATCH 025/333] STK500v2: Retries for reading from serial This would normally not fly at all but we give it a pass here since we only use that module for auto detection anyhow (and probably not for much longer since that approach is simply too error prone). Adapted from https://github.com/foosel/OctoPrint/issues/2138#issuecomment-334117910 by @dtynan. Solves #2138 until I come up with something better --- src/octoprint/util/avr_isp/stk500v2.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/octoprint/util/avr_isp/stk500v2.py b/src/octoprint/util/avr_isp/stk500v2.py index 0fe59fe0ba..41561c4ab9 100644 --- a/src/octoprint/util/avr_isp/stk500v2.py +++ b/src/octoprint/util/avr_isp/stk500v2.py @@ -111,8 +111,11 @@ def recvMessage(self): state = 'Start' checksum = 0 while True: - s = self.serial.read() - if len(s) < 1: + for _ in range(0, 5): + s = self.serial.read() + if len(s) > 0: + break + else: raise ispBase.IspError("Timeout") b = struct.unpack(">B", s)[0] checksum ^= b From 054bbd8a9b9598e148a3600b6fe1323d9c74c56a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Wed, 11 Oct 2017 16:20:37 +0200 Subject: [PATCH 026/333] comm: no need to fetch potential ports again --- src/octoprint/util/comm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/octoprint/util/comm.py b/src/octoprint/util/comm.py index 23c34a1f01..f1ee6be049 100644 --- a/src/octoprint/util/comm.py +++ b/src/octoprint/util/comm.py @@ -1867,7 +1867,7 @@ def _detect_port(self): elif len(potentials) > 1: programmer = stk500v2.Stk500v2() - for p in serialList(): + for p in potentials: serial_obj = None try: From 42018ae29a27d73ac006025f2bc53aaf780ebf68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Wed, 11 Oct 2017 16:27:51 +0200 Subject: [PATCH 027/333] starts_with => startswith Now that was embarrassing. Fixes #2151 --- src/octoprint/plugins/pluginmanager/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/octoprint/plugins/pluginmanager/__init__.py b/src/octoprint/plugins/pluginmanager/__init__.py index 35f38447aa..a6806ac7f5 100644 --- a/src/octoprint/plugins/pluginmanager/__init__.py +++ b/src/octoprint/plugins/pluginmanager/__init__.py @@ -306,7 +306,7 @@ def on_api_command(self, command, data): def command_install(self, url=None, path=None, force=False, reinstall=None, dependency_links=False): if url is not None: - if not any(map(lambda scheme: url.starts_with(scheme + "://"), self.URL_SCHEMES)): + if not any(map(lambda scheme: url.startswith(scheme + "://"), self.URL_SCHEMES)): raise ValueError("Invalid URL to pip install from") source = url From 42ac1334369109b52a944bbe8d6fcbf615043128 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Mon, 28 Aug 2017 18:52:19 +0200 Subject: [PATCH 028/333] Support full UTF8 file names Files (and folders) are still slugified to ASCII for storage on disk, but now the original filename is stored alongside in metadata.yaml and used for display in the file list and print status. The slicing dialog also inherits that display name for use as base for the suggested GCODE name. Internally, everything still depends completely on the slugified ASCII version. This implements #2094 --- docs/api/datamodel.rst | 18 +- setup.py | 3 +- src/octoprint/filemanager/__init__.py | 28 +- src/octoprint/filemanager/storage.py | 297 ++++++++++++++---- src/octoprint/printer/standard.py | 5 + src/octoprint/server/__init__.py | 9 +- src/octoprint/server/api/files.py | 51 ++- src/octoprint/server/util/tornado.py | 13 +- src/octoprint/static/css/octoprint.css | 2 +- .../static/js/app/viewmodels/files.js | 6 +- .../static/js/app/viewmodels/printerstate.js | 3 + .../static/js/app/viewmodels/slicing.js | 15 +- src/octoprint/static/less/octoprint.less | 6 +- src/octoprint/templates/sidebar/files.jinja2 | 9 +- src/octoprint/templates/sidebar/state.jinja2 | 2 +- src/octoprint/util/__init__.py | 6 +- tests/filemanager/test_filemanager.py | 54 +++- tests/filemanager/test_localstorage.py | 137 +++++++- 18 files changed, 533 insertions(+), 131 deletions(-) diff --git a/docs/api/datamodel.rst b/docs/api/datamodel.rst index 768e420b03..92e32050f5 100644 --- a/docs/api/datamodel.rst +++ b/docs/api/datamodel.rst @@ -232,12 +232,18 @@ File information * - ``name`` - 1 - String - - The name of the file without path. E.g. "file.gco" for a file "file.gco" located anywhere in the file system. + - The name of the file without path. E.g. "file.gco" for a file "file.gco" located anywhere in the file system. Currently + this will always fit into ASCII. + * - ``display`` + - 1 + - String + - The name of the file without the path, this time potentially with non-ASCII unicode characters. + E.g. "a turtle 🐢.gco" for a file "a_turtle_turtle.gco" located anywhere in the file system. * - ``path`` - 1 - String - The path to the file within the location. E.g. "folder/subfolder/file.gco" for a file "file.gco" located within - "folder" and "subfolder" relative to the root of the location. + "folder" and "subfolder" relative to the root of the location. Currently this will always fit into ASCII. * - ``type`` - 1 - String @@ -329,11 +335,17 @@ Abridged file or folder information - 1 - String - The name of the file or folder without path. E.g. "file.gco" for a file "file.gco" located anywhere in the file system. + Currently this will always fit into ASCII. + * - ``display`` + - 1 + - String + - The name of the file without the path, this potentially with non-ASCII unicode characters. + E.g. "a turtle 🐢.gco" for a file "a_turtle_turtle.gco" located anywhere in the file system. * - ``path`` - 1 - String - The path to the file or folder within the location. E.g. "folder/subfolder/file.gco" for a file "file.gco" located within - "folder" and "subfolder" relative to the root of the location. + "folder" and "subfolder" relative to the root of the location. Currently this will always fit into ASCII. * - ``origin`` - 1 - String, either ``local`` or ``sdcard`` diff --git a/setup.py b/setup.py index 25f9d54df3..c8d4e1401a 100644 --- a/setup.py +++ b/setup.py @@ -50,7 +50,8 @@ "websocket-client>=0.40,<0.41", "python-dateutil>=2.6,<2.7", "wrapt>=1.10.10,<1.11", - "futures>=3.1.1,<3.2" + "futures>=3.1.1,<3.2", + "emoji>=0.4.5,<0.5" ] if sys.platform == "darwin": diff --git a/src/octoprint/filemanager/__init__.py b/src/octoprint/filemanager/__init__.py index 8c59e8aa27..513e91e882 100644 --- a/src/octoprint/filemanager/__init__.py +++ b/src/octoprint/filemanager/__init__.py @@ -277,10 +277,12 @@ def analyse(self, destination, path, printer_profile_id=None): return False def slice(self, slicer_name, source_location, source_path, dest_location, dest_path, - position=None, profile=None, printer_profile_id=None, overrides=None, callback=None, callback_args=None): + position=None, profile=None, printer_profile_id=None, overrides=None, display=None, + callback=None, callback_args=None): absolute_source_path = self.path_on_disk(source_location, source_path) - def stlProcessed(source_location, source_path, tmp_path, dest_location, dest_path, start_time, printer_profile_id, callback, callback_args, _error=None, _cancelled=False, _analysis=None): + def stlProcessed(source_location, source_path, tmp_path, dest_location, dest_path, start_time, + printer_profile_id, callback, callback_args, _error=None, _cancelled=False, _analysis=None): try: if _error: eventManager().fire(Events.SLICING_FAILED, dict(stl=source_path, @@ -305,7 +307,9 @@ def stlProcessed(source_location, source_path, tmp_path, dest_location, dest_pat io.FileIO(tmp_path, "rb")) printer_profile = self._printer_profile_manager.get(printer_profile_id) - self.add_file(dest_location, dest_path, file_obj, links=links, allow_overwrite=True, printer_profile=printer_profile, analysis=_analysis) + self.add_file(dest_location, dest_path, file_obj, + display=display, links=links, allow_overwrite=True, + printer_profile=printer_profile, analysis=_analysis) end_time = time.time() eventManager().fire(Events.SLICING_DONE, dict(stl=source_path, @@ -417,7 +421,7 @@ def list_files(self, destinations=None, path=None, filter=None, recursive=None): result[dst] = self._storage_managers[dst].list_files(path=path, filter=filter, recursive=recursive) return result - def add_file(self, destination, path, file_object, links=None, allow_overwrite=False, printer_profile=None, analysis=None): + def add_file(self, destination, path, file_object, links=None, allow_overwrite=False, printer_profile=None, analysis=None, display=None): if printer_profile is None: printer_profile = self._printer_profile_manager.get_current_or_default() @@ -434,7 +438,7 @@ def add_file(self, destination, path, file_object, links=None, allow_overwrite=F queue_entry = self._analysis_queue_entry(destination, path) self._analysis_queue.dequeue(queue_entry) - path_in_storage = self._storage(destination).add_file(path, file_object, links=links, printer_profile=printer_profile, allow_overwrite=allow_overwrite) + path_in_storage = self._storage(destination).add_file(path, file_object, links=links, printer_profile=printer_profile, allow_overwrite=allow_overwrite, display=display) if analysis is None: queue_entry = self._analysis_queue_entry(destination, path_in_storage, printer_profile=printer_profile) @@ -501,8 +505,8 @@ def move_file(self, destination, source, dst): type=get_file_type(dst_name))) eventManager().fire(Events.UPDATED_FILES, dict(type="printables")) - def add_folder(self, destination, path, ignore_existing=True): - path_in_storage = self._storage(destination).add_folder(path, ignore_existing=ignore_existing) + def add_folder(self, destination, path, ignore_existing=True, display=None): + path_in_storage = self._storage(destination).add_folder(path, ignore_existing=ignore_existing, display=display) _, name = self._storage(destination).split_path(path_in_storage) eventManager().fire(Events.FOLDER_ADDED, dict(storage=destination, @@ -525,7 +529,7 @@ def remove_folder(self, destination, path, recursive=True): def copy_folder(self, destination, source, dst): path_in_storage = self._storage(destination).copy_folder(source, dst) - self._determine_analysis_backlog(destination, self._storage(destination), root=dst) + self._determine_analysis_backlog(destination, self._storage(destination), root=path_in_storage) _, name = self._storage(destination).split_path(path_in_storage) eventManager().fire(Events.FOLDER_ADDED, dict(storage=destination, @@ -536,13 +540,12 @@ def copy_folder(self, destination, source, dst): def move_folder(self, destination, source, dst): self._analysis_queue.dequeue_folder(destination, source) self._analysis_queue.pause() - self._storage(destination).move_folder(source, dst) - self._determine_analysis_backlog(destination, self._storage(destination), root=dst) + dst_path_in_storage = self._storage(destination).move_folder(source, dst) + self._determine_analysis_backlog(destination, self._storage(destination), root=dst_path_in_storage) self._analysis_queue.resume() source_path_in_storage = self._storage(destination).path_in_storage(source) _, source_name = self._storage(destination).split_path(source_path_in_storage) - dst_path_in_storage = self._storage(destination).path_in_storage(destination) _, dst_name = self._storage(destination).split_path(dst_path_in_storage) eventManager().fire(Events.FOLDER_REMOVED, dict(storage=destination, @@ -622,6 +625,9 @@ def remove_additional_metadata(self, destination, path, key): def path_on_disk(self, destination, path): return self._storage(destination).path_on_disk(path) + def canonicalize(self, destination, path): + return self._storage(destination).canonicalize(path) + def sanitize(self, destination, path): return self._storage(destination).sanitize(path) diff --git a/src/octoprint/filemanager/storage.py b/src/octoprint/filemanager/storage.py index 582abaea42..e4d9d9068b 100644 --- a/src/octoprint/filemanager/storage.py +++ b/src/octoprint/filemanager/storage.py @@ -10,6 +10,7 @@ import os import pylru import shutil +import re try: from os import scandir, walk @@ -22,9 +23,12 @@ from past.builtins import basestring +from emoji import demojize +from slugify import Slugify + import octoprint.filemanager -from octoprint.util import is_hidden_path +from octoprint.util import is_hidden_path, to_unicode class StorageInterface(object): """ @@ -154,7 +158,7 @@ def list_files(self, path=None, filter=None, recursive=True): """ raise NotImplementedError() - def add_folder(self, path, ignore_existing=True): + def add_folder(self, path, ignore_existing=True, display=None): """ Adds a folder as ``path`` @@ -162,6 +166,7 @@ def add_folder(self, path, ignore_existing=True): :param string path: the path of the new folder :param bool ignore_existing: if set to True, no error will be raised if the folder to be added already exists + :param unicode display: display name of the folder :return: the sanitized name of the new folder to be used for future references to the folder """ raise NotImplementedError() @@ -198,7 +203,7 @@ def move_folder(self, source, destination): """ raise NotImplementedError() - def add_file(self, path, file_object, printer_profile=None, links=None, allow_overwrite=False): + def add_file(self, path, file_object, printer_profile=None, links=None, allow_overwrite=False, display=None): """ Adds the file ``file_object`` as ``path`` @@ -209,6 +214,7 @@ def add_file(self, path, file_object, printer_profile=None, links=None, allow_ov :param list links: any links to add with the file :param bool allow_overwrite: if set to True no error will be raised if the file already exists and the existing file and its metadata will just be silently overwritten + :param unicode display: display name of the file :return: the sanitized name of the file to be used for future references to it """ raise NotImplementedError() @@ -327,6 +333,23 @@ def remove_additional_metadata(self, path, key): """ raise NotImplementedError() + def canonicalize(self, path): + """ + Canonicalizes the given ``path``. The ``path`` may consist of both folder and file name, the underlying + implementation must separate those if necessary. + + By default, this calls :func:`~octoprint.filemanager.StorageInterface.sanitize`, which also takes care + of stripping any invalid characters. + + Args: + path: the path to canonicalize + + Returns: + a 2-tuple containing the canonicalized path and file name + + """ + return self.sanitize(path) + def sanitize(self, path): """ Sanitizes the given ``path``, stripping it of all invalid characters. The ``path`` may consist of both @@ -403,6 +426,7 @@ class StorageError(Exception): INVALID_DESTINATION = "invalid_destination" DOES_NOT_EXIST = "does_not_exist" ALREADY_EXISTS = "already_exists" + SOURCE_EQUALS_DESTINATION = "source_equals_destination" NOT_EMPTY = "not_empty" def __init__(self, message, code=None, cause=None): @@ -425,6 +449,22 @@ class LocalFileStorage(StorageInterface): This storage type implements :func:`path_on_disk`. """ + _UNICODE_VARIATIONS = re.compile(u"[\uFE00-\uFE0F]") + + @classmethod + def _no_unicode_variations(cls, text): + return cls._UNICODE_VARIATIONS.sub(u"", text) + + _SLUGIFY = Slugify() + _SLUGIFY.safe_chars = "-_.()[] " + + @classmethod + def _slugify(cls, text): + text = to_unicode(text) + text = cls._no_unicode_variations(text) + text = demojize(text, delimiters=(u"", u"")) + return cls._SLUGIFY(text) + def __init__(self, basefolder, create=False): """ Initializes a ``LocalFileStorage`` instance under the given ``basefolder``, creating the necessary folder @@ -435,7 +475,7 @@ def __init__(self, basefolder, create=False): """ self._logger = logging.getLogger(__name__) - self.basefolder = os.path.realpath(os.path.abspath(basefolder)) + self.basefolder = os.path.realpath(os.path.abspath(to_unicode(basefolder))) if not os.path.exists(self.basefolder) and create: os.makedirs(self.basefolder) if not os.path.exists(self.basefolder) or not os.path.isdir(self.basefolder): @@ -447,10 +487,6 @@ def __init__(self, basefolder, create=False): self._metadata_cache = pylru.lrucache(10) - from slugify import Slugify - self._slugify = Slugify() - self._slugify.safe_chars = "-_.()[] " - self._old_metadata = None self._initialize_metadata() @@ -557,17 +593,22 @@ def folder_exists(self, path): def list_files(self, path=None, filter=None, recursive=True): if path: - path = self.sanitize_path(path) + path = self.sanitize_path(to_unicode(path)) base = self.path_in_storage(path) if base: - base += "/" + base += u"/" else: path = self.basefolder - base = "" + base = u"" return self._list_folder(path, base=base, entry_filter=filter, recursive=recursive) - def add_folder(self, path, ignore_existing=True): - path, name = self.sanitize(path) + def add_folder(self, path, ignore_existing=True, display=None): + display_path, display_name = self.canonicalize(path) + path = self.sanitize_path(display_path) + name = self.sanitize_name(display_name) + + if display is not None: + display_name = display folder_path = os.path.join(path, name) if os.path.exists(folder_path): @@ -576,6 +617,11 @@ def add_folder(self, path, ignore_existing=True): else: os.mkdir(folder_path) + if display_name != name: + metadata = self._get_metadata_entry(path, name, default=dict()) + metadata["display"] = display_name + self._update_metadata_entry(path, name, metadata) + return self.path_in_storage((path, name)) def remove_folder(self, path, recursive=True): @@ -598,12 +644,15 @@ def remove_folder(self, path, recursive=True): import shutil shutil.rmtree(folder_path) - self._delete_metadata(folder_path) + self._remove_metadata_entry(path, name) - def _get_source_destination_data(self, source, destination): + def _get_source_destination_data(self, source, destination, must_not_equal=False): """Prepares data dicts about source and destination for copy/move.""" source_path, source_name = self.sanitize(source) - destination_path, destination_name = self.sanitize(destination) + + destination_canon_path, destination_canon_name = self.canonicalize(destination) + destination_path = self.sanitize_path(destination_canon_path) + destination_name = self.sanitize_name(destination_canon_name) source_fullpath = os.path.join(source_path, source_name) destination_fullpath = os.path.join(destination_path, destination_name) @@ -613,45 +662,88 @@ def _get_source_destination_data(self, source, destination): if not os.path.isdir(destination_path): raise StorageError("Destination path {} does not exist or is not a folder".format(destination_path), code=StorageError.INVALID_DESTINATION) - if os.path.exists(destination_fullpath): + if os.path.exists(destination_fullpath) and source_fullpath != destination_fullpath: raise StorageError("{} does already exist in {}".format(destination_name, destination_path), code=StorageError.INVALID_DESTINATION) + source_meta = self._get_metadata_entry(source_path, source_name) + if source_meta: + source_display = source_meta.get("display", source_name) + else: + source_display = source_name + + if (must_not_equal or source_display == destination_canon_name) and source_fullpath == destination_fullpath: + raise StorageError("Source {} and destination {} are the same folder".format(source_path, destination_path), code=StorageError.SOURCE_EQUALS_DESTINATION) + source_data = dict( path=source_path, name=source_name, + display=source_display, fullpath=source_fullpath, ) destination_data = dict( path=destination_path, name=destination_name, + display=destination_canon_name, fullpath=destination_fullpath, ) return source_data, destination_data + def _set_display_metadata(self, destination_data, source_data=None): + if source_data and destination_data["name"] == source_data["name"] and source_data["name"] != source_data["display"]: + display = source_data["display"] + elif destination_data["name"] != destination_data["display"]: + display = destination_data["display"] + else: + display = None + + destination_meta = self._get_metadata_entry(destination_data["path"], destination_data["name"], + default=dict()) + if display: + destination_meta["display"] = display + self._update_metadata_entry(destination_data["path"], destination_data["name"], destination_meta) + elif "display" in destination_meta: + del destination_meta["display"] + self._update_metadata_entry(destination_data["path"], destination_data["name"], destination_meta) + def copy_folder(self, source, destination): - source_data, destination_data = self._get_source_destination_data(source, destination) + source_data, destination_data = self._get_source_destination_data(source, destination, must_not_equal=True) try: shutil.copytree(source_data["fullpath"], destination_data["fullpath"]) except Exception as e: raise StorageError("Could not copy %s in %s to %s in %s" % (source_data["name"], source_data["path"], destination_data["name"], destination_data["path"]), cause=e) + + self._set_display_metadata(destination_data, source_data=source_data) return self.path_in_storage(destination_data["fullpath"]) def move_folder(self, source, destination): source_data, destination_data = self._get_source_destination_data(source, destination) + + # only a display rename? Update that and bail early + if source_data["fullpath"] == destination_data["fullpath"]: + self._set_display_metadata(destination_data) + return self.path_in_storage(destination_data["fullpath"]) try: shutil.move(source_data["fullpath"], destination_data["fullpath"]) except Exception as e: raise StorageError("Could not move %s in %s to %s in %s" % (source_data["name"], source_data["path"], destination_data["name"], destination_data["path"]), cause=e) + self._set_display_metadata(destination_data, source_data=source_data) + self._remove_metadata_entry(source_data["path"], source_data["name"]) self._delete_metadata(source_data["fullpath"]) return self.path_in_storage(destination_data["fullpath"]) - def add_file(self, path, file_object, printer_profile=None, links=None, allow_overwrite=False): - path, name = self.sanitize(path) + def add_file(self, path, file_object, printer_profile=None, links=None, allow_overwrite=False, display=None): + display_path, display_name = self.canonicalize(path) + path = self.sanitize_path(display_path) + name = self.sanitize_name(display_name) + + if display: + display_name = display + if not octoprint.filemanager.valid_file_type(name): raise StorageError("{name} is an unrecognized file type".format(**locals()), code=StorageError.INVALID_FILE) @@ -663,6 +755,7 @@ def add_file(self, path, file_object, printer_profile=None, links=None, allow_ov # make sure folders exist if not os.path.exists(path): + # TODO persist display names of path segments! os.makedirs(path) # save the file @@ -671,9 +764,19 @@ def add_file(self, path, file_object, printer_profile=None, links=None, allow_ov # save the file's hash to the metadata of the folder file_hash = self._create_hash(file_path) metadata = self._get_metadata_entry(path, name, default=dict()) + metadata_dirty = False if not "hash" in metadata or metadata["hash"] != file_hash: # hash changed -> throw away old metadata - self._update_metadata_entry(path, name, dict(hash=file_hash)) + metadata = dict(hash=file_hash) + metadata_dirty = True + + if not "display" in metadata and display_name != name: + # display name is not the same as file name -> store in metadata + metadata["display"] = display_name + metadata_dirty = True + + if metadata_dirty: + self._update_metadata_entry(path, name, metadata) # process any links that were also provided for adding to the file if not links: @@ -706,7 +809,7 @@ def remove_file(self, path): self._remove_metadata_entry(path, name) def copy_file(self, source, destination): - source_data, destination_data = self._get_source_destination_data(source, destination) + source_data, destination_data = self._get_source_destination_data(source, destination, must_not_equal=True) try: shutil.copy2(source_data["fullpath"], destination_data["fullpath"]) @@ -715,12 +818,18 @@ def copy_file(self, source, destination): self._copy_metadata_entry(source_data["path"], source_data["name"], destination_data["path"], destination_data["name"]) + self._set_display_metadata(destination_data, source_data=source_data) return self.path_in_storage(destination_data["fullpath"]) def move_file(self, source, destination, allow_overwrite=False): source_data, destination_data = self._get_source_destination_data(source, destination) + # only a display rename? Update that and bail early + if source_data["fullpath"] == destination_data["fullpath"]: + self._set_display_metadata(destination_data) + return self.path_in_storage(destination_data["fullpath"]) + try: shutil.move(source_data["fullpath"], destination_data["fullpath"]) except Exception as e: @@ -729,7 +838,8 @@ def move_file(self, source, destination, allow_overwrite=False): self._copy_metadata_entry(source_data["path"], source_data["name"], destination_data["path"], destination_data["name"], delete_source=True) - + self._set_display_metadata(destination_data, source_data=source_data) + return self.path_in_storage(destination_data["fullpath"]) def has_analysis(self, path): @@ -762,7 +872,7 @@ def update_history(self, path, index, data): def remove_history(self, path, index): path, name = self.sanitize(path) - self._update_history(name, path, index) + self._delete_history(name, path, index) def set_additional_metadata(self, path, key, data, overwrite=False, merge=False): path, name = self.sanitize(path) @@ -800,14 +910,15 @@ def remove_additional_metadata(self, path, key): self._save_metadata(path, metadata) def split_path(self, path): - split = path.split("/") + path = to_unicode(path) + split = path.split(u"/") if len(split) == 1: - return "", split[0] + return u"", split[0] else: return self.join_path(*split[:-1]), split[-1] def join_path(self, *path): - return "/".join(path) + return u"/".join(map(to_unicode, path)) def sanitize(self, path): """ @@ -824,24 +935,30 @@ def sanitize(self, path): hence be returned at second position. If you only need to convert a folder path, be sure to include a trailing slash for a string ``path`` or an empty last element for a list ``path``. """ + + path, name = self.canonicalize(path) + name = self.sanitize_name(name) + path = self.sanitize_path(path) + return path, name + + def canonicalize(self, path): name = None - if isinstance(path, (str, unicode, basestring)): + if isinstance(path, basestring): + path = to_unicode(path) if path.startswith(self.basefolder): path = path[len(self.basefolder):] - path = path.replace(os.path.sep, "/") - path = path.split("/") + path = path.replace(os.path.sep, u"/") + path = path.split(u"/") if isinstance(path, (list, tuple)): if len(path) == 1: - name = path[0] - path = "/" + name = to_unicode(path[0]) + path = u"" else: - name = path[-1] - path = "/" + self.join_path(*path[:-1]) + name = to_unicode(path[-1]) + path = self.join_path(*map(to_unicode, path[:-1])) if not path: - path = "/" + path = u"" - name = self.sanitize_name(name) - path = self.sanitize_path(path) return path, name def sanitize_name(self, name): @@ -850,14 +967,16 @@ def sanitize_name(self, name): slugifies the given ``name`` by converting it to ASCII, leaving ``-``, ``_``, ``.``, ``(``, and ``)`` as is. """ + name = to_unicode(name) + if name is None: return None - if "/" in name or "\\" in name: + if u"/" in name or u"\\" in name: raise ValueError("name must not contain / or \\") - result = self._slugify(name).replace(" ", "_") - if result and result != "." and result != ".." and result[0] == ".": + result = self._slugify(name).replace(u" ", u"_") + if result and result != u"." and result != u".." and result[0] == u".": # hidden files under *nix result = result[1:] return result @@ -868,12 +987,15 @@ def sanitize_path(self, path): relative path elements (e.g. ``..``) and sanitizes folder names using :func:`sanitize_name`. Final path is the absolute path including leading ``basefolder`` path. """ - if path[0] == "/": - path = path[1:] - elif path[0] == "." and path[1] == "/": - path = path[2:] - - path_elements = path.split("/") + path = to_unicode(path) + + if len(path): + if path[0] == u"/": + path = path[1:] + elif path[0] == u"." and path[1] == u"/": + path = path[2:] + + path_elements = path.split(u"/") joined_path = self.basefolder for path_element in path_elements: joined_path = os.path.join(joined_path, self.sanitize_name(path_element)) @@ -883,6 +1005,7 @@ def sanitize_path(self, path): return path def _sanitize_entry(self, entry, path, entry_path): + entry = to_unicode(entry) sanitized = self.sanitize_name(entry) if sanitized != entry: # entry is not sanitized yet, let's take care of that @@ -892,16 +1015,16 @@ def _sanitize_entry(self, entry, path, entry_path): counter = 1 while os.path.exists(sanitized_path): counter += 1 - sanitized = self.sanitize_name("{}_({}){}".format(sanitized_name, counter, sanitized_ext)) + sanitized = self.sanitize_name(u"{}_({}){}".format(sanitized_name, counter, sanitized_ext)) sanitized_path = os.path.join(path, sanitized) try: shutil.move(entry_path, sanitized_path) - self._logger.info("Sanitized \"{}\" to \"{}\"".format(entry_path, sanitized_path)) + self._logger.info(u"Sanitized \"{}\" to \"{}\"".format(entry_path, sanitized_path)) return sanitized, sanitized_path except: - self._logger.exception("Error while trying to rename \"{}\" to \"{}\", ignoring file".format(entry_path, sanitized_path)) + self._logger.exception(u"Error while trying to rename \"{}\" to \"{}\", ignoring file".format(entry_path, sanitized_path)) raise return entry, entry_path @@ -910,10 +1033,11 @@ def path_in_storage(self, path): if isinstance(path, (tuple, list)): path = self.join_path(*path) if isinstance(path, (str, unicode, basestring)): + path = to_unicode(path) if path.startswith(self.basefolder): path = path[len(self.basefolder):] - path = path.replace(os.path.sep, "/") - if path.startswith("/"): + path = path.replace(os.path.sep, u"/") + if path.startswith(u"/"): path = path[1:] return path @@ -1169,7 +1293,7 @@ def _list_folder(self, path, base="", entry_filter=None, recursive=True, **kwarg continue try: - entry_name = entry.name + entry_name = entry_display = entry.name entry_path = entry.path entry_is_file = entry.is_file() entry_is_dir = entry.is_dir() @@ -1182,6 +1306,7 @@ def _list_folder(self, path, base="", entry_filter=None, recursive=True, **kwarg try: new_entry_name, new_entry_path = self._sanitize_entry(entry_name, path, entry_path) if entry_name != new_entry_name or entry_path != new_entry_path: + entry_display = to_unicode(entry_name) entry_name = new_entry_name entry_path = new_entry_path entry_stat = os.stat(entry_path) @@ -1201,18 +1326,26 @@ def _list_folder(self, path, base="", entry_filter=None, recursive=True, **kwarg file_type = type_path[0] if entry_name in metadata and isinstance(metadata[entry_name], dict): - entry_data = metadata[entry_name] + entry_metadata = metadata[entry_name] + if not "display" in entry_metadata and entry_display != entry_name: + metadata[entry_name]["display"] = entry_display + entry_metadata["display"] = entry_display + metadata_dirty = True else: - entry_data = self._add_basic_metadata(path, entry_name, save=False, metadata=metadata) + entry_metadata = self._add_basic_metadata(path, entry_name, + display_name=entry_display, + save=False, + metadata=metadata) metadata_dirty = True # TODO extract model hash from source if possible to recreate link - if not entry_filter or entry_filter(entry_name, entry_data): + if not entry_filter or entry_filter(entry_name, entry_metadata): # only add files passing the optional filter extended_entry_data = dict() - extended_entry_data.update(entry_data) + extended_entry_data.update(entry_metadata) extended_entry_data["name"] = entry_name + extended_entry_data["display"] = entry_metadata.get("display", entry_name) extended_entry_data["path"] = path_in_location extended_entry_data["type"] = file_type extended_entry_data["typePath"] = type_path @@ -1225,11 +1358,27 @@ def _list_folder(self, path, base="", entry_filter=None, recursive=True, **kwarg # folder recursion elif entry_is_dir: + if entry_name in metadata and isinstance(metadata[entry_name], dict): + entry_metadata = metadata[entry_name] + if not "display" in entry_metadata and entry_display != entry_name: + metadata[entry_name]["display"] = entry_display + entry_metadata["display"] = entry_display + metadata_dirty = True + elif entry_name != entry_display: + entry_metadata = self._add_basic_metadata(path, entry_name, + display_name=entry_display, + save=False, + metadata=metadata) + metadata_dirty = True + else: + entry_metadata = dict() + entry_data = dict( name=entry_name, + display=entry_metadata.get("display", entry_name), path=path_in_location, type="folder", - type_path=["folder"] + typePath=["folder"] ) if recursive: sub_result = self._list_folder(entry_path, base=path_in_location + "/", entry_filter=entry_filter, @@ -1261,22 +1410,33 @@ def get_size(): return result - def _add_basic_metadata(self, path, entry, additional_metadata=None, save=True, metadata=None): + def _add_basic_metadata(self, path, entry, display_name=None, additional_metadata=None, save=True, metadata=None): if additional_metadata is None: additional_metadata = dict() if metadata is None: metadata = self._get_metadata(path) - entry_data = dict( - hash=self._create_hash(os.path.join(path, entry)), - links=[], - notes=[] - ) + entry_path = os.path.join(path, entry) + + if os.path.isfile(entry_path): + entry_data = dict( + hash=self._create_hash(os.path.join(path, entry)), + links=[], + notes=[] + ) + if path == self.basefolder and self._old_metadata is not None and entry in self._old_metadata and "gcodeAnalysis" in self._old_metadata[entry]: + # if there is still old metadata available and that contains an analysis for this file, use it! + entry_data["analysis"] = self._old_metadata[entry]["gcodeAnalysis"] - if path == self.basefolder and self._old_metadata is not None and entry in self._old_metadata and "gcodeAnalysis" in self._old_metadata[entry]: - # if there is still old metadata available and that contains an analysis for this file, use it! - entry_data["analysis"] = self._old_metadata[entry]["gcodeAnalysis"] + elif os.path.isdir(entry_path): + entry_data = dict() + + else: + return + + if display_name is not None and not display_name == entry: + entry_data["display"] = display_name entry_data.update(additional_metadata) metadata[entry] = entry_data @@ -1327,7 +1487,7 @@ def _update_metadata_entry(self, path, name, data): metadata[name] = data self._save_metadata(path, metadata) - def _copy_metadata_entry(self, source_path, source_name, destination_path, destination_name, delete_source=False): + def _copy_metadata_entry(self, source_path, source_name, destination_path, destination_name, delete_source=False, updates=None): with self._get_metadata_lock(source_path): source_data = self._get_metadata_entry(source_path, source_name, default=dict()) if not source_data: @@ -1336,6 +1496,9 @@ def _copy_metadata_entry(self, source_path, source_name, destination_path, desti if delete_source: self._remove_metadata_entry(source_path, source_name) + if updates is not None: + source_data.update(updates) + with self._get_metadata_lock(destination_path): self._update_metadata_entry(destination_path, destination_name, source_data) diff --git a/src/octoprint/printer/standard.py b/src/octoprint/printer/standard.py index 184bb564e8..7379904ba2 100644 --- a/src/octoprint/printer/standard.py +++ b/src/octoprint/printer/standard.py @@ -887,6 +887,7 @@ def _setJobData(self, filename, filesize, sd): "file": { "name": None, "path": None, + "display": None, "origin": None, "size": None, "date": None @@ -903,6 +904,7 @@ def _setJobData(self, filename, filesize, sd): averagePrintTime = None date = None filament = None + display_name = name_in_storage if path_on_disk: # Use a string for mtime because it could be float and the # javascript needs to exact match @@ -914,6 +916,8 @@ def _setJobData(self, filename, filesize, sd): except: fileData = None if fileData is not None: + if "display" in fileData: + display_name = fileData["display"] if "analysis" in fileData: if estimatedPrintTime is None and "estimatedPrintTime" in fileData["analysis"]: estimatedPrintTime = fileData["analysis"]["estimatedPrintTime"] @@ -938,6 +942,7 @@ def _setJobData(self, filename, filesize, sd): "file": { "name": name_in_storage, "path": path_in_storage, + "display": display_name, "origin": FileDestinations.SDCARD if sd else FileDestinations.LOCAL, "size": filesize, "date": date diff --git a/src/octoprint/server/__init__.py b/src/octoprint/server/__init__.py index dbc87c1940..7363adfebf 100644 --- a/src/octoprint/server/__init__.py +++ b/src/octoprint/server/__init__.py @@ -464,6 +464,11 @@ def template_disabled(name, plugin): def mime_type_guesser(path): from octoprint.filemanager import get_mime_type return get_mime_type(path) + + def download_name_generator(path): + metadata = fileManager.get_metadata("local", path) + if metadata and "display" in metadata: + return metadata["display"] download_handler_kwargs = dict( as_attachment=True, @@ -493,7 +498,9 @@ def joined_dict(*dicts): (r"/downloads/timelapse/([^/]*\.mp[g4])", util.tornado.LargeResponseHandler, joined_dict(dict(path=self._settings.getBaseFolder("timelapse")), download_handler_kwargs, no_hidden_files_validator)), - (r"/downloads/files/local/(.*)", util.tornado.LargeResponseHandler, joined_dict(dict(path=self._settings.getBaseFolder("uploads")), + (r"/downloads/files/local/(.*)", util.tornado.LargeResponseHandler, joined_dict(dict(path=self._settings.getBaseFolder("uploads"), + as_attachment=True, + name_generator=download_name_generator), download_handler_kwargs, no_hidden_files_validator, additional_mime_types)), diff --git a/src/octoprint/server/api/files.py b/src/octoprint/server/api/files.py index 91fcaaa532..214735af2b 100644 --- a/src/octoprint/server/api/files.py +++ b/src/octoprint/server/api/files.py @@ -148,6 +148,7 @@ def _getFileList(origin, path=None, filter=None, recursive=False, allow_from_cac file = { "type": "machinecode", "name": sdFile, + "display": sdFile, "path": sdFile, "origin": FileDestinations.SDCARD, "refs": { @@ -287,8 +288,11 @@ def uploadGcodeFile(target): # determine future filename of file to be uploaded, abort if it can't be uploaded try: # FileDestinations.LOCAL = should normally be target, but can't because SDCard handling isn't implemented yet - futurePath, futureFilename = fileManager.sanitize(FileDestinations.LOCAL, upload.filename) + canonPath, canonFilename = fileManager.canonicalize(FileDestinations.LOCAL, upload.filename) + futurePath = fileManager.sanitize_path(FileDestinations.LOCAL, canonPath) + futureFilename = fileManager.sanitize_name(FileDestinations.LOCAL, canonFilename) except: + canonFilename = None futurePath = None futureFilename = None @@ -335,7 +339,9 @@ def selectAndOrPrint(filename, absFilename, destination): printer.select_file(absFilename, destination == FileDestinations.SDCARD, printAfterSelect) try: - added_file = fileManager.add_file(FileDestinations.LOCAL, futureFullPathInStorage, upload, allow_overwrite=True) + added_file = fileManager.add_file(FileDestinations.LOCAL, futureFullPathInStorage, upload, + allow_overwrite=True, + display=canonFilename) except octoprint.filemanager.storage.StorageError as e: if e.code == octoprint.filemanager.storage.StorageError.INVALID_FILE: return make_response("Could not upload the file \"{}\", invalid type".format(upload.filename), 400) @@ -401,7 +407,9 @@ def selectAndOrPrint(filename, absFilename, destination): if not target in [FileDestinations.LOCAL]: return make_response("Unknown target: %s" % target, 400) - futurePath, futureName = fileManager.sanitize(target, foldername) + canonPath, canonName = fileManager.canonicalize(target, foldername) + futurePath = fileManager.sanitize_path(target, canonPath) + futureName = fileManager.sanitize_name(target, canonName) if not futureName or not futurePath: return make_response("Can't create a folder with an empty name", 400) @@ -414,7 +422,7 @@ def selectAndOrPrint(filename, absFilename, destination): return make_response("Can't create a folder named %s, please try another name" % futureName, 409) try: - added_folder = fileManager.add_folder(target, futureFullPath) + added_folder = fileManager.add_folder(target, futureFullPath, display=canonName) except octoprint.filemanager.storage.StorageError as e: if e.code == octoprint.filemanager.storage.StorageError.INVALID_DIRECTORY: return make_response("Could not create folder {}, invalid directory".format(futureName)) @@ -540,6 +548,14 @@ def gcodeFileCommand(filename, target): if path: full_path = fileManager.join_path(target, path, destination) + canon_path, canon_name = fileManager.canonicalize(target, full_path) + sanitized_name = fileManager.sanitize_name(target, canon_name) + + if canon_path: + full_path = fileManager.join_path(target, canon_path, sanitized_name) + else: + full_path = sanitized_name + # prohibit overwriting the file that is currently being printed currentOrigin, currentFilename = _getCurrentFile() if currentFilename == full_path and currentOrigin == target and (printer.is_printing() or printer.is_paused()): @@ -596,6 +612,7 @@ def slicing_done(target, path, select_after_slicing, print_after_slicing): printer_profile_id=printerProfile, position=position, overrides=overrides, + display=canon_name, callback=slicing_done, callback_args=(target, full_path, select_after_slicing, print_after_slicing)) except octoprint.slicing.UnknownProfile: @@ -604,8 +621,9 @@ def slicing_done(target, path, select_after_slicing, print_after_slicing): files = {} location = url_for(".readGcodeFile", target=target, filename=full_path, _external=True) result = { - "name": destination, + "name": sanitized_name, "path": full_path, + "display": canon_name, "origin": FileDestinations.LOCAL, "refs": { "resource": location, @@ -637,14 +655,15 @@ def slicing_done(target, path, select_after_slicing, print_after_slicing): return make_response("File or folder not found on {}: {}".format(target, filename), 404) path, name = fileManager.split_path(target, filename) + destination = data["destination"] - if _verifyFolderExists(target, destination): - # destination is an existing folder, we'll assume we are supposed to move filename to this - # folder under the same name - destination = fileManager.join_path(target, destination, name) + dst_path, dst_name = fileManager.split_path(target, destination) + sanitized_destination = fileManager.join_path(target, dst_path, fileManager.sanitize_name(target, dst_name)) - if _verifyFileExists(target, destination) or _verifyFolderExists(target, destination): - return make_response("File or folder does already exist on {}: {}".format(target, destination), 409) + if _verifyFolderExists(target, destination) and sanitized_destination != filename: + # destination is an existing folder and not ourselves (= display rename), we'll assume we are supposed + # to move filename to this folder under the same name + destination = fileManager.join_path(target, destination, name) is_file = fileManager.file_exists(target, filename) is_folder = fileManager.folder_exists(target, filename) @@ -653,14 +672,24 @@ def slicing_done(target, path, select_after_slicing, print_after_slicing): return make_response("{} on {} is neither file or folder, can't {}".format(filename, target, command), 400) if command == "copy": + # destination already there? error... + if _verifyFileExists(target, destination) or _verifyFolderExists(target, destination): + return make_response("File or folder does already exist on {}: {}".format(target, destination), 409) + if is_file: fileManager.copy_file(target, filename, destination) else: fileManager.copy_folder(target, filename, destination) + elif command == "move": if _isBusy(target, filename): return make_response("Trying to move a file or folder that is currently in use: {}".format(filename), 409) + # destination already there AND not ourselves (= display rename)? error... + if (_verifyFileExists(target, destination) or _verifyFolderExists(target, destination)) \ + and sanitized_destination != filename: + return make_response("File or folder does already exist on {}: {}".format(target, destination), 409) + # deselect the file if it's currently selected currentOrigin, currentFilename = _getCurrentFile() if currentFilename is not None and filename == currentFilename: diff --git a/src/octoprint/server/util/tornado.py b/src/octoprint/server/util/tornado.py index 6373733ea4..5565ecc388 100644 --- a/src/octoprint/server/util/tornado.py +++ b/src/octoprint/server/util/tornado.py @@ -883,7 +883,7 @@ class LargeResponseHandler(tornado.web.StaticFileHandler): """ def initialize(self, path, default_filename=None, as_attachment=False, allow_client_caching=True, - access_validation=None, path_validation=None, etag_generator=None, + access_validation=None, path_validation=None, etag_generator=None, name_generator=None, mime_type_guesser=None): tornado.web.StaticFileHandler.initialize(self, os.path.abspath(path), default_filename) self._as_attachment = as_attachment @@ -891,6 +891,7 @@ def initialize(self, path, default_filename=None, as_attachment=False, allow_cli self._access_validation = access_validation self._path_validation = path_validation self._etag_generator = etag_generator + self._name_generator = name_generator self._mime_type_guesser = mime_type_guesser def get(self, path, include_body=True): @@ -907,7 +908,15 @@ def get(self, path, include_body=True): def set_extra_headers(self, path): if self._as_attachment: - self.set_header("Content-Disposition", "attachment; filename=%s" % os.path.basename(path)) + filename = None + if callable(self._name_generator): + filename = self._name_generator(path) + if filename is None: + filename = os.path.basename(path) + + filename = tornado.escape.url_escape(filename, plus=False) + self.set_header("Content-Disposition", "attachment; filename=\"{}\"; filename*=UTF-8''{}".format(filename, + filename)) if not self._allow_client_caching: self.set_header("Cache-Control", "max-age=0, must-revalidate, private") diff --git a/src/octoprint/static/css/octoprint.css b/src/octoprint/static/css/octoprint.css index bcbfc921fc..eb8f967897 100644 --- a/src/octoprint/static/css/octoprint.css +++ b/src/octoprint/static/css/octoprint.css @@ -1 +1 @@ -.btn{display:inline-block;*display:inline;*zoom:1;padding:4px 12px;margin-bottom:0;font-size:14px;line-height:20px;text-align:center;vertical-align:middle;cursor:pointer;color:#333;text-shadow:0 1px 1px rgba(255,255,255,.75);background-color:#f5f5f5;background-image:-moz-linear-gradient(top,#fff,#e6e6e6);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#e6e6e6));background-image:-webkit-linear-gradient(top,#fff,#e6e6e6);background-image:-o-linear-gradient(top,#fff,#e6e6e6);background-image:linear-gradient(to bottom,#fff,#e6e6e6);background-repeat:repeat-x;*background-color:#e6e6e6;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);border:1px solid #ccc;*border:0;border-bottom-color:#b3b3b3;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;*margin-left:.3em;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05)}.btn.active,.btn.disabled,.btn:active,.btn:focus,.btn:hover,.btn[disabled]{color:#333;background-color:#e6e6e6;*background-color:#d9d9d9}.btn.active,.btn:active{background-color:#ccc \9}.btn:first-child{*margin-left:0}.btn:focus,.btn:hover{color:#333;text-decoration:none;background-position:0 -15px;-webkit-transition:background-position .1s linear;-moz-transition:background-position .1s linear;-o-transition:background-position .1s linear;transition:background-position .1s linear}.btn:focus{outline:#333 dotted thin;outline:-webkit-focus-ring-color auto 5px;outline-offset:-2px}.btn.active,.btn:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,.15),0 1px 2px rgba(0,0,0,.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,.15),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 2px 4px rgba(0,0,0,.15),0 1px 2px rgba(0,0,0,.05)}.btn.disabled,.btn[disabled]{cursor:default;background-image:none;opacity:.65;filter:alpha(opacity=65);-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.btn-large{padding:11px 19px;font-size:17.5px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.btn-large [class*=" icon-"],.btn-large [class^=icon-]{margin-top:4px}.btn-small{padding:2px 10px;font-size:11.9px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.btn-small [class*=" icon-"],.btn-small [class^=icon-]{margin-top:0}.btn-mini [class*=" icon-"],.btn-mini [class^=icon-]{margin-top:-1px}.btn-mini{padding:0 6px;font-size:10.5px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.btn-block{display:block;width:100%;padding-left:0;padding-right:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.btn-block+.btn-block{margin-top:5px}input[type=submit].btn-block,input[type=reset].btn-block,input[type=button].btn-block{width:100%}.btn-primary{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,.25);background-color:#006dcc;background-image:-moz-linear-gradient(top,#08c,#04c);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#04c));background-image:-webkit-linear-gradient(top,#08c,#04c);background-image:-o-linear-gradient(top,#08c,#04c);background-image:linear-gradient(to bottom,#08c,#04c);background-repeat:repeat-x;border-color:#04c #04c #002a80;border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);*background-color:#04c;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-primary.active,.btn-primary.disabled,.btn-primary:active,.btn-primary:focus,.btn-primary:hover,.btn-primary[disabled]{color:#fff;background-color:#04c;*background-color:#003bb3}.btn-primary.active,.btn-primary:active{background-color:#039 \9}.btn-warning{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,.25);background-color:#faa732;background-image:-moz-linear-gradient(top,#fbb450,#f89406);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fbb450),to(#f89406));background-image:-webkit-linear-gradient(top,#fbb450,#f89406);background-image:-o-linear-gradient(top,#fbb450,#f89406);background-image:linear-gradient(to bottom,#fbb450,#f89406);background-repeat:repeat-x;border-color:#f89406 #f89406 #ad6704;border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);*background-color:#f89406;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-warning.active,.btn-warning.disabled,.btn-warning:active,.btn-warning:focus,.btn-warning:hover,.btn-warning[disabled]{color:#fff;background-color:#f89406;*background-color:#df8505}.btn-warning.active,.btn-warning:active{background-color:#c67605 \9}.btn-danger{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,.25);background-color:#da4f49;background-image:-moz-linear-gradient(top,#ee5f5b,#bd362f);background-image:-webkit-gradient(linear,0 0,0 100%,from(#ee5f5b),to(#bd362f));background-image:-webkit-linear-gradient(top,#ee5f5b,#bd362f);background-image:-o-linear-gradient(top,#ee5f5b,#bd362f);background-image:linear-gradient(to bottom,#ee5f5b,#bd362f);background-repeat:repeat-x;border-color:#bd362f #bd362f #802420;border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);*background-color:#bd362f;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-danger.active,.btn-danger.disabled,.btn-danger:active,.btn-danger:focus,.btn-danger:hover,.btn-danger[disabled]{color:#fff;background-color:#bd362f;*background-color:#a9302a}.btn-danger.active,.btn-danger:active{background-color:#942a25 \9}.btn-success{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,.25);background-color:#5bb75b;background-image:-moz-linear-gradient(top,#62c462,#51a351);background-image:-webkit-gradient(linear,0 0,0 100%,from(#62c462),to(#51a351));background-image:-webkit-linear-gradient(top,#62c462,#51a351);background-image:-o-linear-gradient(top,#62c462,#51a351);background-image:linear-gradient(to bottom,#62c462,#51a351);background-repeat:repeat-x;border-color:#51a351 #51a351 #387038;border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);*background-color:#51a351;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-success.active,.btn-success.disabled,.btn-success:active,.btn-success:focus,.btn-success:hover,.btn-success[disabled]{color:#fff;background-color:#51a351;*background-color:#499249}.btn-success.active,.btn-success:active{background-color:#408140 \9}.btn-info{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,.25);background-color:#49afcd;background-image:-moz-linear-gradient(top,#5bc0de,#2f96b4);background-image:-webkit-gradient(linear,0 0,0 100%,from(#5bc0de),to(#2f96b4));background-image:-webkit-linear-gradient(top,#5bc0de,#2f96b4);background-image:-o-linear-gradient(top,#5bc0de,#2f96b4);background-image:linear-gradient(to bottom,#5bc0de,#2f96b4);background-repeat:repeat-x;border-color:#2f96b4 #2f96b4 #1f6377;border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);*background-color:#2f96b4;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-info.active,.btn-info.disabled,.btn-info:active,.btn-info:focus,.btn-info:hover,.btn-info[disabled]{color:#fff;background-color:#2f96b4;*background-color:#2a85a0}.btn-info.active,.btn-info:active{background-color:#24748c \9}.btn-inverse{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,.25);background-color:#363636;background-image:-moz-linear-gradient(top,#444,#222);background-image:-webkit-gradient(linear,0 0,0 100%,from(#444),to(#222));background-image:-webkit-linear-gradient(top,#444,#222);background-image:-o-linear-gradient(top,#444,#222);background-image:linear-gradient(to bottom,#444,#222);background-repeat:repeat-x;border-color:#222 #222 #000;border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);*background-color:#222;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-inverse.active,.btn-inverse.disabled,.btn-inverse:active,.btn-inverse:focus,.btn-inverse:hover,.btn-inverse[disabled]{color:#fff;background-color:#222;*background-color:#151515}.btn-inverse.active,.btn-inverse:active{background-color:#080808 \9}button.btn,input[type=submit].btn{*padding-top:3px;*padding-bottom:3px}button.btn::-moz-focus-inner,input[type=submit].btn::-moz-focus-inner{padding:0;border:0}button.btn.btn-large,input[type=submit].btn.btn-large{*padding-top:7px;*padding-bottom:7px}button.btn.btn-small,input[type=submit].btn.btn-small{*padding-top:3px;*padding-bottom:3px}button.btn.btn-mini,input[type=submit].btn.btn-mini{*padding-top:1px;*padding-bottom:1px}.btn-link,.btn-link:active,.btn-link[disabled]{background-color:transparent;background-image:none;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.btn-link{border-color:transparent;cursor:pointer;color:#08c;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.btn-link:focus,.btn-link:hover{color:#005580;text-decoration:underline;background-color:transparent}.btn-link[disabled]:focus,.btn-link[disabled]:hover{color:#333;text-decoration:none}.clearfix{*zoom:1}.clearfix:after,.clearfix:before{display:table;content:"";line-height:0}.clearfix:after{clear:both}.hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.input-block-level{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.nowrap{white-space:nowrap}.actioncol{text-align:center;white-space:nowrap}.actioncol a{text-decoration:none;color:#000}.actioncol a.disabled{color:#ccc;cursor:default}#navbar .navbar-inner{background-color:#ebebeb;background-image:-moz-linear-gradient(top,#fff,#ccc);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#ccc));background-image:-webkit-linear-gradient(top,#fff,#ccc);background-image:-o-linear-gradient(top,#fff,#ccc);background-image:linear-gradient(to bottom,#fff,#ccc);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffcccccc', GradientType=0)}#navbar .navbar-inner .brand,#navbar .navbar-inner .nav>li>a{text-shadow:0 1px 0 #ccc;color:#333}#navbar .navbar-inner .brand .caret,#navbar .navbar-inner .nav>li>a .caret{border-bottom-color:#939393;border-top-color:#939393}#navbar .navbar-inner .brand:focus .caret,#navbar .navbar-inner .brand:hover .caret,#navbar .navbar-inner .nav>li>a:focus .caret,#navbar .navbar-inner .nav>li>a:hover .caret{border-bottom-color:#636363;border-top-color:#636363}#navbar .navbar-inner .brand span{background-image:url(../img/tentacle-20x20.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner .brand span{background-image:url(../img/tentacle-20x20@2x.png)}}#navbar .navbar-inner .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner .nav>li.dropdown.open>.dropdown-toggle{background-color:#e0e0e0;background-image:-moz-linear-gradient(top,#ccc,#fff);background-image:-webkit-gradient(linear,0 0,0 100%,from(#ccc),to(#fff));background-image:-webkit-linear-gradient(top,#ccc,#fff);background-image:-o-linear-gradient(top,#ccc,#fff);background-image:linear-gradient(to bottom,#ccc,#fff);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffcccccc', endColorstr='#ffffffff', GradientType=0)}#navbar .navbar-inner .nav>li>a:hover{background-color:#dedede;background-image:-moz-linear-gradient(top,#f2f2f2,#bfbfbf);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f2f2f2),to(#bfbfbf));background-image:-webkit-linear-gradient(top,#f2f2f2,#bfbfbf);background-image:-o-linear-gradient(top,#f2f2f2,#bfbfbf);background-image:linear-gradient(to bottom,#f2f2f2,#bfbfbf);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2f2f2', endColorstr='#ffbfbfbf', GradientType=0)}#navbar .navbar-inner.transparent{background-color:rgba(235,235,235,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(255,255,255,.6),rgba(204,204,204,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(255,255,255,.6)),to(rgba(204,204,204,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(255,255,255,.6),rgba(204,204,204,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(255,255,255,.6),rgba(204,204,204,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(255,255,255,.6),rgba(204,204,204,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99ffffff', endColorstr='#99cccccc', GradientType=0)}#navbar .navbar-inner.transparent .brand,#navbar .navbar-inner.transparent .nav>li>a{text-shadow:0 1px 0 #ccc;color:#333}#navbar .navbar-inner.transparent .brand .caret,#navbar .navbar-inner.transparent .nav>li>a .caret{border-bottom-color:#939393;border-top-color:#939393}#navbar .navbar-inner.transparent .brand:focus .caret,#navbar .navbar-inner.transparent .brand:hover .caret,#navbar .navbar-inner.transparent .nav>li>a:focus .caret,#navbar .navbar-inner.transparent .nav>li>a:hover .caret{border-bottom-color:#636363;border-top-color:#636363}#navbar .navbar-inner.transparent .brand span{background-image:url(../img/tentacle-20x20.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.transparent .brand span{background-image:url(../img/tentacle-20x20@2x.png)}}#navbar .navbar-inner.transparent .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.transparent .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.transparent .nav>li.dropdown.open>.dropdown-toggle{background-color:rgba(224,224,224,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(204,204,204,.6),rgba(255,255,255,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(204,204,204,.6)),to(rgba(255,255,255,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(204,204,204,.6),rgba(255,255,255,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(204,204,204,.6),rgba(255,255,255,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(204,204,204,.6),rgba(255,255,255,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99cccccc', endColorstr='#99ffffff', GradientType=0)}#navbar .navbar-inner.transparent .nav>li>a:hover{background-color:rgba(222,222,222,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(242,242,242,.6),rgba(191,191,191,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(242,242,242,.6)),to(rgba(191,191,191,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(242,242,242,.6),rgba(191,191,191,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(242,242,242,.6),rgba(191,191,191,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(242,242,242,.6),rgba(191,191,191,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99f2f2f2', endColorstr='#99bfbfbf', GradientType=0)}#navbar .navbar-inner.red{background-color:#bb645f;background-image:-moz-linear-gradient(top,#e28e8a,#802420);background-image:-webkit-gradient(linear,0 0,0 100%,from(#e28e8a),to(#802420));background-image:-webkit-linear-gradient(top,#e28e8a,#802420);background-image:-o-linear-gradient(top,#e28e8a,#802420);background-image:linear-gradient(to bottom,#e28e8a,#802420);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe28e8a', endColorstr='#ff802420', GradientType=0)}#navbar .navbar-inner.red .brand,#navbar .navbar-inner.red .nav>li>a{text-shadow:0 1px 0 #d86761;color:#f2f2f2}#navbar .navbar-inner.red .brand .caret,#navbar .navbar-inner.red .nav>li>a .caret{border-bottom-color:#d89491;border-top-color:#d89491}#navbar .navbar-inner.red .brand:focus .caret,#navbar .navbar-inner.red .brand:hover .caret,#navbar .navbar-inner.red .nav>li>a:focus .caret,#navbar .navbar-inner.red .nav>li>a:hover .caret{border-bottom-color:#e5c3c1;border-top-color:#e5c3c1}#navbar .navbar-inner.red .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.red .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.red .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.red .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.red .nav>li.dropdown.open>.dropdown-toggle{background-color:#a74f4a;background-image:-moz-linear-gradient(top,#802420,#e28e8a);background-image:-webkit-gradient(linear,0 0,0 100%,from(#802420),to(#e28e8a));background-image:-webkit-linear-gradient(top,#802420,#e28e8a);background-image:-o-linear-gradient(top,#802420,#e28e8a);background-image:linear-gradient(to bottom,#802420,#e28e8a);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff802420', endColorstr='#ffe28e8a', GradientType=0)}#navbar .navbar-inner.red .nav>li>a:hover{background-color:#af5651;background-image:-moz-linear-gradient(top,#dd7a75,#6b1f1b);background-image:-webkit-gradient(linear,0 0,0 100%,from(#dd7a75),to(#6b1f1b));background-image:-webkit-linear-gradient(top,#dd7a75,#6b1f1b);background-image:-o-linear-gradient(top,#dd7a75,#6b1f1b);background-image:linear-gradient(to bottom,#dd7a75,#6b1f1b);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdd7a75', endColorstr='#ff6b1f1b', GradientType=0)}#navbar .navbar-inner.red.transparent{background-color:rgba(187,100,95,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(226,142,138,.6),rgba(128,36,32,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(226,142,138,.6)),to(rgba(128,36,32,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(226,142,138,.6),rgba(128,36,32,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(226,142,138,.6),rgba(128,36,32,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(226,142,138,.6),rgba(128,36,32,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99e28e8a', endColorstr='#99802420', GradientType=0)}#navbar .navbar-inner.red.transparent .brand,#navbar .navbar-inner.red.transparent .nav>li>a{text-shadow:0 1px 0 #d86761;color:#f2f2f2}#navbar .navbar-inner.red.transparent .brand .caret,#navbar .navbar-inner.red.transparent .nav>li>a .caret{border-bottom-color:#d89491;border-top-color:#d89491}#navbar .navbar-inner.red.transparent .brand:focus .caret,#navbar .navbar-inner.red.transparent .brand:hover .caret,#navbar .navbar-inner.red.transparent .nav>li>a:focus .caret,#navbar .navbar-inner.red.transparent .nav>li>a:hover .caret{border-bottom-color:#e5c3c1;border-top-color:#e5c3c1}#navbar .navbar-inner.red.transparent .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.red.transparent .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.red.transparent .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.red.transparent .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.red.transparent .nav>li.dropdown.open>.dropdown-toggle{background-color:rgba(167,79,74,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(128,36,32,.6),rgba(226,142,138,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(128,36,32,.6)),to(rgba(226,142,138,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(128,36,32,.6),rgba(226,142,138,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(128,36,32,.6),rgba(226,142,138,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(128,36,32,.6),rgba(226,142,138,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99802420', endColorstr='#99e28e8a', GradientType=0)}#navbar .navbar-inner.red.transparent .nav>li>a:hover{background-color:rgba(175,86,81,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(221,122,117,.6),rgba(107,31,27,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(221,122,117,.6)),to(rgba(107,31,27,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(221,122,117,.6),rgba(107,31,27,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(221,122,117,.6),rgba(107,31,27,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(221,122,117,.6),rgba(107,31,27,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99dd7a75', endColorstr='#996b1f1b', GradientType=0)}#navbar .navbar-inner.orange{background-color:#e39665;background-image:-moz-linear-gradient(top,#f9c3a0,#c2530c);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f9c3a0),to(#c2530c));background-image:-webkit-linear-gradient(top,#f9c3a0,#c2530c);background-image:-o-linear-gradient(top,#f9c3a0,#c2530c);background-image:linear-gradient(to bottom,#f9c3a0,#c2530c);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff9c3a0', endColorstr='#ffc2530c', GradientType=0)}#navbar .navbar-inner.orange .brand,#navbar .navbar-inner.orange .nav>li>a{text-shadow:0 1px 0 #f6a570;color:#f2f2f2}#navbar .navbar-inner.orange .brand .caret,#navbar .navbar-inner.orange .nav>li>a .caret{border-bottom-color:#f2b58d;border-top-color:#f2b58d}#navbar .navbar-inner.orange .brand:focus .caret,#navbar .navbar-inner.orange .brand:hover .caret,#navbar .navbar-inner.orange .nav>li>a:focus .caret,#navbar .navbar-inner.orange .nav>li>a:hover .caret{border-bottom-color:#f2d3bf;border-top-color:#f2d3bf}#navbar .navbar-inner.orange .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.orange .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.orange .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.orange .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.orange .nav>li.dropdown.open>.dropdown-toggle{background-color:#d88047;background-image:-moz-linear-gradient(top,#c2530c,#f9c3a0);background-image:-webkit-gradient(linear,0 0,0 100%,from(#c2530c),to(#f9c3a0));background-image:-webkit-linear-gradient(top,#c2530c,#f9c3a0);background-image:-o-linear-gradient(top,#c2530c,#f9c3a0);background-image:linear-gradient(to bottom,#c2530c,#f9c3a0);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffc2530c', endColorstr='#fff9c3a0', GradientType=0)}#navbar .navbar-inner.orange .nav>li>a:hover{background-color:#d98956;background-image:-moz-linear-gradient(top,#f8b488,#aa490a);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f8b488),to(#aa490a));background-image:-webkit-linear-gradient(top,#f8b488,#aa490a);background-image:-o-linear-gradient(top,#f8b488,#aa490a);background-image:linear-gradient(to bottom,#f8b488,#aa490a);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff8b488', endColorstr='#ffaa490a', GradientType=0)}#navbar .navbar-inner.orange.transparent{background-color:rgba(227,150,101,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(249,195,160,.6),rgba(194,83,12,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(249,195,160,.6)),to(rgba(194,83,12,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(249,195,160,.6),rgba(194,83,12,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(249,195,160,.6),rgba(194,83,12,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(249,195,160,.6),rgba(194,83,12,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99f9c3a0', endColorstr='#99c2530c', GradientType=0)}#navbar .navbar-inner.orange.transparent .brand,#navbar .navbar-inner.orange.transparent .nav>li>a{text-shadow:0 1px 0 #f6a570;color:#f2f2f2}#navbar .navbar-inner.orange.transparent .brand .caret,#navbar .navbar-inner.orange.transparent .nav>li>a .caret{border-bottom-color:#f2b58d;border-top-color:#f2b58d}#navbar .navbar-inner.orange.transparent .brand:focus .caret,#navbar .navbar-inner.orange.transparent .brand:hover .caret,#navbar .navbar-inner.orange.transparent .nav>li>a:focus .caret,#navbar .navbar-inner.orange.transparent .nav>li>a:hover .caret{border-bottom-color:#f2d3bf;border-top-color:#f2d3bf}#navbar .navbar-inner.orange.transparent .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.orange.transparent .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.orange.transparent .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.orange.transparent .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.orange.transparent .nav>li.dropdown.open>.dropdown-toggle{background-color:rgba(216,128,71,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(194,83,12,.6),rgba(249,195,160,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(194,83,12,.6)),to(rgba(249,195,160,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(194,83,12,.6),rgba(249,195,160,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(194,83,12,.6),rgba(249,195,160,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(194,83,12,.6),rgba(249,195,160,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99c2530c', endColorstr='#99f9c3a0', GradientType=0)}#navbar .navbar-inner.orange.transparent .nav>li>a:hover{background-color:rgba(217,137,86,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(248,180,136,.6),rgba(170,73,10,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(248,180,136,.6)),to(rgba(170,73,10,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(248,180,136,.6),rgba(170,73,10,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(248,180,136,.6),rgba(170,73,10,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(248,180,136,.6),rgba(170,73,10,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99f8b488', endColorstr='#99aa490a', GradientType=0)}#navbar .navbar-inner.yellow{background-color:#e3d765;background-image:-moz-linear-gradient(top,#f9f0a0,#c2b00c);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f9f0a0),to(#c2b00c));background-image:-webkit-linear-gradient(top,#f9f0a0,#c2b00c);background-image:-o-linear-gradient(top,#f9f0a0,#c2b00c);background-image:linear-gradient(to bottom,#f9f0a0,#c2b00c);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff9f0a0', endColorstr='#ffc2b00c', GradientType=0)}#navbar .navbar-inner.yellow .brand,#navbar .navbar-inner.yellow .nav>li>a{text-shadow:0 1px 0 #c2b00c;color:#f2f2f2}#navbar .navbar-inner.yellow .brand .caret,#navbar .navbar-inner.yellow .nav>li>a .caret{border-bottom-color:#f2e88d;border-top-color:#f2e88d}#navbar .navbar-inner.yellow .brand:focus .caret,#navbar .navbar-inner.yellow .brand:hover .caret,#navbar .navbar-inner.yellow .nav>li>a:focus .caret,#navbar .navbar-inner.yellow .nav>li>a:hover .caret{border-bottom-color:#f2edbf;border-top-color:#f2edbf}#navbar .navbar-inner.yellow .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.yellow .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.yellow .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.yellow .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.yellow .nav>li.dropdown.open>.dropdown-toggle{background-color:#d8ca47;background-image:-moz-linear-gradient(top,#c2b00c,#f9f0a0);background-image:-webkit-gradient(linear,0 0,0 100%,from(#c2b00c),to(#f9f0a0));background-image:-webkit-linear-gradient(top,#c2b00c,#f9f0a0);background-image:-o-linear-gradient(top,#c2b00c,#f9f0a0);background-image:linear-gradient(to bottom,#c2b00c,#f9f0a0);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffc2b00c', endColorstr='#fff9f0a0', GradientType=0)}#navbar .navbar-inner.yellow .nav>li>a:hover{background-color:#d9cc56;background-image:-moz-linear-gradient(top,#f8ed88,#aa9a0a);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f8ed88),to(#aa9a0a));background-image:-webkit-linear-gradient(top,#f8ed88,#aa9a0a);background-image:-o-linear-gradient(top,#f8ed88,#aa9a0a);background-image:linear-gradient(to bottom,#f8ed88,#aa9a0a);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff8ed88', endColorstr='#ffaa9a0a', GradientType=0)}#navbar .navbar-inner.yellow.transparent{background-color:rgba(227,215,101,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(249,240,160,.6),rgba(194,176,12,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(249,240,160,.6)),to(rgba(194,176,12,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(249,240,160,.6),rgba(194,176,12,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(249,240,160,.6),rgba(194,176,12,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(249,240,160,.6),rgba(194,176,12,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99f9f0a0', endColorstr='#99c2b00c', GradientType=0)}#navbar .navbar-inner.yellow.transparent .brand,#navbar .navbar-inner.yellow.transparent .nav>li>a{text-shadow:0 1px 0 #c2b00c;color:#f2f2f2}#navbar .navbar-inner.yellow.transparent .brand .caret,#navbar .navbar-inner.yellow.transparent .nav>li>a .caret{border-bottom-color:#f2e88d;border-top-color:#f2e88d}#navbar .navbar-inner.yellow.transparent .brand:focus .caret,#navbar .navbar-inner.yellow.transparent .brand:hover .caret,#navbar .navbar-inner.yellow.transparent .nav>li>a:focus .caret,#navbar .navbar-inner.yellow.transparent .nav>li>a:hover .caret{border-bottom-color:#f2edbf;border-top-color:#f2edbf}#navbar .navbar-inner.yellow.transparent .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.yellow.transparent .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.yellow.transparent .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.yellow.transparent .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.yellow.transparent .nav>li.dropdown.open>.dropdown-toggle{background-color:rgba(216,202,71,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(194,176,12,.6),rgba(249,240,160,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(194,176,12,.6)),to(rgba(249,240,160,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(194,176,12,.6),rgba(249,240,160,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(194,176,12,.6),rgba(249,240,160,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(194,176,12,.6),rgba(249,240,160,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99c2b00c', endColorstr='#99f9f0a0', GradientType=0)}#navbar .navbar-inner.yellow.transparent .nav>li>a:hover{background-color:rgba(217,204,86,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(248,237,136,.6),rgba(170,154,10,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(248,237,136,.6)),to(rgba(170,154,10,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(248,237,136,.6),rgba(170,154,10,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(248,237,136,.6),rgba(170,154,10,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(248,237,136,.6),rgba(170,154,10,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99f8ed88', endColorstr='#99aa9a0a', GradientType=0)}#navbar .navbar-inner.green{background-color:#98f064;background-image:-moz-linear-gradient(top,#c8ffa7,#50da00);background-image:-webkit-gradient(linear,0 0,0 100%,from(#c8ffa7),to(#50da00));background-image:-webkit-linear-gradient(top,#c8ffa7,#50da00);background-image:-o-linear-gradient(top,#c8ffa7,#50da00);background-image:linear-gradient(to bottom,#c8ffa7,#50da00);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffc8ffa7', endColorstr='#ff50da00', GradientType=0)}#navbar .navbar-inner.green .brand,#navbar .navbar-inner.green .nav>li>a{text-shadow:0 1px 0 #50da00;color:#333}#navbar .navbar-inner.green .brand .caret,#navbar .navbar-inner.green .nav>li>a .caret{border-bottom-color:#55992e;border-top-color:#55992e}#navbar .navbar-inner.green .brand:focus .caret,#navbar .navbar-inner.green .brand:hover .caret,#navbar .navbar-inner.green .nav>li>a:focus .caret,#navbar .navbar-inner.green .nav>li>a:hover .caret{border-bottom-color:#446630;border-top-color:#446630}#navbar .navbar-inner.green .brand span{background-image:url(../img/tentacle-20x20.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.green .brand span{background-image:url(../img/tentacle-20x20@2x.png)}}#navbar .navbar-inner.green .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.green .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.green .nav>li.dropdown.open>.dropdown-toggle{background-color:#80e943;background-image:-moz-linear-gradient(top,#50da00,#c8ffa7);background-image:-webkit-gradient(linear,0 0,0 100%,from(#50da00),to(#c8ffa7));background-image:-webkit-linear-gradient(top,#50da00,#c8ffa7);background-image:-o-linear-gradient(top,#50da00,#c8ffa7);background-image:linear-gradient(to bottom,#50da00,#c8ffa7);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff50da00', endColorstr='#ffc8ffa7', GradientType=0)}#navbar .navbar-inner.green .nav>li>a:hover{background-color:#8ae655;background-image:-moz-linear-gradient(top,#b8ff8e,#47c100);background-image:-webkit-gradient(linear,0 0,0 100%,from(#b8ff8e),to(#47c100));background-image:-webkit-linear-gradient(top,#b8ff8e,#47c100);background-image:-o-linear-gradient(top,#b8ff8e,#47c100);background-image:linear-gradient(to bottom,#b8ff8e,#47c100);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffb8ff8e', endColorstr='#ff47c100', GradientType=0)}#navbar .navbar-inner.green.transparent{background-color:rgba(152,240,100,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(200,255,167,.6),rgba(80,218,0,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(200,255,167,.6)),to(rgba(80,218,0,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(200,255,167,.6),rgba(80,218,0,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(200,255,167,.6),rgba(80,218,0,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(200,255,167,.6),rgba(80,218,0,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99c8ffa7', endColorstr='#9950da00', GradientType=0)}#navbar .navbar-inner.green.transparent .brand,#navbar .navbar-inner.green.transparent .nav>li>a{text-shadow:0 1px 0 #50da00;color:#333}#navbar .navbar-inner.green.transparent .brand .caret,#navbar .navbar-inner.green.transparent .nav>li>a .caret{border-bottom-color:#55992e;border-top-color:#55992e}#navbar .navbar-inner.green.transparent .brand:focus .caret,#navbar .navbar-inner.green.transparent .brand:hover .caret,#navbar .navbar-inner.green.transparent .nav>li>a:focus .caret,#navbar .navbar-inner.green.transparent .nav>li>a:hover .caret{border-bottom-color:#446630;border-top-color:#446630}#navbar .navbar-inner.green.transparent .brand span{background-image:url(../img/tentacle-20x20.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.green.transparent .brand span{background-image:url(../img/tentacle-20x20@2x.png)}}#navbar .navbar-inner.green.transparent .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.green.transparent .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.green.transparent .nav>li.dropdown.open>.dropdown-toggle{background-color:rgba(128,233,67,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(80,218,0,.6),rgba(200,255,167,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(80,218,0,.6)),to(rgba(200,255,167,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(80,218,0,.6),rgba(200,255,167,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(80,218,0,.6),rgba(200,255,167,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(80,218,0,.6),rgba(200,255,167,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#9950da00', endColorstr='#99c8ffa7', GradientType=0)}#navbar .navbar-inner.green.transparent .nav>li>a:hover{background-color:rgba(138,230,85,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(184,255,142,.6),rgba(71,193,0,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(184,255,142,.6)),to(rgba(71,193,0,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(184,255,142,.6),rgba(71,193,0,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(184,255,142,.6),rgba(71,193,0,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(184,255,142,.6),rgba(71,193,0,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99b8ff8e', endColorstr='#9947c100', GradientType=0)}#navbar .navbar-inner.blue{background-color:#2e63cc;background-image:-moz-linear-gradient(top,#4d88ff,#002b80);background-image:-webkit-gradient(linear,0 0,0 100%,from(#4d88ff),to(#002b80));background-image:-webkit-linear-gradient(top,#4d88ff,#002b80);background-image:-o-linear-gradient(top,#4d88ff,#002b80);background-image:linear-gradient(to bottom,#4d88ff,#002b80);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff4d88ff', endColorstr='#ff002b80', GradientType=0)}#navbar .navbar-inner.blue .brand,#navbar .navbar-inner.blue .nav>li>a{text-shadow:0 1px 0 #1a66ff;color:#f2f2f2}#navbar .navbar-inner.blue .brand .caret,#navbar .navbar-inner.blue .nav>li>a .caret{border-bottom-color:#799bdf;border-top-color:#799bdf}#navbar .navbar-inner.blue .brand:focus .caret,#navbar .navbar-inner.blue .brand:hover .caret,#navbar .navbar-inner.blue .nav>li>a:focus .caret,#navbar .navbar-inner.blue .nav>li>a:hover .caret{border-bottom-color:#b6c7e9;border-top-color:#b6c7e9}#navbar .navbar-inner.blue .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.blue .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.blue .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.blue .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.blue .nav>li.dropdown.open>.dropdown-toggle{background-color:#1f50b3;background-image:-moz-linear-gradient(top,#002b80,#4d88ff);background-image:-webkit-gradient(linear,0 0,0 100%,from(#002b80),to(#4d88ff));background-image:-webkit-linear-gradient(top,#002b80,#4d88ff);background-image:-o-linear-gradient(top,#002b80,#4d88ff);background-image:linear-gradient(to bottom,#002b80,#4d88ff);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff002b80', endColorstr='#ff4d88ff', GradientType=0)}#navbar .navbar-inner.blue .nav>li>a:hover{background-color:#1f55c2;background-image:-moz-linear-gradient(top,#37f,#026);background-image:-webkit-gradient(linear,0 0,0 100%,from(#37f),to(#026));background-image:-webkit-linear-gradient(top,#37f,#026);background-image:-o-linear-gradient(top,#37f,#026);background-image:linear-gradient(to bottom,#37f,#026);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3377ff', endColorstr='#ff002266', GradientType=0)}#navbar .navbar-inner.blue.transparent{background-color:rgba(46,99,204,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(77,136,255,.6),rgba(0,43,128,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(77,136,255,.6)),to(rgba(0,43,128,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(77,136,255,.6),rgba(0,43,128,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(77,136,255,.6),rgba(0,43,128,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(77,136,255,.6),rgba(0,43,128,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#994d88ff', endColorstr='#99002b80', GradientType=0)}#navbar .navbar-inner.blue.transparent .brand,#navbar .navbar-inner.blue.transparent .nav>li>a{text-shadow:0 1px 0 #1a66ff;color:#f2f2f2}#navbar .navbar-inner.blue.transparent .brand .caret,#navbar .navbar-inner.blue.transparent .nav>li>a .caret{border-bottom-color:#799bdf;border-top-color:#799bdf}#navbar .navbar-inner.blue.transparent .brand:focus .caret,#navbar .navbar-inner.blue.transparent .brand:hover .caret,#navbar .navbar-inner.blue.transparent .nav>li>a:focus .caret,#navbar .navbar-inner.blue.transparent .nav>li>a:hover .caret{border-bottom-color:#b6c7e9;border-top-color:#b6c7e9}#navbar .navbar-inner.blue.transparent .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.blue.transparent .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.blue.transparent .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.blue.transparent .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.blue.transparent .nav>li.dropdown.open>.dropdown-toggle{background-color:rgba(31,80,179,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(0,43,128,.6),rgba(77,136,255,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(0,43,128,.6)),to(rgba(77,136,255,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(0,43,128,.6),rgba(77,136,255,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(0,43,128,.6),rgba(77,136,255,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(0,43,128,.6),rgba(77,136,255,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99002b80', endColorstr='#994d88ff', GradientType=0)}#navbar .navbar-inner.blue.transparent .nav>li>a:hover{background-color:rgba(31,85,194,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(51,119,255,.6),rgba(0,34,102,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(51,119,255,.6)),to(rgba(0,34,102,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(51,119,255,.6),rgba(0,34,102,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(51,119,255,.6),rgba(0,34,102,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(51,119,255,.6),rgba(0,34,102,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#993377ff', endColorstr='#99002266', GradientType=0)}#navbar .navbar-inner.violet{background-color:#9864f0;background-image:-moz-linear-gradient(top,#c8a7ff,#5000da);background-image:-webkit-gradient(linear,0 0,0 100%,from(#c8a7ff),to(#5000da));background-image:-webkit-linear-gradient(top,#c8a7ff,#5000da);background-image:-o-linear-gradient(top,#c8a7ff,#5000da);background-image:linear-gradient(to bottom,#c8a7ff,#5000da);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffc8a7ff', endColorstr='#ff5000da', GradientType=0)}#navbar .navbar-inner.violet .brand,#navbar .navbar-inner.violet .nav>li>a{text-shadow:0 1px 0 #a774ff;color:#f2f2f2}#navbar .navbar-inner.violet .brand .caret,#navbar .navbar-inner.violet .nav>li>a .caret{border-bottom-color:#b58df9;border-top-color:#b58df9}#navbar .navbar-inner.violet .brand:focus .caret,#navbar .navbar-inner.violet .brand:hover .caret,#navbar .navbar-inner.violet .nav>li>a:focus .caret,#navbar .navbar-inner.violet .nav>li>a:hover .caret{border-bottom-color:#d3bff5;border-top-color:#d3bff5}#navbar .navbar-inner.violet .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.violet .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.violet .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.violet .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.violet .nav>li.dropdown.open>.dropdown-toggle{background-color:#8043e9;background-image:-moz-linear-gradient(top,#5000da,#c8a7ff);background-image:-webkit-gradient(linear,0 0,0 100%,from(#5000da),to(#c8a7ff));background-image:-webkit-linear-gradient(top,#5000da,#c8a7ff);background-image:-o-linear-gradient(top,#5000da,#c8a7ff);background-image:linear-gradient(to bottom,#5000da,#c8a7ff);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5000da', endColorstr='#ffc8a7ff', GradientType=0)}#navbar .navbar-inner.violet .nav>li>a:hover{background-color:#8a55e6;background-image:-moz-linear-gradient(top,#b88eff,#4700c1);background-image:-webkit-gradient(linear,0 0,0 100%,from(#b88eff),to(#4700c1));background-image:-webkit-linear-gradient(top,#b88eff,#4700c1);background-image:-o-linear-gradient(top,#b88eff,#4700c1);background-image:linear-gradient(to bottom,#b88eff,#4700c1);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffb88eff', endColorstr='#ff4700c1', GradientType=0)}#navbar .navbar-inner.violet.transparent{background-color:rgba(152,100,240,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(200,167,255,.6),rgba(80,0,218,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(200,167,255,.6)),to(rgba(80,0,218,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(200,167,255,.6),rgba(80,0,218,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(200,167,255,.6),rgba(80,0,218,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(200,167,255,.6),rgba(80,0,218,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99c8a7ff', endColorstr='#995000da', GradientType=0)}#navbar .navbar-inner.violet.transparent .brand,#navbar .navbar-inner.violet.transparent .nav>li>a{text-shadow:0 1px 0 #a774ff;color:#f2f2f2}#navbar .navbar-inner.violet.transparent .brand .caret,#navbar .navbar-inner.violet.transparent .nav>li>a .caret{border-bottom-color:#b58df9;border-top-color:#b58df9}#navbar .navbar-inner.violet.transparent .brand:focus .caret,#navbar .navbar-inner.violet.transparent .brand:hover .caret,#navbar .navbar-inner.violet.transparent .nav>li>a:focus .caret,#navbar .navbar-inner.violet.transparent .nav>li>a:hover .caret{border-bottom-color:#d3bff5;border-top-color:#d3bff5}#navbar .navbar-inner.violet.transparent .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.violet.transparent .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.violet.transparent .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.violet.transparent .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.violet.transparent .nav>li.dropdown.open>.dropdown-toggle{background-color:rgba(128,67,233,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(80,0,218,.6),rgba(200,167,255,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(80,0,218,.6)),to(rgba(200,167,255,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(80,0,218,.6),rgba(200,167,255,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(80,0,218,.6),rgba(200,167,255,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(80,0,218,.6),rgba(200,167,255,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#995000da', endColorstr='#99c8a7ff', GradientType=0)}#navbar .navbar-inner.violet.transparent .nav>li>a:hover{background-color:rgba(138,85,230,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(184,142,255,.6),rgba(71,0,193,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(184,142,255,.6)),to(rgba(71,0,193,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(184,142,255,.6),rgba(71,0,193,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(184,142,255,.6),rgba(71,0,193,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(184,142,255,.6),rgba(71,0,193,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99b88eff', endColorstr='#994700c1', GradientType=0)}#navbar .navbar-inner.black{background-color:#4f4f4f;background-image:-moz-linear-gradient(top,#787878,#121212);background-image:-webkit-gradient(linear,0 0,0 100%,from(#787878),to(#121212));background-image:-webkit-linear-gradient(top,#787878,#121212);background-image:-o-linear-gradient(top,#787878,#121212);background-image:linear-gradient(to bottom,#787878,#121212);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff787878', endColorstr='#ff121212', GradientType=0)}#navbar .navbar-inner.black .brand,#navbar .navbar-inner.black .nav>li>a{text-shadow:0 1px 0 #5e5e5e;color:#f2f2f2}#navbar .navbar-inner.black .brand .caret,#navbar .navbar-inner.black .nav>li>a .caret{border-bottom-color:#959595;border-top-color:#959595}#navbar .navbar-inner.black .brand:focus .caret,#navbar .navbar-inner.black .brand:hover .caret,#navbar .navbar-inner.black .nav>li>a:focus .caret,#navbar .navbar-inner.black .nav>li>a:hover .caret{border-bottom-color:#c4c4c4;border-top-color:#c4c4c4}#navbar .navbar-inner.black .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.black .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.black .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.black .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.black .nav>li.dropdown.open>.dropdown-toggle{background-color:#3b3b3b;background-image:-moz-linear-gradient(top,#121212,#787878);background-image:-webkit-gradient(linear,0 0,0 100%,from(#121212),to(#787878));background-image:-webkit-linear-gradient(top,#121212,#787878);background-image:-o-linear-gradient(top,#121212,#787878);background-image:linear-gradient(to bottom,#121212,#787878);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff121212', endColorstr='#ff787878', GradientType=0)}#navbar .navbar-inner.black .nav>li>a:hover{background-color:#424242;background-image:-moz-linear-gradient(top,#6b6b6b,#050505);background-image:-webkit-gradient(linear,0 0,0 100%,from(#6b6b6b),to(#050505));background-image:-webkit-linear-gradient(top,#6b6b6b,#050505);background-image:-o-linear-gradient(top,#6b6b6b,#050505);background-image:linear-gradient(to bottom,#6b6b6b,#050505);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff6b6b6b', endColorstr='#ff050505', GradientType=0)}#navbar .navbar-inner.black.transparent{background-color:rgba(79,79,79,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(120,120,120,.6),rgba(18,18,18,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(120,120,120,.6)),to(rgba(18,18,18,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(120,120,120,.6),rgba(18,18,18,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(120,120,120,.6),rgba(18,18,18,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(120,120,120,.6),rgba(18,18,18,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99787878', endColorstr='#99121212', GradientType=0)}#navbar .navbar-inner.black.transparent .brand,#navbar .navbar-inner.black.transparent .nav>li>a{text-shadow:0 1px 0 #5e5e5e;color:#f2f2f2}#navbar .navbar-inner.black.transparent .brand .caret,#navbar .navbar-inner.black.transparent .nav>li>a .caret{border-bottom-color:#959595;border-top-color:#959595}#navbar .navbar-inner.black.transparent .brand:focus .caret,#navbar .navbar-inner.black.transparent .brand:hover .caret,#navbar .navbar-inner.black.transparent .nav>li>a:focus .caret,#navbar .navbar-inner.black.transparent .nav>li>a:hover .caret{border-bottom-color:#c4c4c4;border-top-color:#c4c4c4}#navbar .navbar-inner.black.transparent .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.black.transparent .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.black.transparent .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.black.transparent .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.black.transparent .nav>li.dropdown.open>.dropdown-toggle{background-color:rgba(59,59,59,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(18,18,18,.6),rgba(120,120,120,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(18,18,18,.6)),to(rgba(120,120,120,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(18,18,18,.6),rgba(120,120,120,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(18,18,18,.6),rgba(120,120,120,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(18,18,18,.6),rgba(120,120,120,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99121212', endColorstr='#99787878', GradientType=0)}#navbar .navbar-inner.black.transparent .nav>li>a:hover{background-color:rgba(66,66,66,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(107,107,107,.6),rgba(5,5,5,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(107,107,107,.6)),to(rgba(5,5,5,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(107,107,107,.6),rgba(5,5,5,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(107,107,107,.6),rgba(5,5,5,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(107,107,107,.6),rgba(5,5,5,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#996b6b6b', endColorstr='#99050505', GradientType=0)}#navbar .navbar-inner.white{background-color:#e9e9e9;background-image:-moz-linear-gradient(top,#fff,#c8c8c8);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#c8c8c8));background-image:-webkit-linear-gradient(top,#fff,#c8c8c8);background-image:-o-linear-gradient(top,#fff,#c8c8c8);background-image:linear-gradient(to bottom,#fff,#c8c8c8);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffc8c8c8', GradientType=0)}#navbar .navbar-inner.white .brand,#navbar .navbar-inner.white .nav>li>a{text-shadow:0 1px 0 #c8c8c8;color:#333}#navbar .navbar-inner.white .brand .caret,#navbar .navbar-inner.white .nav>li>a .caret{border-bottom-color:#919191;border-top-color:#919191}#navbar .navbar-inner.white .brand:focus .caret,#navbar .navbar-inner.white .brand:hover .caret,#navbar .navbar-inner.white .nav>li>a:focus .caret,#navbar .navbar-inner.white .nav>li>a:hover .caret{border-bottom-color:#626262;border-top-color:#626262}#navbar .navbar-inner.white .brand span{background-image:url(../img/tentacle-20x20.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.white .brand span{background-image:url(../img/tentacle-20x20@2x.png)}}#navbar .navbar-inner.white .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.white .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.white .nav>li.dropdown.open>.dropdown-toggle{background-color:#dedede;background-image:-moz-linear-gradient(top,#c8c8c8,#fff);background-image:-webkit-gradient(linear,0 0,0 100%,from(#c8c8c8),to(#fff));background-image:-webkit-linear-gradient(top,#c8c8c8,#fff);background-image:-o-linear-gradient(top,#c8c8c8,#fff);background-image:linear-gradient(to bottom,#c8c8c8,#fff);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffc8c8c8', endColorstr='#ffffffff', GradientType=0)}#navbar .navbar-inner.white .nav>li>a:hover{background-color:#dcdcdc;background-image:-moz-linear-gradient(top,#f2f2f2,#bbb);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f2f2f2),to(#bbb));background-image:-webkit-linear-gradient(top,#f2f2f2,#bbb);background-image:-o-linear-gradient(top,#f2f2f2,#bbb);background-image:linear-gradient(to bottom,#f2f2f2,#bbb);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2f2f2', endColorstr='#ffbbbbbb', GradientType=0)}#navbar .navbar-inner.white.transparent{background-color:rgba(233,233,233,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(255,255,255,.6),rgba(200,200,200,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(255,255,255,.6)),to(rgba(200,200,200,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(255,255,255,.6),rgba(200,200,200,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(255,255,255,.6),rgba(200,200,200,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(255,255,255,.6),rgba(200,200,200,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99ffffff', endColorstr='#99c8c8c8', GradientType=0)}#navbar .navbar-inner.white.transparent .brand,#navbar .navbar-inner.white.transparent .nav>li>a{text-shadow:0 1px 0 #c8c8c8;color:#333}#navbar .navbar-inner.white.transparent .brand .caret,#navbar .navbar-inner.white.transparent .nav>li>a .caret{border-bottom-color:#919191;border-top-color:#919191}#navbar .navbar-inner.white.transparent .brand:focus .caret,#navbar .navbar-inner.white.transparent .brand:hover .caret,#navbar .navbar-inner.white.transparent .nav>li>a:focus .caret,#navbar .navbar-inner.white.transparent .nav>li>a:hover .caret{border-bottom-color:#626262;border-top-color:#626262}#navbar .navbar-inner.white.transparent .brand span{background-image:url(../img/tentacle-20x20.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.white.transparent .brand span{background-image:url(../img/tentacle-20x20@2x.png)}}#navbar .navbar-inner.white.transparent .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.white.transparent .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.white.transparent .nav>li.dropdown.open>.dropdown-toggle{background-color:rgba(222,222,222,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(200,200,200,.6),rgba(255,255,255,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(200,200,200,.6)),to(rgba(255,255,255,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(200,200,200,.6),rgba(255,255,255,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(200,200,200,.6),rgba(255,255,255,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(200,200,200,.6),rgba(255,255,255,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99c8c8c8', endColorstr='#99ffffff', GradientType=0)}#navbar .navbar-inner.white.transparent .nav>li>a:hover{background-color:rgba(220,220,220,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(242,242,242,.6),rgba(187,187,187,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(242,242,242,.6)),to(rgba(187,187,187,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(242,242,242,.6),rgba(187,187,187,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(242,242,242,.6),rgba(187,187,187,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(242,242,242,.6),rgba(187,187,187,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99f2f2f2', endColorstr='#99bbbbbb', GradientType=0)}#navbar .navbar-inner .brand{padding:10px 20px 6px}#navbar .navbar-inner .brand span{padding-left:26px;background-size:20px 20px;background-repeat:no-repeat;display:inline-block;max-width:250px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;vertical-align:top;line-height:20px;height:24px}#navbar_login a.dropdown-toggle span{display:inline-block;max-width:100px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;vertical-align:top}.octoprint-container{margin-top:20px}.octoprint-container .tab-content{padding:9px 15px;border-left:1px solid #DDD;border-right:1px solid #DDD;border-bottom:1px solid #DDD;-webkit-border-bottom-right-radius:4px;-moz-border-radius-bottomright:4px;border-bottom-right-radius:4px;-webkit-border-bottom-left-radius:4px;-moz-border-radius-bottomleft:4px;border-bottom-left-radius:4px}.octoprint-container .nav{margin-bottom:0}.octoprint-container .tab-content h1{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:40px;color:#333;border:0;border-bottom:1px solid #E5E5E5;font-weight:400}.octoprint-container .accordion-heading .accordion-heading-button{float:right}.octoprint-container .accordion-heading .accordion-heading-button a{display:inline-block;padding:8px 15px;font-size:14px;line-height:20px;color:#000;text-decoration:none;background:0 0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.octoprint-container .accordion-heading a.accordion-toggle{display:inline-block}.octoprint-container .accordion-heading [class*=" icon-"],.octoprint-container .accordion-heading [class^=icon-]{color:#000}.print-control .btn{padding-left:4px;padding-right:4px}.upload-buttons .btn{margin-right:0}table{table-layout:fixed}table .popover-title{text-overflow:ellipsis;word-break:break-all}table td,table th{overflow:hidden}table td.gcode_files_name,table th.gcode_files_name{text-overflow:ellipsis;text-align:left;white-space:nowrap}table td.gcode_files_action,table th.gcode_files_action{width:90px;text-align:center;white-space:nowrap}table td.gcode_files_action a,table th.gcode_files_action a{text-decoration:none;color:#000}table td.gcode_files_action a.disabled,table th.gcode_files_action a.disabled{color:#ccc;cursor:default}table td.timelapse_files_checkbox,table td.timelapse_unrendered_checkbox,table th.timelapse_files_checkbox,table th.timelapse_unrendered_checkbox{text-align:center;width:10px}table td.timelapse_files_checkbox input[type=checkbox],table td.timelapse_unrendered_checkbox input[type=checkbox],table th.timelapse_files_checkbox input[type=checkbox],table th.timelapse_unrendered_checkbox input[type=checkbox]{margin-top:0}table td.timelapse_files_name,table td.timelapse_unrendered_name,table th.timelapse_files_name,table th.timelapse_unrendered_name{text-overflow:ellipsis;text-align:left}table td.timelapse_files_size,table td.timelapse_unrendered_size,table th.timelapse_files_size,table th.timelapse_unrendered_size{text-align:right;width:55px}table td.timelapse_unrendered_count,table th.timelapse_unrendered_count{text-align:right;width:45px}table td.timelapse_files_action,table td.timelapse_unrendered_action,table th.timelapse_files_action,table th.timelapse_unrendered_action{width:45px;text-align:center;white-space:nowrap}table td.timelapse_files_action a,table td.timelapse_unrendered_action a,table th.timelapse_files_action a,table th.timelapse_unrendered_action a{text-decoration:none;color:#000}table td.timelapse_files_action a.disabled,table td.timelapse_unrendered_action a.disabled,table th.timelapse_files_action a.disabled,table th.timelapse_unrendered_action a.disabled{color:#ccc;cursor:default}table td.settings_users_name,table th.settings_users_name{text-overflow:ellipsis;text-align:left}table td.settings_users_active,table td.settings_users_admin,table th.settings_users_active,table th.settings_users_admin{text-align:center;width:55px}table td.settings_users_actions,table th.settings_users_actions{width:60px;text-align:center;white-space:nowrap}table td.settings_users_actions a,table th.settings_users_actions a{text-decoration:none;color:#000}table td.settings_users_actions a.disabled,table th.settings_users_actions a.disabled{color:#ccc;cursor:default}table td.settings_logs_name,table th.settings_logs_name{text-overflow:ellipsis;text-align:left}table td.settings_logs_size,table th.settings_logs_size{text-align:right;width:70px}table td.settings_logs_date,table th.settings_logs_date{text-align:left;width:130px}table td.settings_logs_action,table th.settings_logs_action{width:70px;text-align:center;white-space:nowrap}table td.settings_logs_action a,table th.settings_logs_action a{text-decoration:none;color:#000}table td.settings_logs_action a.disabled,table th.settings_logs_action a.disabled{color:#ccc;cursor:default}table td.settings_printerProfiles_profiles_name,table th.settings_printerProfiles_profiles_name{text-overflow:ellipsis;text-align:left}table td.settings_printerProfiles_profiles_model,table th.settings_printerProfiles_profiles_model{text-align:left;width:250px}table td.settings_printerProfiles_profiles_action,table th.settings_printerProfiles_profiles_action{width:80px;text-align:center;white-space:nowrap}table td.settings_printerProfiles_profiles_action a,table th.settings_printerProfiles_profiles_action a{text-decoration:none;color:#000}table td.settings_printerProfiles_profiles_action a.disabled,table th.settings_printerProfiles_profiles_action a.disabled{color:#ccc;cursor:default}#temperature-graph{height:350px;width:100%;background:url(../img/graph-background.png) center no-repeat}#temperature-table{table-layout:fixed;width:100%;margin-top:20px}#temperature-table td.temperature_actual,#temperature-table td.temperature_offset,#temperature-table td.temperature_target,#temperature-table td.temperature_tool,#temperature-table th.temperature_actual,#temperature-table th.temperature_offset,#temperature-table th.temperature_target,#temperature-table th.temperature_tool{vertical-align:middle;text-align:center}#temperature-table td.temperature_actual form,#temperature-table td.temperature_offset form,#temperature-table td.temperature_target form,#temperature-table td.temperature_tool form,#temperature-table th.temperature_actual form,#temperature-table th.temperature_offset form,#temperature-table th.temperature_target form,#temperature-table th.temperature_tool form{margin:0}#temperature-table td.temperature_actual .dropdown-menu,#temperature-table td.temperature_offset .dropdown-menu,#temperature-table td.temperature_target .dropdown-menu,#temperature-table td.temperature_tool .dropdown-menu,#temperature-table th.temperature_actual .dropdown-menu,#temperature-table th.temperature_offset .dropdown-menu,#temperature-table th.temperature_target .dropdown-menu,#temperature-table th.temperature_tool .dropdown-menu{text-align:left}#temperature-table td.temperature_tool,#temperature-table th.temperature_tool{width:16%;text-align:left;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}#temperature-table td.temperature_actual,#temperature-table th.temperature_actual{width:12%}#temperature-table td.temperature_target,#temperature-table th.temperature_target{width:42%;overflow:visible}#temperature-table td.temperature_offset,#temperature-table th.temperature_offset{width:30%}.tab-content,.tab-pane{overflow:visible}#speed_fill,#speed_innerWall,#speed_outerWall,#speed_support,#temp_newBedTemp,#temp_newTemp,#webcam_timelapse_fps,#webcam_timelapse_interval,#webcam_timelapse_postRoll,#webcam_timelapse_retractionZHop{text-align:right}ul.dropdown-menu li a{cursor:pointer}#connection_baudrates,#connection_ports,#connection_printers{width:100%}#offline_overlay,#reloadui_overlay{position:fixed;top:0;left:0;width:100%;height:100%;display:none}#offline_overlay{z-index:10002}#reloadui_overlay{z-index:10001}#offline_overlay_background,#reloadui_overlay_background{position:fixed;top:0;left:0;width:100%;height:100%;background-color:#000;filter:alpha(opacity=50);-moz-opacity:.5;-khtml-opacity:.5;opacity:.5}#offline_overlay_wrapper,#reloadui_overlay_wrapper{position:absolute;top:0;bottom:0;left:0;right:0;padding-top:60px}#offline_overlay_wrapper .container,#reloadui_overlay_wrapper .container{margin:auto}#webcam_container{width:100%;position:relative;outline:0;background-color:#000}#webcam_container .keycontrol_overlay{position:absolute;left:10px;right:10px;bottom:10px;background:rgba(0,0,0,.5);font-size:85%;color:#fff;padding:0}#webcam_container .keycontrol_overlay kbd{border:1px solid #eee;border-radius:3px;margin-left:2px;margin-right:2px;font-size:90%;padding:2px;min-width:1em}#webcam_container .keycontrol_overlay .keycontrol_overlay_heading{position:relative;padding:10px;font-weight:700}#webcam_container .keycontrol_overlay .keycontrol_overlay_column{position:relative;width:45%;padding:10px;float:left}#webcam_container .nowebcam{position:absolute;top:0;left:0;right:0;bottom:0}#webcam_container .nowebcam .text{color:#fff;text-align:center;position:relative;margin:auto;width:80%;top:50%;transform:translateY(-50%);display:block}#webcam_container .nowebcam .text.webcam_loading{animation:pulsate 3s ease-out;animation-iteration-count:infinite}#webcam_container .webcam_rotated{position:relative;width:100%;padding-bottom:100%;pointer-events:none}#webcam_container .webcam_rotated .webcam_fixed_ratio{position:absolute;transform:rotate(-90deg);top:0;bottom:0;pointer-events:none}#webcam_container .webcam_rotated .webcam_fixed_ratio .webcam_fixed_ratio_inner{width:100%;height:100%;pointer-events:none}#webcam_container .webcam_unrotated .webcam_fixed_ratio{width:100%;pointer-events:none;padding-bottom:100%;position:relative}#webcam_container .webcam_unrotated .webcam_fixed_ratio.ratio43{padding-bottom:75%}#webcam_container .webcam_unrotated .webcam_fixed_ratio.ratio169{padding-bottom:56.25%}#webcam_container .webcam_unrotated .webcam_fixed_ratio.ratio1610{padding-bottom:62.5%}#webcam_container .webcam_unrotated .webcam_fixed_ratio .webcam_fixed_ratio_inner{position:absolute;top:0;bottom:0;left:0;right:0;pointer-events:none}#webcam_container img{width:100%;height:100%;object-fit:contain}#state_wrapper hr{margin:5px 0}#files .gcode_files{padding-right:7px}#files .gcode_files .entry{padding:5px;line-height:20px;border-bottom:1px solid #ddd;position:relative}#files .gcode_files .entry:hover{background-color:#f5f5f5}#files .gcode_files .entry .title{text-overflow:ellipsis;word-break:break-all}#files .gcode_files .entry .additionalInfo,#files .gcode_files .entry .size,#files .gcode_files .entry .uploaded{font-size:85%;color:#999}#files .gcode_files .entry .action-buttons{position:absolute;bottom:5px;right:5px}#files .gcode_files .entry .additionalInfo{padding-bottom:22px}@keyframes highlightframes{0%{background:#ff0}100%{background:0 0}}#files .gcode_files .entry.highlight{animation:highlightframes 2s}#files .gcode_files .back .back-path{white-space:nowrap}#files .gcode_files .back .back-path span{word-wrap:break-word;white-space:pre-line}#files .upload-buttons{margin-top:10px}#files .form-search{text-align:center;margin-bottom:5px!important}#control{overflow:hidden}#control .jog-panel{float:left;margin-right:19px}#control h1{text-align:left}#control .jog-panel>div{text-align:center}#control .jog-panel>div.distance{text-align:left}#control .jog-panel .slider{margin-bottom:10px}#control .box{width:30px;height:30px;margin-right:10px;margin-bottom:10px;padding-left:8px}#control .control-box{display:block;height:30px;margin-bottom:10px}#control .btn-group{margin-bottom:10px}#control .btn-group.distance>.btn{width:43px;padding:3px 0;height:30px}#control .slider-handle{width:14px;height:14px;margin-left:-7px;margin-top:-3px}#control .custom_section h1{cursor:pointer}#control .custom_section_horizontal>.custom_control{display:inline-block}#control .custom_section_vertical>.custom_control{display:block}#control .custom_control .slider{margin-left:10px;margin-right:10px;margin-bottom:2px}#gcode .progress{width:588px}#gcode .progress .bar{-webkit-transition:width 0s linear;-moz-transition:width 0s linear;-o-transition:width 0s linear;transition:width 0s linear}#gcode .canvas_container{position:relative}#gcode .canvas_container:active,#gcode .canvas_container:hover{outline:0}#gcode .layer-buttons{padding-top:5px;padding-bottom:7px}#gcode #gcode_layer_slider{position:absolute;right:0;top:0;height:568px;float:right}#gcode #gcode_layer_slider .slider-handle{width:14px;height:14px;margin-left:-3px;margin-top:-7px}#gcode #gcode_command_slider .slider-handle{width:14px;height:14px;margin-left:-7px;margin-top:-3px}#term .terminal{margin-bottom:30px}#term .terminal #terminal-output,#term .terminal #terminal-output-lowfi{min-height:340px;margin-bottom:5px}#settings_dialog .aboutlink{float:left}#settings_dialog_menu,#wizard_dialog_menu{margin-left:0}#wizard_firstrun_acl .acl_decision{margin-top:1em}#settings_appearance_managelanguagesdialog_emptylist{overflow:hidden;width:100%;height:300px;text-align:center;display:table}#settings_appearance_managelanguagesdialog_emptylist div{display:table-cell;vertical-align:middle}.footer ul{margin:0}.footer ul li{display:inline;margin-left:1em;font-size:85%}.footer ul li:first-child{margin-left:0}.footer ul li a{color:#555}.ui-pnotify .alert a{color:#c09853}.ui-pnotify .alert-danger a,.ui-pnotify .alert-error a{color:#b94a48}.ui-pnotify .alert-success a{color:#468847}.ui-pnotify .alert-info a{color:#3a87ad}.pnotify_additional_info .pnotify_more{font-size:85%}.text-right{text-align:right}.text-center{text-align:center}.overflow_visible{overflow:visible!important}.clickable{cursor:pointer}.border_box{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.hidden{display:none}textarea.block{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;width:100%}@keyframes pulsate{0%{opacity:.5}50%{opacity:1}100%{opacity:.5}}#drop_overlay{position:fixed;top:0;left:0;width:100%;height:100%;z-index:10000;display:none}#drop_overlay.in{display:block}#drop_overlay #drop_overlay_background{position:fixed;top:0;left:0;width:100%;height:100%;background-color:#000;filter:alpha(opacity=50);-moz-opacity:.5;-khtml-opacity:.5;opacity:.5}#drop_overlay #drop_overlay_wrapper{position:absolute;top:0;bottom:0;left:0;right:0;padding-top:60px}#drop_overlay #drop_overlay_wrapper #drop,#drop_overlay #drop_overlay_wrapper #drop_background{position:absolute;top:0;left:0;margin-left:0;width:100%}#drop_overlay #drop_overlay_wrapper #drop_locally,#drop_overlay #drop_overlay_wrapper #drop_locally_background{position:absolute;top:0;left:50%;margin-left:-50%;width:50%;border-right:2px dashed #ccc}#drop_overlay #drop_overlay_wrapper #drop_sd,#drop_overlay #drop_overlay_wrapper #drop_sd_background{position:absolute;top:0;left:50%;margin-left:0;width:50%;border-left:2px dashed #ccc}#drop_overlay #drop_overlay_wrapper .dropzone{height:100%;z-index:10001;color:#fff;font-size:30px}#drop_overlay #drop_overlay_wrapper .dropzone i{font-size:50px}#drop_overlay #drop_overlay_wrapper .dropzone .text{display:block;text-align:center;line-height:40px;position:absolute;width:100%;bottom:5%;filter:alpha(opacity=100);-moz-opacity:1;-khtml-opacity:1;opacity:1}#drop_overlay #drop_overlay_wrapper .dropzone_background{width:50%;height:100%;background-color:#000;filter:alpha(opacity=25);-moz-opacity:.25;-khtml-opacity:.25;opacity:.25}#drop_overlay #drop_overlay_wrapper .dropzone_background.hover{background-color:#000;filter:alpha(opacity=50);-moz-opacity:.5;-khtml-opacity:.5;opacity:.5}#drop_overlay #drop_overlay_wrapper .dropzone_background.fade{-webkit-transition:all .3s ease-out;-moz-transition:all .3s ease-out;-ms-transition:all .3s ease-out;-o-transition:all .3s ease-out;transition:all .3s ease-out;opacity:1}.icon-sd-black-14{background:url(../img/icon-sd-black-14.png) 0 3px no-repeat;width:11px;height:17px;display:inline-block!important}.center{float:none;margin-left:auto;margin-right:auto}.flipH{-webkit-transform:scaleX(-1);-moz-transform:scaleX(-1);-ms-transform:scaleX(-1);transform:scaleX(-1)}.flipV{-webkit-transform:scaleY(-1);-moz-transform:scaleY(-1);-ms-transform:scaleY(-1);transform:scaleY(-1)}.flipH.flipV{-webkit-transform:scaleX(-1) scaleY(-1);-moz-transform:scaleX(-1) scaleY(-1);-ms-transform:scaleX(-1) scaleY(-1);transform:scaleX(-1) scaleY(-1)}.rotate90{-webkit-transform:rotate(-90deg);transform:rotate(-90deg)}.ui-pnotify a{text-decoration:underline}.btn-mini .caret,.btn-small .caret{margin-top:8px}.dropdown-menu-right{right:0;left:auto}.slider .slider-selection{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,.25);background-color:#006dcc;background-image:-moz-linear-gradient(top,#08c,#04c);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#04c));background-image:-webkit-linear-gradient(top,#08c,#04c);background-image:-o-linear-gradient(top,#08c,#04c);background-image:linear-gradient(to bottom,#08c,#04c);background-repeat:repeat-x;border-color:#04c #04c #002a80;border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);*background-color:#04c;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.slider .slider-selection.active,.slider .slider-selection.disabled,.slider .slider-selection:active,.slider .slider-selection:focus,.slider .slider-selection:hover,.slider .slider-selection[disabled]{color:#fff;background-color:#04c;*background-color:#003bb3}.slider .slider-selection.active,.slider .slider-selection:active{background-color:#039 \9}.slider.slider-disabled .slider-selection{background-image:none;opacity:.65;filter:alpha(opacity=65);-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.slider .slider-track{background-color:#f5f5f5;border:1px solid #e3e3e3;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05)}.slider.slider-disabled .slider-track{background-image:none;opacity:.65;filter:alpha(opacity=65);-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.slider .slider-handle{display:inline-block;*display:inline;*zoom:1;font-size:14px;line-height:20px;text-align:center;vertical-align:middle;cursor:pointer;color:#333;text-shadow:0 1px 1px rgba(255,255,255,.75);background-color:#f5f5f5;background-image:-moz-linear-gradient(top,#fff,#e6e6e6);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#e6e6e6));background-image:-webkit-linear-gradient(top,#fff,#e6e6e6);background-image:-o-linear-gradient(top,#fff,#e6e6e6);background-image:linear-gradient(to bottom,#fff,#e6e6e6);background-repeat:repeat-x;*background-color:#e6e6e6;border:1px solid #ccc;*border:0;border-bottom-color:#b3b3b3;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;*margin-left:.3em;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05);padding:0;margin-bottom:0;opacity:1;filter:alpha(opacity=100)}.slider .slider-handle.active,.slider .slider-handle.disabled,.slider .slider-handle:active,.slider .slider-handle:focus,.slider .slider-handle:hover,.slider .slider-handle[disabled]{color:#333;background-color:#e6e6e6;*background-color:#d9d9d9}.slider .slider-handle.active,.slider .slider-handle:active{background-color:#ccc \9}.slider .slider-handle:first-child{*margin-left:0}.slider .slider-handle:focus,.slider .slider-handle:hover{color:#333;text-decoration:none;background-position:0 -15px;-webkit-transition:background-position .1s linear;-moz-transition:background-position .1s linear;-o-transition:background-position .1s linear;transition:background-position .1s linear}.slider .slider-handle:focus{outline:#333 dotted thin;outline:-webkit-focus-ring-color auto 5px;outline-offset:-2px}.slider .slider-handle.active,.slider .slider-handle:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,.15),0 1px 2px rgba(0,0,0,.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,.15),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 2px 4px rgba(0,0,0,.15),0 1px 2px rgba(0,0,0,.05)}.slider .slider-handle.disabled,.slider .slider-handle[disabled]{cursor:default;background-image:none;opacity:.65;filter:alpha(opacity=65);-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.slider .slider-handle.hide{display:none}.slider .slider-handle.round{-webkit-border-radius:50%;-moz-border-radius:50%;border-radius:50%}.modal.large{width:975px;margin-left:-487px}.full-sized-box{position:absolute;bottom:0;left:0;right:0;top:0;padding:15px}.full-sized-box .row-fluid{height:100%}@media (max-width:979px){.full-sized-box{position:static}}:root .full-sized-box,_::-webkit-full-page-media,_:future{position:static}.scrollable{height:100%;overflow:auto;-webkit-overflow-scrolling:touch}.pre-output span{display:block}.input-append .add-on.add-on-limited,.input-prepend .add-on.add-on-limited{overflow-x:hidden;text-overflow:ellipsis;width:inherit}.input-append .btn-group:first-child .btn:first-child,.input-prepend .btn-group:first-child .btn:first-child{-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.input-append .btn-group .btn:first-child,.input-prepend .btn-group .btn:first-child{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.input-append.input-block-level,.input-prepend.input-block-level{display:table}.input-append.input-block-level .add-on,.input-prepend.input-block-level .add-on{display:table-cell;width:1%}.input-append.input-block-level>input,.input-prepend.input-block-level>input{box-sizing:border-box;display:table;min-height:inherit;width:100%}.input-append.input-block-level :not(:last-child),.input-prepend.input-block-level :not(:last-child){border-right:0}.control-group.error .input-append .fileinput-button,.control-group.error .input-prepend .fileinput-button{border-color:#b94a48}.control-text{padding-top:5px;cursor:default}input[type=number]{text-align:right}input[type=number].input-nospin::-webkit-inner-spin-button,input[type=number].input-nospin::-webkit-outer-spin-button{-webkit-appearance:none;margin:0}input[type=number].input-nospin{-moz-appearance:textfield}.progress-text,.progress-text-centered{position:relative}.progress-text .progress-text-back,.progress-text .progress-text-front,.progress-text-centered .progress-text-back,.progress-text-centered .progress-text-front{white-space:nowrap}.progress-text .progress-text-front,.progress-text-centered .progress-text-front{box-sizing:border-box;padding:0 10px;width:100%;display:block}.progress-text .progress-text-back,.progress-text-centered .progress-text-back{position:absolute;font-size:12px;line-height:20px;display:block;box-sizing:border-box;text-align:center;padding:0 10px}.progress-text .bar,.progress-text-centered .bar{position:absolute;overflow:hidden}.progress-text-centered .progress-text-front{position:absolute;font-size:12px;line-height:20px;display:block;text-align:center;color:#fff}.progress-text-centered .progress-text-back{width:100%}#navbar_login:not(.open) #login_dropdown_loggedout{display:block;z-index:-1;height:0;width:0;padding:0!important;overflow:hidden;border:0;box-shadow:none;left:-9999px}#navbar_login:not(.open) #login_dropdown_loggedout.hide{display:none}#loginForm{margin:0}#loginForm button{margin-top:20px} \ No newline at end of file +.btn{display:inline-block;*display:inline;*zoom:1;padding:4px 12px;margin-bottom:0;font-size:14px;line-height:20px;text-align:center;vertical-align:middle;cursor:pointer;color:#333;text-shadow:0 1px 1px rgba(255,255,255,.75);background-color:#f5f5f5;background-image:-moz-linear-gradient(top,#fff,#e6e6e6);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#e6e6e6));background-image:-webkit-linear-gradient(top,#fff,#e6e6e6);background-image:-o-linear-gradient(top,#fff,#e6e6e6);background-image:linear-gradient(to bottom,#fff,#e6e6e6);background-repeat:repeat-x;*background-color:#e6e6e6;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);border:1px solid #ccc;*border:0;border-bottom-color:#b3b3b3;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;*margin-left:.3em;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05)}.btn.active,.btn.disabled,.btn:active,.btn:focus,.btn:hover,.btn[disabled]{color:#333;background-color:#e6e6e6;*background-color:#d9d9d9}.btn.active,.btn:active{background-color:#ccc \9}.btn:first-child{*margin-left:0}.btn:focus,.btn:hover{color:#333;text-decoration:none;background-position:0 -15px;-webkit-transition:background-position .1s linear;-moz-transition:background-position .1s linear;-o-transition:background-position .1s linear;transition:background-position .1s linear}.btn:focus{outline:#333 dotted thin;outline:-webkit-focus-ring-color auto 5px;outline-offset:-2px}.btn.active,.btn:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,.15),0 1px 2px rgba(0,0,0,.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,.15),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 2px 4px rgba(0,0,0,.15),0 1px 2px rgba(0,0,0,.05)}.btn.disabled,.btn[disabled]{cursor:default;background-image:none;opacity:.65;filter:alpha(opacity=65);-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.btn-large{padding:11px 19px;font-size:17.5px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.btn-large [class*=" icon-"],.btn-large [class^=icon-]{margin-top:4px}.btn-small{padding:2px 10px;font-size:11.9px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.btn-small [class*=" icon-"],.btn-small [class^=icon-]{margin-top:0}.btn-mini [class*=" icon-"],.btn-mini [class^=icon-]{margin-top:-1px}.btn-mini{padding:0 6px;font-size:10.5px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.btn-block{display:block;width:100%;padding-left:0;padding-right:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.btn-block+.btn-block{margin-top:5px}input[type=submit].btn-block,input[type=reset].btn-block,input[type=button].btn-block{width:100%}.btn-primary{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,.25);background-color:#006dcc;background-image:-moz-linear-gradient(top,#08c,#04c);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#04c));background-image:-webkit-linear-gradient(top,#08c,#04c);background-image:-o-linear-gradient(top,#08c,#04c);background-image:linear-gradient(to bottom,#08c,#04c);background-repeat:repeat-x;border-color:#04c #04c #002a80;border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);*background-color:#04c;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-primary.active,.btn-primary.disabled,.btn-primary:active,.btn-primary:focus,.btn-primary:hover,.btn-primary[disabled]{color:#fff;background-color:#04c;*background-color:#003bb3}.btn-primary.active,.btn-primary:active{background-color:#039 \9}.btn-warning{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,.25);background-color:#faa732;background-image:-moz-linear-gradient(top,#fbb450,#f89406);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fbb450),to(#f89406));background-image:-webkit-linear-gradient(top,#fbb450,#f89406);background-image:-o-linear-gradient(top,#fbb450,#f89406);background-image:linear-gradient(to bottom,#fbb450,#f89406);background-repeat:repeat-x;border-color:#f89406 #f89406 #ad6704;border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);*background-color:#f89406;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-warning.active,.btn-warning.disabled,.btn-warning:active,.btn-warning:focus,.btn-warning:hover,.btn-warning[disabled]{color:#fff;background-color:#f89406;*background-color:#df8505}.btn-warning.active,.btn-warning:active{background-color:#c67605 \9}.btn-danger{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,.25);background-color:#da4f49;background-image:-moz-linear-gradient(top,#ee5f5b,#bd362f);background-image:-webkit-gradient(linear,0 0,0 100%,from(#ee5f5b),to(#bd362f));background-image:-webkit-linear-gradient(top,#ee5f5b,#bd362f);background-image:-o-linear-gradient(top,#ee5f5b,#bd362f);background-image:linear-gradient(to bottom,#ee5f5b,#bd362f);background-repeat:repeat-x;border-color:#bd362f #bd362f #802420;border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);*background-color:#bd362f;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-danger.active,.btn-danger.disabled,.btn-danger:active,.btn-danger:focus,.btn-danger:hover,.btn-danger[disabled]{color:#fff;background-color:#bd362f;*background-color:#a9302a}.btn-danger.active,.btn-danger:active{background-color:#942a25 \9}.btn-success{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,.25);background-color:#5bb75b;background-image:-moz-linear-gradient(top,#62c462,#51a351);background-image:-webkit-gradient(linear,0 0,0 100%,from(#62c462),to(#51a351));background-image:-webkit-linear-gradient(top,#62c462,#51a351);background-image:-o-linear-gradient(top,#62c462,#51a351);background-image:linear-gradient(to bottom,#62c462,#51a351);background-repeat:repeat-x;border-color:#51a351 #51a351 #387038;border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);*background-color:#51a351;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-success.active,.btn-success.disabled,.btn-success:active,.btn-success:focus,.btn-success:hover,.btn-success[disabled]{color:#fff;background-color:#51a351;*background-color:#499249}.btn-success.active,.btn-success:active{background-color:#408140 \9}.btn-info{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,.25);background-color:#49afcd;background-image:-moz-linear-gradient(top,#5bc0de,#2f96b4);background-image:-webkit-gradient(linear,0 0,0 100%,from(#5bc0de),to(#2f96b4));background-image:-webkit-linear-gradient(top,#5bc0de,#2f96b4);background-image:-o-linear-gradient(top,#5bc0de,#2f96b4);background-image:linear-gradient(to bottom,#5bc0de,#2f96b4);background-repeat:repeat-x;border-color:#2f96b4 #2f96b4 #1f6377;border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);*background-color:#2f96b4;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-info.active,.btn-info.disabled,.btn-info:active,.btn-info:focus,.btn-info:hover,.btn-info[disabled]{color:#fff;background-color:#2f96b4;*background-color:#2a85a0}.btn-info.active,.btn-info:active{background-color:#24748c \9}.btn-inverse{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,.25);background-color:#363636;background-image:-moz-linear-gradient(top,#444,#222);background-image:-webkit-gradient(linear,0 0,0 100%,from(#444),to(#222));background-image:-webkit-linear-gradient(top,#444,#222);background-image:-o-linear-gradient(top,#444,#222);background-image:linear-gradient(to bottom,#444,#222);background-repeat:repeat-x;border-color:#222 #222 #000;border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);*background-color:#222;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-inverse.active,.btn-inverse.disabled,.btn-inverse:active,.btn-inverse:focus,.btn-inverse:hover,.btn-inverse[disabled]{color:#fff;background-color:#222;*background-color:#151515}.btn-inverse.active,.btn-inverse:active{background-color:#080808 \9}button.btn,input[type=submit].btn{*padding-top:3px;*padding-bottom:3px}button.btn::-moz-focus-inner,input[type=submit].btn::-moz-focus-inner{padding:0;border:0}button.btn.btn-large,input[type=submit].btn.btn-large{*padding-top:7px;*padding-bottom:7px}button.btn.btn-small,input[type=submit].btn.btn-small{*padding-top:3px;*padding-bottom:3px}button.btn.btn-mini,input[type=submit].btn.btn-mini{*padding-top:1px;*padding-bottom:1px}.btn-link,.btn-link:active,.btn-link[disabled]{background-color:transparent;background-image:none;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.btn-link{border-color:transparent;cursor:pointer;color:#08c;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.btn-link:focus,.btn-link:hover{color:#005580;text-decoration:underline;background-color:transparent}.btn-link[disabled]:focus,.btn-link[disabled]:hover{color:#333;text-decoration:none}.clearfix{*zoom:1}.clearfix:after,.clearfix:before{display:table;content:"";line-height:0}.clearfix:after{clear:both}.hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.input-block-level{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.nowrap{white-space:nowrap}.actioncol{text-align:center;white-space:nowrap}.actioncol a{text-decoration:none;color:#000}.actioncol a.disabled{color:#ccc;cursor:default}#navbar .navbar-inner{background-color:#ebebeb;background-image:-moz-linear-gradient(top,#fff,#ccc);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#ccc));background-image:-webkit-linear-gradient(top,#fff,#ccc);background-image:-o-linear-gradient(top,#fff,#ccc);background-image:linear-gradient(to bottom,#fff,#ccc);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffcccccc', GradientType=0)}#navbar .navbar-inner .brand,#navbar .navbar-inner .nav>li>a{text-shadow:0 1px 0 #ccc;color:#333}#navbar .navbar-inner .brand .caret,#navbar .navbar-inner .nav>li>a .caret{border-bottom-color:#939393;border-top-color:#939393}#navbar .navbar-inner .brand:focus .caret,#navbar .navbar-inner .brand:hover .caret,#navbar .navbar-inner .nav>li>a:focus .caret,#navbar .navbar-inner .nav>li>a:hover .caret{border-bottom-color:#636363;border-top-color:#636363}#navbar .navbar-inner .brand span{background-image:url(../img/tentacle-20x20.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner .brand span{background-image:url(../img/tentacle-20x20@2x.png)}}#navbar .navbar-inner .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner .nav>li.dropdown.open>.dropdown-toggle{background-color:#e0e0e0;background-image:-moz-linear-gradient(top,#ccc,#fff);background-image:-webkit-gradient(linear,0 0,0 100%,from(#ccc),to(#fff));background-image:-webkit-linear-gradient(top,#ccc,#fff);background-image:-o-linear-gradient(top,#ccc,#fff);background-image:linear-gradient(to bottom,#ccc,#fff);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffcccccc', endColorstr='#ffffffff', GradientType=0)}#navbar .navbar-inner .nav>li>a:hover{background-color:#dedede;background-image:-moz-linear-gradient(top,#f2f2f2,#bfbfbf);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f2f2f2),to(#bfbfbf));background-image:-webkit-linear-gradient(top,#f2f2f2,#bfbfbf);background-image:-o-linear-gradient(top,#f2f2f2,#bfbfbf);background-image:linear-gradient(to bottom,#f2f2f2,#bfbfbf);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2f2f2', endColorstr='#ffbfbfbf', GradientType=0)}#navbar .navbar-inner.transparent{background-color:rgba(235,235,235,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(255,255,255,.6),rgba(204,204,204,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(255,255,255,.6)),to(rgba(204,204,204,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(255,255,255,.6),rgba(204,204,204,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(255,255,255,.6),rgba(204,204,204,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(255,255,255,.6),rgba(204,204,204,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99ffffff', endColorstr='#99cccccc', GradientType=0)}#navbar .navbar-inner.transparent .brand,#navbar .navbar-inner.transparent .nav>li>a{text-shadow:0 1px 0 #ccc;color:#333}#navbar .navbar-inner.transparent .brand .caret,#navbar .navbar-inner.transparent .nav>li>a .caret{border-bottom-color:#939393;border-top-color:#939393}#navbar .navbar-inner.transparent .brand:focus .caret,#navbar .navbar-inner.transparent .brand:hover .caret,#navbar .navbar-inner.transparent .nav>li>a:focus .caret,#navbar .navbar-inner.transparent .nav>li>a:hover .caret{border-bottom-color:#636363;border-top-color:#636363}#navbar .navbar-inner.transparent .brand span{background-image:url(../img/tentacle-20x20.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.transparent .brand span{background-image:url(../img/tentacle-20x20@2x.png)}}#navbar .navbar-inner.transparent .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.transparent .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.transparent .nav>li.dropdown.open>.dropdown-toggle{background-color:rgba(224,224,224,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(204,204,204,.6),rgba(255,255,255,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(204,204,204,.6)),to(rgba(255,255,255,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(204,204,204,.6),rgba(255,255,255,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(204,204,204,.6),rgba(255,255,255,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(204,204,204,.6),rgba(255,255,255,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99cccccc', endColorstr='#99ffffff', GradientType=0)}#navbar .navbar-inner.transparent .nav>li>a:hover{background-color:rgba(222,222,222,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(242,242,242,.6),rgba(191,191,191,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(242,242,242,.6)),to(rgba(191,191,191,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(242,242,242,.6),rgba(191,191,191,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(242,242,242,.6),rgba(191,191,191,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(242,242,242,.6),rgba(191,191,191,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99f2f2f2', endColorstr='#99bfbfbf', GradientType=0)}#navbar .navbar-inner.red{background-color:#bb645f;background-image:-moz-linear-gradient(top,#e28e8a,#802420);background-image:-webkit-gradient(linear,0 0,0 100%,from(#e28e8a),to(#802420));background-image:-webkit-linear-gradient(top,#e28e8a,#802420);background-image:-o-linear-gradient(top,#e28e8a,#802420);background-image:linear-gradient(to bottom,#e28e8a,#802420);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe28e8a', endColorstr='#ff802420', GradientType=0)}#navbar .navbar-inner.red .brand,#navbar .navbar-inner.red .nav>li>a{text-shadow:0 1px 0 #d86761;color:#f2f2f2}#navbar .navbar-inner.red .brand .caret,#navbar .navbar-inner.red .nav>li>a .caret{border-bottom-color:#d89491;border-top-color:#d89491}#navbar .navbar-inner.red .brand:focus .caret,#navbar .navbar-inner.red .brand:hover .caret,#navbar .navbar-inner.red .nav>li>a:focus .caret,#navbar .navbar-inner.red .nav>li>a:hover .caret{border-bottom-color:#e5c3c1;border-top-color:#e5c3c1}#navbar .navbar-inner.red .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.red .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.red .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.red .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.red .nav>li.dropdown.open>.dropdown-toggle{background-color:#a74f4a;background-image:-moz-linear-gradient(top,#802420,#e28e8a);background-image:-webkit-gradient(linear,0 0,0 100%,from(#802420),to(#e28e8a));background-image:-webkit-linear-gradient(top,#802420,#e28e8a);background-image:-o-linear-gradient(top,#802420,#e28e8a);background-image:linear-gradient(to bottom,#802420,#e28e8a);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff802420', endColorstr='#ffe28e8a', GradientType=0)}#navbar .navbar-inner.red .nav>li>a:hover{background-color:#af5651;background-image:-moz-linear-gradient(top,#dd7a75,#6b1f1b);background-image:-webkit-gradient(linear,0 0,0 100%,from(#dd7a75),to(#6b1f1b));background-image:-webkit-linear-gradient(top,#dd7a75,#6b1f1b);background-image:-o-linear-gradient(top,#dd7a75,#6b1f1b);background-image:linear-gradient(to bottom,#dd7a75,#6b1f1b);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdd7a75', endColorstr='#ff6b1f1b', GradientType=0)}#navbar .navbar-inner.red.transparent{background-color:rgba(187,100,95,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(226,142,138,.6),rgba(128,36,32,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(226,142,138,.6)),to(rgba(128,36,32,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(226,142,138,.6),rgba(128,36,32,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(226,142,138,.6),rgba(128,36,32,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(226,142,138,.6),rgba(128,36,32,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99e28e8a', endColorstr='#99802420', GradientType=0)}#navbar .navbar-inner.red.transparent .brand,#navbar .navbar-inner.red.transparent .nav>li>a{text-shadow:0 1px 0 #d86761;color:#f2f2f2}#navbar .navbar-inner.red.transparent .brand .caret,#navbar .navbar-inner.red.transparent .nav>li>a .caret{border-bottom-color:#d89491;border-top-color:#d89491}#navbar .navbar-inner.red.transparent .brand:focus .caret,#navbar .navbar-inner.red.transparent .brand:hover .caret,#navbar .navbar-inner.red.transparent .nav>li>a:focus .caret,#navbar .navbar-inner.red.transparent .nav>li>a:hover .caret{border-bottom-color:#e5c3c1;border-top-color:#e5c3c1}#navbar .navbar-inner.red.transparent .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.red.transparent .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.red.transparent .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.red.transparent .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.red.transparent .nav>li.dropdown.open>.dropdown-toggle{background-color:rgba(167,79,74,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(128,36,32,.6),rgba(226,142,138,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(128,36,32,.6)),to(rgba(226,142,138,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(128,36,32,.6),rgba(226,142,138,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(128,36,32,.6),rgba(226,142,138,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(128,36,32,.6),rgba(226,142,138,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99802420', endColorstr='#99e28e8a', GradientType=0)}#navbar .navbar-inner.red.transparent .nav>li>a:hover{background-color:rgba(175,86,81,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(221,122,117,.6),rgba(107,31,27,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(221,122,117,.6)),to(rgba(107,31,27,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(221,122,117,.6),rgba(107,31,27,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(221,122,117,.6),rgba(107,31,27,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(221,122,117,.6),rgba(107,31,27,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99dd7a75', endColorstr='#996b1f1b', GradientType=0)}#navbar .navbar-inner.orange{background-color:#e39665;background-image:-moz-linear-gradient(top,#f9c3a0,#c2530c);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f9c3a0),to(#c2530c));background-image:-webkit-linear-gradient(top,#f9c3a0,#c2530c);background-image:-o-linear-gradient(top,#f9c3a0,#c2530c);background-image:linear-gradient(to bottom,#f9c3a0,#c2530c);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff9c3a0', endColorstr='#ffc2530c', GradientType=0)}#navbar .navbar-inner.orange .brand,#navbar .navbar-inner.orange .nav>li>a{text-shadow:0 1px 0 #f6a570;color:#f2f2f2}#navbar .navbar-inner.orange .brand .caret,#navbar .navbar-inner.orange .nav>li>a .caret{border-bottom-color:#f2b58d;border-top-color:#f2b58d}#navbar .navbar-inner.orange .brand:focus .caret,#navbar .navbar-inner.orange .brand:hover .caret,#navbar .navbar-inner.orange .nav>li>a:focus .caret,#navbar .navbar-inner.orange .nav>li>a:hover .caret{border-bottom-color:#f2d3bf;border-top-color:#f2d3bf}#navbar .navbar-inner.orange .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.orange .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.orange .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.orange .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.orange .nav>li.dropdown.open>.dropdown-toggle{background-color:#d88047;background-image:-moz-linear-gradient(top,#c2530c,#f9c3a0);background-image:-webkit-gradient(linear,0 0,0 100%,from(#c2530c),to(#f9c3a0));background-image:-webkit-linear-gradient(top,#c2530c,#f9c3a0);background-image:-o-linear-gradient(top,#c2530c,#f9c3a0);background-image:linear-gradient(to bottom,#c2530c,#f9c3a0);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffc2530c', endColorstr='#fff9c3a0', GradientType=0)}#navbar .navbar-inner.orange .nav>li>a:hover{background-color:#d98956;background-image:-moz-linear-gradient(top,#f8b488,#aa490a);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f8b488),to(#aa490a));background-image:-webkit-linear-gradient(top,#f8b488,#aa490a);background-image:-o-linear-gradient(top,#f8b488,#aa490a);background-image:linear-gradient(to bottom,#f8b488,#aa490a);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff8b488', endColorstr='#ffaa490a', GradientType=0)}#navbar .navbar-inner.orange.transparent{background-color:rgba(227,150,101,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(249,195,160,.6),rgba(194,83,12,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(249,195,160,.6)),to(rgba(194,83,12,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(249,195,160,.6),rgba(194,83,12,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(249,195,160,.6),rgba(194,83,12,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(249,195,160,.6),rgba(194,83,12,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99f9c3a0', endColorstr='#99c2530c', GradientType=0)}#navbar .navbar-inner.orange.transparent .brand,#navbar .navbar-inner.orange.transparent .nav>li>a{text-shadow:0 1px 0 #f6a570;color:#f2f2f2}#navbar .navbar-inner.orange.transparent .brand .caret,#navbar .navbar-inner.orange.transparent .nav>li>a .caret{border-bottom-color:#f2b58d;border-top-color:#f2b58d}#navbar .navbar-inner.orange.transparent .brand:focus .caret,#navbar .navbar-inner.orange.transparent .brand:hover .caret,#navbar .navbar-inner.orange.transparent .nav>li>a:focus .caret,#navbar .navbar-inner.orange.transparent .nav>li>a:hover .caret{border-bottom-color:#f2d3bf;border-top-color:#f2d3bf}#navbar .navbar-inner.orange.transparent .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.orange.transparent .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.orange.transparent .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.orange.transparent .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.orange.transparent .nav>li.dropdown.open>.dropdown-toggle{background-color:rgba(216,128,71,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(194,83,12,.6),rgba(249,195,160,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(194,83,12,.6)),to(rgba(249,195,160,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(194,83,12,.6),rgba(249,195,160,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(194,83,12,.6),rgba(249,195,160,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(194,83,12,.6),rgba(249,195,160,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99c2530c', endColorstr='#99f9c3a0', GradientType=0)}#navbar .navbar-inner.orange.transparent .nav>li>a:hover{background-color:rgba(217,137,86,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(248,180,136,.6),rgba(170,73,10,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(248,180,136,.6)),to(rgba(170,73,10,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(248,180,136,.6),rgba(170,73,10,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(248,180,136,.6),rgba(170,73,10,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(248,180,136,.6),rgba(170,73,10,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99f8b488', endColorstr='#99aa490a', GradientType=0)}#navbar .navbar-inner.yellow{background-color:#e3d765;background-image:-moz-linear-gradient(top,#f9f0a0,#c2b00c);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f9f0a0),to(#c2b00c));background-image:-webkit-linear-gradient(top,#f9f0a0,#c2b00c);background-image:-o-linear-gradient(top,#f9f0a0,#c2b00c);background-image:linear-gradient(to bottom,#f9f0a0,#c2b00c);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff9f0a0', endColorstr='#ffc2b00c', GradientType=0)}#navbar .navbar-inner.yellow .brand,#navbar .navbar-inner.yellow .nav>li>a{text-shadow:0 1px 0 #c2b00c;color:#f2f2f2}#navbar .navbar-inner.yellow .brand .caret,#navbar .navbar-inner.yellow .nav>li>a .caret{border-bottom-color:#f2e88d;border-top-color:#f2e88d}#navbar .navbar-inner.yellow .brand:focus .caret,#navbar .navbar-inner.yellow .brand:hover .caret,#navbar .navbar-inner.yellow .nav>li>a:focus .caret,#navbar .navbar-inner.yellow .nav>li>a:hover .caret{border-bottom-color:#f2edbf;border-top-color:#f2edbf}#navbar .navbar-inner.yellow .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.yellow .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.yellow .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.yellow .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.yellow .nav>li.dropdown.open>.dropdown-toggle{background-color:#d8ca47;background-image:-moz-linear-gradient(top,#c2b00c,#f9f0a0);background-image:-webkit-gradient(linear,0 0,0 100%,from(#c2b00c),to(#f9f0a0));background-image:-webkit-linear-gradient(top,#c2b00c,#f9f0a0);background-image:-o-linear-gradient(top,#c2b00c,#f9f0a0);background-image:linear-gradient(to bottom,#c2b00c,#f9f0a0);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffc2b00c', endColorstr='#fff9f0a0', GradientType=0)}#navbar .navbar-inner.yellow .nav>li>a:hover{background-color:#d9cc56;background-image:-moz-linear-gradient(top,#f8ed88,#aa9a0a);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f8ed88),to(#aa9a0a));background-image:-webkit-linear-gradient(top,#f8ed88,#aa9a0a);background-image:-o-linear-gradient(top,#f8ed88,#aa9a0a);background-image:linear-gradient(to bottom,#f8ed88,#aa9a0a);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff8ed88', endColorstr='#ffaa9a0a', GradientType=0)}#navbar .navbar-inner.yellow.transparent{background-color:rgba(227,215,101,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(249,240,160,.6),rgba(194,176,12,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(249,240,160,.6)),to(rgba(194,176,12,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(249,240,160,.6),rgba(194,176,12,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(249,240,160,.6),rgba(194,176,12,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(249,240,160,.6),rgba(194,176,12,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99f9f0a0', endColorstr='#99c2b00c', GradientType=0)}#navbar .navbar-inner.yellow.transparent .brand,#navbar .navbar-inner.yellow.transparent .nav>li>a{text-shadow:0 1px 0 #c2b00c;color:#f2f2f2}#navbar .navbar-inner.yellow.transparent .brand .caret,#navbar .navbar-inner.yellow.transparent .nav>li>a .caret{border-bottom-color:#f2e88d;border-top-color:#f2e88d}#navbar .navbar-inner.yellow.transparent .brand:focus .caret,#navbar .navbar-inner.yellow.transparent .brand:hover .caret,#navbar .navbar-inner.yellow.transparent .nav>li>a:focus .caret,#navbar .navbar-inner.yellow.transparent .nav>li>a:hover .caret{border-bottom-color:#f2edbf;border-top-color:#f2edbf}#navbar .navbar-inner.yellow.transparent .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.yellow.transparent .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.yellow.transparent .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.yellow.transparent .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.yellow.transparent .nav>li.dropdown.open>.dropdown-toggle{background-color:rgba(216,202,71,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(194,176,12,.6),rgba(249,240,160,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(194,176,12,.6)),to(rgba(249,240,160,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(194,176,12,.6),rgba(249,240,160,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(194,176,12,.6),rgba(249,240,160,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(194,176,12,.6),rgba(249,240,160,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99c2b00c', endColorstr='#99f9f0a0', GradientType=0)}#navbar .navbar-inner.yellow.transparent .nav>li>a:hover{background-color:rgba(217,204,86,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(248,237,136,.6),rgba(170,154,10,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(248,237,136,.6)),to(rgba(170,154,10,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(248,237,136,.6),rgba(170,154,10,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(248,237,136,.6),rgba(170,154,10,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(248,237,136,.6),rgba(170,154,10,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99f8ed88', endColorstr='#99aa9a0a', GradientType=0)}#navbar .navbar-inner.green{background-color:#98f064;background-image:-moz-linear-gradient(top,#c8ffa7,#50da00);background-image:-webkit-gradient(linear,0 0,0 100%,from(#c8ffa7),to(#50da00));background-image:-webkit-linear-gradient(top,#c8ffa7,#50da00);background-image:-o-linear-gradient(top,#c8ffa7,#50da00);background-image:linear-gradient(to bottom,#c8ffa7,#50da00);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffc8ffa7', endColorstr='#ff50da00', GradientType=0)}#navbar .navbar-inner.green .brand,#navbar .navbar-inner.green .nav>li>a{text-shadow:0 1px 0 #50da00;color:#333}#navbar .navbar-inner.green .brand .caret,#navbar .navbar-inner.green .nav>li>a .caret{border-bottom-color:#55992e;border-top-color:#55992e}#navbar .navbar-inner.green .brand:focus .caret,#navbar .navbar-inner.green .brand:hover .caret,#navbar .navbar-inner.green .nav>li>a:focus .caret,#navbar .navbar-inner.green .nav>li>a:hover .caret{border-bottom-color:#446630;border-top-color:#446630}#navbar .navbar-inner.green .brand span{background-image:url(../img/tentacle-20x20.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.green .brand span{background-image:url(../img/tentacle-20x20@2x.png)}}#navbar .navbar-inner.green .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.green .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.green .nav>li.dropdown.open>.dropdown-toggle{background-color:#80e943;background-image:-moz-linear-gradient(top,#50da00,#c8ffa7);background-image:-webkit-gradient(linear,0 0,0 100%,from(#50da00),to(#c8ffa7));background-image:-webkit-linear-gradient(top,#50da00,#c8ffa7);background-image:-o-linear-gradient(top,#50da00,#c8ffa7);background-image:linear-gradient(to bottom,#50da00,#c8ffa7);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff50da00', endColorstr='#ffc8ffa7', GradientType=0)}#navbar .navbar-inner.green .nav>li>a:hover{background-color:#8ae655;background-image:-moz-linear-gradient(top,#b8ff8e,#47c100);background-image:-webkit-gradient(linear,0 0,0 100%,from(#b8ff8e),to(#47c100));background-image:-webkit-linear-gradient(top,#b8ff8e,#47c100);background-image:-o-linear-gradient(top,#b8ff8e,#47c100);background-image:linear-gradient(to bottom,#b8ff8e,#47c100);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffb8ff8e', endColorstr='#ff47c100', GradientType=0)}#navbar .navbar-inner.green.transparent{background-color:rgba(152,240,100,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(200,255,167,.6),rgba(80,218,0,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(200,255,167,.6)),to(rgba(80,218,0,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(200,255,167,.6),rgba(80,218,0,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(200,255,167,.6),rgba(80,218,0,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(200,255,167,.6),rgba(80,218,0,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99c8ffa7', endColorstr='#9950da00', GradientType=0)}#navbar .navbar-inner.green.transparent .brand,#navbar .navbar-inner.green.transparent .nav>li>a{text-shadow:0 1px 0 #50da00;color:#333}#navbar .navbar-inner.green.transparent .brand .caret,#navbar .navbar-inner.green.transparent .nav>li>a .caret{border-bottom-color:#55992e;border-top-color:#55992e}#navbar .navbar-inner.green.transparent .brand:focus .caret,#navbar .navbar-inner.green.transparent .brand:hover .caret,#navbar .navbar-inner.green.transparent .nav>li>a:focus .caret,#navbar .navbar-inner.green.transparent .nav>li>a:hover .caret{border-bottom-color:#446630;border-top-color:#446630}#navbar .navbar-inner.green.transparent .brand span{background-image:url(../img/tentacle-20x20.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.green.transparent .brand span{background-image:url(../img/tentacle-20x20@2x.png)}}#navbar .navbar-inner.green.transparent .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.green.transparent .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.green.transparent .nav>li.dropdown.open>.dropdown-toggle{background-color:rgba(128,233,67,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(80,218,0,.6),rgba(200,255,167,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(80,218,0,.6)),to(rgba(200,255,167,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(80,218,0,.6),rgba(200,255,167,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(80,218,0,.6),rgba(200,255,167,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(80,218,0,.6),rgba(200,255,167,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#9950da00', endColorstr='#99c8ffa7', GradientType=0)}#navbar .navbar-inner.green.transparent .nav>li>a:hover{background-color:rgba(138,230,85,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(184,255,142,.6),rgba(71,193,0,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(184,255,142,.6)),to(rgba(71,193,0,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(184,255,142,.6),rgba(71,193,0,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(184,255,142,.6),rgba(71,193,0,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(184,255,142,.6),rgba(71,193,0,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99b8ff8e', endColorstr='#9947c100', GradientType=0)}#navbar .navbar-inner.blue{background-color:#2e63cc;background-image:-moz-linear-gradient(top,#4d88ff,#002b80);background-image:-webkit-gradient(linear,0 0,0 100%,from(#4d88ff),to(#002b80));background-image:-webkit-linear-gradient(top,#4d88ff,#002b80);background-image:-o-linear-gradient(top,#4d88ff,#002b80);background-image:linear-gradient(to bottom,#4d88ff,#002b80);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff4d88ff', endColorstr='#ff002b80', GradientType=0)}#navbar .navbar-inner.blue .brand,#navbar .navbar-inner.blue .nav>li>a{text-shadow:0 1px 0 #1a66ff;color:#f2f2f2}#navbar .navbar-inner.blue .brand .caret,#navbar .navbar-inner.blue .nav>li>a .caret{border-bottom-color:#799bdf;border-top-color:#799bdf}#navbar .navbar-inner.blue .brand:focus .caret,#navbar .navbar-inner.blue .brand:hover .caret,#navbar .navbar-inner.blue .nav>li>a:focus .caret,#navbar .navbar-inner.blue .nav>li>a:hover .caret{border-bottom-color:#b6c7e9;border-top-color:#b6c7e9}#navbar .navbar-inner.blue .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.blue .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.blue .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.blue .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.blue .nav>li.dropdown.open>.dropdown-toggle{background-color:#1f50b3;background-image:-moz-linear-gradient(top,#002b80,#4d88ff);background-image:-webkit-gradient(linear,0 0,0 100%,from(#002b80),to(#4d88ff));background-image:-webkit-linear-gradient(top,#002b80,#4d88ff);background-image:-o-linear-gradient(top,#002b80,#4d88ff);background-image:linear-gradient(to bottom,#002b80,#4d88ff);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff002b80', endColorstr='#ff4d88ff', GradientType=0)}#navbar .navbar-inner.blue .nav>li>a:hover{background-color:#1f55c2;background-image:-moz-linear-gradient(top,#37f,#026);background-image:-webkit-gradient(linear,0 0,0 100%,from(#37f),to(#026));background-image:-webkit-linear-gradient(top,#37f,#026);background-image:-o-linear-gradient(top,#37f,#026);background-image:linear-gradient(to bottom,#37f,#026);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3377ff', endColorstr='#ff002266', GradientType=0)}#navbar .navbar-inner.blue.transparent{background-color:rgba(46,99,204,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(77,136,255,.6),rgba(0,43,128,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(77,136,255,.6)),to(rgba(0,43,128,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(77,136,255,.6),rgba(0,43,128,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(77,136,255,.6),rgba(0,43,128,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(77,136,255,.6),rgba(0,43,128,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#994d88ff', endColorstr='#99002b80', GradientType=0)}#navbar .navbar-inner.blue.transparent .brand,#navbar .navbar-inner.blue.transparent .nav>li>a{text-shadow:0 1px 0 #1a66ff;color:#f2f2f2}#navbar .navbar-inner.blue.transparent .brand .caret,#navbar .navbar-inner.blue.transparent .nav>li>a .caret{border-bottom-color:#799bdf;border-top-color:#799bdf}#navbar .navbar-inner.blue.transparent .brand:focus .caret,#navbar .navbar-inner.blue.transparent .brand:hover .caret,#navbar .navbar-inner.blue.transparent .nav>li>a:focus .caret,#navbar .navbar-inner.blue.transparent .nav>li>a:hover .caret{border-bottom-color:#b6c7e9;border-top-color:#b6c7e9}#navbar .navbar-inner.blue.transparent .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.blue.transparent .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.blue.transparent .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.blue.transparent .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.blue.transparent .nav>li.dropdown.open>.dropdown-toggle{background-color:rgba(31,80,179,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(0,43,128,.6),rgba(77,136,255,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(0,43,128,.6)),to(rgba(77,136,255,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(0,43,128,.6),rgba(77,136,255,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(0,43,128,.6),rgba(77,136,255,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(0,43,128,.6),rgba(77,136,255,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99002b80', endColorstr='#994d88ff', GradientType=0)}#navbar .navbar-inner.blue.transparent .nav>li>a:hover{background-color:rgba(31,85,194,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(51,119,255,.6),rgba(0,34,102,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(51,119,255,.6)),to(rgba(0,34,102,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(51,119,255,.6),rgba(0,34,102,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(51,119,255,.6),rgba(0,34,102,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(51,119,255,.6),rgba(0,34,102,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#993377ff', endColorstr='#99002266', GradientType=0)}#navbar .navbar-inner.violet{background-color:#9864f0;background-image:-moz-linear-gradient(top,#c8a7ff,#5000da);background-image:-webkit-gradient(linear,0 0,0 100%,from(#c8a7ff),to(#5000da));background-image:-webkit-linear-gradient(top,#c8a7ff,#5000da);background-image:-o-linear-gradient(top,#c8a7ff,#5000da);background-image:linear-gradient(to bottom,#c8a7ff,#5000da);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffc8a7ff', endColorstr='#ff5000da', GradientType=0)}#navbar .navbar-inner.violet .brand,#navbar .navbar-inner.violet .nav>li>a{text-shadow:0 1px 0 #a774ff;color:#f2f2f2}#navbar .navbar-inner.violet .brand .caret,#navbar .navbar-inner.violet .nav>li>a .caret{border-bottom-color:#b58df9;border-top-color:#b58df9}#navbar .navbar-inner.violet .brand:focus .caret,#navbar .navbar-inner.violet .brand:hover .caret,#navbar .navbar-inner.violet .nav>li>a:focus .caret,#navbar .navbar-inner.violet .nav>li>a:hover .caret{border-bottom-color:#d3bff5;border-top-color:#d3bff5}#navbar .navbar-inner.violet .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.violet .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.violet .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.violet .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.violet .nav>li.dropdown.open>.dropdown-toggle{background-color:#8043e9;background-image:-moz-linear-gradient(top,#5000da,#c8a7ff);background-image:-webkit-gradient(linear,0 0,0 100%,from(#5000da),to(#c8a7ff));background-image:-webkit-linear-gradient(top,#5000da,#c8a7ff);background-image:-o-linear-gradient(top,#5000da,#c8a7ff);background-image:linear-gradient(to bottom,#5000da,#c8a7ff);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5000da', endColorstr='#ffc8a7ff', GradientType=0)}#navbar .navbar-inner.violet .nav>li>a:hover{background-color:#8a55e6;background-image:-moz-linear-gradient(top,#b88eff,#4700c1);background-image:-webkit-gradient(linear,0 0,0 100%,from(#b88eff),to(#4700c1));background-image:-webkit-linear-gradient(top,#b88eff,#4700c1);background-image:-o-linear-gradient(top,#b88eff,#4700c1);background-image:linear-gradient(to bottom,#b88eff,#4700c1);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffb88eff', endColorstr='#ff4700c1', GradientType=0)}#navbar .navbar-inner.violet.transparent{background-color:rgba(152,100,240,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(200,167,255,.6),rgba(80,0,218,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(200,167,255,.6)),to(rgba(80,0,218,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(200,167,255,.6),rgba(80,0,218,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(200,167,255,.6),rgba(80,0,218,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(200,167,255,.6),rgba(80,0,218,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99c8a7ff', endColorstr='#995000da', GradientType=0)}#navbar .navbar-inner.violet.transparent .brand,#navbar .navbar-inner.violet.transparent .nav>li>a{text-shadow:0 1px 0 #a774ff;color:#f2f2f2}#navbar .navbar-inner.violet.transparent .brand .caret,#navbar .navbar-inner.violet.transparent .nav>li>a .caret{border-bottom-color:#b58df9;border-top-color:#b58df9}#navbar .navbar-inner.violet.transparent .brand:focus .caret,#navbar .navbar-inner.violet.transparent .brand:hover .caret,#navbar .navbar-inner.violet.transparent .nav>li>a:focus .caret,#navbar .navbar-inner.violet.transparent .nav>li>a:hover .caret{border-bottom-color:#d3bff5;border-top-color:#d3bff5}#navbar .navbar-inner.violet.transparent .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.violet.transparent .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.violet.transparent .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.violet.transparent .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.violet.transparent .nav>li.dropdown.open>.dropdown-toggle{background-color:rgba(128,67,233,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(80,0,218,.6),rgba(200,167,255,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(80,0,218,.6)),to(rgba(200,167,255,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(80,0,218,.6),rgba(200,167,255,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(80,0,218,.6),rgba(200,167,255,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(80,0,218,.6),rgba(200,167,255,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#995000da', endColorstr='#99c8a7ff', GradientType=0)}#navbar .navbar-inner.violet.transparent .nav>li>a:hover{background-color:rgba(138,85,230,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(184,142,255,.6),rgba(71,0,193,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(184,142,255,.6)),to(rgba(71,0,193,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(184,142,255,.6),rgba(71,0,193,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(184,142,255,.6),rgba(71,0,193,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(184,142,255,.6),rgba(71,0,193,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99b88eff', endColorstr='#994700c1', GradientType=0)}#navbar .navbar-inner.black{background-color:#4f4f4f;background-image:-moz-linear-gradient(top,#787878,#121212);background-image:-webkit-gradient(linear,0 0,0 100%,from(#787878),to(#121212));background-image:-webkit-linear-gradient(top,#787878,#121212);background-image:-o-linear-gradient(top,#787878,#121212);background-image:linear-gradient(to bottom,#787878,#121212);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff787878', endColorstr='#ff121212', GradientType=0)}#navbar .navbar-inner.black .brand,#navbar .navbar-inner.black .nav>li>a{text-shadow:0 1px 0 #5e5e5e;color:#f2f2f2}#navbar .navbar-inner.black .brand .caret,#navbar .navbar-inner.black .nav>li>a .caret{border-bottom-color:#959595;border-top-color:#959595}#navbar .navbar-inner.black .brand:focus .caret,#navbar .navbar-inner.black .brand:hover .caret,#navbar .navbar-inner.black .nav>li>a:focus .caret,#navbar .navbar-inner.black .nav>li>a:hover .caret{border-bottom-color:#c4c4c4;border-top-color:#c4c4c4}#navbar .navbar-inner.black .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.black .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.black .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.black .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.black .nav>li.dropdown.open>.dropdown-toggle{background-color:#3b3b3b;background-image:-moz-linear-gradient(top,#121212,#787878);background-image:-webkit-gradient(linear,0 0,0 100%,from(#121212),to(#787878));background-image:-webkit-linear-gradient(top,#121212,#787878);background-image:-o-linear-gradient(top,#121212,#787878);background-image:linear-gradient(to bottom,#121212,#787878);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff121212', endColorstr='#ff787878', GradientType=0)}#navbar .navbar-inner.black .nav>li>a:hover{background-color:#424242;background-image:-moz-linear-gradient(top,#6b6b6b,#050505);background-image:-webkit-gradient(linear,0 0,0 100%,from(#6b6b6b),to(#050505));background-image:-webkit-linear-gradient(top,#6b6b6b,#050505);background-image:-o-linear-gradient(top,#6b6b6b,#050505);background-image:linear-gradient(to bottom,#6b6b6b,#050505);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff6b6b6b', endColorstr='#ff050505', GradientType=0)}#navbar .navbar-inner.black.transparent{background-color:rgba(79,79,79,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(120,120,120,.6),rgba(18,18,18,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(120,120,120,.6)),to(rgba(18,18,18,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(120,120,120,.6),rgba(18,18,18,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(120,120,120,.6),rgba(18,18,18,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(120,120,120,.6),rgba(18,18,18,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99787878', endColorstr='#99121212', GradientType=0)}#navbar .navbar-inner.black.transparent .brand,#navbar .navbar-inner.black.transparent .nav>li>a{text-shadow:0 1px 0 #5e5e5e;color:#f2f2f2}#navbar .navbar-inner.black.transparent .brand .caret,#navbar .navbar-inner.black.transparent .nav>li>a .caret{border-bottom-color:#959595;border-top-color:#959595}#navbar .navbar-inner.black.transparent .brand:focus .caret,#navbar .navbar-inner.black.transparent .brand:hover .caret,#navbar .navbar-inner.black.transparent .nav>li>a:focus .caret,#navbar .navbar-inner.black.transparent .nav>li>a:hover .caret{border-bottom-color:#c4c4c4;border-top-color:#c4c4c4}#navbar .navbar-inner.black.transparent .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.black.transparent .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.black.transparent .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.black.transparent .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.black.transparent .nav>li.dropdown.open>.dropdown-toggle{background-color:rgba(59,59,59,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(18,18,18,.6),rgba(120,120,120,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(18,18,18,.6)),to(rgba(120,120,120,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(18,18,18,.6),rgba(120,120,120,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(18,18,18,.6),rgba(120,120,120,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(18,18,18,.6),rgba(120,120,120,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99121212', endColorstr='#99787878', GradientType=0)}#navbar .navbar-inner.black.transparent .nav>li>a:hover{background-color:rgba(66,66,66,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(107,107,107,.6),rgba(5,5,5,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(107,107,107,.6)),to(rgba(5,5,5,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(107,107,107,.6),rgba(5,5,5,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(107,107,107,.6),rgba(5,5,5,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(107,107,107,.6),rgba(5,5,5,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#996b6b6b', endColorstr='#99050505', GradientType=0)}#navbar .navbar-inner.white{background-color:#e9e9e9;background-image:-moz-linear-gradient(top,#fff,#c8c8c8);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#c8c8c8));background-image:-webkit-linear-gradient(top,#fff,#c8c8c8);background-image:-o-linear-gradient(top,#fff,#c8c8c8);background-image:linear-gradient(to bottom,#fff,#c8c8c8);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffc8c8c8', GradientType=0)}#navbar .navbar-inner.white .brand,#navbar .navbar-inner.white .nav>li>a{text-shadow:0 1px 0 #c8c8c8;color:#333}#navbar .navbar-inner.white .brand .caret,#navbar .navbar-inner.white .nav>li>a .caret{border-bottom-color:#919191;border-top-color:#919191}#navbar .navbar-inner.white .brand:focus .caret,#navbar .navbar-inner.white .brand:hover .caret,#navbar .navbar-inner.white .nav>li>a:focus .caret,#navbar .navbar-inner.white .nav>li>a:hover .caret{border-bottom-color:#626262;border-top-color:#626262}#navbar .navbar-inner.white .brand span{background-image:url(../img/tentacle-20x20.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.white .brand span{background-image:url(../img/tentacle-20x20@2x.png)}}#navbar .navbar-inner.white .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.white .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.white .nav>li.dropdown.open>.dropdown-toggle{background-color:#dedede;background-image:-moz-linear-gradient(top,#c8c8c8,#fff);background-image:-webkit-gradient(linear,0 0,0 100%,from(#c8c8c8),to(#fff));background-image:-webkit-linear-gradient(top,#c8c8c8,#fff);background-image:-o-linear-gradient(top,#c8c8c8,#fff);background-image:linear-gradient(to bottom,#c8c8c8,#fff);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffc8c8c8', endColorstr='#ffffffff', GradientType=0)}#navbar .navbar-inner.white .nav>li>a:hover{background-color:#dcdcdc;background-image:-moz-linear-gradient(top,#f2f2f2,#bbb);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f2f2f2),to(#bbb));background-image:-webkit-linear-gradient(top,#f2f2f2,#bbb);background-image:-o-linear-gradient(top,#f2f2f2,#bbb);background-image:linear-gradient(to bottom,#f2f2f2,#bbb);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2f2f2', endColorstr='#ffbbbbbb', GradientType=0)}#navbar .navbar-inner.white.transparent{background-color:rgba(233,233,233,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(255,255,255,.6),rgba(200,200,200,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(255,255,255,.6)),to(rgba(200,200,200,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(255,255,255,.6),rgba(200,200,200,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(255,255,255,.6),rgba(200,200,200,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(255,255,255,.6),rgba(200,200,200,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99ffffff', endColorstr='#99c8c8c8', GradientType=0)}#navbar .navbar-inner.white.transparent .brand,#navbar .navbar-inner.white.transparent .nav>li>a{text-shadow:0 1px 0 #c8c8c8;color:#333}#navbar .navbar-inner.white.transparent .brand .caret,#navbar .navbar-inner.white.transparent .nav>li>a .caret{border-bottom-color:#919191;border-top-color:#919191}#navbar .navbar-inner.white.transparent .brand:focus .caret,#navbar .navbar-inner.white.transparent .brand:hover .caret,#navbar .navbar-inner.white.transparent .nav>li>a:focus .caret,#navbar .navbar-inner.white.transparent .nav>li>a:hover .caret{border-bottom-color:#626262;border-top-color:#626262}#navbar .navbar-inner.white.transparent .brand span{background-image:url(../img/tentacle-20x20.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.white.transparent .brand span{background-image:url(../img/tentacle-20x20@2x.png)}}#navbar .navbar-inner.white.transparent .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.white.transparent .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.white.transparent .nav>li.dropdown.open>.dropdown-toggle{background-color:rgba(222,222,222,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(200,200,200,.6),rgba(255,255,255,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(200,200,200,.6)),to(rgba(255,255,255,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(200,200,200,.6),rgba(255,255,255,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(200,200,200,.6),rgba(255,255,255,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(200,200,200,.6),rgba(255,255,255,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99c8c8c8', endColorstr='#99ffffff', GradientType=0)}#navbar .navbar-inner.white.transparent .nav>li>a:hover{background-color:rgba(220,220,220,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(242,242,242,.6),rgba(187,187,187,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(242,242,242,.6)),to(rgba(187,187,187,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(242,242,242,.6),rgba(187,187,187,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(242,242,242,.6),rgba(187,187,187,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(242,242,242,.6),rgba(187,187,187,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99f2f2f2', endColorstr='#99bbbbbb', GradientType=0)}#navbar .navbar-inner .brand{padding:10px 20px 6px}#navbar .navbar-inner .brand span{padding-left:26px;background-size:20px 20px;background-repeat:no-repeat;display:inline-block;max-width:250px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;vertical-align:top;line-height:20px;height:24px}#navbar_login a.dropdown-toggle span{display:inline-block;max-width:100px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;vertical-align:top}.octoprint-container{margin-top:20px}.octoprint-container .tab-content{padding:9px 15px;border-left:1px solid #DDD;border-right:1px solid #DDD;border-bottom:1px solid #DDD;-webkit-border-bottom-right-radius:4px;-moz-border-radius-bottomright:4px;border-bottom-right-radius:4px;-webkit-border-bottom-left-radius:4px;-moz-border-radius-bottomleft:4px;border-bottom-left-radius:4px}.octoprint-container .nav{margin-bottom:0}.octoprint-container .tab-content h1{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:40px;color:#333;border:0;border-bottom:1px solid #E5E5E5;font-weight:400}.octoprint-container .accordion-heading .accordion-heading-button{float:right}.octoprint-container .accordion-heading .accordion-heading-button a{display:inline-block;padding:8px 15px;font-size:14px;line-height:20px;color:#000;text-decoration:none;background:0 0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.octoprint-container .accordion-heading a.accordion-toggle{display:inline-block}.octoprint-container .accordion-heading [class*=" icon-"],.octoprint-container .accordion-heading [class^=icon-]{color:#000}.print-control .btn{padding-left:4px;padding-right:4px}.upload-buttons .btn{margin-right:0}table{table-layout:fixed}table .popover-title{text-overflow:ellipsis;word-break:break-all}table td,table th{overflow:hidden}table td.gcode_files_name,table th.gcode_files_name{text-overflow:ellipsis;text-align:left;white-space:nowrap}table td.gcode_files_action,table th.gcode_files_action{width:90px;text-align:center;white-space:nowrap}table td.gcode_files_action a,table th.gcode_files_action a{text-decoration:none;color:#000}table td.gcode_files_action a.disabled,table th.gcode_files_action a.disabled{color:#ccc;cursor:default}table td.timelapse_files_checkbox,table td.timelapse_unrendered_checkbox,table th.timelapse_files_checkbox,table th.timelapse_unrendered_checkbox{text-align:center;width:10px}table td.timelapse_files_checkbox input[type=checkbox],table td.timelapse_unrendered_checkbox input[type=checkbox],table th.timelapse_files_checkbox input[type=checkbox],table th.timelapse_unrendered_checkbox input[type=checkbox]{margin-top:0}table td.timelapse_files_name,table td.timelapse_unrendered_name,table th.timelapse_files_name,table th.timelapse_unrendered_name{text-overflow:ellipsis;text-align:left}table td.timelapse_files_size,table td.timelapse_unrendered_size,table th.timelapse_files_size,table th.timelapse_unrendered_size{text-align:right;width:55px}table td.timelapse_unrendered_count,table th.timelapse_unrendered_count{text-align:right;width:45px}table td.timelapse_files_action,table td.timelapse_unrendered_action,table th.timelapse_files_action,table th.timelapse_unrendered_action{width:45px;text-align:center;white-space:nowrap}table td.timelapse_files_action a,table td.timelapse_unrendered_action a,table th.timelapse_files_action a,table th.timelapse_unrendered_action a{text-decoration:none;color:#000}table td.timelapse_files_action a.disabled,table td.timelapse_unrendered_action a.disabled,table th.timelapse_files_action a.disabled,table th.timelapse_unrendered_action a.disabled{color:#ccc;cursor:default}table td.settings_users_name,table th.settings_users_name{text-overflow:ellipsis;text-align:left}table td.settings_users_active,table td.settings_users_admin,table th.settings_users_active,table th.settings_users_admin{text-align:center;width:55px}table td.settings_users_actions,table th.settings_users_actions{width:60px;text-align:center;white-space:nowrap}table td.settings_users_actions a,table th.settings_users_actions a{text-decoration:none;color:#000}table td.settings_users_actions a.disabled,table th.settings_users_actions a.disabled{color:#ccc;cursor:default}table td.settings_logs_name,table th.settings_logs_name{text-overflow:ellipsis;text-align:left}table td.settings_logs_size,table th.settings_logs_size{text-align:right;width:70px}table td.settings_logs_date,table th.settings_logs_date{text-align:left;width:130px}table td.settings_logs_action,table th.settings_logs_action{width:70px;text-align:center;white-space:nowrap}table td.settings_logs_action a,table th.settings_logs_action a{text-decoration:none;color:#000}table td.settings_logs_action a.disabled,table th.settings_logs_action a.disabled{color:#ccc;cursor:default}table td.settings_printerProfiles_profiles_name,table th.settings_printerProfiles_profiles_name{text-overflow:ellipsis;text-align:left}table td.settings_printerProfiles_profiles_model,table th.settings_printerProfiles_profiles_model{text-align:left;width:250px}table td.settings_printerProfiles_profiles_action,table th.settings_printerProfiles_profiles_action{width:80px;text-align:center;white-space:nowrap}table td.settings_printerProfiles_profiles_action a,table th.settings_printerProfiles_profiles_action a{text-decoration:none;color:#000}table td.settings_printerProfiles_profiles_action a.disabled,table th.settings_printerProfiles_profiles_action a.disabled{color:#ccc;cursor:default}#temperature-graph{height:350px;width:100%;background:url(../img/graph-background.png) center no-repeat}#temperature-table{table-layout:fixed;width:100%;margin-top:20px}#temperature-table td.temperature_actual,#temperature-table td.temperature_offset,#temperature-table td.temperature_target,#temperature-table td.temperature_tool,#temperature-table th.temperature_actual,#temperature-table th.temperature_offset,#temperature-table th.temperature_target,#temperature-table th.temperature_tool{vertical-align:middle;text-align:center}#temperature-table td.temperature_actual form,#temperature-table td.temperature_offset form,#temperature-table td.temperature_target form,#temperature-table td.temperature_tool form,#temperature-table th.temperature_actual form,#temperature-table th.temperature_offset form,#temperature-table th.temperature_target form,#temperature-table th.temperature_tool form{margin:0}#temperature-table td.temperature_actual .dropdown-menu,#temperature-table td.temperature_offset .dropdown-menu,#temperature-table td.temperature_target .dropdown-menu,#temperature-table td.temperature_tool .dropdown-menu,#temperature-table th.temperature_actual .dropdown-menu,#temperature-table th.temperature_offset .dropdown-menu,#temperature-table th.temperature_target .dropdown-menu,#temperature-table th.temperature_tool .dropdown-menu{text-align:left}#temperature-table td.temperature_tool,#temperature-table th.temperature_tool{width:16%;text-align:left;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}#temperature-table td.temperature_actual,#temperature-table th.temperature_actual{width:12%}#temperature-table td.temperature_target,#temperature-table th.temperature_target{width:42%;overflow:visible}#temperature-table td.temperature_offset,#temperature-table th.temperature_offset{width:30%}.tab-content,.tab-pane{overflow:visible}#speed_fill,#speed_innerWall,#speed_outerWall,#speed_support,#temp_newBedTemp,#temp_newTemp,#webcam_timelapse_fps,#webcam_timelapse_interval,#webcam_timelapse_postRoll,#webcam_timelapse_retractionZHop{text-align:right}ul.dropdown-menu li a{cursor:pointer}#connection_baudrates,#connection_ports,#connection_printers{width:100%}#offline_overlay,#reloadui_overlay{position:fixed;top:0;left:0;width:100%;height:100%;display:none}#offline_overlay{z-index:10002}#reloadui_overlay{z-index:10001}#offline_overlay_background,#reloadui_overlay_background{position:fixed;top:0;left:0;width:100%;height:100%;background-color:#000;filter:alpha(opacity=50);-moz-opacity:.5;-khtml-opacity:.5;opacity:.5}#offline_overlay_wrapper,#reloadui_overlay_wrapper{position:absolute;top:0;bottom:0;left:0;right:0;padding-top:60px}#offline_overlay_wrapper .container,#reloadui_overlay_wrapper .container{margin:auto}#webcam_container{width:100%;position:relative;outline:0;background-color:#000}#webcam_container .keycontrol_overlay{position:absolute;left:10px;right:10px;bottom:10px;background:rgba(0,0,0,.5);font-size:85%;color:#fff;padding:0}#webcam_container .keycontrol_overlay kbd{border:1px solid #eee;border-radius:3px;margin-left:2px;margin-right:2px;font-size:90%;padding:2px;min-width:1em}#webcam_container .keycontrol_overlay .keycontrol_overlay_heading{position:relative;padding:10px;font-weight:700}#webcam_container .keycontrol_overlay .keycontrol_overlay_column{position:relative;width:45%;padding:10px;float:left}#webcam_container .nowebcam{position:absolute;top:0;left:0;right:0;bottom:0}#webcam_container .nowebcam .text{color:#fff;text-align:center;position:relative;margin:auto;width:80%;top:50%;transform:translateY(-50%);display:block}#webcam_container .nowebcam .text.webcam_loading{animation:pulsate 3s ease-out;animation-iteration-count:infinite}#webcam_container .webcam_rotated{position:relative;width:100%;padding-bottom:100%;pointer-events:none}#webcam_container .webcam_rotated .webcam_fixed_ratio{position:absolute;transform:rotate(-90deg);top:0;bottom:0;pointer-events:none}#webcam_container .webcam_rotated .webcam_fixed_ratio .webcam_fixed_ratio_inner{width:100%;height:100%;pointer-events:none}#webcam_container .webcam_unrotated .webcam_fixed_ratio{width:100%;pointer-events:none;padding-bottom:100%;position:relative}#webcam_container .webcam_unrotated .webcam_fixed_ratio.ratio43{padding-bottom:75%}#webcam_container .webcam_unrotated .webcam_fixed_ratio.ratio169{padding-bottom:56.25%}#webcam_container .webcam_unrotated .webcam_fixed_ratio.ratio1610{padding-bottom:62.5%}#webcam_container .webcam_unrotated .webcam_fixed_ratio .webcam_fixed_ratio_inner{position:absolute;top:0;bottom:0;left:0;right:0;pointer-events:none}#webcam_container img{width:100%;height:100%;object-fit:contain}#state_wrapper hr{margin:5px 0}#files .gcode_files{padding-right:7px}#files .gcode_files .entry{padding:5px;line-height:20px;border-bottom:1px solid #ddd;position:relative}#files .gcode_files .entry:hover{background-color:#f5f5f5}#files .gcode_files .entry .title{text-overflow:ellipsis;word-break:break-all}#files .gcode_files .entry .additionalInfo,#files .gcode_files .entry .internal,#files .gcode_files .entry .size,#files .gcode_files .entry .uploaded{font-size:85%;color:#999}#files .gcode_files .entry .internal{word-break:break-all}#files .gcode_files .entry .action-buttons{position:absolute;bottom:5px;right:5px}#files .gcode_files .entry .additionalInfo{padding-bottom:22px}@keyframes highlightframes{0%{background:#ff0}100%{background:0 0}}#files .gcode_files .entry.highlight{animation:highlightframes 2s}#files .gcode_files .back .back-path{white-space:nowrap}#files .gcode_files .back .back-path span{word-wrap:break-word;white-space:pre-line}#files .upload-buttons{margin-top:10px}#files .form-search{text-align:center;margin-bottom:5px!important}#control{overflow:hidden}#control .jog-panel{float:left;margin-right:19px}#control h1{text-align:left}#control .jog-panel>div{text-align:center}#control .jog-panel>div.distance{text-align:left}#control .jog-panel .slider{margin-bottom:10px}#control .box{width:30px;height:30px;margin-right:10px;margin-bottom:10px;padding-left:8px}#control .control-box{display:block;height:30px;margin-bottom:10px}#control .btn-group{margin-bottom:10px}#control .btn-group.distance>.btn{width:43px;padding:3px 0;height:30px}#control .slider-handle{width:14px;height:14px;margin-left:-7px;margin-top:-3px}#control .custom_section h1{cursor:pointer}#control .custom_section_horizontal>.custom_control{display:inline-block}#control .custom_section_vertical>.custom_control{display:block}#control .custom_control .slider{margin-left:10px;margin-right:10px;margin-bottom:2px}#gcode .progress{width:588px}#gcode .progress .bar{-webkit-transition:width 0s linear;-moz-transition:width 0s linear;-o-transition:width 0s linear;transition:width 0s linear}#gcode .canvas_container{position:relative}#gcode .canvas_container:active,#gcode .canvas_container:hover{outline:0}#gcode .layer-buttons{padding-top:5px;padding-bottom:7px}#gcode #gcode_layer_slider{position:absolute;right:0;top:0;height:568px;float:right}#gcode #gcode_layer_slider .slider-handle{width:14px;height:14px;margin-left:-3px;margin-top:-7px}#gcode #gcode_command_slider .slider-handle{width:14px;height:14px;margin-left:-7px;margin-top:-3px}#term .terminal{margin-bottom:30px}#term .terminal #terminal-output,#term .terminal #terminal-output-lowfi{min-height:340px;margin-bottom:5px}#settings_dialog .aboutlink{float:left}#settings_dialog_menu,#wizard_dialog_menu{margin-left:0}#wizard_firstrun_acl .acl_decision{margin-top:1em}#settings_appearance_managelanguagesdialog_emptylist{overflow:hidden;width:100%;height:300px;text-align:center;display:table}#settings_appearance_managelanguagesdialog_emptylist div{display:table-cell;vertical-align:middle}.footer ul{margin:0}.footer ul li{display:inline;margin-left:1em;font-size:85%}.footer ul li:first-child{margin-left:0}.footer ul li a{color:#555}.ui-pnotify .alert a{color:#c09853}.ui-pnotify .alert-danger a,.ui-pnotify .alert-error a{color:#b94a48}.ui-pnotify .alert-success a{color:#468847}.ui-pnotify .alert-info a{color:#3a87ad}.pnotify_additional_info .pnotify_more{font-size:85%}.text-right{text-align:right}.text-center{text-align:center}.overflow_visible{overflow:visible!important}.clickable{cursor:pointer}.border_box{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.hidden{display:none}textarea.block{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;width:100%}@keyframes pulsate{0%{opacity:.5}50%{opacity:1}100%{opacity:.5}}#drop_overlay{position:fixed;top:0;left:0;width:100%;height:100%;z-index:10000;display:none}#drop_overlay.in{display:block}#drop_overlay #drop_overlay_background{position:fixed;top:0;left:0;width:100%;height:100%;background-color:#000;filter:alpha(opacity=50);-moz-opacity:.5;-khtml-opacity:.5;opacity:.5}#drop_overlay #drop_overlay_wrapper{position:absolute;top:0;bottom:0;left:0;right:0;padding-top:60px}#drop_overlay #drop_overlay_wrapper #drop,#drop_overlay #drop_overlay_wrapper #drop_background{position:absolute;top:0;left:0;margin-left:0;width:100%}#drop_overlay #drop_overlay_wrapper #drop_locally,#drop_overlay #drop_overlay_wrapper #drop_locally_background{position:absolute;top:0;left:50%;margin-left:-50%;width:50%;border-right:2px dashed #ccc}#drop_overlay #drop_overlay_wrapper #drop_sd,#drop_overlay #drop_overlay_wrapper #drop_sd_background{position:absolute;top:0;left:50%;margin-left:0;width:50%;border-left:2px dashed #ccc}#drop_overlay #drop_overlay_wrapper .dropzone{height:100%;z-index:10001;color:#fff;font-size:30px}#drop_overlay #drop_overlay_wrapper .dropzone i{font-size:50px}#drop_overlay #drop_overlay_wrapper .dropzone .text{display:block;text-align:center;line-height:40px;position:absolute;width:100%;bottom:5%;filter:alpha(opacity=100);-moz-opacity:1;-khtml-opacity:1;opacity:1}#drop_overlay #drop_overlay_wrapper .dropzone_background{width:50%;height:100%;background-color:#000;filter:alpha(opacity=25);-moz-opacity:.25;-khtml-opacity:.25;opacity:.25}#drop_overlay #drop_overlay_wrapper .dropzone_background.hover{background-color:#000;filter:alpha(opacity=50);-moz-opacity:.5;-khtml-opacity:.5;opacity:.5}#drop_overlay #drop_overlay_wrapper .dropzone_background.fade{-webkit-transition:all .3s ease-out;-moz-transition:all .3s ease-out;-ms-transition:all .3s ease-out;-o-transition:all .3s ease-out;transition:all .3s ease-out;opacity:1}.icon-sd-black-14{background:url(../img/icon-sd-black-14.png) 0 3px no-repeat;width:11px;height:17px;display:inline-block!important}.center{float:none;margin-left:auto;margin-right:auto}.flipH{-webkit-transform:scaleX(-1);-moz-transform:scaleX(-1);-ms-transform:scaleX(-1);transform:scaleX(-1)}.flipV{-webkit-transform:scaleY(-1);-moz-transform:scaleY(-1);-ms-transform:scaleY(-1);transform:scaleY(-1)}.flipH.flipV{-webkit-transform:scaleX(-1) scaleY(-1);-moz-transform:scaleX(-1) scaleY(-1);-ms-transform:scaleX(-1) scaleY(-1);transform:scaleX(-1) scaleY(-1)}.rotate90{-webkit-transform:rotate(-90deg);transform:rotate(-90deg)}.ui-pnotify a{text-decoration:underline}.btn-mini .caret,.btn-small .caret{margin-top:8px}.dropdown-menu-right{right:0;left:auto}.slider .slider-selection{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,.25);background-color:#006dcc;background-image:-moz-linear-gradient(top,#08c,#04c);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#04c));background-image:-webkit-linear-gradient(top,#08c,#04c);background-image:-o-linear-gradient(top,#08c,#04c);background-image:linear-gradient(to bottom,#08c,#04c);background-repeat:repeat-x;border-color:#04c #04c #002a80;border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);*background-color:#04c;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.slider .slider-selection.active,.slider .slider-selection.disabled,.slider .slider-selection:active,.slider .slider-selection:focus,.slider .slider-selection:hover,.slider .slider-selection[disabled]{color:#fff;background-color:#04c;*background-color:#003bb3}.slider .slider-selection.active,.slider .slider-selection:active{background-color:#039 \9}.slider.slider-disabled .slider-selection{background-image:none;opacity:.65;filter:alpha(opacity=65);-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.slider .slider-track{background-color:#f5f5f5;border:1px solid #e3e3e3;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05)}.slider.slider-disabled .slider-track{background-image:none;opacity:.65;filter:alpha(opacity=65);-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.slider .slider-handle{display:inline-block;*display:inline;*zoom:1;font-size:14px;line-height:20px;text-align:center;vertical-align:middle;cursor:pointer;color:#333;text-shadow:0 1px 1px rgba(255,255,255,.75);background-color:#f5f5f5;background-image:-moz-linear-gradient(top,#fff,#e6e6e6);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#e6e6e6));background-image:-webkit-linear-gradient(top,#fff,#e6e6e6);background-image:-o-linear-gradient(top,#fff,#e6e6e6);background-image:linear-gradient(to bottom,#fff,#e6e6e6);background-repeat:repeat-x;*background-color:#e6e6e6;border:1px solid #ccc;*border:0;border-bottom-color:#b3b3b3;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;*margin-left:.3em;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05);padding:0;margin-bottom:0;opacity:1;filter:alpha(opacity=100)}.slider .slider-handle.active,.slider .slider-handle.disabled,.slider .slider-handle:active,.slider .slider-handle:focus,.slider .slider-handle:hover,.slider .slider-handle[disabled]{color:#333;background-color:#e6e6e6;*background-color:#d9d9d9}.slider .slider-handle.active,.slider .slider-handle:active{background-color:#ccc \9}.slider .slider-handle:first-child{*margin-left:0}.slider .slider-handle:focus,.slider .slider-handle:hover{color:#333;text-decoration:none;background-position:0 -15px;-webkit-transition:background-position .1s linear;-moz-transition:background-position .1s linear;-o-transition:background-position .1s linear;transition:background-position .1s linear}.slider .slider-handle:focus{outline:#333 dotted thin;outline:-webkit-focus-ring-color auto 5px;outline-offset:-2px}.slider .slider-handle.active,.slider .slider-handle:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,.15),0 1px 2px rgba(0,0,0,.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,.15),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 2px 4px rgba(0,0,0,.15),0 1px 2px rgba(0,0,0,.05)}.slider .slider-handle.disabled,.slider .slider-handle[disabled]{cursor:default;background-image:none;opacity:.65;filter:alpha(opacity=65);-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.slider .slider-handle.hide{display:none}.slider .slider-handle.round{-webkit-border-radius:50%;-moz-border-radius:50%;border-radius:50%}.modal.large{width:975px;margin-left:-487px}.full-sized-box{position:absolute;bottom:0;left:0;right:0;top:0;padding:15px}.full-sized-box .row-fluid{height:100%}@media (max-width:979px){.full-sized-box{position:static}}:root .full-sized-box,_::-webkit-full-page-media,_:future{position:static}.scrollable{height:100%;overflow:auto;-webkit-overflow-scrolling:touch}.pre-output span{display:block}.input-append .add-on.add-on-limited,.input-prepend .add-on.add-on-limited{overflow-x:hidden;text-overflow:ellipsis;width:inherit}.input-append .btn-group:first-child .btn:first-child,.input-prepend .btn-group:first-child .btn:first-child{-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.input-append .btn-group .btn:first-child,.input-prepend .btn-group .btn:first-child{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.input-append.input-block-level,.input-prepend.input-block-level{display:table}.input-append.input-block-level .add-on,.input-prepend.input-block-level .add-on{display:table-cell;width:1%}.input-append.input-block-level>input,.input-prepend.input-block-level>input{box-sizing:border-box;display:table;min-height:inherit;width:100%}.input-append.input-block-level :not(:last-child),.input-prepend.input-block-level :not(:last-child){border-right:0}.control-group.error .input-append .fileinput-button,.control-group.error .input-prepend .fileinput-button{border-color:#b94a48}.control-text{padding-top:5px;cursor:default}input[type=number]{text-align:right}input[type=number].input-nospin::-webkit-inner-spin-button,input[type=number].input-nospin::-webkit-outer-spin-button{-webkit-appearance:none;margin:0}input[type=number].input-nospin{-moz-appearance:textfield}.progress-text,.progress-text-centered{position:relative}.progress-text .progress-text-back,.progress-text .progress-text-front,.progress-text-centered .progress-text-back,.progress-text-centered .progress-text-front{white-space:nowrap}.progress-text .progress-text-front,.progress-text-centered .progress-text-front{box-sizing:border-box;padding:0 10px;width:100%;display:block}.progress-text .progress-text-back,.progress-text-centered .progress-text-back{position:absolute;font-size:12px;line-height:20px;display:block;box-sizing:border-box;text-align:center;padding:0 10px}.progress-text .bar,.progress-text-centered .bar{position:absolute;overflow:hidden}.progress-text-centered .progress-text-front{position:absolute;font-size:12px;line-height:20px;display:block;text-align:center;color:#fff}.progress-text-centered .progress-text-back{width:100%}#navbar_login:not(.open) #login_dropdown_loggedout{display:block;z-index:-1;height:0;width:0;padding:0!important;overflow:hidden;border:0;box-shadow:none;left:-9999px}#navbar_login:not(.open) #login_dropdown_loggedout.hide{display:none}#loginForm{margin:0}#loginForm button{margin-top:20px} \ No newline at end of file diff --git a/src/octoprint/static/js/app/viewmodels/files.js b/src/octoprint/static/js/app/viewmodels/files.js index 159bfc3c66..39963c87c6 100644 --- a/src/octoprint/static/js/app/viewmodels/files.js +++ b/src/octoprint/static/js/app/viewmodels/files.js @@ -89,8 +89,8 @@ $(function() { { "name": function(a, b) { // sorts ascending - if (a["name"].toLocaleLowerCase() < b["name"].toLocaleLowerCase()) return -1; - if (a["name"].toLocaleLowerCase() > b["name"].toLocaleLowerCase()) return 1; + if (a["display"].toLowerCase() < b["display"].toLowerCase()) return -1; + if (a["display"].toLowerCase() > b["display"].toLowerCase()) return 1; return 0; }, "upload": function(a, b) { @@ -437,7 +437,7 @@ $(function() { return; } - self.slicing.show(file.origin, file.path, true); + self.slicing.show(file.origin, file.path, true, undefined, {display: file.display}); }; self.initSdCard = function() { diff --git a/src/octoprint/static/js/app/viewmodels/printerstate.js b/src/octoprint/static/js/app/viewmodels/printerstate.js index 4826132ff7..f59db8aa74 100644 --- a/src/octoprint/static/js/app/viewmodels/printerstate.js +++ b/src/octoprint/static/js/app/viewmodels/printerstate.js @@ -27,6 +27,7 @@ $(function() { self.filename = ko.observable(undefined); self.filepath = ko.observable(undefined); + self.filedisplay = ko.observable(undefined); self.progress = ko.observable(undefined); self.filesize = ko.observable(undefined); self.filepos = ko.observable(undefined); @@ -212,11 +213,13 @@ $(function() { self.filename(data.file.name); self.filepath(data.file.path); self.filesize(data.file.size); + self.filedisplay(data.file.display); self.sd(data.file.origin == "sdcard"); } else { self.filename(undefined); self.filepath(undefined); self.filesize(undefined); + self.filedisplay(undefined); self.sd(undefined); } diff --git a/src/octoprint/static/js/app/viewmodels/slicing.js b/src/octoprint/static/js/app/viewmodels/slicing.js index a02a6a5eba..47b16995d0 100644 --- a/src/octoprint/static/js/app/viewmodels/slicing.js +++ b/src/octoprint/static/js/app/viewmodels/slicing.js @@ -126,23 +126,28 @@ $(function() { ]; self.afterSlicing = ko.observable("none"); - self.show = function(target, file, force, path) { + self.show = function(target, file, force, path, options) { + options = options || {}; + if (!self.enableSlicingDialog() && !force) { return; } - var filename = file.substr(0, file.lastIndexOf(".")); + var filename = file; if (filename.lastIndexOf("/") != 0) { path = path || filename.substr(0, filename.lastIndexOf("/")); filename = filename.substr(filename.lastIndexOf("/") + 1); } + var display = options.display || filename; + var destination = display.substr(0, display.lastIndexOf(".")); + self.requestData(); self.target = target; self.file(file); self.path = path; - self.title(_.sprintf(gettext("Slicing %(filename)s"), {filename: filename})); - self.destinationFilename(filename); + self.title(_.sprintf(gettext("Slicing %(filename)s"), {filename: display})); + self.destinationFilename(destination); self.printerProfile(self.printerProfiles.currentProfile()); self.afterSlicing("none"); @@ -253,7 +258,7 @@ $(function() { return; } - var destinationFilename = self._sanitize(self.destinationFilename()); + var destinationFilename = self.destinationFilename(); var destinationExtensions = self.data[self.slicer()] && self.data[self.slicer()].extensions && self.data[self.slicer()].extensions.destination ? self.data[self.slicer()].extensions.destination diff --git a/src/octoprint/static/less/octoprint.less b/src/octoprint/static/less/octoprint.less index aae4afce15..9908b8eceb 100644 --- a/src/octoprint/static/less/octoprint.less +++ b/src/octoprint/static/less/octoprint.less @@ -686,11 +686,15 @@ ul.dropdown-menu li a { word-break: break-all; } - .uploaded, .size, .additionalInfo { + .internal, .uploaded, .size, .additionalInfo { font-size: 85%; color: #999; } + .internal { + word-break: break-all; + } + .action-buttons { position: absolute; bottom: @padding; diff --git a/src/octoprint/templates/sidebar/files.jinja2 b/src/octoprint/templates/sidebar/files.jinja2 index 80a8e46b65..46d8cc8b12 100644 --- a/src/octoprint/templates/sidebar/files.jinja2 +++ b/src/octoprint/templates/sidebar/files.jinja2 @@ -12,7 +12,8 @@ From dc539420159321c982ea275c7c18e296123b71cd Mon Sep 17 00:00:00 2001 From: Josh Major Date: Sat, 4 Nov 2017 10:08:04 -0500 Subject: [PATCH 088/333] Added plugin Javascript isolation --- AUTHORS.md | 1 + src/octoprint/server/__init__.py | 8 +++++--- src/octoprint/server/util/webassets.py | 9 +++++++++ 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/AUTHORS.md b/AUTHORS.md index eb521bba59..4fb77ffbf1 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -84,6 +84,7 @@ date of first contribution): * [Goswin von Brederlow](https://github.com/mrvn) * [Luke McKechnie](https://github.com/galamdring) * [Peter Backx](https://github.com/pbackx) + * [Josh Major](https://github.com/astateofblank) OctoPrint started off as a fork of [Cura](https://github.com/daid/Cura) by [Daid Braam](https://github.com/daid). Parts of its communication layer and diff --git a/src/octoprint/server/__init__.py b/src/octoprint/server/__init__.py index e792846a3b..bf60a8c035 100644 --- a/src/octoprint/server/__init__.py +++ b/src/octoprint/server/__init__.py @@ -454,7 +454,7 @@ def template_disabled(name, plugin): def mime_type_guesser(path): from octoprint.filemanager import get_mime_type return get_mime_type(path) - + def download_name_generator(path): metadata = fileManager.get_metadata("local", path) if metadata and "display" in metadata: @@ -1235,16 +1235,18 @@ def directory(self): less_plugins = list(dynamic_plugin_assets["external"]["less"]) # a couple of custom filters - from octoprint.server.util.webassets import LessImportRewrite, JsDelimiterBundler, SourceMapRewrite, SourceMapRemove + from octoprint.server.util.webassets import LessImportRewrite, JsDelimiterBundler, JsPluginDelimiterBundler, SourceMapRewrite, SourceMapRemove from webassets.filter import register_filter register_filter(LessImportRewrite) register_filter(SourceMapRewrite) register_filter(SourceMapRemove) register_filter(JsDelimiterBundler) + register_filter(JsPluginDelimiterBundler) # JS js_filters = ["sourcemap_remove", "js_delimiter_bundler"] + js_plugin_filters = js_filters + ["js_plugin_delimiter_bundler"] js_libs_bundle = Bundle(*js_libs, output="webassets/packed_libs.js", filters=",".join(js_filters)) @@ -1254,7 +1256,7 @@ def directory(self): if len(js_plugins) == 0: js_plugins_bundle = Bundle(*[]) else: - js_plugins_bundle = Bundle(*js_plugins, output="webassets/packed_plugins.js", filters=",".join(js_filters)) + js_plugins_bundle = Bundle(*js_plugins, output="webassets/packed_plugins.js", filters=",".join(js_plugin_filters)) js_app_bundle = Bundle(js_plugins_bundle, js_core_bundle, output="webassets/packed_app.js", filters=",".join(js_filters)) diff --git a/src/octoprint/server/util/webassets.py b/src/octoprint/server/util/webassets.py index 376de4ab8c..9e41ceb7b1 100644 --- a/src/octoprint/server/util/webassets.py +++ b/src/octoprint/server/util/webassets.py @@ -98,3 +98,12 @@ class JsDelimiterBundler(Filter): def input(self, _in, out, **kwargs): out.write(_in.read()) out.write("\n;\n") + +class JsPluginDelimiterBundler(Filter): + name = "js_plugin_delimiter_bundler" + options = {} + + def input(self, _in, out, **kwargs): + out.write("(function () {\n try {\n ") + out.write(_in.read().replace('\n', '\n ')) + out.write("\n } catch (error) {\n console.error(error);\n }\n})();\n") From 9837f67140e1bd90946f6b3fc7ea27a6d6508a6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Mon, 6 Nov 2017 11:39:11 +0100 Subject: [PATCH 089/333] Small adjustments in webasset filters * Add comment with source file to JsDelimiterBundler and JsPluginDelimiterBundler * Include source file name in JsPluginDelimiterBundler catch block and use log.error instead of console.error --- src/octoprint/server/__init__.py | 5 +++-- src/octoprint/server/util/webassets.py | 9 ++++++++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/octoprint/server/__init__.py b/src/octoprint/server/__init__.py index bf60a8c035..f9e2a95ef8 100644 --- a/src/octoprint/server/__init__.py +++ b/src/octoprint/server/__init__.py @@ -1235,7 +1235,8 @@ def directory(self): less_plugins = list(dynamic_plugin_assets["external"]["less"]) # a couple of custom filters - from octoprint.server.util.webassets import LessImportRewrite, JsDelimiterBundler, JsPluginDelimiterBundler, SourceMapRewrite, SourceMapRemove + from octoprint.server.util.webassets import LessImportRewrite, JsDelimiterBundler, JsPluginDelimiterBundler, \ + SourceMapRewrite, SourceMapRemove from webassets.filter import register_filter register_filter(LessImportRewrite) @@ -1246,7 +1247,7 @@ def directory(self): # JS js_filters = ["sourcemap_remove", "js_delimiter_bundler"] - js_plugin_filters = js_filters + ["js_plugin_delimiter_bundler"] + js_plugin_filters = ["sourcemap_remove", "js_plugin_delimiter_bundler"] js_libs_bundle = Bundle(*js_libs, output="webassets/packed_libs.js", filters=",".join(js_filters)) diff --git a/src/octoprint/server/util/webassets.py b/src/octoprint/server/util/webassets.py index 9e41ceb7b1..74e40392e0 100644 --- a/src/octoprint/server/util/webassets.py +++ b/src/octoprint/server/util/webassets.py @@ -96,14 +96,21 @@ class JsDelimiterBundler(Filter): options = {} def input(self, _in, out, **kwargs): + source = kwargs.get("source", "n/a") + + out.write("// source: " + source + "\n") out.write(_in.read()) out.write("\n;\n") + class JsPluginDelimiterBundler(Filter): name = "js_plugin_delimiter_bundler" options = {} def input(self, _in, out, **kwargs): + source = kwargs.get("source", "n/a") + + out.write("// source: " + source + "\n") out.write("(function () {\n try {\n ") out.write(_in.read().replace('\n', '\n ')) - out.write("\n } catch (error) {\n console.error(error);\n }\n})();\n") + out.write("\n } catch (error) {\n log.error(\"Error in bundled asset " + source + ":\", (error.stack || error));\n }\n})();\n") From 5aad696c5c97c1143f68ae9d38cc212f71845ff3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Mon, 6 Nov 2017 12:24:19 +0100 Subject: [PATCH 090/333] Do not enable timelapse if settings are missing If snapshot url and/or ffmpeg path are not set, the timelapse cannot work regardless of what type has been configured. So if the current configuration still contains a timelapse type other than "off" but snapshot url or ffmpeg path are unset, force a disabled timelapse. Fixes #2206 --- src/octoprint/timelapse.py | 76 +++++++++++++++++++++++--------------- 1 file changed, 47 insertions(+), 29 deletions(-) diff --git a/src/octoprint/timelapse.py b/src/octoprint/timelapse.py index 5b645853d9..d117caebb2 100644 --- a/src/octoprint/timelapse.py +++ b/src/octoprint/timelapse.py @@ -286,40 +286,58 @@ def configure_timelapse(config=None, persist=False): if current is not None: current.unload() + snapshot_url = settings().get(["webcam", "snapshot"]) + ffmpeg_path = settings().get(["webcam", "ffmpeg"]) + timelapse_precondition = snapshot_url is not None and snapshot_url.strip() != "" \ + and ffmpeg_path is not None and ffmpeg_path.strip() != "" + type = config["type"] - - postRoll = 0 - if "postRoll" in config and config["postRoll"] >= 0: - postRoll = config["postRoll"] - - fps = 25 - if "fps" in config and config["fps"] > 0: - fps = config["fps"] + if not timelapse_precondition and type is not None and type != "off": + logging.getLogger(__name__).warn("Essential timelapse settings unconfigured (snapshot URL or FFMPEG path) " + "but timelapse enabled, forcing disabled timelapse and disabling it " + "in the config as well.") + type = "off" + config["type"] = "off" + + if not persist: + # make sure we persist at least that timelapse is now disabled by default - we don't want the above + # warning to log + settings().set(["webcam", "timelapse", "type"], "off") + settings().save() if type is None or "off" == type: current = None - elif "zchange" == type: - retractionZHop = 0 - if "options" in config and "retractionZHop" in config["options"] and config["options"]["retractionZHop"] >= 0: - retractionZHop = config["options"]["retractionZHop"] - - minDelay = 5 - if "options" in config and "minDelay" in config["options"] and config["options"]["minDelay"] > 0: - minDelay = config["options"]["minDelay"] - - current = ZTimelapse(post_roll=postRoll, retraction_zhop=retractionZHop, min_delay=minDelay, fps=fps) - - elif "timed" == type: - interval = 10 - if "options" in config and "interval" in config["options"] and config["options"]["interval"] > 0: - interval = config["options"]["interval"] - - capture_post_roll = True - if "options" in config and "capturePostRoll" in config["options"] and isinstance(config["options"]["capturePostRoll"], bool): - capture_post_roll = config["options"]["capturePostRoll"] - - current = TimedTimelapse(post_roll=postRoll, interval=interval, fps=fps, capture_post_roll=capture_post_roll) + else: + postRoll = 0 + if "postRoll" in config and config["postRoll"] >= 0: + postRoll = config["postRoll"] + + fps = 25 + if "fps" in config and config["fps"] > 0: + fps = config["fps"] + + if "zchange" == type: + retractionZHop = 0 + if "options" in config and "retractionZHop" in config["options"] and config["options"]["retractionZHop"] >= 0: + retractionZHop = config["options"]["retractionZHop"] + + minDelay = 5 + if "options" in config and "minDelay" in config["options"] and config["options"]["minDelay"] > 0: + minDelay = config["options"]["minDelay"] + + current = ZTimelapse(post_roll=postRoll, retraction_zhop=retractionZHop, min_delay=minDelay, fps=fps) + + elif "timed" == type: + interval = 10 + if "options" in config and "interval" in config["options"] and config["options"]["interval"] > 0: + interval = config["options"]["interval"] + + capture_post_roll = True + if "options" in config and "capturePostRoll" in config["options"] and isinstance(config["options"]["capturePostRoll"], bool): + capture_post_roll = config["options"]["capturePostRoll"] + + current = TimedTimelapse(post_roll=postRoll, interval=interval, fps=fps, capture_post_roll=capture_post_roll) notify_callbacks(current) From 3ef97bf4d776357060e166d9d3b53ef79e2ad3ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Mon, 6 Nov 2017 17:09:16 +0100 Subject: [PATCH 091/333] Update psutil version --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index b9d0e6002f..85b66aa483 100644 --- a/setup.py +++ b/setup.py @@ -40,7 +40,7 @@ "pkginfo>=1.2.1,<1.3", "requests>=2.18.4,<3", "semantic_version>=2.4.2,<2.5", - "psutil>=3.2.1,<3.3", + "psutil>=5.4.0,<6", "Click>=6.2,<6.3", "awesome-slugify>=1.6.5,<1.7", "feedparser>=5.2.1,<5.3", From 00067768dacd357302e20f4eaf790c731cb5b7eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Wed, 8 Nov 2017 18:08:06 +0100 Subject: [PATCH 092/333] Docs: PyCharm run config with dependency update --- docs/development/environment.rst | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/docs/development/environment.rst b/docs/development/environment.rst index 785e90fda0..253956c7f6 100644 --- a/docs/development/environment.rst +++ b/docs/development/environment.rst @@ -58,7 +58,7 @@ Then: virtualenv venv source ./venv/bin/activate pip install --upgrade pip - pip install -e .[develop] + pip install -e .[develop,plugins] You can then start OctoPrint via ``~/devel/OctoPrint/venv/bin/octoprint`` or just ``octoprint`` if you activated the virtual environment. @@ -89,11 +89,11 @@ Open the Git Bash you just installed and in that: git clone https://github.com/foosel/OctoPrint.git cd OctoPrint virtualenv venv - source ./venv/bin/activate + source ./venv/Scripts/activate pip install --upgrade pip - pip install -e .[develop] + pip install -e .[develop,plugins] -You can then start OctoPrint via ``/c/Devel/OctoPrint/venv/bin/octoprint`` or just ``octoprint`` if you activated the virtual +You can then start OctoPrint via ``/c/Devel/OctoPrint/venv/Scripts/octoprint`` or just ``octoprint`` if you activated the virtual environment. .. _sec-development-environment-mac: @@ -136,7 +136,8 @@ You'll need a user account with administrator privileges. cd OctoPrint virtualenv venv source venv/bin/activate - pip install -e .[develop] + pip install --upgrade pip + pip install -e .[develop,plugins] You can then start OctoPrint via ``~/devel/OctoPrint/venv/bin/octoprint`` or just ``octoprint`` if you activated the virtual environment. @@ -165,10 +166,16 @@ PyCharm * Name: OctoPrint server * Script: path to ``run`` in the OctoPrint checkout folder (e.g. ``~/devel/OctoPrint/run`` or ``C:\Devel\OctoPrint\run``) - * Script parameters: ``--debug`` + * Script parameters: ``serve --debug`` * Project: ``OctoPrint`` * Python interpreter: the ``venv`` local virtual environment * Working directory: the OctoPrint checkout folder (e.g. ``~/devel/OctoPrint`` or ``C:\Devel\OctoPrint``) + * If you want dependencies to auto-update on run if necessary: "Before Launch" > "+" > "Run external tool" > "+" + + * Name: Update OctoPrint dependencies + * Program: ``$PyInterpreterDirectory$/pip`` (or ``$PyInterpreterDirectory$/pip.exe`` on Windows) + * Parameters: ``install -e .[develop,plugins]`` + * Working directory: ``$ProjectFileDir$`` .. note:: From 2364474ea6573cf4ab8375a31189e3f0cc6e6bca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Wed, 8 Nov 2017 18:10:37 +0100 Subject: [PATCH 093/333] Docs: Fix language on code-block & indentation --- docs/bundledplugins/softwareupdate.rst | 48 +++++++++++++------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/docs/bundledplugins/softwareupdate.rst b/docs/bundledplugins/softwareupdate.rst index 904a392294..b68a6751b4 100644 --- a/docs/bundledplugins/softwareupdate.rst +++ b/docs/bundledplugins/softwareupdate.rst @@ -117,29 +117,29 @@ Configuring the Plugin .. code-block:: yaml - plugins: - softwareupdate: - # the time-to-live of the version cache, in minutes - cache_ttl: 60 - - # configured version check and update methods - checks: - # "octoprint" is reserved for OctoPrint - octoprint: - # this defines an version check that will check against releases - # published on OctoPrint's Github repository and pip as update method - # against the release archives on Github - this is the default - type: github_release - user: foosel - repo: OctoPrint - method: pip - pip: 'https://github.com/foosel/OctoPrint/archive/{target_version}.zip' - - # further checks may be define here - - # pip command, if another one than the automatically detected one should be - # used - should normally NOT be necessary and hence set - pip_command: /path/to/pip + plugins: + softwareupdate: + # the time-to-live of the version cache, in minutes + cache_ttl: 60 + + # configured version check and update methods + checks: + # "octoprint" is reserved for OctoPrint + octoprint: + # this defines an version check that will check against releases + # published on OctoPrint's Github repository and pip as update method + # against the release archives on Github - this is the default + type: github_release + user: foosel + repo: OctoPrint + method: pip + pip: 'https://github.com/foosel/OctoPrint/archive/{target_version}.zip' + + # further checks may be define here + + # pip command, if another one than the automatically detected one should be + # used - should normally NOT be necessary and hence set + pip_command: /path/to/pip .. _sec-bundledplugins-softwareupdate-configuration-versionchecks: @@ -245,7 +245,7 @@ Update methods ``method`` to ``pip``, the Software Update plugin is instructed to use that as update method. - .. code-block:: + .. code-block:: yaml plugins: softwareupdate: From 69f9c523f9567df9948d86fda68be4756b4718ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Thu, 9 Nov 2017 10:49:39 +0100 Subject: [PATCH 094/333] Add utm tags to announcement links --- .../plugins/announcements/__init__.py | 5 ++- src/octoprint/util/__init__.py | 40 +++++++++++++++++-- tests/util/test_misc.py | 16 ++++++++ 3 files changed, 57 insertions(+), 4 deletions(-) diff --git a/src/octoprint/plugins/announcements/__init__.py b/src/octoprint/plugins/announcements/__init__.py index c064d860f7..e9a83bd1c4 100644 --- a/src/octoprint/plugins/announcements/__init__.py +++ b/src/octoprint/plugins/announcements/__init__.py @@ -22,7 +22,9 @@ from octoprint.server import admin_permission from octoprint.server.util.flask import restricted_access, with_revalidation_checking, check_etag +from octoprint.util import utmify from flask.ext.babel import gettext +from octoprint import __version__ as OCTOPRINT_VERSION class AnnouncementPlugin(octoprint.plugin.AssetPlugin, octoprint.plugin.SettingsPlugin, @@ -173,6 +175,7 @@ def etag(): hash = hashlib.sha1() hash.update(repr(sorted(enabled))) hash.update(repr(sorted(forced))) + hash.update(OCTOPRINT_VERSION) for channel in sorted(channel_configs.keys()): hash.update(repr(channel_configs[channel])) @@ -386,7 +389,7 @@ def _to_internal_entry(self, entry, read_until=None): summary=_lazy_images(entry["summary"]), summary_without_images=_strip_images(entry["summary"]), published=published, - link=entry["link"], + link=utmify(entry["link"], source="octoprint", medium="announcements", content=OCTOPRINT_VERSION), read=read) def _get_channel_cache_path(self, key): diff --git a/src/octoprint/util/__init__.py b/src/octoprint/util/__init__.py index 08fcc48883..f09f129176 100644 --- a/src/octoprint/util/__init__.py +++ b/src/octoprint/util/__init__.py @@ -875,14 +875,14 @@ def is_hidden_path(path): # no glob.escape - we need to implement our own _glob_escape_check = re.compile("([*?[])") _glob_escape_check_bytes = re.compile(b"([*?[])") - + def glob_escape(pathname): """ Ported from Python 3.4 - + See https://github.com/python/cpython/commit/fd32fffa5ada8b8be8a65bd51b001d989f99a3d3 """ - + drive, pathname = os.path.splitdrive(pathname) if isinstance(pathname, bytes): pathname = _glob_escape_check_bytes.sub(br"[\1]", pathname) @@ -900,6 +900,40 @@ def glob_escape(pathname): monotonic_time = time.time +def utmify(link, source=None, medium=None, name=None, term=None, content=None): + if source is None: + return link + + from collections import OrderedDict + try: + import urlparse + from urllib import urlencode + except ImportError: + # python 3 + import urllib.parse as urlparse + from urllib.parse import urlencode + + # inspired by https://stackoverflow.com/a/2506477 + parts = list(urlparse.urlparse(link)) + + # parts[4] is the url query + query = OrderedDict(urlparse.parse_qs(parts[4])) + + query["utm_source"] = source + if medium is not None: + query["utm_medium"] = medium + if name is not None: + query["utm_name"] = name + if term is not None: + query["utm_term"] = term + if content is not None: + query["utm_content"] = content + + parts[4] = urlencode(query, doseq=True) + + return urlparse.urlunparse(parts) + + class RepeatedTimer(threading.Thread): """ This class represents an action that should be run repeatedly in an interval. It is similar to python's diff --git a/tests/util/test_misc.py b/tests/util/test_misc.py index 644654b761..791753a6f4 100644 --- a/tests/util/test_misc.py +++ b/tests/util/test_misc.py @@ -6,9 +6,11 @@ import unittest +import ddt import octoprint.util +@ddt.ddt class MiscTestCase(unittest.TestCase): def test_get_class(self): @@ -29,3 +31,17 @@ def test_get_class_wrongclass(self): except ImportError: # success pass + + @ddt.data( + ("http://example.com", dict(source="source"), "http://example.com?utm_source=source"), + ("http://example.com?q=1", dict(source="source"), "http://example.com?q=1&utm_source=source"), + ("http://example.com", dict(source="source", medium="medium"), "http://example.com?utm_source=source&utm_medium=medium"), + ("http://example.com", dict(source="source", medium="medium", content="content with spaces"), "http://example.com?utm_source=source&utm_medium=medium&utm_content=content+with+spaces"), + + # no handling + ("http://example.com", dict(), "http://example.com"), + ) + @ddt.unpack + def test_utmify(self, link, kwargs, expected): + actual = octoprint.util.utmify(link, **kwargs) + self.assertEqual(actual, expected) From 651a8f315b87ec08cfcd4cc0a4f2d1c909e467fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Fri, 10 Nov 2017 10:50:25 +0100 Subject: [PATCH 095/333] Strip query params from displayed link URLs Looks to chaotic otherwise. --- .../plugins/announcements/static/js/announcements.js | 12 ++++++++++++ .../announcements/templates/announcements.jinja2 | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/octoprint/plugins/announcements/static/js/announcements.js b/src/octoprint/plugins/announcements/static/js/announcements.js index dd31096ee8..ee34b32c00 100644 --- a/src/octoprint/plugins/announcements/static/js/announcements.js +++ b/src/octoprint/plugins/announcements/static/js/announcements.js @@ -54,6 +54,18 @@ $(function() { return !data.forced; }; + self.cleanedLink = function(data) { + // Strips any query parameters from the link and returns it + var link = data.link; + if (!link) return link; + + var queryPos = link.indexOf("?"); + if (queryPos !== -1) { + link = link.substr(0, queryPos); + } + return link; + }; + self.markRead = function(channel, until) { if (!self.loginState.isAdmin()) return; diff --git a/src/octoprint/plugins/announcements/templates/announcements.jinja2 b/src/octoprint/plugins/announcements/templates/announcements.jinja2 index 0560c8ebc8..524e8850a9 100644 --- a/src/octoprint/plugins/announcements/templates/announcements.jinja2 +++ b/src/octoprint/plugins/announcements/templates/announcements.jinja2 @@ -27,7 +27,7 @@

From 80bc82df55082cc48cfa714eee9257bd1744756c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Fri, 10 Nov 2017 11:07:02 +0100 Subject: [PATCH 096/333] Protect against broken packages in python env As seen in https://groups.google.com/forum/#!msg/octoprint/DyXdqhR0U7c/kKMUsMmIBgAJ a broken entry_points.txt in some arbitrary Python package installed in the same python envrionment as OctoPrint can make our whole plugin detection fail and hence interrupt regular server startup. This adds better protection against such cases. --- src/octoprint/plugin/core.py | 186 ++++++++++++++++++++--------------- 1 file changed, 108 insertions(+), 78 deletions(-) diff --git a/src/octoprint/plugin/core.py b/src/octoprint/plugin/core.py index d8a58e9e90..4231b56cc0 100644 --- a/src/octoprint/plugin/core.py +++ b/src/octoprint/plugin/core.py @@ -551,53 +551,69 @@ def find_plugins(self, existing=None, ignore_uninstalled=True): result = OrderedDict() if self.plugin_folders: - result.update(self._find_plugins_from_folders(self.plugin_folders, existing, ignored_uninstalled=ignore_uninstalled)) + try: + result.update(self._find_plugins_from_folders(self.plugin_folders, + existing, + ignored_uninstalled=ignore_uninstalled)) + except: + self.logger.exception("Error fetching plugins from folders") if self.plugin_entry_points: existing.update(result) - result.update(self._find_plugins_from_entry_points(self.plugin_entry_points, existing, ignore_uninstalled=ignore_uninstalled)) + try: + result.update(self._find_plugins_from_entry_points(self.plugin_entry_points, + existing, + ignore_uninstalled=ignore_uninstalled)) + except: + self.logger.exception("Error fetching plugins from entry points") return result def _find_plugins_from_folders(self, folders, existing, ignored_uninstalled=True): result = OrderedDict() for folder in folders: - flagged_readonly = False - if isinstance(folder, (list, tuple)): - if len(folder) == 2: - folder, flagged_readonly = folder - else: - continue - actual_readonly = not os.access(folder, os.W_OK) - - if not os.path.exists(folder): - self.logger.warn("Plugin folder {folder} could not be found, skipping it".format(folder=folder)) - continue - - for entry in scandir(folder): - if entry.is_dir() and os.path.isfile(os.path.join(entry.path, "__init__.py")): - key = entry.name - elif entry.is_file() and entry.name.endswith(".py"): - key = entry.name[:-3] # strip off the .py extension - if key.startswith("__"): - # might be an __init__.py in our plugins folder, or something else we don't want - # to handle + try: + flagged_readonly = False + if isinstance(folder, (list, tuple)): + if len(folder) == 2: + folder, flagged_readonly = folder + else: continue - else: - continue + actual_readonly = not os.access(folder, os.W_OK) - if key in existing or key in result or (ignored_uninstalled and key in self.marked_plugins["uninstalled"]): - # plugin is already defined, ignore it + if not os.path.exists(folder): + self.logger.warn("Plugin folder {folder} could not be found, skipping it".format(folder=folder)) continue - plugin = self._import_plugin_from_module(key, folder=folder) - if plugin: - plugin.origin = FolderOrigin("folder", folder) - plugin.managable = not flagged_readonly and not actual_readonly - plugin.bundled = flagged_readonly - - plugin.enabled = False - - result[key] = plugin + for entry in scandir(folder): + try: + if entry.is_dir() and os.path.isfile(os.path.join(entry.path, "__init__.py")): + key = entry.name + elif entry.is_file() and entry.name.endswith(".py"): + key = entry.name[:-3] # strip off the .py extension + if key.startswith("__"): + # might be an __init__.py in our plugins folder, or something else we don't want + # to handle + continue + else: + continue + + if key in existing or key in result or (ignored_uninstalled and key in self.marked_plugins["uninstalled"]): + # plugin is already defined, ignore it + continue + + plugin = self._import_plugin_from_module(key, folder=folder) + if plugin: + plugin.origin = FolderOrigin("folder", folder) + plugin.managable = not flagged_readonly and not actual_readonly + plugin.bundled = flagged_readonly + + plugin.enabled = False + + result[key] = plugin + except: + self.logger.exception("Error processing folder entry {!r} from folder {}".format(entry, folder)) + except: + self.logger.exception("Error processing folder {}".format(folder)) return result @@ -619,51 +635,65 @@ def _find_plugins_from_entry_points(self, groups, existing, ignore_uninstalled=T if not isinstance(groups, (list, tuple)): groups = [groups] + def wrapped(gen): + # to protect against some issues in installed packages that make iteration over entry points + # fall on its face - e.g. https://groups.google.com/forum/#!msg/octoprint/DyXdqhR0U7c/kKMUsMmIBgAJ + try: + yield next(gen) + except StopIteration: + raise + except: + self.logger.exception("Something went wrong while processing the entry points of a package in the " + "Python environment - broken entry_points.txt in some package?") + for group in groups: - for entry_point in working_set.iter_entry_points(group=group, name=None): - key = entry_point.name - module_name = entry_point.module_name - version = entry_point.dist.version + for entry_point in wrapped(working_set.iter_entry_points(group=group, name=None)): + try: + key = entry_point.name + module_name = entry_point.module_name + version = entry_point.dist.version - if key in existing or key in result or (ignore_uninstalled and key in self.marked_plugins["uninstalled"]): - # plugin is already defined or marked as uninstalled, ignore it - continue + if key in existing or key in result or (ignore_uninstalled and key in self.marked_plugins["uninstalled"]): + # plugin is already defined or marked as uninstalled, ignore it + continue - kwargs = dict(module_name=module_name, version=version) - package_name = None - try: - module_pkginfo = InstalledEntryPoint(entry_point) + kwargs = dict(module_name=module_name, version=version) + package_name = None + try: + module_pkginfo = InstalledEntryPoint(entry_point) + except: + self.logger.exception("Something went wrong while retrieving package info data for module %s" % module_name) + else: + kwargs.update(dict( + name=module_pkginfo.name, + summary=module_pkginfo.summary, + author=module_pkginfo.author, + url=module_pkginfo.home_page, + license=module_pkginfo.license + )) + package_name = module_pkginfo.name + + plugin = self._import_plugin_from_module(key, **kwargs) + if plugin: + plugin.origin = EntryPointOrigin("entry_point", group, module_name, package_name, version) + + # plugin is manageable if its location is writable and OctoPrint + # is either not running from a virtual env or the plugin is + # installed in that virtual env - the virtual env's pip will not + # allow us to uninstall stuff that is installed outside + # of the virtual env, so this check is necessary + plugin.managable = os.access(plugin.location, os.W_OK) \ + and (not self._python_virtual_env + or is_sub_path_of(plugin.location, self._python_prefix) + or is_editable_install(self._python_install_dir, + package_name, + module_name, + plugin.location)) + + plugin.enabled = False + result[key] = plugin except: - self.logger.exception("Something went wrong while retrieving package info data for module %s" % module_name) - else: - kwargs.update(dict( - name=module_pkginfo.name, - summary=module_pkginfo.summary, - author=module_pkginfo.author, - url=module_pkginfo.home_page, - license=module_pkginfo.license - )) - package_name = module_pkginfo.name - - plugin = self._import_plugin_from_module(key, **kwargs) - if plugin: - plugin.origin = EntryPointOrigin("entry_point", group, module_name, package_name, version) - - # plugin is manageable if its location is writable and OctoPrint - # is either not running from a virtual env or the plugin is - # installed in that virtual env - the virtual env's pip will not - # allow us to uninstall stuff that is installed outside - # of the virtual env, so this check is necessary - plugin.managable = os.access(plugin.location, os.W_OK) \ - and (not self._python_virtual_env - or is_sub_path_of(plugin.location, self._python_prefix) - or is_editable_install(self._python_install_dir, - package_name, - module_name, - plugin.location)) - - plugin.enabled = False - result[key] = plugin + self.logger.exception("Error processing entry point {!r} for group {}".format(entry_point, group)) return result @@ -717,10 +747,10 @@ def matches_plugin(entry): entry_key, entry_version = entry return entry_key == key and entry_version == version return False - + return any(map(lambda entry: matches_plugin(entry), self.plugin_blacklist)) - + def reload_plugins(self, startup=False, initialize_implementations=True, force_reload=None): self.logger.info("Loading plugins from {folders} and installed plugin packages...".format( folders=", ".join(map(lambda x: x[0] if isinstance(x, tuple) else str(x), self.plugin_folders)) From a6eac3911b822624f4e4da7317b3bf1b054bd444 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Fri, 10 Nov 2017 12:26:07 +0100 Subject: [PATCH 097/333] Reset temperature offsets on disconnect Fixes #2203 --- src/octoprint/printer/standard.py | 16 ++++++++++++---- .../static/js/app/viewmodels/temperature.js | 4 ++++ 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/octoprint/printer/standard.py b/src/octoprint/printer/standard.py index cd94590c2f..fa82aebf91 100644 --- a/src/octoprint/printer/standard.py +++ b/src/octoprint/printer/standard.py @@ -117,7 +117,8 @@ def __init__(self, fileManager, analysisQueue, printerProfileManager): } }, progress={"completion": None, "filepos": None, "printTime": None, "printTimeLeft": None}, - current_z=None + current_z=None, + offsets=dict() ) eventManager().subscribe(Events.METADATA_ANALYSIS_FINISHED, self._on_event_MetadataAnalysisFinished) @@ -366,7 +367,7 @@ def set_temperature_offset(self, offsets=None): return self._comm.setTemperatureOffset(offsets) - self._stateMonitor.set_temp_offsets(offsets) + self._setOffsets(offsets) def _convert_rate_value(self, factor, min=0, max=200): if not isinstance(factor, (int, float, long)): @@ -633,6 +634,9 @@ def refresh_sd_files(self, blocking=False): #~~ state monitoring + def _setOffsets(self, offsets): + self._stateMonitor.set_temp_offsets(offsets) + def _setCurrentZ(self, currentZ): self._currentZ = currentZ self._stateMonitor.set_current_z(self._currentZ) @@ -1032,6 +1036,7 @@ def log_print(): self._updateProgressData() self._setCurrentZ(None) self._setJobData(None, None, None) + self._setOffsets(None) self._printerProfileManager.deselect() eventManager().fire(Events.DISCONNECTED) @@ -1266,7 +1271,7 @@ def __init__(self, interval=0.5, on_update=None, on_add_temperature=None, on_add self._state = None self._job_data = None self._current_z = None - self._offsets = {} + self._offsets = dict() self._progress = None self._progress_dirty = False @@ -1285,11 +1290,12 @@ def _get_current_progress(self): return self._on_get_progress() return self._progress - def reset(self, state=None, job_data=None, progress=None, current_z=None): + def reset(self, state=None, job_data=None, progress=None, current_z=None, offsets=None): self.set_state(state) self.set_job_data(job_data) self.set_progress(progress) self.set_current_z(current_z) + self.set_temp_offsets(offsets) def add_temperature(self, temperature): self._on_add_temperature(temperature) @@ -1328,6 +1334,8 @@ def set_progress(self, progress): self._change_event.set() def set_temp_offsets(self, offsets): + if offsets is None: + offsets = dict() self._offsets = offsets self._change_event.set() diff --git a/src/octoprint/static/js/app/viewmodels/temperature.js b/src/octoprint/static/js/app/viewmodels/temperature.js index 493948355f..ef9704cf54 100644 --- a/src/octoprint/static/js/app/viewmodels/temperature.js +++ b/src/octoprint/static/js/app/viewmodels/temperature.js @@ -237,11 +237,15 @@ $(function() { for (var i = 0; i < tools.length; i++) { if (data.hasOwnProperty("tool" + i)) { tools[i]["offset"](data["tool" + i]); + } else { + tools[i]["offset"](0); } } if (data.hasOwnProperty("bed")) { self.bedTemp["offset"](data["bed"]); + } else { + self.bedTemp["offset"](0); } }; From 92b048c7892163c007b76903595c966b90e8a843 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Fri, 10 Nov 2017 13:03:36 +0100 Subject: [PATCH 098/333] Reset temperature data on disconnect --- src/octoprint/printer/standard.py | 24 ++++++----- .../static/js/app/viewmodels/temperature.js | 41 ++++++++++--------- 2 files changed, 36 insertions(+), 29 deletions(-) diff --git a/src/octoprint/printer/standard.py b/src/octoprint/printer/standard.py index fa82aebf91..1976b750d1 100644 --- a/src/octoprint/printer/standard.py +++ b/src/octoprint/printer/standard.py @@ -832,27 +832,30 @@ def _estimatePrintTimeLeft(self, progress, printTime, cleanedPrintTime, statisti return printTimeLeft, printTimeLeftOrigin - def _addTemperatureData(self, temp, bedTemp): + def _addTemperatureData(self, tools=None, bed=None): + if tools is None: + tools = dict() + currentTimeUtc = int(time.time()) data = { "time": currentTimeUtc } - for tool in temp.keys(): + for tool in tools.keys(): data["tool%d" % tool] = { - "actual": temp[tool][0], - "target": temp[tool][1] + "actual": tools[tool][0], + "target": tools[tool][1] } - if bedTemp is not None and isinstance(bedTemp, tuple): + if bed is not None and isinstance(bed, tuple): data["bed"] = { - "actual": bedTemp[0], - "target": bedTemp[1] + "actual": bed[0], + "target": bed[1] } self._temps.append(data) - self._temp = temp - self._bedTemp = bedTemp + self._temp = tools + self._bedTemp = bed self._stateMonitor.add_temperature(data) @@ -992,7 +995,7 @@ def on_comm_log(self, message): self._addLog(to_unicode(message, "utf-8", errors="replace")) def on_comm_temperature_update(self, temp, bedTemp): - self._addTemperatureData(copy.deepcopy(temp), copy.deepcopy(bedTemp)) + self._addTemperatureData(tools=copy.deepcopy(temp), bed=copy.deepcopy(bedTemp)) def on_comm_position_update(self, position, reason=None): payload = dict(reason=reason) @@ -1037,6 +1040,7 @@ def log_print(): self._setCurrentZ(None) self._setJobData(None, None, None) self._setOffsets(None) + self._addTemperatureData() self._printerProfileManager.deselect() eventManager().fire(Events.DISCONNECTED) diff --git a/src/octoprint/static/js/app/viewmodels/temperature.js b/src/octoprint/static/js/app/viewmodels/temperature.js index ef9704cf54..2b2db5ac35 100644 --- a/src/octoprint/static/js/app/viewmodels/temperature.js +++ b/src/octoprint/static/js/app/viewmodels/temperature.js @@ -41,7 +41,7 @@ $(function() { }); entry.offset.subscribe(function(newValue) { - if (self.changingOffset.item !== undefined && self.changingOffset.item.key() == entry.key()) { + if (self.changingOffset.item !== undefined && self.changingOffset.item.key() === entry.key()) { // if our we currently have the offset dialog open for this entry and the offset changed // meanwhile, update the displayed value in the dialog self.changingOffset.offset(newValue); @@ -115,7 +115,7 @@ $(function() { tools[extruder]["name"](gettext("Tool") + " " + extruder); tools[extruder]["key"]("tool" + extruder); } - } else if (numExtruders == 1 || sharedNozzle) { + } else if (numExtruders === 1 || sharedNozzle) { // only one extruder, no need to add numbers color = graphColors[0]; heaterOptions["tool0"] = {name: "T", color: color}; @@ -200,7 +200,7 @@ $(function() { }; self._processTemperatureUpdateData = function(serverTime, data) { - if (data.length == 0) + if (data.length === 0) return; var lastData = data[data.length - 1]; @@ -219,6 +219,9 @@ $(function() { if (lastData.hasOwnProperty("bed")) { self.bedTemp["actual"](lastData.bed.actual); self.bedTemp["target"](lastData.bed.target); + } else { + self.bedTemp["actual"](0); + self.bedTemp["target"](0); } if (!CONFIG_TEMPERATURE_GRAPH) return; @@ -278,7 +281,7 @@ $(function() { }); var temperature_cutoff = self.temperature_cutoff(); - if (temperature_cutoff != undefined) { + if (temperature_cutoff !== undefined) { var filterOld = function(item) { return item[0] >= clientTime - temperature_cutoff * 60 * 1000; }; @@ -484,7 +487,7 @@ $(function() { self.incrementTarget = function(item) { var value = item.newTarget(); - if (value === undefined || (typeof(value) == "string" && value.trim() == "")) { + if (value === undefined || (typeof(value) === "string" && value.trim() === "")) { value = item.target(); } try { @@ -499,7 +502,7 @@ $(function() { self.decrementTarget = function(item) { var value = item.newTarget(); - if (value === undefined || (typeof(value) == "string" && value.trim() == "")) { + if (value === undefined || (typeof(value) === "string" && value.trim() === "")) { value = item.target(); } try { @@ -541,7 +544,7 @@ $(function() { if (form !== undefined) { $(form).find("input").blur(); } - if (value === undefined || (typeof(value) == "string" && value.trim() == "")) return OctoPrintClient.createRejectedDeferred(); + if (value === undefined || (typeof(value) === "string" && value.trim() === "")) return OctoPrintClient.createRejectedDeferred(); self.clearAutosendTarget(item); return self.setTargetToValue(item, value); @@ -551,7 +554,7 @@ $(function() { if (!profile) return OctoPrintClient.createRejectedDeferred(); self.clearAutosendTarget(item); - return self.setTargetToValue(item, (item.key() == "bed" ? profile.bed : profile.extruder)); + return self.setTargetToValue(item, (item.key() === "bed" ? profile.bed : profile.extruder)); }; self.setTargetToZero = function(item) { @@ -575,7 +578,7 @@ $(function() { item.newTarget(""); }; - if (item.key() == "bed") { + if (item.key() === "bed") { return self._setBedTemperature(value) .done(onSuccess); } else { @@ -596,7 +599,7 @@ $(function() { self.incrementChangeOffset = function() { var value = self.changingOffset.newOffset(); - if (value === undefined || (typeof(value) == "string" && value.trim() == "")) value = self.changingOffset.offset(); + if (value === undefined || (typeof(value) === "string" && value.trim() === "")) value = self.changingOffset.offset(); try { value = parseInt(value); if (value >= 50) return; @@ -608,7 +611,7 @@ $(function() { self.decrementChangeOffset = function() { var value = self.changingOffset.newOffset(); - if (value === undefined || (typeof(value) == "string" && value.trim() == "")) value = self.changingOffset.offset(); + if (value === undefined || (typeof(value) === "string" && value.trim() === "")) value = self.changingOffset.offset(); try { value = parseInt(value); if (value <= -50) return; @@ -640,7 +643,7 @@ $(function() { self.setOffset = function(item) { var value = item.newOffset(); - if (value === undefined || (typeof(value) == "string" && value.trim() == "")) return OctoPrintClient.createRejectedDeferred(); + if (value === undefined || (typeof(value) === "string" && value.trim() === "")) return OctoPrintClient.createRejectedDeferred(); return self.setOffsetToValue(item, value); }; @@ -662,7 +665,7 @@ $(function() { item.newOffset(""); }; - if (item.key() == "bed") { + if (item.key() === "bed") { return self._setBedOffset(value) .done(onSuccess); } else { @@ -708,26 +711,26 @@ $(function() { }; self.handleEnter = function(event, type, item) { - if (event.keyCode == 13) { - if (type == "target") { + if (event.keyCode === 13) { + if (type === "target") { self.setTarget(item) .done(function() { event.target.blur(); }); - } else if (type == "offset") { + } else if (type === "offset") { self.confirmChangeOffset(); } } }; self.handleFocus = function(event, type, item) { - if (type == "target") { + if (type === "target") { var value = item.newTarget(); - if (value === undefined || (typeof(value) == "string" && value.trim() == "")) { + if (value === undefined || (typeof(value) === "string" && value.trim() === "")) { item.newTarget(item.target()); } event.target.select(); - } else if (type == "offset") { + } else if (type === "offset") { event.target.select(); } }; From 0bb343e1d3fd32dd6b68accf84f78748bed68a57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Fri, 10 Nov 2017 13:25:16 +0100 Subject: [PATCH 099/333] Sort timelapses by mtime instead of ctime Otherwise we get bad sorting after copying over the files from another instance (e.g. after backup/restore as described on the wiki). --- .../static/js/app/viewmodels/timelapse.js | 2 +- src/octoprint/templates/tabs/timelapse.jinja2 | 2 +- src/octoprint/timelapse.py | 32 +++++++++---------- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/octoprint/static/js/app/viewmodels/timelapse.js b/src/octoprint/static/js/app/viewmodels/timelapse.js index 1bd1299159..a2b65374e1 100644 --- a/src/octoprint/static/js/app/viewmodels/timelapse.js +++ b/src/octoprint/static/js/app/viewmodels/timelapse.js @@ -93,7 +93,7 @@ $(function() { if (a["name"].toLocaleLowerCase() > b["name"].toLocaleLowerCase()) return 1; return 0; }, - "creation": function(a, b) { + "date": function(a, b) { // sorts descending if (a["date"] > b["date"]) return -1; if (a["date"] < b["date"]) return 1; diff --git a/src/octoprint/templates/tabs/timelapse.jinja2 b/src/octoprint/templates/tabs/timelapse.jinja2 index 82e6dc3608..3c2cbf4952 100644 --- a/src/octoprint/templates/tabs/timelapse.jinja2 +++ b/src/octoprint/templates/tabs/timelapse.jinja2 @@ -106,7 +106,7 @@
diff --git a/src/octoprint/timelapse.py b/src/octoprint/timelapse.py index d117caebb2..dcd2708ded 100644 --- a/src/octoprint/timelapse.py +++ b/src/octoprint/timelapse.py @@ -92,7 +92,7 @@ def get_finished_timelapses(): "name": entry.name, "size": util.get_formatted_size(entry.stat().st_size), "bytes": entry.stat().st_size, - "date": util.get_formatted_datetime(datetime.datetime.fromtimestamp(entry.stat().st_ctime)) + "date": util.get_formatted_datetime(datetime.datetime.fromtimestamp(entry.stat().st_mtime)) }) return files @@ -116,8 +116,8 @@ def get_unrendered_timelapses(): jobs[prefix]["count"] += 1 jobs[prefix]["bytes"] += entry.stat().st_size - if jobs[prefix]["timestamp"] is None or entry.stat().st_ctime < jobs[prefix]["timestamp"]: - jobs[prefix]["timestamp"] = entry.stat().st_ctime + if jobs[prefix]["timestamp"] is None or entry.stat().st_mtime < jobs[prefix]["timestamp"]: + jobs[prefix]["timestamp"] = entry.stat().st_mtime with _job_lock: global current_render_job @@ -140,7 +140,7 @@ def finalize_fields(prefix, job): def delete_unrendered_timelapse(name): global _cleanup_lock - + pattern = "{}*.jpg".format(util.glob_escape(name)) basedir = settings().getBaseFolder("timelapse_tmp") @@ -290,7 +290,7 @@ def configure_timelapse(config=None, persist=False): ffmpeg_path = settings().get(["webcam", "ffmpeg"]) timelapse_precondition = snapshot_url is not None and snapshot_url.strip() != "" \ and ffmpeg_path is not None and ffmpeg_path.strip() != "" - + type = config["type"] if not timelapse_precondition and type is not None and type != "off": logging.getLogger(__name__).warn("Essential timelapse settings unconfigured (snapshot URL or FFMPEG path) " @@ -298,7 +298,7 @@ def configure_timelapse(config=None, persist=False): "in the config as well.") type = "off" config["type"] = "off" - + if not persist: # make sure we persist at least that timelapse is now disabled by default - we don't want the above # warning to log @@ -312,31 +312,31 @@ def configure_timelapse(config=None, persist=False): postRoll = 0 if "postRoll" in config and config["postRoll"] >= 0: postRoll = config["postRoll"] - + fps = 25 if "fps" in config and config["fps"] > 0: fps = config["fps"] - + if "zchange" == type: retractionZHop = 0 if "options" in config and "retractionZHop" in config["options"] and config["options"]["retractionZHop"] >= 0: retractionZHop = config["options"]["retractionZHop"] - + minDelay = 5 if "options" in config and "minDelay" in config["options"] and config["options"]["minDelay"] > 0: minDelay = config["options"]["minDelay"] - + current = ZTimelapse(post_roll=postRoll, retraction_zhop=retractionZHop, min_delay=minDelay, fps=fps) - + elif "timed" == type: interval = 10 if "options" in config and "interval" in config["options"] and config["options"]["interval"] > 0: interval = config["options"]["interval"] - + capture_post_roll = True if "options" in config and "capturePostRoll" in config["options"] and isinstance(config["options"]["capturePostRoll"], bool): capture_post_roll = config["options"]["capturePostRoll"] - + current = TimedTimelapse(post_roll=postRoll, interval=interval, fps=fps, capture_post_roll=capture_post_roll) notify_callbacks(current) @@ -630,10 +630,10 @@ def clean_capture_dir(self): class ZTimelapse(Timelapse): def __init__(self, retraction_zhop=0, min_delay=5.0, post_roll=0, fps=25): Timelapse.__init__(self, post_roll=post_roll, fps=fps) - + if min_delay < 0: min_delay = 0 - + self._retraction_zhop = retraction_zhop self._min_delay = min_delay self._last_snapshot = None @@ -642,7 +642,7 @@ def __init__(self, retraction_zhop=0, min_delay=5.0, post_roll=0, fps=25): @property def retraction_zhop(self): return self._retraction_zhop - + @property def min_delay(self): return self._min_delay From b2d70de14412d356903cfdf269ce67aecd2d9701 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Mon, 6 Nov 2017 17:11:04 +0100 Subject: [PATCH 100/333] Environment detection & logging on startup Incl. OctoPi version & RPi model through bundled plugin that only gets loaded if OctoPi is detected. --- setup.py | 2 +- src/octoprint/__init__.py | 14 +- src/octoprint/cli/server.py | 27 +-- src/octoprint/environment.py | 133 +++++++++++++++ src/octoprint/plugin/__init__.py | 3 +- src/octoprint/plugin/types.py | 10 ++ .../plugins/octopi_support/__init__.py | 154 ++++++++++++++++++ .../static/css/octopi_support.css | 3 + .../static/js/octopi_support.js | 53 ++++++ .../templates/octopi_support_about.jinja2 | 41 +++++ .../plugins/pluginmanager/__init__.py | 13 +- src/octoprint/server/__init__.py | 12 +- src/octoprint/settings.py | 2 +- src/octoprint/static/css/octoprint.css | 2 +- src/octoprint/static/less/octoprint.less | 5 + src/octoprint/templates/index.jinja2 | 10 +- src/octoprint/util/platform/__init__.py | 16 ++ tests/plugins/test_pluginmanager.py | 17 -- tests/util/test_platform.py | 30 ++++ 19 files changed, 494 insertions(+), 53 deletions(-) create mode 100644 src/octoprint/environment.py create mode 100644 src/octoprint/plugins/octopi_support/__init__.py create mode 100644 src/octoprint/plugins/octopi_support/static/css/octopi_support.css create mode 100644 src/octoprint/plugins/octopi_support/static/js/octopi_support.js create mode 100644 src/octoprint/plugins/octopi_support/templates/octopi_support_about.jinja2 create mode 100644 tests/util/test_platform.py diff --git a/setup.py b/setup.py index 85b66aa483..dd0ec9b6cb 100644 --- a/setup.py +++ b/setup.py @@ -40,7 +40,7 @@ "pkginfo>=1.2.1,<1.3", "requests>=2.18.4,<3", "semantic_version>=2.4.2,<2.5", - "psutil>=5.4.0,<6", + "psutil>=5.4.1,<6", "Click>=6.2,<6.3", "awesome-slugify>=1.6.5,<1.7", "feedparser>=5.2.1,<5.3", diff --git a/src/octoprint/__init__.py b/src/octoprint/__init__.py index 765fa308fa..7a85c48793 100644 --- a/src/octoprint/__init__.py +++ b/src/octoprint/__init__.py @@ -60,7 +60,7 @@ def init_platform(basedir, configfile, use_logging_file=True, logging_file=None, uncaught_handler=None, safe_mode=False, ignore_blacklist=False, after_preinit_logging=None, after_settings=None, after_logging=None, after_safe_mode=None, after_event_manager=None, after_connectivity_checker=None, - after_plugin_manager=None): + after_plugin_manager=None, after_environment_detector=None): kwargs = dict() logger, recorder = preinit_logging(debug, verbosity, uncaught_logger, uncaught_handler) @@ -115,8 +115,14 @@ def init_platform(basedir, configfile, use_logging_file=True, logging_file=None, if callable(after_plugin_manager): after_plugin_manager(**kwargs) + + environment_detector = init_environment_detector(plugin_manager) + kwargs["environment_detector"] = environment_detector + + if callable(after_environment_detector): + after_environment_detector(**kwargs) - return settings, logger, safe_mode, event_manager, connectivity_checker, plugin_manager + return settings, logger, safe_mode, event_manager, connectivity_checker, plugin_manager, environment_detector def init_settings(basedir, configfile): @@ -522,6 +528,10 @@ def on_connectivity_change(old_value, new_value): return connectivityChecker +def init_environment_detector(plugin_manager): + from octoprint.environment import EnvironmentDetector + return EnvironmentDetector(plugin_manager) + #~~ server main method def main(): diff --git a/src/octoprint/cli/server.py b/src/octoprint/cli/server.py index 3adea9f5bf..a9d90ea08e 100644 --- a/src/octoprint/cli/server.py +++ b/src/octoprint/cli/server.py @@ -48,7 +48,7 @@ def log_startup(recorder=None, safe_mode=None, **kwargs): "https://urllib3.readthedocs.org/en/latest/security.html#openssl-pyopenssl") logger.info(get_divider_line("*")) - def log_register_rollover(safe_mode=None, plugin_manager=None, **kwargs): + def log_register_rollover(safe_mode=None, plugin_manager=None, environment_detector=None, **kwargs): from octoprint.logging import get_handler, log_to_handler, get_divider_line from octoprint.logging.handlers import OctoPrintLogHandler @@ -67,22 +67,24 @@ def _log(message, level=logging.INFO): if safe_mode: _log("SAFE MODE is active. Third party plugins are disabled!") plugin_manager.log_all_plugins(only_to_handler=handler) + environment_detector.log_detected_environment(only_to_handler=handler) _log(get_divider_line("-")) OctoPrintLogHandler.registerRolloverCallback(rollover_callback) try: - settings, _, safe_mode, event_manager, \ - connectivity_checker, plugin_manager = init_platform(basedir, - configfile, - logging_file=logging_config, - debug=debug, - verbosity=verbosity, - uncaught_logger=__name__, - safe_mode=safe_mode, - ignore_blacklist=ignore_blacklist, - after_safe_mode=log_startup, - after_plugin_manager=log_register_rollover) + components = init_platform(basedir, configfile, + logging_file=logging_config, + debug=debug, + verbosity=verbosity, + uncaught_logger=__name__, + safe_mode=safe_mode, + ignore_blacklist=ignore_blacklist, + after_safe_mode=log_startup, + after_plugin_manager=log_register_rollover) + + settings, _, safe_mode, event_manager, connectivity_checker, plugin_manager, environment_detector = components + except FatalStartupError as e: click.echo(e.message, err=True) click.echo("There was a fatal error starting up OctoPrint.", err=True) @@ -92,6 +94,7 @@ def _log(message, level=logging.INFO): plugin_manager=plugin_manager, event_manager=event_manager, connectivity_checker=connectivity_checker, + environment_detector=environment_detector, host=host, port=port, debug=debug, diff --git a/src/octoprint/environment.py b/src/octoprint/environment.py new file mode 100644 index 0000000000..2da7bcc427 --- /dev/null +++ b/src/octoprint/environment.py @@ -0,0 +1,133 @@ +from __future__ import absolute_import + +import copy +import logging +import os +import platform +import sys +import threading +import yaml + +import psutil + +from octoprint.plugin import EnvironmentDetectionPlugin +from octoprint.util import get_formatted_size +from octoprint.util.platform import get_os + +class EnvironmentDetector(object): + + def __init__(self, plugin_manager): + self._plugin_manager = plugin_manager + + self._cache = None + self._cache_lock = threading.RLock() + + self._environment_plugins = self._plugin_manager.get_implementations(EnvironmentDetectionPlugin) + + self._logger = logging.getLogger(__name__) + + @property + def environment(self): + with self._cache_lock: + if self._cache is None: + self.run_detection() + return copy.deepcopy(self._cache) + + def run_detection(self, notify_plugins=True): + environment = dict() + environment["os"] = self._detect_os() + environment["python"] = self._detect_python() + environment["hardware"] = self._detect_hardware() + + plugin_result = self._detect_from_plugins() + if plugin_result: + environment["plugins"] = plugin_result + + with self._cache_lock: + self._cache = environment + + if notify_plugins: + self.notify_plugins() + + return environment + + def _detect_os(self): + return dict(id=get_os(), + platform=sys.platform) + + def _detect_python(self): + result = dict() + + # determine python version + result["version"] = platform.python_version() + + # determine if we are running from a virtual environment + if hasattr(sys, "real_prefix") or (hasattr(sys, "base_prefix") and os.path.realpath(sys.prefix) != os.path.realpath(sys.base_prefix)): + result["virtualenv"] = sys.prefix + + # try to find pip version + try: + import pip + result["pip"] = pip.__version__ + except: + result["pip"] = "unknown" + + return result + + def _detect_hardware(self): + return dict(cores=psutil.cpu_count(), + freq=psutil.cpu_freq().max, + ram=get_formatted_size(psutil.virtual_memory().total)) + + def _detect_from_plugins(self): + result = dict() + + for implementation in self._environment_plugins: + try: + additional = implementation.get_additional_environment() + if additional is not None and isinstance(additional, dict) and len(additional): + result[implementation._identifier] = additional + except: + self._logger.exception("Error while fetching additional " + "environment data from plugin {}".format(implementation._identifier)) + + return result + + def log_detected_environment(self, only_to_handler=None): + def _log(message, level=logging.INFO): + if only_to_handler is not None: + import octoprint.logging + octoprint.logging.log_to_handler(self._logger, only_to_handler, level, message, []) + else: + self._logger.log(level, message) + + _log(self._format()) + + def _format(self): + with self._cache_lock: + if self._cache is None: + self.run_detection() + environment = copy.deepcopy(self._cache) + + dumped_environment = yaml.safe_dump(environment, + default_flow_style=False, + indent=" ", + allow_unicode=True).strip() + environment_lines = "\n".join(map(lambda l: "| {}".format(l), dumped_environment.split("\n"))) + return u"Detected environment is Python {} under {} ({}). Details:\n{}".format(environment["python"]["version"], + environment["os"]["id"].title(), + environment["os"]["platform"], + environment_lines) + + def notify_plugins(self): + with self._cache_lock: + if self._cache is None: + self.run_detection(notify_plugins=False) + environment = copy.deepcopy(self._cache) + + for implementation in self._environment_plugins: + try: + implementation.on_environment_detected(environment) + except: + self._logger.exception("Error while sending environment " + "detection result to plugin {}".format(implementation._identifier)) diff --git a/src/octoprint/plugin/__init__.py b/src/octoprint/plugin/__init__.py index 114d2fe61f..8885ded56c 100644 --- a/src/octoprint/plugin/__init__.py +++ b/src/octoprint/plugin/__init__.py @@ -91,7 +91,8 @@ def plugin_manager(init=False, plugin_folders=None, plugin_types=None, plugin_en else: if init: if plugin_types is None: - plugin_types = [StartupPlugin, + plugin_types = [EnvironmentDetectionPlugin, + StartupPlugin, ShutdownPlugin, TemplatePlugin, SettingsPlugin, diff --git a/src/octoprint/plugin/types.py b/src/octoprint/plugin/types.py index 196a237619..ceb8916c11 100644 --- a/src/octoprint/plugin/types.py +++ b/src/octoprint/plugin/types.py @@ -105,6 +105,16 @@ class ReloadNeedingPlugin(Plugin): Mixin for plugin types that need a reload of the UI after enabling/disabling them. """ + +class EnvironmentDetectionPlugin(OctoPrintPlugin, RestartNeedingPlugin): + + def get_additional_environment(self): + pass + + def on_environment_detected(self, environment, *args, **kwargs): + pass + + class StartupPlugin(OctoPrintPlugin, SortablePlugin): """ The ``StartupPlugin`` allows hooking into the startup of OctoPrint. It can be used to start up additional services diff --git a/src/octoprint/plugins/octopi_support/__init__.py b/src/octoprint/plugins/octopi_support/__init__.py new file mode 100644 index 0000000000..0ffc6d1134 --- /dev/null +++ b/src/octoprint/plugins/octopi_support/__init__.py @@ -0,0 +1,154 @@ +# coding=utf-8 +from __future__ import absolute_import, division, print_function + +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' +__copyright__ = "Copyright (C) 2017 The OctoPrint Project - Released under terms of the AGPLv3 License" + +import flask +import os + +import octoprint.plugin + +_OCTOPI_VERSION_PATH = "/etc/octopi_version" +_CPUINFO_PATH = "/proc/cpuinfo" + +# based on https://elinux.org/RPi_HardwareHistory#Which_Pi_have_I_got.3F +_RPI_REVISION_MAP = { + "Beta": "1B (Beta)", + "0002": "1B", + "0003": "1B", + "0004": "1B", + "0005": "1B", + "0006": "1B", + "0007": "1A", + "0008": "1A", + "0009": "1A", + "000d": "1B", + "000e": "1B", + "000f": "1B", + "0010": "B+", + "0011": "CM1", + "0012": "A+", + "0013": "B+", + "0014": "CM1", + "0015": "A+", + "a01040": "2B", + "a01041": "2B", + "a21041": "2B", + "a22042": "2B", + "900021": "A+", + "900032": "B+", + "900092": "Zero", + "900093": "Zero", + "920093": "Zero", + "9000c1": "Zero W", + "a02082": "3B", + "a020a0": "CM3", + "a22082": "3B", + "a32082": "3B", +} + + +def get_octopi_version(): + with open(_OCTOPI_VERSION_PATH, "r") as f: + version_line = f.readline() + return version_line.strip() + + +def get_pi_revision(): + with open(_CPUINFO_PATH) as f: + for line in f: + if line and line.startswith("Revision:"): + return line[line.index(":") + 1:].strip() + return "unknown" + + +def get_pi_model(revision): + if revision.startswith("1000"): + # strip flag for over-volted (https://elinux.org/RPi_HardwareHistory#Which_Pi_have_I_got.3F) + revision = revision[4:] + return _RPI_REVISION_MAP.get(revision.lower(), "unknown") + + +class OctoPiSupportPlugin(octoprint.plugin.EnvironmentDetectionPlugin, + octoprint.plugin.SimpleApiPlugin, + octoprint.plugin.AssetPlugin, + octoprint.plugin.TemplatePlugin): + + def __init__(self): + self._version = None + self._revision = None + self._model = None + + #~~ EnvironmentDetectionPlugin + + def get_additional_environment(self): + return dict(version=self._get_version(), + revision=self._get_revision(), + model=self._get_model()) + + #~~ SimpleApiPlugin + + def on_api_get(self, request): + return flask.jsonify(version=self._get_version(), + revision=self._get_revision(), + model=self._get_model()) + + #~~ AssetPlugin + + def get_assets(self): + return dict( + js=["js/octopi_support.js"], + css=["css/octopi_support.css"] + ) + + #~~ TemplatePlugin + + def get_template_configs(self): + return [ + dict(type="about", name="About OctoPi", template="octopi_support_about.jinja2") + ] + + def get_template_vars(self): + version = self._get_version() + revision = self._get_revision() + model = self._get_model() + + return dict(version=version, + rpi=(revision is not None and revision != "unknown" and model is not None and model != "unknown"), + rpi_revision=revision, + rpi_model=model) + + #~~ Helpers + + def _get_version(self): + if self._version is None: + try: + self._version = get_octopi_version() + except: + self._logger.exception("Error while reading OctoPi version from file {}".format(_OCTOPI_VERSION_PATH)) + return self._version + + def _get_model(self): + if self._model is None: + try: + self._model = get_pi_model(self._get_revision()) + except: + self._logger.exception("Error while detecting RPi model") + return self._model + + def _get_revision(self): + if self._revision is None: + try: + self._revision = get_pi_revision() + except: + self._logger.exception("Error while detecting RPi revision") + return self._revision + +def __plugin_check__(): + from octoprint.util.platform import get_os + return get_os() == "linux" and os.path.exists(_OCTOPI_VERSION_PATH) + +def __plugin_load__(): + global __plugin_implementation__ + __plugin_implementation__ = OctoPiSupportPlugin() diff --git a/src/octoprint/plugins/octopi_support/static/css/octopi_support.css b/src/octoprint/plugins/octopi_support/static/css/octopi_support.css new file mode 100644 index 0000000000..61b3a10efe --- /dev/null +++ b/src/octoprint/plugins/octopi_support/static/css/octopi_support.css @@ -0,0 +1,3 @@ +#octopi_support_footer { + white-space: nowrap; +} diff --git a/src/octoprint/plugins/octopi_support/static/js/octopi_support.js b/src/octoprint/plugins/octopi_support/static/js/octopi_support.js new file mode 100644 index 0000000000..4d091e1f92 --- /dev/null +++ b/src/octoprint/plugins/octopi_support/static/js/octopi_support.js @@ -0,0 +1,53 @@ +(function (global, factory) { + if (typeof define === "function" && define.amd) { + define(["OctoPrintClient"], factory); + } else { + factory(global.OctoPrintClient); + } +})(this, function(OctoPrintClient) { + var OctoPrintOctoPiSupportClient = function(base) { + this.base = base; + }; + + OctoPrintOctoPiSupportClient.prototype.get = function(opts) { + return this.base.get(this.base.getSimpleApiUrl("octopi_support")); + }; + + OctoPrintClient.registerPluginComponent("octopi_support", OctoPrintOctoPiSupportClient); + return OctoPrintOctoPiSupportClient; +}); + +$(function() { + + function OctoPiSupportViewModel(parameters) { + var self = this; + + self.requestData = function() { + OctoPrint.plugins.octopi_support.get() + .done(function(response) { + $("#octopi_support_footer").remove(); + if (!response.version) return; + + var octoPrintVersion = $(".footer span.version"); + var octoPiVersion = $(" " + gettext("running on") + " " + gettext("OctoPi") + + " " + response.version + "") + $(octoPiVersion).insertAfter(octoPrintVersion); + }) + }; + + self.onStartup = function() { + self.requestData(); + }; + + self.onServerReconnect = function() { + self.requestData(); + }; + } + + // view model class, parameters for constructor, container to bind to + ADDITIONAL_VIEWMODELS.push([ + OctoPiSupportViewModel, + [], + [] + ]); +}); diff --git a/src/octoprint/plugins/octopi_support/templates/octopi_support_about.jinja2 b/src/octoprint/plugins/octopi_support/templates/octopi_support_about.jinja2 new file mode 100644 index 0000000000..6272b1ab8c --- /dev/null +++ b/src/octoprint/plugins/octopi_support/templates/octopi_support_about.jinja2 @@ -0,0 +1,41 @@ +

{{ _('About OctoPi') }}

+ +

{{ _('The ready-to-go Raspberry Pi image with OctoPrint') }}

+ +

Version {{ plugin_octopi_support_version }}

+ + + +

+ © 2013-{{ now.strftime("%Y") }} The OctoPi Authors +

+ +

+ OctoPi is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. +

+

+ OctoPi is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. +

+

+ For a copy of the GNU General Public License, see + www.gnu.org/licenses/gpl-3.0.en.html. +

+ +{% if plugin_octopi_support_rpi %} +

{{ _('About this Raspberry Pi') }}

+ +
    +
  • Model: {{ plugin_octopi_support_rpi_model }}
  • +
  • Revision: {{ plugin_octopi_support_rpi_revision }}
  • +
+{% endif %} + diff --git a/src/octoprint/plugins/pluginmanager/__init__.py b/src/octoprint/plugins/pluginmanager/__init__.py index 1892a0e880..56f880f1ac 100644 --- a/src/octoprint/plugins/pluginmanager/__init__.py +++ b/src/octoprint/plugins/pluginmanager/__init__.py @@ -15,6 +15,7 @@ from octoprint.server import admin_permission, VERSION from octoprint.util.pip import LocalPipCaller, UnknownPip from octoprint.util.version import get_octoprint_version_string, get_octoprint_version, is_octoprint_compatible +from octoprint.util.platform import get_os from flask import jsonify, make_response from flask.ext.babel import gettext @@ -241,7 +242,7 @@ def view(): available=self._repository_available, plugins=self._repository_plugins ), - os=self._get_os(), + os=get_os(), octoprint=get_octoprint_version_string(), pip=dict( available=self._pip_caller.available, @@ -759,7 +760,7 @@ def _refresh_repository(self, repo_data=None): if repo_data is None: return False - current_os = self._get_os() + current_os = get_os() octoprint_version = get_octoprint_version(base=True) def map_repository_entry(entry): @@ -878,14 +879,6 @@ def _is_os_compatible(current_os, compatibility_entries): return positive_match and not negative_match - @classmethod - def _get_os(cls): - for identifier, platforms in cls.OPERATING_SYSTEMS.items(): - if (callable(platforms) and platforms(sys.platform)) or (isinstance(platforms, list) and sys.platform in platforms): - return identifier - else: - return "unmapped" - @property def _reconnect_hooks(self): reconnect_hooks = self.__class__.RECONNECT_HOOKS diff --git a/src/octoprint/server/__init__.py b/src/octoprint/server/__init__.py index f9e2a95ef8..8b5dfd1816 100644 --- a/src/octoprint/server/__init__.py +++ b/src/octoprint/server/__init__.py @@ -146,11 +146,13 @@ def load_user(id): class Server(object): - def __init__(self, settings=None, plugin_manager=None, connectivity_checker=None, event_manager=None, - host="0.0.0.0", port=5000, debug=False, safe_mode=False, allow_root=False, octoprint_daemon=None): + def __init__(self, settings=None, plugin_manager=None, connectivity_checker=None, environment_detector=None, + event_manager=None, host="0.0.0.0", port=5000, debug=False, safe_mode=False, allow_root=False, + octoprint_daemon=None): self._settings = settings self._plugin_manager = plugin_manager self._connectivity_checker = connectivity_checker + self._environment_detector = environment_detector self._event_manager = event_manager self._host = host self._port = port @@ -290,7 +292,8 @@ def on_settings_update(*args, **kwargs): app_session_manager=appSessionManager, plugin_lifecycle_manager=pluginLifecycleManager, preemptive_cache=preemptiveCache, - connectivity_checker=connectivityChecker + connectivity_checker=connectivityChecker, + environment_detector=self._environment_detector ) # create user manager instance @@ -390,6 +393,9 @@ def settings_plugin_config_migration_and_cleanup(identifier, implementation): pluginManager.implementation_post_inits=[settings_plugin_config_migration_and_cleanup] pluginManager.log_all_plugins() + + # log environment data now + self._environment_detector.log_detected_environment() # initialize file manager and register it for changes in the registered plugins fileManager.initialize() diff --git a/src/octoprint/settings.py b/src/octoprint/settings.py index d5ec134cb2..9ffb7d4c65 100644 --- a/src/octoprint/settings.py +++ b/src/octoprint/settings.py @@ -274,7 +274,7 @@ def settings(init=False, basedir=None, configfile=None): ], "usersettings": ["access", "interface"], "wizard": ["access"], - "about": ["about", "supporters", "authors", "changelog", "license", "thirdparty", "plugin_pluginmanager"], + "about": ["about", "plugin_octopi_support", "supporters", "authors", "changelog", "license", "thirdparty", "plugin_pluginmanager"], "generic": [] }, "disabled": { diff --git a/src/octoprint/static/css/octoprint.css b/src/octoprint/static/css/octoprint.css index 3b70bc6db4..997982e7a4 100644 --- a/src/octoprint/static/css/octoprint.css +++ b/src/octoprint/static/css/octoprint.css @@ -1 +1 @@ -.btn{display:inline-block;*display:inline;*zoom:1;padding:4px 12px;margin-bottom:0;font-size:14px;line-height:20px;text-align:center;vertical-align:middle;cursor:pointer;color:#333;text-shadow:0 1px 1px rgba(255,255,255,.75);background-color:#f5f5f5;background-image:-moz-linear-gradient(top,#fff,#e6e6e6);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#e6e6e6));background-image:-webkit-linear-gradient(top,#fff,#e6e6e6);background-image:-o-linear-gradient(top,#fff,#e6e6e6);background-image:linear-gradient(to bottom,#fff,#e6e6e6);background-repeat:repeat-x;*background-color:#e6e6e6;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);border:1px solid #ccc;*border:0;border-bottom-color:#b3b3b3;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;*margin-left:.3em;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05)}.btn.active,.btn.disabled,.btn:active,.btn:focus,.btn:hover,.btn[disabled]{color:#333;background-color:#e6e6e6;*background-color:#d9d9d9}.btn.active,.btn:active{background-color:#ccc \9}.btn:first-child{*margin-left:0}.btn:focus,.btn:hover{color:#333;text-decoration:none;background-position:0 -15px;-webkit-transition:background-position .1s linear;-moz-transition:background-position .1s linear;-o-transition:background-position .1s linear;transition:background-position .1s linear}.btn:focus{outline:#333 dotted thin;outline:-webkit-focus-ring-color auto 5px;outline-offset:-2px}.btn.active,.btn:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,.15),0 1px 2px rgba(0,0,0,.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,.15),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 2px 4px rgba(0,0,0,.15),0 1px 2px rgba(0,0,0,.05)}.btn.disabled,.btn[disabled]{cursor:default;background-image:none;opacity:.65;filter:alpha(opacity=65);-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.btn-large{padding:11px 19px;font-size:17.5px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.btn-large [class*=" icon-"],.btn-large [class^=icon-]{margin-top:4px}.btn-small{padding:2px 10px;font-size:11.9px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.btn-small [class*=" icon-"],.btn-small [class^=icon-]{margin-top:0}.btn-mini [class*=" icon-"],.btn-mini [class^=icon-]{margin-top:-1px}.btn-mini{padding:0 6px;font-size:10.5px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.btn-block{display:block;width:100%;padding-left:0;padding-right:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.btn-block+.btn-block{margin-top:5px}input[type=submit].btn-block,input[type=reset].btn-block,input[type=button].btn-block{width:100%}.btn-primary{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,.25);background-color:#006dcc;background-image:-moz-linear-gradient(top,#08c,#04c);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#04c));background-image:-webkit-linear-gradient(top,#08c,#04c);background-image:-o-linear-gradient(top,#08c,#04c);background-image:linear-gradient(to bottom,#08c,#04c);background-repeat:repeat-x;border-color:#04c #04c #002a80;border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);*background-color:#04c;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-primary.active,.btn-primary.disabled,.btn-primary:active,.btn-primary:focus,.btn-primary:hover,.btn-primary[disabled]{color:#fff;background-color:#04c;*background-color:#003bb3}.btn-primary.active,.btn-primary:active{background-color:#039 \9}.btn-warning{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,.25);background-color:#faa732;background-image:-moz-linear-gradient(top,#fbb450,#f89406);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fbb450),to(#f89406));background-image:-webkit-linear-gradient(top,#fbb450,#f89406);background-image:-o-linear-gradient(top,#fbb450,#f89406);background-image:linear-gradient(to bottom,#fbb450,#f89406);background-repeat:repeat-x;border-color:#f89406 #f89406 #ad6704;border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);*background-color:#f89406;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-warning.active,.btn-warning.disabled,.btn-warning:active,.btn-warning:focus,.btn-warning:hover,.btn-warning[disabled]{color:#fff;background-color:#f89406;*background-color:#df8505}.btn-warning.active,.btn-warning:active{background-color:#c67605 \9}.btn-danger{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,.25);background-color:#da4f49;background-image:-moz-linear-gradient(top,#ee5f5b,#bd362f);background-image:-webkit-gradient(linear,0 0,0 100%,from(#ee5f5b),to(#bd362f));background-image:-webkit-linear-gradient(top,#ee5f5b,#bd362f);background-image:-o-linear-gradient(top,#ee5f5b,#bd362f);background-image:linear-gradient(to bottom,#ee5f5b,#bd362f);background-repeat:repeat-x;border-color:#bd362f #bd362f #802420;border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);*background-color:#bd362f;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-danger.active,.btn-danger.disabled,.btn-danger:active,.btn-danger:focus,.btn-danger:hover,.btn-danger[disabled]{color:#fff;background-color:#bd362f;*background-color:#a9302a}.btn-danger.active,.btn-danger:active{background-color:#942a25 \9}.btn-success{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,.25);background-color:#5bb75b;background-image:-moz-linear-gradient(top,#62c462,#51a351);background-image:-webkit-gradient(linear,0 0,0 100%,from(#62c462),to(#51a351));background-image:-webkit-linear-gradient(top,#62c462,#51a351);background-image:-o-linear-gradient(top,#62c462,#51a351);background-image:linear-gradient(to bottom,#62c462,#51a351);background-repeat:repeat-x;border-color:#51a351 #51a351 #387038;border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);*background-color:#51a351;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-success.active,.btn-success.disabled,.btn-success:active,.btn-success:focus,.btn-success:hover,.btn-success[disabled]{color:#fff;background-color:#51a351;*background-color:#499249}.btn-success.active,.btn-success:active{background-color:#408140 \9}.btn-info{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,.25);background-color:#49afcd;background-image:-moz-linear-gradient(top,#5bc0de,#2f96b4);background-image:-webkit-gradient(linear,0 0,0 100%,from(#5bc0de),to(#2f96b4));background-image:-webkit-linear-gradient(top,#5bc0de,#2f96b4);background-image:-o-linear-gradient(top,#5bc0de,#2f96b4);background-image:linear-gradient(to bottom,#5bc0de,#2f96b4);background-repeat:repeat-x;border-color:#2f96b4 #2f96b4 #1f6377;border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);*background-color:#2f96b4;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-info.active,.btn-info.disabled,.btn-info:active,.btn-info:focus,.btn-info:hover,.btn-info[disabled]{color:#fff;background-color:#2f96b4;*background-color:#2a85a0}.btn-info.active,.btn-info:active{background-color:#24748c \9}.btn-inverse{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,.25);background-color:#363636;background-image:-moz-linear-gradient(top,#444,#222);background-image:-webkit-gradient(linear,0 0,0 100%,from(#444),to(#222));background-image:-webkit-linear-gradient(top,#444,#222);background-image:-o-linear-gradient(top,#444,#222);background-image:linear-gradient(to bottom,#444,#222);background-repeat:repeat-x;border-color:#222 #222 #000;border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);*background-color:#222;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-inverse.active,.btn-inverse.disabled,.btn-inverse:active,.btn-inverse:focus,.btn-inverse:hover,.btn-inverse[disabled]{color:#fff;background-color:#222;*background-color:#151515}.btn-inverse.active,.btn-inverse:active{background-color:#080808 \9}button.btn,input[type=submit].btn{*padding-top:3px;*padding-bottom:3px}button.btn::-moz-focus-inner,input[type=submit].btn::-moz-focus-inner{padding:0;border:0}button.btn.btn-large,input[type=submit].btn.btn-large{*padding-top:7px;*padding-bottom:7px}button.btn.btn-small,input[type=submit].btn.btn-small{*padding-top:3px;*padding-bottom:3px}button.btn.btn-mini,input[type=submit].btn.btn-mini{*padding-top:1px;*padding-bottom:1px}.btn-link,.btn-link:active,.btn-link[disabled]{background-color:transparent;background-image:none;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.btn-link{border-color:transparent;cursor:pointer;color:#08c;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.btn-link:focus,.btn-link:hover{color:#005580;text-decoration:underline;background-color:transparent}.btn-link[disabled]:focus,.btn-link[disabled]:hover{color:#333;text-decoration:none}.clearfix{*zoom:1}.clearfix:after,.clearfix:before{display:table;content:"";line-height:0}.clearfix:after{clear:both}.hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.input-block-level{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.nowrap{white-space:nowrap}.actioncol{text-align:center;white-space:nowrap}.actioncol a{text-decoration:none;color:#000}.actioncol a.disabled{color:#ccc;cursor:default}#navbar .navbar-inner{background-color:#ebebeb;background-image:-moz-linear-gradient(top,#fff,#ccc);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#ccc));background-image:-webkit-linear-gradient(top,#fff,#ccc);background-image:-o-linear-gradient(top,#fff,#ccc);background-image:linear-gradient(to bottom,#fff,#ccc);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffcccccc', GradientType=0)}#navbar .navbar-inner .brand,#navbar .navbar-inner .nav>li>a{text-shadow:0 1px 0 #ccc;color:#333}#navbar .navbar-inner .brand .caret,#navbar .navbar-inner .nav>li>a .caret{border-bottom-color:#939393;border-top-color:#939393}#navbar .navbar-inner .brand:focus .caret,#navbar .navbar-inner .brand:hover .caret,#navbar .navbar-inner .nav>li>a:focus .caret,#navbar .navbar-inner .nav>li>a:hover .caret{border-bottom-color:#636363;border-top-color:#636363}#navbar .navbar-inner .brand span{background-image:url(../img/tentacle-20x20.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner .brand span{background-image:url(../img/tentacle-20x20@2x.png)}}#navbar .navbar-inner .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner .nav>li.dropdown.open>.dropdown-toggle{background-color:#e0e0e0;background-image:-moz-linear-gradient(top,#ccc,#fff);background-image:-webkit-gradient(linear,0 0,0 100%,from(#ccc),to(#fff));background-image:-webkit-linear-gradient(top,#ccc,#fff);background-image:-o-linear-gradient(top,#ccc,#fff);background-image:linear-gradient(to bottom,#ccc,#fff);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffcccccc', endColorstr='#ffffffff', GradientType=0)}#navbar .navbar-inner .nav>li>a:hover{background-color:#dedede;background-image:-moz-linear-gradient(top,#f2f2f2,#bfbfbf);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f2f2f2),to(#bfbfbf));background-image:-webkit-linear-gradient(top,#f2f2f2,#bfbfbf);background-image:-o-linear-gradient(top,#f2f2f2,#bfbfbf);background-image:linear-gradient(to bottom,#f2f2f2,#bfbfbf);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2f2f2', endColorstr='#ffbfbfbf', GradientType=0)}#navbar .navbar-inner.transparent{background-color:rgba(235,235,235,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(255,255,255,.6),rgba(204,204,204,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(255,255,255,.6)),to(rgba(204,204,204,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(255,255,255,.6),rgba(204,204,204,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(255,255,255,.6),rgba(204,204,204,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(255,255,255,.6),rgba(204,204,204,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99ffffff', endColorstr='#99cccccc', GradientType=0)}#navbar .navbar-inner.transparent .brand,#navbar .navbar-inner.transparent .nav>li>a{text-shadow:0 1px 0 #ccc;color:#333}#navbar .navbar-inner.transparent .brand .caret,#navbar .navbar-inner.transparent .nav>li>a .caret{border-bottom-color:#939393;border-top-color:#939393}#navbar .navbar-inner.transparent .brand:focus .caret,#navbar .navbar-inner.transparent .brand:hover .caret,#navbar .navbar-inner.transparent .nav>li>a:focus .caret,#navbar .navbar-inner.transparent .nav>li>a:hover .caret{border-bottom-color:#636363;border-top-color:#636363}#navbar .navbar-inner.transparent .brand span{background-image:url(../img/tentacle-20x20.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.transparent .brand span{background-image:url(../img/tentacle-20x20@2x.png)}}#navbar .navbar-inner.transparent .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.transparent .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.transparent .nav>li.dropdown.open>.dropdown-toggle{background-color:rgba(224,224,224,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(204,204,204,.6),rgba(255,255,255,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(204,204,204,.6)),to(rgba(255,255,255,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(204,204,204,.6),rgba(255,255,255,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(204,204,204,.6),rgba(255,255,255,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(204,204,204,.6),rgba(255,255,255,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99cccccc', endColorstr='#99ffffff', GradientType=0)}#navbar .navbar-inner.transparent .nav>li>a:hover{background-color:rgba(222,222,222,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(242,242,242,.6),rgba(191,191,191,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(242,242,242,.6)),to(rgba(191,191,191,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(242,242,242,.6),rgba(191,191,191,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(242,242,242,.6),rgba(191,191,191,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(242,242,242,.6),rgba(191,191,191,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99f2f2f2', endColorstr='#99bfbfbf', GradientType=0)}#navbar .navbar-inner.red{background-color:#bb645f;background-image:-moz-linear-gradient(top,#e28e8a,#802420);background-image:-webkit-gradient(linear,0 0,0 100%,from(#e28e8a),to(#802420));background-image:-webkit-linear-gradient(top,#e28e8a,#802420);background-image:-o-linear-gradient(top,#e28e8a,#802420);background-image:linear-gradient(to bottom,#e28e8a,#802420);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe28e8a', endColorstr='#ff802420', GradientType=0)}#navbar .navbar-inner.red .brand,#navbar .navbar-inner.red .nav>li>a{text-shadow:0 1px 0 #d86761;color:#f2f2f2}#navbar .navbar-inner.red .brand .caret,#navbar .navbar-inner.red .nav>li>a .caret{border-bottom-color:#d89491;border-top-color:#d89491}#navbar .navbar-inner.red .brand:focus .caret,#navbar .navbar-inner.red .brand:hover .caret,#navbar .navbar-inner.red .nav>li>a:focus .caret,#navbar .navbar-inner.red .nav>li>a:hover .caret{border-bottom-color:#e5c3c1;border-top-color:#e5c3c1}#navbar .navbar-inner.red .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.red .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.red .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.red .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.red .nav>li.dropdown.open>.dropdown-toggle{background-color:#a74f4a;background-image:-moz-linear-gradient(top,#802420,#e28e8a);background-image:-webkit-gradient(linear,0 0,0 100%,from(#802420),to(#e28e8a));background-image:-webkit-linear-gradient(top,#802420,#e28e8a);background-image:-o-linear-gradient(top,#802420,#e28e8a);background-image:linear-gradient(to bottom,#802420,#e28e8a);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff802420', endColorstr='#ffe28e8a', GradientType=0)}#navbar .navbar-inner.red .nav>li>a:hover{background-color:#af5651;background-image:-moz-linear-gradient(top,#dd7a75,#6b1f1b);background-image:-webkit-gradient(linear,0 0,0 100%,from(#dd7a75),to(#6b1f1b));background-image:-webkit-linear-gradient(top,#dd7a75,#6b1f1b);background-image:-o-linear-gradient(top,#dd7a75,#6b1f1b);background-image:linear-gradient(to bottom,#dd7a75,#6b1f1b);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdd7a75', endColorstr='#ff6b1f1b', GradientType=0)}#navbar .navbar-inner.red.transparent{background-color:rgba(187,100,95,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(226,142,138,.6),rgba(128,36,32,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(226,142,138,.6)),to(rgba(128,36,32,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(226,142,138,.6),rgba(128,36,32,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(226,142,138,.6),rgba(128,36,32,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(226,142,138,.6),rgba(128,36,32,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99e28e8a', endColorstr='#99802420', GradientType=0)}#navbar .navbar-inner.red.transparent .brand,#navbar .navbar-inner.red.transparent .nav>li>a{text-shadow:0 1px 0 #d86761;color:#f2f2f2}#navbar .navbar-inner.red.transparent .brand .caret,#navbar .navbar-inner.red.transparent .nav>li>a .caret{border-bottom-color:#d89491;border-top-color:#d89491}#navbar .navbar-inner.red.transparent .brand:focus .caret,#navbar .navbar-inner.red.transparent .brand:hover .caret,#navbar .navbar-inner.red.transparent .nav>li>a:focus .caret,#navbar .navbar-inner.red.transparent .nav>li>a:hover .caret{border-bottom-color:#e5c3c1;border-top-color:#e5c3c1}#navbar .navbar-inner.red.transparent .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.red.transparent .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.red.transparent .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.red.transparent .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.red.transparent .nav>li.dropdown.open>.dropdown-toggle{background-color:rgba(167,79,74,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(128,36,32,.6),rgba(226,142,138,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(128,36,32,.6)),to(rgba(226,142,138,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(128,36,32,.6),rgba(226,142,138,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(128,36,32,.6),rgba(226,142,138,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(128,36,32,.6),rgba(226,142,138,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99802420', endColorstr='#99e28e8a', GradientType=0)}#navbar .navbar-inner.red.transparent .nav>li>a:hover{background-color:rgba(175,86,81,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(221,122,117,.6),rgba(107,31,27,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(221,122,117,.6)),to(rgba(107,31,27,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(221,122,117,.6),rgba(107,31,27,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(221,122,117,.6),rgba(107,31,27,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(221,122,117,.6),rgba(107,31,27,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99dd7a75', endColorstr='#996b1f1b', GradientType=0)}#navbar .navbar-inner.orange{background-color:#e39665;background-image:-moz-linear-gradient(top,#f9c3a0,#c2530c);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f9c3a0),to(#c2530c));background-image:-webkit-linear-gradient(top,#f9c3a0,#c2530c);background-image:-o-linear-gradient(top,#f9c3a0,#c2530c);background-image:linear-gradient(to bottom,#f9c3a0,#c2530c);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff9c3a0', endColorstr='#ffc2530c', GradientType=0)}#navbar .navbar-inner.orange .brand,#navbar .navbar-inner.orange .nav>li>a{text-shadow:0 1px 0 #f6a570;color:#f2f2f2}#navbar .navbar-inner.orange .brand .caret,#navbar .navbar-inner.orange .nav>li>a .caret{border-bottom-color:#f2b58d;border-top-color:#f2b58d}#navbar .navbar-inner.orange .brand:focus .caret,#navbar .navbar-inner.orange .brand:hover .caret,#navbar .navbar-inner.orange .nav>li>a:focus .caret,#navbar .navbar-inner.orange .nav>li>a:hover .caret{border-bottom-color:#f2d3bf;border-top-color:#f2d3bf}#navbar .navbar-inner.orange .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.orange .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.orange .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.orange .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.orange .nav>li.dropdown.open>.dropdown-toggle{background-color:#d88047;background-image:-moz-linear-gradient(top,#c2530c,#f9c3a0);background-image:-webkit-gradient(linear,0 0,0 100%,from(#c2530c),to(#f9c3a0));background-image:-webkit-linear-gradient(top,#c2530c,#f9c3a0);background-image:-o-linear-gradient(top,#c2530c,#f9c3a0);background-image:linear-gradient(to bottom,#c2530c,#f9c3a0);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffc2530c', endColorstr='#fff9c3a0', GradientType=0)}#navbar .navbar-inner.orange .nav>li>a:hover{background-color:#d98956;background-image:-moz-linear-gradient(top,#f8b488,#aa490a);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f8b488),to(#aa490a));background-image:-webkit-linear-gradient(top,#f8b488,#aa490a);background-image:-o-linear-gradient(top,#f8b488,#aa490a);background-image:linear-gradient(to bottom,#f8b488,#aa490a);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff8b488', endColorstr='#ffaa490a', GradientType=0)}#navbar .navbar-inner.orange.transparent{background-color:rgba(227,150,101,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(249,195,160,.6),rgba(194,83,12,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(249,195,160,.6)),to(rgba(194,83,12,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(249,195,160,.6),rgba(194,83,12,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(249,195,160,.6),rgba(194,83,12,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(249,195,160,.6),rgba(194,83,12,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99f9c3a0', endColorstr='#99c2530c', GradientType=0)}#navbar .navbar-inner.orange.transparent .brand,#navbar .navbar-inner.orange.transparent .nav>li>a{text-shadow:0 1px 0 #f6a570;color:#f2f2f2}#navbar .navbar-inner.orange.transparent .brand .caret,#navbar .navbar-inner.orange.transparent .nav>li>a .caret{border-bottom-color:#f2b58d;border-top-color:#f2b58d}#navbar .navbar-inner.orange.transparent .brand:focus .caret,#navbar .navbar-inner.orange.transparent .brand:hover .caret,#navbar .navbar-inner.orange.transparent .nav>li>a:focus .caret,#navbar .navbar-inner.orange.transparent .nav>li>a:hover .caret{border-bottom-color:#f2d3bf;border-top-color:#f2d3bf}#navbar .navbar-inner.orange.transparent .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.orange.transparent .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.orange.transparent .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.orange.transparent .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.orange.transparent .nav>li.dropdown.open>.dropdown-toggle{background-color:rgba(216,128,71,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(194,83,12,.6),rgba(249,195,160,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(194,83,12,.6)),to(rgba(249,195,160,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(194,83,12,.6),rgba(249,195,160,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(194,83,12,.6),rgba(249,195,160,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(194,83,12,.6),rgba(249,195,160,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99c2530c', endColorstr='#99f9c3a0', GradientType=0)}#navbar .navbar-inner.orange.transparent .nav>li>a:hover{background-color:rgba(217,137,86,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(248,180,136,.6),rgba(170,73,10,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(248,180,136,.6)),to(rgba(170,73,10,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(248,180,136,.6),rgba(170,73,10,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(248,180,136,.6),rgba(170,73,10,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(248,180,136,.6),rgba(170,73,10,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99f8b488', endColorstr='#99aa490a', GradientType=0)}#navbar .navbar-inner.yellow{background-color:#e3d765;background-image:-moz-linear-gradient(top,#f9f0a0,#c2b00c);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f9f0a0),to(#c2b00c));background-image:-webkit-linear-gradient(top,#f9f0a0,#c2b00c);background-image:-o-linear-gradient(top,#f9f0a0,#c2b00c);background-image:linear-gradient(to bottom,#f9f0a0,#c2b00c);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff9f0a0', endColorstr='#ffc2b00c', GradientType=0)}#navbar .navbar-inner.yellow .brand,#navbar .navbar-inner.yellow .nav>li>a{text-shadow:0 1px 0 #c2b00c;color:#f2f2f2}#navbar .navbar-inner.yellow .brand .caret,#navbar .navbar-inner.yellow .nav>li>a .caret{border-bottom-color:#f2e88d;border-top-color:#f2e88d}#navbar .navbar-inner.yellow .brand:focus .caret,#navbar .navbar-inner.yellow .brand:hover .caret,#navbar .navbar-inner.yellow .nav>li>a:focus .caret,#navbar .navbar-inner.yellow .nav>li>a:hover .caret{border-bottom-color:#f2edbf;border-top-color:#f2edbf}#navbar .navbar-inner.yellow .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.yellow .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.yellow .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.yellow .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.yellow .nav>li.dropdown.open>.dropdown-toggle{background-color:#d8ca47;background-image:-moz-linear-gradient(top,#c2b00c,#f9f0a0);background-image:-webkit-gradient(linear,0 0,0 100%,from(#c2b00c),to(#f9f0a0));background-image:-webkit-linear-gradient(top,#c2b00c,#f9f0a0);background-image:-o-linear-gradient(top,#c2b00c,#f9f0a0);background-image:linear-gradient(to bottom,#c2b00c,#f9f0a0);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffc2b00c', endColorstr='#fff9f0a0', GradientType=0)}#navbar .navbar-inner.yellow .nav>li>a:hover{background-color:#d9cc56;background-image:-moz-linear-gradient(top,#f8ed88,#aa9a0a);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f8ed88),to(#aa9a0a));background-image:-webkit-linear-gradient(top,#f8ed88,#aa9a0a);background-image:-o-linear-gradient(top,#f8ed88,#aa9a0a);background-image:linear-gradient(to bottom,#f8ed88,#aa9a0a);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff8ed88', endColorstr='#ffaa9a0a', GradientType=0)}#navbar .navbar-inner.yellow.transparent{background-color:rgba(227,215,101,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(249,240,160,.6),rgba(194,176,12,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(249,240,160,.6)),to(rgba(194,176,12,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(249,240,160,.6),rgba(194,176,12,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(249,240,160,.6),rgba(194,176,12,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(249,240,160,.6),rgba(194,176,12,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99f9f0a0', endColorstr='#99c2b00c', GradientType=0)}#navbar .navbar-inner.yellow.transparent .brand,#navbar .navbar-inner.yellow.transparent .nav>li>a{text-shadow:0 1px 0 #c2b00c;color:#f2f2f2}#navbar .navbar-inner.yellow.transparent .brand .caret,#navbar .navbar-inner.yellow.transparent .nav>li>a .caret{border-bottom-color:#f2e88d;border-top-color:#f2e88d}#navbar .navbar-inner.yellow.transparent .brand:focus .caret,#navbar .navbar-inner.yellow.transparent .brand:hover .caret,#navbar .navbar-inner.yellow.transparent .nav>li>a:focus .caret,#navbar .navbar-inner.yellow.transparent .nav>li>a:hover .caret{border-bottom-color:#f2edbf;border-top-color:#f2edbf}#navbar .navbar-inner.yellow.transparent .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.yellow.transparent .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.yellow.transparent .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.yellow.transparent .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.yellow.transparent .nav>li.dropdown.open>.dropdown-toggle{background-color:rgba(216,202,71,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(194,176,12,.6),rgba(249,240,160,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(194,176,12,.6)),to(rgba(249,240,160,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(194,176,12,.6),rgba(249,240,160,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(194,176,12,.6),rgba(249,240,160,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(194,176,12,.6),rgba(249,240,160,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99c2b00c', endColorstr='#99f9f0a0', GradientType=0)}#navbar .navbar-inner.yellow.transparent .nav>li>a:hover{background-color:rgba(217,204,86,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(248,237,136,.6),rgba(170,154,10,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(248,237,136,.6)),to(rgba(170,154,10,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(248,237,136,.6),rgba(170,154,10,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(248,237,136,.6),rgba(170,154,10,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(248,237,136,.6),rgba(170,154,10,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99f8ed88', endColorstr='#99aa9a0a', GradientType=0)}#navbar .navbar-inner.green{background-color:#98f064;background-image:-moz-linear-gradient(top,#c8ffa7,#50da00);background-image:-webkit-gradient(linear,0 0,0 100%,from(#c8ffa7),to(#50da00));background-image:-webkit-linear-gradient(top,#c8ffa7,#50da00);background-image:-o-linear-gradient(top,#c8ffa7,#50da00);background-image:linear-gradient(to bottom,#c8ffa7,#50da00);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffc8ffa7', endColorstr='#ff50da00', GradientType=0)}#navbar .navbar-inner.green .brand,#navbar .navbar-inner.green .nav>li>a{text-shadow:0 1px 0 #50da00;color:#333}#navbar .navbar-inner.green .brand .caret,#navbar .navbar-inner.green .nav>li>a .caret{border-bottom-color:#55992e;border-top-color:#55992e}#navbar .navbar-inner.green .brand:focus .caret,#navbar .navbar-inner.green .brand:hover .caret,#navbar .navbar-inner.green .nav>li>a:focus .caret,#navbar .navbar-inner.green .nav>li>a:hover .caret{border-bottom-color:#446630;border-top-color:#446630}#navbar .navbar-inner.green .brand span{background-image:url(../img/tentacle-20x20.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.green .brand span{background-image:url(../img/tentacle-20x20@2x.png)}}#navbar .navbar-inner.green .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.green .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.green .nav>li.dropdown.open>.dropdown-toggle{background-color:#80e943;background-image:-moz-linear-gradient(top,#50da00,#c8ffa7);background-image:-webkit-gradient(linear,0 0,0 100%,from(#50da00),to(#c8ffa7));background-image:-webkit-linear-gradient(top,#50da00,#c8ffa7);background-image:-o-linear-gradient(top,#50da00,#c8ffa7);background-image:linear-gradient(to bottom,#50da00,#c8ffa7);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff50da00', endColorstr='#ffc8ffa7', GradientType=0)}#navbar .navbar-inner.green .nav>li>a:hover{background-color:#8ae655;background-image:-moz-linear-gradient(top,#b8ff8e,#47c100);background-image:-webkit-gradient(linear,0 0,0 100%,from(#b8ff8e),to(#47c100));background-image:-webkit-linear-gradient(top,#b8ff8e,#47c100);background-image:-o-linear-gradient(top,#b8ff8e,#47c100);background-image:linear-gradient(to bottom,#b8ff8e,#47c100);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffb8ff8e', endColorstr='#ff47c100', GradientType=0)}#navbar .navbar-inner.green.transparent{background-color:rgba(152,240,100,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(200,255,167,.6),rgba(80,218,0,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(200,255,167,.6)),to(rgba(80,218,0,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(200,255,167,.6),rgba(80,218,0,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(200,255,167,.6),rgba(80,218,0,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(200,255,167,.6),rgba(80,218,0,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99c8ffa7', endColorstr='#9950da00', GradientType=0)}#navbar .navbar-inner.green.transparent .brand,#navbar .navbar-inner.green.transparent .nav>li>a{text-shadow:0 1px 0 #50da00;color:#333}#navbar .navbar-inner.green.transparent .brand .caret,#navbar .navbar-inner.green.transparent .nav>li>a .caret{border-bottom-color:#55992e;border-top-color:#55992e}#navbar .navbar-inner.green.transparent .brand:focus .caret,#navbar .navbar-inner.green.transparent .brand:hover .caret,#navbar .navbar-inner.green.transparent .nav>li>a:focus .caret,#navbar .navbar-inner.green.transparent .nav>li>a:hover .caret{border-bottom-color:#446630;border-top-color:#446630}#navbar .navbar-inner.green.transparent .brand span{background-image:url(../img/tentacle-20x20.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.green.transparent .brand span{background-image:url(../img/tentacle-20x20@2x.png)}}#navbar .navbar-inner.green.transparent .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.green.transparent .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.green.transparent .nav>li.dropdown.open>.dropdown-toggle{background-color:rgba(128,233,67,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(80,218,0,.6),rgba(200,255,167,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(80,218,0,.6)),to(rgba(200,255,167,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(80,218,0,.6),rgba(200,255,167,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(80,218,0,.6),rgba(200,255,167,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(80,218,0,.6),rgba(200,255,167,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#9950da00', endColorstr='#99c8ffa7', GradientType=0)}#navbar .navbar-inner.green.transparent .nav>li>a:hover{background-color:rgba(138,230,85,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(184,255,142,.6),rgba(71,193,0,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(184,255,142,.6)),to(rgba(71,193,0,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(184,255,142,.6),rgba(71,193,0,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(184,255,142,.6),rgba(71,193,0,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(184,255,142,.6),rgba(71,193,0,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99b8ff8e', endColorstr='#9947c100', GradientType=0)}#navbar .navbar-inner.blue{background-color:#2e63cc;background-image:-moz-linear-gradient(top,#4d88ff,#002b80);background-image:-webkit-gradient(linear,0 0,0 100%,from(#4d88ff),to(#002b80));background-image:-webkit-linear-gradient(top,#4d88ff,#002b80);background-image:-o-linear-gradient(top,#4d88ff,#002b80);background-image:linear-gradient(to bottom,#4d88ff,#002b80);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff4d88ff', endColorstr='#ff002b80', GradientType=0)}#navbar .navbar-inner.blue .brand,#navbar .navbar-inner.blue .nav>li>a{text-shadow:0 1px 0 #1a66ff;color:#f2f2f2}#navbar .navbar-inner.blue .brand .caret,#navbar .navbar-inner.blue .nav>li>a .caret{border-bottom-color:#799bdf;border-top-color:#799bdf}#navbar .navbar-inner.blue .brand:focus .caret,#navbar .navbar-inner.blue .brand:hover .caret,#navbar .navbar-inner.blue .nav>li>a:focus .caret,#navbar .navbar-inner.blue .nav>li>a:hover .caret{border-bottom-color:#b6c7e9;border-top-color:#b6c7e9}#navbar .navbar-inner.blue .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.blue .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.blue .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.blue .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.blue .nav>li.dropdown.open>.dropdown-toggle{background-color:#1f50b3;background-image:-moz-linear-gradient(top,#002b80,#4d88ff);background-image:-webkit-gradient(linear,0 0,0 100%,from(#002b80),to(#4d88ff));background-image:-webkit-linear-gradient(top,#002b80,#4d88ff);background-image:-o-linear-gradient(top,#002b80,#4d88ff);background-image:linear-gradient(to bottom,#002b80,#4d88ff);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff002b80', endColorstr='#ff4d88ff', GradientType=0)}#navbar .navbar-inner.blue .nav>li>a:hover{background-color:#1f55c2;background-image:-moz-linear-gradient(top,#37f,#026);background-image:-webkit-gradient(linear,0 0,0 100%,from(#37f),to(#026));background-image:-webkit-linear-gradient(top,#37f,#026);background-image:-o-linear-gradient(top,#37f,#026);background-image:linear-gradient(to bottom,#37f,#026);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3377ff', endColorstr='#ff002266', GradientType=0)}#navbar .navbar-inner.blue.transparent{background-color:rgba(46,99,204,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(77,136,255,.6),rgba(0,43,128,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(77,136,255,.6)),to(rgba(0,43,128,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(77,136,255,.6),rgba(0,43,128,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(77,136,255,.6),rgba(0,43,128,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(77,136,255,.6),rgba(0,43,128,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#994d88ff', endColorstr='#99002b80', GradientType=0)}#navbar .navbar-inner.blue.transparent .brand,#navbar .navbar-inner.blue.transparent .nav>li>a{text-shadow:0 1px 0 #1a66ff;color:#f2f2f2}#navbar .navbar-inner.blue.transparent .brand .caret,#navbar .navbar-inner.blue.transparent .nav>li>a .caret{border-bottom-color:#799bdf;border-top-color:#799bdf}#navbar .navbar-inner.blue.transparent .brand:focus .caret,#navbar .navbar-inner.blue.transparent .brand:hover .caret,#navbar .navbar-inner.blue.transparent .nav>li>a:focus .caret,#navbar .navbar-inner.blue.transparent .nav>li>a:hover .caret{border-bottom-color:#b6c7e9;border-top-color:#b6c7e9}#navbar .navbar-inner.blue.transparent .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.blue.transparent .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.blue.transparent .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.blue.transparent .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.blue.transparent .nav>li.dropdown.open>.dropdown-toggle{background-color:rgba(31,80,179,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(0,43,128,.6),rgba(77,136,255,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(0,43,128,.6)),to(rgba(77,136,255,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(0,43,128,.6),rgba(77,136,255,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(0,43,128,.6),rgba(77,136,255,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(0,43,128,.6),rgba(77,136,255,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99002b80', endColorstr='#994d88ff', GradientType=0)}#navbar .navbar-inner.blue.transparent .nav>li>a:hover{background-color:rgba(31,85,194,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(51,119,255,.6),rgba(0,34,102,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(51,119,255,.6)),to(rgba(0,34,102,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(51,119,255,.6),rgba(0,34,102,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(51,119,255,.6),rgba(0,34,102,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(51,119,255,.6),rgba(0,34,102,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#993377ff', endColorstr='#99002266', GradientType=0)}#navbar .navbar-inner.violet{background-color:#9864f0;background-image:-moz-linear-gradient(top,#c8a7ff,#5000da);background-image:-webkit-gradient(linear,0 0,0 100%,from(#c8a7ff),to(#5000da));background-image:-webkit-linear-gradient(top,#c8a7ff,#5000da);background-image:-o-linear-gradient(top,#c8a7ff,#5000da);background-image:linear-gradient(to bottom,#c8a7ff,#5000da);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffc8a7ff', endColorstr='#ff5000da', GradientType=0)}#navbar .navbar-inner.violet .brand,#navbar .navbar-inner.violet .nav>li>a{text-shadow:0 1px 0 #a774ff;color:#f2f2f2}#navbar .navbar-inner.violet .brand .caret,#navbar .navbar-inner.violet .nav>li>a .caret{border-bottom-color:#b58df9;border-top-color:#b58df9}#navbar .navbar-inner.violet .brand:focus .caret,#navbar .navbar-inner.violet .brand:hover .caret,#navbar .navbar-inner.violet .nav>li>a:focus .caret,#navbar .navbar-inner.violet .nav>li>a:hover .caret{border-bottom-color:#d3bff5;border-top-color:#d3bff5}#navbar .navbar-inner.violet .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.violet .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.violet .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.violet .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.violet .nav>li.dropdown.open>.dropdown-toggle{background-color:#8043e9;background-image:-moz-linear-gradient(top,#5000da,#c8a7ff);background-image:-webkit-gradient(linear,0 0,0 100%,from(#5000da),to(#c8a7ff));background-image:-webkit-linear-gradient(top,#5000da,#c8a7ff);background-image:-o-linear-gradient(top,#5000da,#c8a7ff);background-image:linear-gradient(to bottom,#5000da,#c8a7ff);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5000da', endColorstr='#ffc8a7ff', GradientType=0)}#navbar .navbar-inner.violet .nav>li>a:hover{background-color:#8a55e6;background-image:-moz-linear-gradient(top,#b88eff,#4700c1);background-image:-webkit-gradient(linear,0 0,0 100%,from(#b88eff),to(#4700c1));background-image:-webkit-linear-gradient(top,#b88eff,#4700c1);background-image:-o-linear-gradient(top,#b88eff,#4700c1);background-image:linear-gradient(to bottom,#b88eff,#4700c1);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffb88eff', endColorstr='#ff4700c1', GradientType=0)}#navbar .navbar-inner.violet.transparent{background-color:rgba(152,100,240,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(200,167,255,.6),rgba(80,0,218,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(200,167,255,.6)),to(rgba(80,0,218,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(200,167,255,.6),rgba(80,0,218,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(200,167,255,.6),rgba(80,0,218,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(200,167,255,.6),rgba(80,0,218,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99c8a7ff', endColorstr='#995000da', GradientType=0)}#navbar .navbar-inner.violet.transparent .brand,#navbar .navbar-inner.violet.transparent .nav>li>a{text-shadow:0 1px 0 #a774ff;color:#f2f2f2}#navbar .navbar-inner.violet.transparent .brand .caret,#navbar .navbar-inner.violet.transparent .nav>li>a .caret{border-bottom-color:#b58df9;border-top-color:#b58df9}#navbar .navbar-inner.violet.transparent .brand:focus .caret,#navbar .navbar-inner.violet.transparent .brand:hover .caret,#navbar .navbar-inner.violet.transparent .nav>li>a:focus .caret,#navbar .navbar-inner.violet.transparent .nav>li>a:hover .caret{border-bottom-color:#d3bff5;border-top-color:#d3bff5}#navbar .navbar-inner.violet.transparent .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.violet.transparent .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.violet.transparent .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.violet.transparent .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.violet.transparent .nav>li.dropdown.open>.dropdown-toggle{background-color:rgba(128,67,233,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(80,0,218,.6),rgba(200,167,255,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(80,0,218,.6)),to(rgba(200,167,255,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(80,0,218,.6),rgba(200,167,255,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(80,0,218,.6),rgba(200,167,255,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(80,0,218,.6),rgba(200,167,255,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#995000da', endColorstr='#99c8a7ff', GradientType=0)}#navbar .navbar-inner.violet.transparent .nav>li>a:hover{background-color:rgba(138,85,230,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(184,142,255,.6),rgba(71,0,193,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(184,142,255,.6)),to(rgba(71,0,193,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(184,142,255,.6),rgba(71,0,193,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(184,142,255,.6),rgba(71,0,193,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(184,142,255,.6),rgba(71,0,193,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99b88eff', endColorstr='#994700c1', GradientType=0)}#navbar .navbar-inner.black{background-color:#4f4f4f;background-image:-moz-linear-gradient(top,#787878,#121212);background-image:-webkit-gradient(linear,0 0,0 100%,from(#787878),to(#121212));background-image:-webkit-linear-gradient(top,#787878,#121212);background-image:-o-linear-gradient(top,#787878,#121212);background-image:linear-gradient(to bottom,#787878,#121212);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff787878', endColorstr='#ff121212', GradientType=0)}#navbar .navbar-inner.black .brand,#navbar .navbar-inner.black .nav>li>a{text-shadow:0 1px 0 #5e5e5e;color:#f2f2f2}#navbar .navbar-inner.black .brand .caret,#navbar .navbar-inner.black .nav>li>a .caret{border-bottom-color:#959595;border-top-color:#959595}#navbar .navbar-inner.black .brand:focus .caret,#navbar .navbar-inner.black .brand:hover .caret,#navbar .navbar-inner.black .nav>li>a:focus .caret,#navbar .navbar-inner.black .nav>li>a:hover .caret{border-bottom-color:#c4c4c4;border-top-color:#c4c4c4}#navbar .navbar-inner.black .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.black .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.black .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.black .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.black .nav>li.dropdown.open>.dropdown-toggle{background-color:#3b3b3b;background-image:-moz-linear-gradient(top,#121212,#787878);background-image:-webkit-gradient(linear,0 0,0 100%,from(#121212),to(#787878));background-image:-webkit-linear-gradient(top,#121212,#787878);background-image:-o-linear-gradient(top,#121212,#787878);background-image:linear-gradient(to bottom,#121212,#787878);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff121212', endColorstr='#ff787878', GradientType=0)}#navbar .navbar-inner.black .nav>li>a:hover{background-color:#424242;background-image:-moz-linear-gradient(top,#6b6b6b,#050505);background-image:-webkit-gradient(linear,0 0,0 100%,from(#6b6b6b),to(#050505));background-image:-webkit-linear-gradient(top,#6b6b6b,#050505);background-image:-o-linear-gradient(top,#6b6b6b,#050505);background-image:linear-gradient(to bottom,#6b6b6b,#050505);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff6b6b6b', endColorstr='#ff050505', GradientType=0)}#navbar .navbar-inner.black.transparent{background-color:rgba(79,79,79,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(120,120,120,.6),rgba(18,18,18,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(120,120,120,.6)),to(rgba(18,18,18,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(120,120,120,.6),rgba(18,18,18,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(120,120,120,.6),rgba(18,18,18,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(120,120,120,.6),rgba(18,18,18,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99787878', endColorstr='#99121212', GradientType=0)}#navbar .navbar-inner.black.transparent .brand,#navbar .navbar-inner.black.transparent .nav>li>a{text-shadow:0 1px 0 #5e5e5e;color:#f2f2f2}#navbar .navbar-inner.black.transparent .brand .caret,#navbar .navbar-inner.black.transparent .nav>li>a .caret{border-bottom-color:#959595;border-top-color:#959595}#navbar .navbar-inner.black.transparent .brand:focus .caret,#navbar .navbar-inner.black.transparent .brand:hover .caret,#navbar .navbar-inner.black.transparent .nav>li>a:focus .caret,#navbar .navbar-inner.black.transparent .nav>li>a:hover .caret{border-bottom-color:#c4c4c4;border-top-color:#c4c4c4}#navbar .navbar-inner.black.transparent .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.black.transparent .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.black.transparent .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.black.transparent .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.black.transparent .nav>li.dropdown.open>.dropdown-toggle{background-color:rgba(59,59,59,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(18,18,18,.6),rgba(120,120,120,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(18,18,18,.6)),to(rgba(120,120,120,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(18,18,18,.6),rgba(120,120,120,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(18,18,18,.6),rgba(120,120,120,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(18,18,18,.6),rgba(120,120,120,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99121212', endColorstr='#99787878', GradientType=0)}#navbar .navbar-inner.black.transparent .nav>li>a:hover{background-color:rgba(66,66,66,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(107,107,107,.6),rgba(5,5,5,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(107,107,107,.6)),to(rgba(5,5,5,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(107,107,107,.6),rgba(5,5,5,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(107,107,107,.6),rgba(5,5,5,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(107,107,107,.6),rgba(5,5,5,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#996b6b6b', endColorstr='#99050505', GradientType=0)}#navbar .navbar-inner.white{background-color:#e9e9e9;background-image:-moz-linear-gradient(top,#fff,#c8c8c8);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#c8c8c8));background-image:-webkit-linear-gradient(top,#fff,#c8c8c8);background-image:-o-linear-gradient(top,#fff,#c8c8c8);background-image:linear-gradient(to bottom,#fff,#c8c8c8);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffc8c8c8', GradientType=0)}#navbar .navbar-inner.white .brand,#navbar .navbar-inner.white .nav>li>a{text-shadow:0 1px 0 #c8c8c8;color:#333}#navbar .navbar-inner.white .brand .caret,#navbar .navbar-inner.white .nav>li>a .caret{border-bottom-color:#919191;border-top-color:#919191}#navbar .navbar-inner.white .brand:focus .caret,#navbar .navbar-inner.white .brand:hover .caret,#navbar .navbar-inner.white .nav>li>a:focus .caret,#navbar .navbar-inner.white .nav>li>a:hover .caret{border-bottom-color:#626262;border-top-color:#626262}#navbar .navbar-inner.white .brand span{background-image:url(../img/tentacle-20x20.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.white .brand span{background-image:url(../img/tentacle-20x20@2x.png)}}#navbar .navbar-inner.white .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.white .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.white .nav>li.dropdown.open>.dropdown-toggle{background-color:#dedede;background-image:-moz-linear-gradient(top,#c8c8c8,#fff);background-image:-webkit-gradient(linear,0 0,0 100%,from(#c8c8c8),to(#fff));background-image:-webkit-linear-gradient(top,#c8c8c8,#fff);background-image:-o-linear-gradient(top,#c8c8c8,#fff);background-image:linear-gradient(to bottom,#c8c8c8,#fff);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffc8c8c8', endColorstr='#ffffffff', GradientType=0)}#navbar .navbar-inner.white .nav>li>a:hover{background-color:#dcdcdc;background-image:-moz-linear-gradient(top,#f2f2f2,#bbb);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f2f2f2),to(#bbb));background-image:-webkit-linear-gradient(top,#f2f2f2,#bbb);background-image:-o-linear-gradient(top,#f2f2f2,#bbb);background-image:linear-gradient(to bottom,#f2f2f2,#bbb);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2f2f2', endColorstr='#ffbbbbbb', GradientType=0)}#navbar .navbar-inner.white.transparent{background-color:rgba(233,233,233,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(255,255,255,.6),rgba(200,200,200,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(255,255,255,.6)),to(rgba(200,200,200,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(255,255,255,.6),rgba(200,200,200,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(255,255,255,.6),rgba(200,200,200,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(255,255,255,.6),rgba(200,200,200,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99ffffff', endColorstr='#99c8c8c8', GradientType=0)}#navbar .navbar-inner.white.transparent .brand,#navbar .navbar-inner.white.transparent .nav>li>a{text-shadow:0 1px 0 #c8c8c8;color:#333}#navbar .navbar-inner.white.transparent .brand .caret,#navbar .navbar-inner.white.transparent .nav>li>a .caret{border-bottom-color:#919191;border-top-color:#919191}#navbar .navbar-inner.white.transparent .brand:focus .caret,#navbar .navbar-inner.white.transparent .brand:hover .caret,#navbar .navbar-inner.white.transparent .nav>li>a:focus .caret,#navbar .navbar-inner.white.transparent .nav>li>a:hover .caret{border-bottom-color:#626262;border-top-color:#626262}#navbar .navbar-inner.white.transparent .brand span{background-image:url(../img/tentacle-20x20.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.white.transparent .brand span{background-image:url(../img/tentacle-20x20@2x.png)}}#navbar .navbar-inner.white.transparent .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.white.transparent .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.white.transparent .nav>li.dropdown.open>.dropdown-toggle{background-color:rgba(222,222,222,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(200,200,200,.6),rgba(255,255,255,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(200,200,200,.6)),to(rgba(255,255,255,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(200,200,200,.6),rgba(255,255,255,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(200,200,200,.6),rgba(255,255,255,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(200,200,200,.6),rgba(255,255,255,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99c8c8c8', endColorstr='#99ffffff', GradientType=0)}#navbar .navbar-inner.white.transparent .nav>li>a:hover{background-color:rgba(220,220,220,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(242,242,242,.6),rgba(187,187,187,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(242,242,242,.6)),to(rgba(187,187,187,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(242,242,242,.6),rgba(187,187,187,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(242,242,242,.6),rgba(187,187,187,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(242,242,242,.6),rgba(187,187,187,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99f2f2f2', endColorstr='#99bbbbbb', GradientType=0)}#navbar .navbar-inner .brand{padding:10px 20px 6px}#navbar .navbar-inner .brand span{padding-left:26px;background-size:20px 20px;background-repeat:no-repeat;display:inline-block;max-width:250px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;vertical-align:top;line-height:20px;height:24px}#navbar_login a.dropdown-toggle span{display:inline-block;max-width:100px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;vertical-align:top}.octoprint-container{margin-top:20px}.octoprint-container .tab-content{padding:9px 15px;border-left:1px solid #DDD;border-right:1px solid #DDD;border-bottom:1px solid #DDD;-webkit-border-bottom-right-radius:4px;-moz-border-radius-bottomright:4px;border-bottom-right-radius:4px;-webkit-border-bottom-left-radius:4px;-moz-border-radius-bottomleft:4px;border-bottom-left-radius:4px}.octoprint-container .nav{margin-bottom:0}.octoprint-container .tab-content h1{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:40px;color:#333;border:0;border-bottom:1px solid #E5E5E5;font-weight:400}.octoprint-container .accordion-heading .accordion-heading-button{float:right}.octoprint-container .accordion-heading .accordion-heading-button a{display:inline-block;padding:8px 15px;font-size:14px;line-height:20px;color:#000;text-decoration:none;background:0 0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.octoprint-container .accordion-heading a.accordion-toggle{display:inline-block}.octoprint-container .accordion-heading [class*=" icon-"],.octoprint-container .accordion-heading [class^=icon-]{color:#000}.print-control .btn{padding-left:4px;padding-right:4px}.upload-buttons .btn{margin-right:0}table{table-layout:fixed}table .popover-title{text-overflow:ellipsis;word-break:break-all}table td,table th{overflow:hidden}table td.gcode_files_name,table th.gcode_files_name{text-overflow:ellipsis;text-align:left;white-space:nowrap}table td.gcode_files_action,table th.gcode_files_action{width:90px;text-align:center;white-space:nowrap}table td.gcode_files_action a,table th.gcode_files_action a{text-decoration:none;color:#000}table td.gcode_files_action a.disabled,table th.gcode_files_action a.disabled{color:#ccc;cursor:default}table td.timelapse_files_checkbox,table td.timelapse_unrendered_checkbox,table th.timelapse_files_checkbox,table th.timelapse_unrendered_checkbox{text-align:center;width:10px}table td.timelapse_files_checkbox input[type=checkbox],table td.timelapse_unrendered_checkbox input[type=checkbox],table th.timelapse_files_checkbox input[type=checkbox],table th.timelapse_unrendered_checkbox input[type=checkbox]{margin-top:0}table td.timelapse_files_name,table td.timelapse_unrendered_name,table th.timelapse_files_name,table th.timelapse_unrendered_name{text-overflow:ellipsis;text-align:left}table td.timelapse_files_size,table td.timelapse_unrendered_size,table th.timelapse_files_size,table th.timelapse_unrendered_size{text-align:right;width:55px}table td.timelapse_unrendered_count,table th.timelapse_unrendered_count{text-align:right;width:45px}table td.timelapse_files_action,table td.timelapse_unrendered_action,table th.timelapse_files_action,table th.timelapse_unrendered_action{width:45px;text-align:center;white-space:nowrap}table td.timelapse_files_action a,table td.timelapse_unrendered_action a,table th.timelapse_files_action a,table th.timelapse_unrendered_action a{text-decoration:none;color:#000}table td.timelapse_files_action a.disabled,table td.timelapse_unrendered_action a.disabled,table th.timelapse_files_action a.disabled,table th.timelapse_unrendered_action a.disabled{color:#ccc;cursor:default}table td.settings_users_name,table th.settings_users_name{text-overflow:ellipsis;text-align:left}table td.settings_users_active,table td.settings_users_admin,table th.settings_users_active,table th.settings_users_admin{text-align:center;width:55px}table td.settings_users_actions,table th.settings_users_actions{width:60px;text-align:center;white-space:nowrap}table td.settings_users_actions a,table th.settings_users_actions a{text-decoration:none;color:#000}table td.settings_users_actions a.disabled,table th.settings_users_actions a.disabled{color:#ccc;cursor:default}table td.settings_logs_name,table th.settings_logs_name{text-overflow:ellipsis;text-align:left}table td.settings_logs_size,table th.settings_logs_size{text-align:right;width:70px}table td.settings_logs_date,table th.settings_logs_date{text-align:left;width:130px}table td.settings_logs_action,table th.settings_logs_action{width:70px;text-align:center;white-space:nowrap}table td.settings_logs_action a,table th.settings_logs_action a{text-decoration:none;color:#000}table td.settings_logs_action a.disabled,table th.settings_logs_action a.disabled{color:#ccc;cursor:default}table td.settings_printerProfiles_profiles_name,table th.settings_printerProfiles_profiles_name{text-overflow:ellipsis;text-align:left}table td.settings_printerProfiles_profiles_model,table th.settings_printerProfiles_profiles_model{text-align:left;width:250px}table td.settings_printerProfiles_profiles_action,table th.settings_printerProfiles_profiles_action{width:80px;text-align:center;white-space:nowrap}table td.settings_printerProfiles_profiles_action a,table th.settings_printerProfiles_profiles_action a{text-decoration:none;color:#000}table td.settings_printerProfiles_profiles_action a.disabled,table th.settings_printerProfiles_profiles_action a.disabled{color:#ccc;cursor:default}#temperature-graph{height:350px;width:100%;background:url(../img/graph-background.png) center no-repeat}#temperature-table{table-layout:fixed;width:100%;margin-top:20px}#temperature-table td.temperature_actual,#temperature-table td.temperature_offset,#temperature-table td.temperature_target,#temperature-table td.temperature_tool,#temperature-table th.temperature_actual,#temperature-table th.temperature_offset,#temperature-table th.temperature_target,#temperature-table th.temperature_tool{vertical-align:middle;text-align:center}#temperature-table td.temperature_actual form,#temperature-table td.temperature_offset form,#temperature-table td.temperature_target form,#temperature-table td.temperature_tool form,#temperature-table th.temperature_actual form,#temperature-table th.temperature_offset form,#temperature-table th.temperature_target form,#temperature-table th.temperature_tool form{margin:0}#temperature-table td.temperature_actual .dropdown-menu,#temperature-table td.temperature_offset .dropdown-menu,#temperature-table td.temperature_target .dropdown-menu,#temperature-table td.temperature_tool .dropdown-menu,#temperature-table th.temperature_actual .dropdown-menu,#temperature-table th.temperature_offset .dropdown-menu,#temperature-table th.temperature_target .dropdown-menu,#temperature-table th.temperature_tool .dropdown-menu{text-align:left}#temperature-table td.temperature_tool,#temperature-table th.temperature_tool{width:16%;text-align:left;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}#temperature-table td.temperature_actual,#temperature-table th.temperature_actual{width:12%}#temperature-table td.temperature_target,#temperature-table th.temperature_target{width:42%;overflow:visible}#temperature-table td.temperature_offset,#temperature-table th.temperature_offset{width:30%}.tab-content,.tab-pane{overflow:visible}#speed_fill,#speed_innerWall,#speed_outerWall,#speed_support,#temp_newBedTemp,#temp_newTemp,#webcam_timelapse_fps,#webcam_timelapse_interval,#webcam_timelapse_postRoll,#webcam_timelapse_retractionZHop{text-align:right}ul.dropdown-menu li a{cursor:pointer}#connection_baudrates,#connection_ports,#connection_printers{width:100%}#offline_overlay,#reloadui_overlay{position:fixed;top:0;left:0;width:100%;height:100%;display:none}#offline_overlay{z-index:10002}#reloadui_overlay{z-index:10001}#offline_overlay_background,#reloadui_overlay_background{position:fixed;top:0;left:0;width:100%;height:100%;background-color:#000;filter:alpha(opacity=50);-moz-opacity:.5;-khtml-opacity:.5;opacity:.5}#offline_overlay_wrapper,#reloadui_overlay_wrapper{position:absolute;top:0;bottom:0;left:0;right:0;padding-top:60px}#offline_overlay_wrapper .container,#reloadui_overlay_wrapper .container{margin:auto}#webcam_container{width:100%;position:relative;outline:0;background-color:#000}#webcam_container .keycontrol_overlay{position:absolute;left:10px;right:10px;bottom:10px;background:rgba(0,0,0,.5);font-size:85%;color:#fff;padding:0}#webcam_container .keycontrol_overlay kbd{border:1px solid #eee;border-radius:3px;margin-left:2px;margin-right:2px;font-size:90%;padding:2px;min-width:1em}#webcam_container .keycontrol_overlay .keycontrol_overlay_heading{position:relative;padding:10px;font-weight:700}#webcam_container .keycontrol_overlay .keycontrol_overlay_column{position:relative;width:45%;padding:10px;float:left}#webcam_container .nowebcam{position:absolute;top:0;left:0;right:0;bottom:0}#webcam_container .nowebcam .text{color:#fff;text-align:center;position:relative;margin:auto;width:80%;top:50%;transform:translateY(-50%);display:block}#webcam_container .nowebcam .text.webcam_loading{animation:pulsate 3s ease-out;animation-iteration-count:infinite}#webcam_container .webcam_rotated{position:relative;width:100%;padding-bottom:100%;pointer-events:none}#webcam_container .webcam_rotated .webcam_fixed_ratio{position:absolute;transform:rotate(-90deg);top:0;bottom:0;pointer-events:none}#webcam_container .webcam_rotated .webcam_fixed_ratio .webcam_fixed_ratio_inner{width:100%;height:100%;pointer-events:none}#webcam_container .webcam_unrotated .webcam_fixed_ratio{width:100%;pointer-events:none;padding-bottom:100%;position:relative}#webcam_container .webcam_unrotated .webcam_fixed_ratio.ratio43{padding-bottom:75%}#webcam_container .webcam_unrotated .webcam_fixed_ratio.ratio169{padding-bottom:56.25%}#webcam_container .webcam_unrotated .webcam_fixed_ratio.ratio1610{padding-bottom:62.5%}#webcam_container .webcam_unrotated .webcam_fixed_ratio .webcam_fixed_ratio_inner{position:absolute;top:0;bottom:0;left:0;right:0;pointer-events:none}#webcam_container img{width:100%;height:100%;object-fit:contain}#state_wrapper hr{margin:5px 0}#files .gcode_files{padding-right:12px}#files .gcode_files .entry{padding:5px;line-height:20px;border-bottom:1px solid #ddd;position:relative}#files .gcode_files .entry:hover{background-color:#f5f5f5}#files .gcode_files .entry .title{text-overflow:ellipsis;word-break:break-all}#files .gcode_files .entry .additionalInfo,#files .gcode_files .entry .internal,#files .gcode_files .entry .size,#files .gcode_files .entry .uploaded{font-size:85%;color:#999}#files .gcode_files .entry .internal{word-break:break-all}#files .gcode_files .entry .action-buttons{position:absolute;bottom:5px;right:5px}#files .gcode_files .entry .additionalInfo{padding-bottom:22px}@keyframes highlightframes{0%{background:#ff0}100%{background:0 0}}#files .gcode_files .entry.highlight{animation:highlightframes 2s}#files .gcode_files .back .back-path{white-space:nowrap}#files .gcode_files .back .back-path span{word-wrap:break-word;white-space:pre-line}#files .upload-buttons{margin-top:10px}#files .form-search{text-align:center;margin-bottom:5px!important}#control{overflow:hidden}#control .jog-panel{float:left;margin-right:19px}#control h1{text-align:left}#control .jog-panel>div{text-align:center}#control .jog-panel>div.distance{text-align:left}#control .jog-panel .slider{margin-bottom:10px}#control .box{width:30px;height:30px;margin-right:10px;margin-bottom:10px;padding-left:8px}#control .control-box{display:block;height:30px;margin-bottom:10px}#control .btn-group{margin-bottom:10px}#control .btn-group.distance>.btn{width:43px;padding:3px 0;height:30px}#control .slider-handle{width:14px;height:14px;margin-left:-7px;margin-top:-3px}#control .custom_section h1{cursor:pointer}#control .custom_section_horizontal>.custom_control{display:inline-block}#control .custom_section_vertical>.custom_control{display:block}#control .custom_control .slider{margin-left:10px;margin-right:10px;margin-bottom:2px}#gcode .progress{width:588px}#gcode .progress .bar{-webkit-transition:width 0s linear;-moz-transition:width 0s linear;-o-transition:width 0s linear;transition:width 0s linear}#gcode .canvas_container{position:relative}#gcode .canvas_container:active,#gcode .canvas_container:hover{outline:0}#gcode .layer-buttons{padding-top:5px;padding-bottom:7px}#gcode #gcode_layer_slider{position:absolute;right:0;top:0;height:568px;float:right}#gcode #gcode_layer_slider .slider-handle{width:14px;height:14px;margin-left:-3px;margin-top:-7px}#gcode #gcode_command_slider .slider-handle{width:14px;height:14px;margin-left:-7px;margin-top:-3px}#term .terminal{margin-bottom:30px}#term .terminal #terminal-output,#term .terminal #terminal-output-lowfi{min-height:340px;margin-bottom:5px}#settings_dialog .aboutlink{float:left}#settings_dialog_menu,#wizard_dialog_menu{margin-left:0}#wizard_firstrun_acl .acl_decision{margin-top:1em}#wizard_firstrun_end p,#wizard_firstrun_start p{margin-bottom:1.5em;line-height:1.5}#settings_appearance_managelanguagesdialog_emptylist{overflow:hidden;width:100%;height:300px;text-align:center;display:table}#settings_appearance_managelanguagesdialog_emptylist div{display:table-cell;vertical-align:middle}.footer ul{margin:0}.footer ul li{display:inline;margin-left:1em;font-size:85%}.footer ul li:first-child{margin-left:0}.footer ul li a{color:#555}.ui-pnotify .alert a{color:#c09853}.ui-pnotify .alert-danger a,.ui-pnotify .alert-error a{color:#b94a48}.ui-pnotify .alert-success a{color:#468847}.ui-pnotify .alert-info a{color:#3a87ad}.pnotify_additional_info .pnotify_more{font-size:85%}.text-right{text-align:right}.text-center{text-align:center}.text-block{display:block}.overflow_visible{overflow:visible!important}.clickable{cursor:pointer}.border_box{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.hidden{display:none}textarea.block{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;width:100%}@keyframes pulsate{0%{opacity:.5}50%{opacity:1}100%{opacity:.5}}#drop_overlay{position:fixed;top:0;left:0;width:100%;height:100%;z-index:10000;display:none}#drop_overlay.in{display:block}#drop_overlay #drop_overlay_background{position:fixed;top:0;left:0;width:100%;height:100%;background-color:#000;filter:alpha(opacity=50);-moz-opacity:.5;-khtml-opacity:.5;opacity:.5}#drop_overlay #drop_overlay_wrapper{position:absolute;top:0;bottom:0;left:0;right:0;padding-top:60px}#drop_overlay #drop_overlay_wrapper #drop,#drop_overlay #drop_overlay_wrapper #drop_background{position:absolute;top:0;left:0;margin-left:0;width:100%}#drop_overlay #drop_overlay_wrapper #drop_locally,#drop_overlay #drop_overlay_wrapper #drop_locally_background{position:absolute;top:0;left:50%;margin-left:-50%;width:50%;border-right:2px dashed #ccc}#drop_overlay #drop_overlay_wrapper #drop_sd,#drop_overlay #drop_overlay_wrapper #drop_sd_background{position:absolute;top:0;left:50%;margin-left:0;width:50%;border-left:2px dashed #ccc}#drop_overlay #drop_overlay_wrapper .dropzone{height:100%;z-index:10001;color:#fff;font-size:30px}#drop_overlay #drop_overlay_wrapper .dropzone i{font-size:50px}#drop_overlay #drop_overlay_wrapper .dropzone .text{display:block;text-align:center;line-height:40px;position:absolute;width:100%;bottom:5%;filter:alpha(opacity=100);-moz-opacity:1;-khtml-opacity:1;opacity:1}#drop_overlay #drop_overlay_wrapper .dropzone_background{width:50%;height:100%;background-color:#000;filter:alpha(opacity=25);-moz-opacity:.25;-khtml-opacity:.25;opacity:.25}#drop_overlay #drop_overlay_wrapper .dropzone_background.hover{background-color:#000;filter:alpha(opacity=50);-moz-opacity:.5;-khtml-opacity:.5;opacity:.5}#drop_overlay #drop_overlay_wrapper .dropzone_background.fade{-webkit-transition:all .3s ease-out;-moz-transition:all .3s ease-out;-ms-transition:all .3s ease-out;-o-transition:all .3s ease-out;transition:all .3s ease-out;opacity:1}.icon-sd-black-14{background:url(../img/icon-sd-black-14.png) 0 3px no-repeat;width:11px;height:17px;display:inline-block!important}.center{float:none;margin-left:auto;margin-right:auto}.flipH{-webkit-transform:scaleX(-1);-moz-transform:scaleX(-1);-ms-transform:scaleX(-1);transform:scaleX(-1)}.flipV{-webkit-transform:scaleY(-1);-moz-transform:scaleY(-1);-ms-transform:scaleY(-1);transform:scaleY(-1)}.flipH.flipV{-webkit-transform:scaleX(-1) scaleY(-1);-moz-transform:scaleX(-1) scaleY(-1);-ms-transform:scaleX(-1) scaleY(-1);transform:scaleX(-1) scaleY(-1)}.rotate90{-webkit-transform:rotate(-90deg);transform:rotate(-90deg)}.ui-pnotify a{text-decoration:underline}.btn-mini .caret,.btn-small .caret{margin-top:8px}.dropdown-menu-right{right:0;left:auto}.slider .slider-selection{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,.25);background-color:#006dcc;background-image:-moz-linear-gradient(top,#08c,#04c);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#04c));background-image:-webkit-linear-gradient(top,#08c,#04c);background-image:-o-linear-gradient(top,#08c,#04c);background-image:linear-gradient(to bottom,#08c,#04c);background-repeat:repeat-x;border-color:#04c #04c #002a80;border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);*background-color:#04c;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.slider .slider-selection.active,.slider .slider-selection.disabled,.slider .slider-selection:active,.slider .slider-selection:focus,.slider .slider-selection:hover,.slider .slider-selection[disabled]{color:#fff;background-color:#04c;*background-color:#003bb3}.slider .slider-selection.active,.slider .slider-selection:active{background-color:#039 \9}.slider.slider-disabled .slider-selection{background-image:none;opacity:.65;filter:alpha(opacity=65);-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.slider .slider-track{background-color:#f5f5f5;border:1px solid #e3e3e3;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05)}.slider.slider-disabled .slider-track{background-image:none;opacity:.65;filter:alpha(opacity=65);-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.slider .slider-handle{display:inline-block;*display:inline;*zoom:1;font-size:14px;line-height:20px;text-align:center;vertical-align:middle;cursor:pointer;color:#333;text-shadow:0 1px 1px rgba(255,255,255,.75);background-color:#f5f5f5;background-image:-moz-linear-gradient(top,#fff,#e6e6e6);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#e6e6e6));background-image:-webkit-linear-gradient(top,#fff,#e6e6e6);background-image:-o-linear-gradient(top,#fff,#e6e6e6);background-image:linear-gradient(to bottom,#fff,#e6e6e6);background-repeat:repeat-x;*background-color:#e6e6e6;border:1px solid #ccc;*border:0;border-bottom-color:#b3b3b3;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;*margin-left:.3em;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05);padding:0;margin-bottom:0;opacity:1;filter:alpha(opacity=100)}.slider .slider-handle.active,.slider .slider-handle.disabled,.slider .slider-handle:active,.slider .slider-handle:focus,.slider .slider-handle:hover,.slider .slider-handle[disabled]{color:#333;background-color:#e6e6e6;*background-color:#d9d9d9}.slider .slider-handle.active,.slider .slider-handle:active{background-color:#ccc \9}.slider .slider-handle:first-child{*margin-left:0}.slider .slider-handle:focus,.slider .slider-handle:hover{color:#333;text-decoration:none;background-position:0 -15px;-webkit-transition:background-position .1s linear;-moz-transition:background-position .1s linear;-o-transition:background-position .1s linear;transition:background-position .1s linear}.slider .slider-handle:focus{outline:#333 dotted thin;outline:-webkit-focus-ring-color auto 5px;outline-offset:-2px}.slider .slider-handle.active,.slider .slider-handle:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,.15),0 1px 2px rgba(0,0,0,.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,.15),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 2px 4px rgba(0,0,0,.15),0 1px 2px rgba(0,0,0,.05)}.slider .slider-handle.disabled,.slider .slider-handle[disabled]{cursor:default;background-image:none;opacity:.65;filter:alpha(opacity=65);-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.slider .slider-handle.hide{display:none}.slider .slider-handle.round{-webkit-border-radius:50%;-moz-border-radius:50%;border-radius:50%}.modal.large{width:975px;margin-left:-487px}.full-sized-box{position:absolute;bottom:0;left:0;right:0;top:0;padding:15px}.full-sized-box .row-fluid{height:100%}@media (max-width:979px){.full-sized-box{position:static}}:root .full-sized-box,_::-webkit-full-page-media,_:future{position:static}.scrollable{height:100%;overflow:auto;-webkit-overflow-scrolling:touch}.pre-output span{display:block}.input-append .add-on.add-on-limited,.input-prepend .add-on.add-on-limited{overflow-x:hidden;text-overflow:ellipsis;width:inherit}.input-append .btn-group:first-child .btn:first-child,.input-prepend .btn-group:first-child .btn:first-child{-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.input-append .btn-group .btn:first-child,.input-prepend .btn-group .btn:first-child{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.input-append.input-block-level,.input-prepend.input-block-level{display:table}.input-append.input-block-level .add-on,.input-prepend.input-block-level .add-on{display:table-cell;width:1%}.input-append.input-block-level>input,.input-prepend.input-block-level>input{box-sizing:border-box;display:table;min-height:inherit;width:100%}.input-append.input-block-level :not(:last-child),.input-prepend.input-block-level :not(:last-child){border-right:0}.control-group.error .input-append .fileinput-button,.control-group.error .input-prepend .fileinput-button{border-color:#b94a48}.control-text{padding-top:5px;cursor:default}input[type=number]{text-align:right}input[type=number].input-nospin::-webkit-inner-spin-button,input[type=number].input-nospin::-webkit-outer-spin-button{-webkit-appearance:none;margin:0}input[type=number].input-nospin{-moz-appearance:textfield}.progress-text,.progress-text-centered{position:relative}.progress-text .progress-text-back,.progress-text .progress-text-front,.progress-text-centered .progress-text-back,.progress-text-centered .progress-text-front{white-space:nowrap}.progress-text .progress-text-front,.progress-text-centered .progress-text-front{box-sizing:border-box;padding:0 10px;width:100%;display:block}.progress-text .progress-text-back,.progress-text-centered .progress-text-back{position:absolute;font-size:12px;line-height:20px;display:block;box-sizing:border-box;text-align:center;padding:0 10px}.progress-text .bar,.progress-text-centered .bar{position:absolute;overflow:hidden}.progress-text-centered .progress-text-front{position:absolute;font-size:12px;line-height:20px;display:block;text-align:center;color:#fff}.progress-text-centered .progress-text-back{width:100%}#navbar_login:not(.open) #login_dropdown_loggedout{display:block;z-index:-1;height:0;width:0;padding:0!important;overflow:hidden;border:0;box-shadow:none;left:-9999px}#navbar_login:not(.open) #login_dropdown_loggedout.hide{display:none}#loginForm{margin:0}#loginForm button{margin-top:20px} \ No newline at end of file +.btn{display:inline-block;*display:inline;*zoom:1;padding:4px 12px;margin-bottom:0;font-size:14px;line-height:20px;text-align:center;vertical-align:middle;cursor:pointer;color:#333;text-shadow:0 1px 1px rgba(255,255,255,.75);background-color:#f5f5f5;background-image:-moz-linear-gradient(top,#fff,#e6e6e6);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#e6e6e6));background-image:-webkit-linear-gradient(top,#fff,#e6e6e6);background-image:-o-linear-gradient(top,#fff,#e6e6e6);background-image:linear-gradient(to bottom,#fff,#e6e6e6);background-repeat:repeat-x;*background-color:#e6e6e6;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);border:1px solid #ccc;*border:0;border-bottom-color:#b3b3b3;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;*margin-left:.3em;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05)}.btn.active,.btn.disabled,.btn:active,.btn:focus,.btn:hover,.btn[disabled]{color:#333;background-color:#e6e6e6;*background-color:#d9d9d9}.btn.active,.btn:active{background-color:#ccc \9}.btn:first-child{*margin-left:0}.btn:focus,.btn:hover{color:#333;text-decoration:none;background-position:0 -15px;-webkit-transition:background-position .1s linear;-moz-transition:background-position .1s linear;-o-transition:background-position .1s linear;transition:background-position .1s linear}.btn:focus{outline:#333 dotted thin;outline:-webkit-focus-ring-color auto 5px;outline-offset:-2px}.btn.active,.btn:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,.15),0 1px 2px rgba(0,0,0,.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,.15),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 2px 4px rgba(0,0,0,.15),0 1px 2px rgba(0,0,0,.05)}.btn.disabled,.btn[disabled]{cursor:default;background-image:none;opacity:.65;filter:alpha(opacity=65);-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.btn-large{padding:11px 19px;font-size:17.5px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.btn-large [class*=" icon-"],.btn-large [class^=icon-]{margin-top:4px}.btn-small{padding:2px 10px;font-size:11.9px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.btn-small [class*=" icon-"],.btn-small [class^=icon-]{margin-top:0}.btn-mini [class*=" icon-"],.btn-mini [class^=icon-]{margin-top:-1px}.btn-mini{padding:0 6px;font-size:10.5px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.btn-block{display:block;width:100%;padding-left:0;padding-right:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.btn-block+.btn-block{margin-top:5px}input[type=submit].btn-block,input[type=reset].btn-block,input[type=button].btn-block{width:100%}.btn-primary{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,.25);background-color:#006dcc;background-image:-moz-linear-gradient(top,#08c,#04c);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#04c));background-image:-webkit-linear-gradient(top,#08c,#04c);background-image:-o-linear-gradient(top,#08c,#04c);background-image:linear-gradient(to bottom,#08c,#04c);background-repeat:repeat-x;border-color:#04c #04c #002a80;border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);*background-color:#04c;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-primary.active,.btn-primary.disabled,.btn-primary:active,.btn-primary:focus,.btn-primary:hover,.btn-primary[disabled]{color:#fff;background-color:#04c;*background-color:#003bb3}.btn-primary.active,.btn-primary:active{background-color:#039 \9}.btn-warning{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,.25);background-color:#faa732;background-image:-moz-linear-gradient(top,#fbb450,#f89406);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fbb450),to(#f89406));background-image:-webkit-linear-gradient(top,#fbb450,#f89406);background-image:-o-linear-gradient(top,#fbb450,#f89406);background-image:linear-gradient(to bottom,#fbb450,#f89406);background-repeat:repeat-x;border-color:#f89406 #f89406 #ad6704;border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);*background-color:#f89406;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-warning.active,.btn-warning.disabled,.btn-warning:active,.btn-warning:focus,.btn-warning:hover,.btn-warning[disabled]{color:#fff;background-color:#f89406;*background-color:#df8505}.btn-warning.active,.btn-warning:active{background-color:#c67605 \9}.btn-danger{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,.25);background-color:#da4f49;background-image:-moz-linear-gradient(top,#ee5f5b,#bd362f);background-image:-webkit-gradient(linear,0 0,0 100%,from(#ee5f5b),to(#bd362f));background-image:-webkit-linear-gradient(top,#ee5f5b,#bd362f);background-image:-o-linear-gradient(top,#ee5f5b,#bd362f);background-image:linear-gradient(to bottom,#ee5f5b,#bd362f);background-repeat:repeat-x;border-color:#bd362f #bd362f #802420;border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);*background-color:#bd362f;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-danger.active,.btn-danger.disabled,.btn-danger:active,.btn-danger:focus,.btn-danger:hover,.btn-danger[disabled]{color:#fff;background-color:#bd362f;*background-color:#a9302a}.btn-danger.active,.btn-danger:active{background-color:#942a25 \9}.btn-success{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,.25);background-color:#5bb75b;background-image:-moz-linear-gradient(top,#62c462,#51a351);background-image:-webkit-gradient(linear,0 0,0 100%,from(#62c462),to(#51a351));background-image:-webkit-linear-gradient(top,#62c462,#51a351);background-image:-o-linear-gradient(top,#62c462,#51a351);background-image:linear-gradient(to bottom,#62c462,#51a351);background-repeat:repeat-x;border-color:#51a351 #51a351 #387038;border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);*background-color:#51a351;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-success.active,.btn-success.disabled,.btn-success:active,.btn-success:focus,.btn-success:hover,.btn-success[disabled]{color:#fff;background-color:#51a351;*background-color:#499249}.btn-success.active,.btn-success:active{background-color:#408140 \9}.btn-info{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,.25);background-color:#49afcd;background-image:-moz-linear-gradient(top,#5bc0de,#2f96b4);background-image:-webkit-gradient(linear,0 0,0 100%,from(#5bc0de),to(#2f96b4));background-image:-webkit-linear-gradient(top,#5bc0de,#2f96b4);background-image:-o-linear-gradient(top,#5bc0de,#2f96b4);background-image:linear-gradient(to bottom,#5bc0de,#2f96b4);background-repeat:repeat-x;border-color:#2f96b4 #2f96b4 #1f6377;border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);*background-color:#2f96b4;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-info.active,.btn-info.disabled,.btn-info:active,.btn-info:focus,.btn-info:hover,.btn-info[disabled]{color:#fff;background-color:#2f96b4;*background-color:#2a85a0}.btn-info.active,.btn-info:active{background-color:#24748c \9}.btn-inverse{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,.25);background-color:#363636;background-image:-moz-linear-gradient(top,#444,#222);background-image:-webkit-gradient(linear,0 0,0 100%,from(#444),to(#222));background-image:-webkit-linear-gradient(top,#444,#222);background-image:-o-linear-gradient(top,#444,#222);background-image:linear-gradient(to bottom,#444,#222);background-repeat:repeat-x;border-color:#222 #222 #000;border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);*background-color:#222;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-inverse.active,.btn-inverse.disabled,.btn-inverse:active,.btn-inverse:focus,.btn-inverse:hover,.btn-inverse[disabled]{color:#fff;background-color:#222;*background-color:#151515}.btn-inverse.active,.btn-inverse:active{background-color:#080808 \9}button.btn,input[type=submit].btn{*padding-top:3px;*padding-bottom:3px}button.btn::-moz-focus-inner,input[type=submit].btn::-moz-focus-inner{padding:0;border:0}button.btn.btn-large,input[type=submit].btn.btn-large{*padding-top:7px;*padding-bottom:7px}button.btn.btn-small,input[type=submit].btn.btn-small{*padding-top:3px;*padding-bottom:3px}button.btn.btn-mini,input[type=submit].btn.btn-mini{*padding-top:1px;*padding-bottom:1px}.btn-link,.btn-link:active,.btn-link[disabled]{background-color:transparent;background-image:none;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.btn-link{border-color:transparent;cursor:pointer;color:#08c;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.btn-link:focus,.btn-link:hover{color:#005580;text-decoration:underline;background-color:transparent}.btn-link[disabled]:focus,.btn-link[disabled]:hover{color:#333;text-decoration:none}.clearfix{*zoom:1}.clearfix:after,.clearfix:before{display:table;content:"";line-height:0}.clearfix:after{clear:both}.hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.input-block-level{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.nowrap{white-space:nowrap}.actioncol{text-align:center;white-space:nowrap}.actioncol a{text-decoration:none;color:#000}.actioncol a.disabled{color:#ccc;cursor:default}#navbar .navbar-inner{background-color:#ebebeb;background-image:-moz-linear-gradient(top,#fff,#ccc);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#ccc));background-image:-webkit-linear-gradient(top,#fff,#ccc);background-image:-o-linear-gradient(top,#fff,#ccc);background-image:linear-gradient(to bottom,#fff,#ccc);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffcccccc', GradientType=0)}#navbar .navbar-inner .brand,#navbar .navbar-inner .nav>li>a{text-shadow:0 1px 0 #ccc;color:#333}#navbar .navbar-inner .brand .caret,#navbar .navbar-inner .nav>li>a .caret{border-bottom-color:#939393;border-top-color:#939393}#navbar .navbar-inner .brand:focus .caret,#navbar .navbar-inner .brand:hover .caret,#navbar .navbar-inner .nav>li>a:focus .caret,#navbar .navbar-inner .nav>li>a:hover .caret{border-bottom-color:#636363;border-top-color:#636363}#navbar .navbar-inner .brand span{background-image:url(../img/tentacle-20x20.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner .brand span{background-image:url(../img/tentacle-20x20@2x.png)}}#navbar .navbar-inner .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner .nav>li.dropdown.open>.dropdown-toggle{background-color:#e0e0e0;background-image:-moz-linear-gradient(top,#ccc,#fff);background-image:-webkit-gradient(linear,0 0,0 100%,from(#ccc),to(#fff));background-image:-webkit-linear-gradient(top,#ccc,#fff);background-image:-o-linear-gradient(top,#ccc,#fff);background-image:linear-gradient(to bottom,#ccc,#fff);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffcccccc', endColorstr='#ffffffff', GradientType=0)}#navbar .navbar-inner .nav>li>a:hover{background-color:#dedede;background-image:-moz-linear-gradient(top,#f2f2f2,#bfbfbf);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f2f2f2),to(#bfbfbf));background-image:-webkit-linear-gradient(top,#f2f2f2,#bfbfbf);background-image:-o-linear-gradient(top,#f2f2f2,#bfbfbf);background-image:linear-gradient(to bottom,#f2f2f2,#bfbfbf);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2f2f2', endColorstr='#ffbfbfbf', GradientType=0)}#navbar .navbar-inner.transparent{background-color:rgba(235,235,235,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(255,255,255,.6),rgba(204,204,204,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(255,255,255,.6)),to(rgba(204,204,204,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(255,255,255,.6),rgba(204,204,204,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(255,255,255,.6),rgba(204,204,204,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(255,255,255,.6),rgba(204,204,204,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99ffffff', endColorstr='#99cccccc', GradientType=0)}#navbar .navbar-inner.transparent .brand,#navbar .navbar-inner.transparent .nav>li>a{text-shadow:0 1px 0 #ccc;color:#333}#navbar .navbar-inner.transparent .brand .caret,#navbar .navbar-inner.transparent .nav>li>a .caret{border-bottom-color:#939393;border-top-color:#939393}#navbar .navbar-inner.transparent .brand:focus .caret,#navbar .navbar-inner.transparent .brand:hover .caret,#navbar .navbar-inner.transparent .nav>li>a:focus .caret,#navbar .navbar-inner.transparent .nav>li>a:hover .caret{border-bottom-color:#636363;border-top-color:#636363}#navbar .navbar-inner.transparent .brand span{background-image:url(../img/tentacle-20x20.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.transparent .brand span{background-image:url(../img/tentacle-20x20@2x.png)}}#navbar .navbar-inner.transparent .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.transparent .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.transparent .nav>li.dropdown.open>.dropdown-toggle{background-color:rgba(224,224,224,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(204,204,204,.6),rgba(255,255,255,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(204,204,204,.6)),to(rgba(255,255,255,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(204,204,204,.6),rgba(255,255,255,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(204,204,204,.6),rgba(255,255,255,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(204,204,204,.6),rgba(255,255,255,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99cccccc', endColorstr='#99ffffff', GradientType=0)}#navbar .navbar-inner.transparent .nav>li>a:hover{background-color:rgba(222,222,222,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(242,242,242,.6),rgba(191,191,191,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(242,242,242,.6)),to(rgba(191,191,191,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(242,242,242,.6),rgba(191,191,191,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(242,242,242,.6),rgba(191,191,191,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(242,242,242,.6),rgba(191,191,191,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99f2f2f2', endColorstr='#99bfbfbf', GradientType=0)}#navbar .navbar-inner.red{background-color:#bb645f;background-image:-moz-linear-gradient(top,#e28e8a,#802420);background-image:-webkit-gradient(linear,0 0,0 100%,from(#e28e8a),to(#802420));background-image:-webkit-linear-gradient(top,#e28e8a,#802420);background-image:-o-linear-gradient(top,#e28e8a,#802420);background-image:linear-gradient(to bottom,#e28e8a,#802420);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe28e8a', endColorstr='#ff802420', GradientType=0)}#navbar .navbar-inner.red .brand,#navbar .navbar-inner.red .nav>li>a{text-shadow:0 1px 0 #d86761;color:#f2f2f2}#navbar .navbar-inner.red .brand .caret,#navbar .navbar-inner.red .nav>li>a .caret{border-bottom-color:#d89491;border-top-color:#d89491}#navbar .navbar-inner.red .brand:focus .caret,#navbar .navbar-inner.red .brand:hover .caret,#navbar .navbar-inner.red .nav>li>a:focus .caret,#navbar .navbar-inner.red .nav>li>a:hover .caret{border-bottom-color:#e5c3c1;border-top-color:#e5c3c1}#navbar .navbar-inner.red .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.red .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.red .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.red .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.red .nav>li.dropdown.open>.dropdown-toggle{background-color:#a74f4a;background-image:-moz-linear-gradient(top,#802420,#e28e8a);background-image:-webkit-gradient(linear,0 0,0 100%,from(#802420),to(#e28e8a));background-image:-webkit-linear-gradient(top,#802420,#e28e8a);background-image:-o-linear-gradient(top,#802420,#e28e8a);background-image:linear-gradient(to bottom,#802420,#e28e8a);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff802420', endColorstr='#ffe28e8a', GradientType=0)}#navbar .navbar-inner.red .nav>li>a:hover{background-color:#af5651;background-image:-moz-linear-gradient(top,#dd7a75,#6b1f1b);background-image:-webkit-gradient(linear,0 0,0 100%,from(#dd7a75),to(#6b1f1b));background-image:-webkit-linear-gradient(top,#dd7a75,#6b1f1b);background-image:-o-linear-gradient(top,#dd7a75,#6b1f1b);background-image:linear-gradient(to bottom,#dd7a75,#6b1f1b);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdd7a75', endColorstr='#ff6b1f1b', GradientType=0)}#navbar .navbar-inner.red.transparent{background-color:rgba(187,100,95,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(226,142,138,.6),rgba(128,36,32,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(226,142,138,.6)),to(rgba(128,36,32,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(226,142,138,.6),rgba(128,36,32,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(226,142,138,.6),rgba(128,36,32,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(226,142,138,.6),rgba(128,36,32,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99e28e8a', endColorstr='#99802420', GradientType=0)}#navbar .navbar-inner.red.transparent .brand,#navbar .navbar-inner.red.transparent .nav>li>a{text-shadow:0 1px 0 #d86761;color:#f2f2f2}#navbar .navbar-inner.red.transparent .brand .caret,#navbar .navbar-inner.red.transparent .nav>li>a .caret{border-bottom-color:#d89491;border-top-color:#d89491}#navbar .navbar-inner.red.transparent .brand:focus .caret,#navbar .navbar-inner.red.transparent .brand:hover .caret,#navbar .navbar-inner.red.transparent .nav>li>a:focus .caret,#navbar .navbar-inner.red.transparent .nav>li>a:hover .caret{border-bottom-color:#e5c3c1;border-top-color:#e5c3c1}#navbar .navbar-inner.red.transparent .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.red.transparent .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.red.transparent .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.red.transparent .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.red.transparent .nav>li.dropdown.open>.dropdown-toggle{background-color:rgba(167,79,74,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(128,36,32,.6),rgba(226,142,138,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(128,36,32,.6)),to(rgba(226,142,138,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(128,36,32,.6),rgba(226,142,138,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(128,36,32,.6),rgba(226,142,138,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(128,36,32,.6),rgba(226,142,138,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99802420', endColorstr='#99e28e8a', GradientType=0)}#navbar .navbar-inner.red.transparent .nav>li>a:hover{background-color:rgba(175,86,81,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(221,122,117,.6),rgba(107,31,27,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(221,122,117,.6)),to(rgba(107,31,27,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(221,122,117,.6),rgba(107,31,27,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(221,122,117,.6),rgba(107,31,27,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(221,122,117,.6),rgba(107,31,27,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99dd7a75', endColorstr='#996b1f1b', GradientType=0)}#navbar .navbar-inner.orange{background-color:#e39665;background-image:-moz-linear-gradient(top,#f9c3a0,#c2530c);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f9c3a0),to(#c2530c));background-image:-webkit-linear-gradient(top,#f9c3a0,#c2530c);background-image:-o-linear-gradient(top,#f9c3a0,#c2530c);background-image:linear-gradient(to bottom,#f9c3a0,#c2530c);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff9c3a0', endColorstr='#ffc2530c', GradientType=0)}#navbar .navbar-inner.orange .brand,#navbar .navbar-inner.orange .nav>li>a{text-shadow:0 1px 0 #f6a570;color:#f2f2f2}#navbar .navbar-inner.orange .brand .caret,#navbar .navbar-inner.orange .nav>li>a .caret{border-bottom-color:#f2b58d;border-top-color:#f2b58d}#navbar .navbar-inner.orange .brand:focus .caret,#navbar .navbar-inner.orange .brand:hover .caret,#navbar .navbar-inner.orange .nav>li>a:focus .caret,#navbar .navbar-inner.orange .nav>li>a:hover .caret{border-bottom-color:#f2d3bf;border-top-color:#f2d3bf}#navbar .navbar-inner.orange .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.orange .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.orange .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.orange .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.orange .nav>li.dropdown.open>.dropdown-toggle{background-color:#d88047;background-image:-moz-linear-gradient(top,#c2530c,#f9c3a0);background-image:-webkit-gradient(linear,0 0,0 100%,from(#c2530c),to(#f9c3a0));background-image:-webkit-linear-gradient(top,#c2530c,#f9c3a0);background-image:-o-linear-gradient(top,#c2530c,#f9c3a0);background-image:linear-gradient(to bottom,#c2530c,#f9c3a0);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffc2530c', endColorstr='#fff9c3a0', GradientType=0)}#navbar .navbar-inner.orange .nav>li>a:hover{background-color:#d98956;background-image:-moz-linear-gradient(top,#f8b488,#aa490a);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f8b488),to(#aa490a));background-image:-webkit-linear-gradient(top,#f8b488,#aa490a);background-image:-o-linear-gradient(top,#f8b488,#aa490a);background-image:linear-gradient(to bottom,#f8b488,#aa490a);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff8b488', endColorstr='#ffaa490a', GradientType=0)}#navbar .navbar-inner.orange.transparent{background-color:rgba(227,150,101,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(249,195,160,.6),rgba(194,83,12,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(249,195,160,.6)),to(rgba(194,83,12,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(249,195,160,.6),rgba(194,83,12,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(249,195,160,.6),rgba(194,83,12,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(249,195,160,.6),rgba(194,83,12,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99f9c3a0', endColorstr='#99c2530c', GradientType=0)}#navbar .navbar-inner.orange.transparent .brand,#navbar .navbar-inner.orange.transparent .nav>li>a{text-shadow:0 1px 0 #f6a570;color:#f2f2f2}#navbar .navbar-inner.orange.transparent .brand .caret,#navbar .navbar-inner.orange.transparent .nav>li>a .caret{border-bottom-color:#f2b58d;border-top-color:#f2b58d}#navbar .navbar-inner.orange.transparent .brand:focus .caret,#navbar .navbar-inner.orange.transparent .brand:hover .caret,#navbar .navbar-inner.orange.transparent .nav>li>a:focus .caret,#navbar .navbar-inner.orange.transparent .nav>li>a:hover .caret{border-bottom-color:#f2d3bf;border-top-color:#f2d3bf}#navbar .navbar-inner.orange.transparent .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.orange.transparent .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.orange.transparent .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.orange.transparent .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.orange.transparent .nav>li.dropdown.open>.dropdown-toggle{background-color:rgba(216,128,71,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(194,83,12,.6),rgba(249,195,160,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(194,83,12,.6)),to(rgba(249,195,160,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(194,83,12,.6),rgba(249,195,160,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(194,83,12,.6),rgba(249,195,160,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(194,83,12,.6),rgba(249,195,160,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99c2530c', endColorstr='#99f9c3a0', GradientType=0)}#navbar .navbar-inner.orange.transparent .nav>li>a:hover{background-color:rgba(217,137,86,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(248,180,136,.6),rgba(170,73,10,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(248,180,136,.6)),to(rgba(170,73,10,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(248,180,136,.6),rgba(170,73,10,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(248,180,136,.6),rgba(170,73,10,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(248,180,136,.6),rgba(170,73,10,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99f8b488', endColorstr='#99aa490a', GradientType=0)}#navbar .navbar-inner.yellow{background-color:#e3d765;background-image:-moz-linear-gradient(top,#f9f0a0,#c2b00c);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f9f0a0),to(#c2b00c));background-image:-webkit-linear-gradient(top,#f9f0a0,#c2b00c);background-image:-o-linear-gradient(top,#f9f0a0,#c2b00c);background-image:linear-gradient(to bottom,#f9f0a0,#c2b00c);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff9f0a0', endColorstr='#ffc2b00c', GradientType=0)}#navbar .navbar-inner.yellow .brand,#navbar .navbar-inner.yellow .nav>li>a{text-shadow:0 1px 0 #c2b00c;color:#f2f2f2}#navbar .navbar-inner.yellow .brand .caret,#navbar .navbar-inner.yellow .nav>li>a .caret{border-bottom-color:#f2e88d;border-top-color:#f2e88d}#navbar .navbar-inner.yellow .brand:focus .caret,#navbar .navbar-inner.yellow .brand:hover .caret,#navbar .navbar-inner.yellow .nav>li>a:focus .caret,#navbar .navbar-inner.yellow .nav>li>a:hover .caret{border-bottom-color:#f2edbf;border-top-color:#f2edbf}#navbar .navbar-inner.yellow .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.yellow .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.yellow .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.yellow .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.yellow .nav>li.dropdown.open>.dropdown-toggle{background-color:#d8ca47;background-image:-moz-linear-gradient(top,#c2b00c,#f9f0a0);background-image:-webkit-gradient(linear,0 0,0 100%,from(#c2b00c),to(#f9f0a0));background-image:-webkit-linear-gradient(top,#c2b00c,#f9f0a0);background-image:-o-linear-gradient(top,#c2b00c,#f9f0a0);background-image:linear-gradient(to bottom,#c2b00c,#f9f0a0);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffc2b00c', endColorstr='#fff9f0a0', GradientType=0)}#navbar .navbar-inner.yellow .nav>li>a:hover{background-color:#d9cc56;background-image:-moz-linear-gradient(top,#f8ed88,#aa9a0a);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f8ed88),to(#aa9a0a));background-image:-webkit-linear-gradient(top,#f8ed88,#aa9a0a);background-image:-o-linear-gradient(top,#f8ed88,#aa9a0a);background-image:linear-gradient(to bottom,#f8ed88,#aa9a0a);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff8ed88', endColorstr='#ffaa9a0a', GradientType=0)}#navbar .navbar-inner.yellow.transparent{background-color:rgba(227,215,101,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(249,240,160,.6),rgba(194,176,12,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(249,240,160,.6)),to(rgba(194,176,12,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(249,240,160,.6),rgba(194,176,12,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(249,240,160,.6),rgba(194,176,12,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(249,240,160,.6),rgba(194,176,12,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99f9f0a0', endColorstr='#99c2b00c', GradientType=0)}#navbar .navbar-inner.yellow.transparent .brand,#navbar .navbar-inner.yellow.transparent .nav>li>a{text-shadow:0 1px 0 #c2b00c;color:#f2f2f2}#navbar .navbar-inner.yellow.transparent .brand .caret,#navbar .navbar-inner.yellow.transparent .nav>li>a .caret{border-bottom-color:#f2e88d;border-top-color:#f2e88d}#navbar .navbar-inner.yellow.transparent .brand:focus .caret,#navbar .navbar-inner.yellow.transparent .brand:hover .caret,#navbar .navbar-inner.yellow.transparent .nav>li>a:focus .caret,#navbar .navbar-inner.yellow.transparent .nav>li>a:hover .caret{border-bottom-color:#f2edbf;border-top-color:#f2edbf}#navbar .navbar-inner.yellow.transparent .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.yellow.transparent .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.yellow.transparent .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.yellow.transparent .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.yellow.transparent .nav>li.dropdown.open>.dropdown-toggle{background-color:rgba(216,202,71,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(194,176,12,.6),rgba(249,240,160,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(194,176,12,.6)),to(rgba(249,240,160,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(194,176,12,.6),rgba(249,240,160,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(194,176,12,.6),rgba(249,240,160,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(194,176,12,.6),rgba(249,240,160,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99c2b00c', endColorstr='#99f9f0a0', GradientType=0)}#navbar .navbar-inner.yellow.transparent .nav>li>a:hover{background-color:rgba(217,204,86,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(248,237,136,.6),rgba(170,154,10,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(248,237,136,.6)),to(rgba(170,154,10,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(248,237,136,.6),rgba(170,154,10,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(248,237,136,.6),rgba(170,154,10,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(248,237,136,.6),rgba(170,154,10,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99f8ed88', endColorstr='#99aa9a0a', GradientType=0)}#navbar .navbar-inner.green{background-color:#98f064;background-image:-moz-linear-gradient(top,#c8ffa7,#50da00);background-image:-webkit-gradient(linear,0 0,0 100%,from(#c8ffa7),to(#50da00));background-image:-webkit-linear-gradient(top,#c8ffa7,#50da00);background-image:-o-linear-gradient(top,#c8ffa7,#50da00);background-image:linear-gradient(to bottom,#c8ffa7,#50da00);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffc8ffa7', endColorstr='#ff50da00', GradientType=0)}#navbar .navbar-inner.green .brand,#navbar .navbar-inner.green .nav>li>a{text-shadow:0 1px 0 #50da00;color:#333}#navbar .navbar-inner.green .brand .caret,#navbar .navbar-inner.green .nav>li>a .caret{border-bottom-color:#55992e;border-top-color:#55992e}#navbar .navbar-inner.green .brand:focus .caret,#navbar .navbar-inner.green .brand:hover .caret,#navbar .navbar-inner.green .nav>li>a:focus .caret,#navbar .navbar-inner.green .nav>li>a:hover .caret{border-bottom-color:#446630;border-top-color:#446630}#navbar .navbar-inner.green .brand span{background-image:url(../img/tentacle-20x20.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.green .brand span{background-image:url(../img/tentacle-20x20@2x.png)}}#navbar .navbar-inner.green .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.green .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.green .nav>li.dropdown.open>.dropdown-toggle{background-color:#80e943;background-image:-moz-linear-gradient(top,#50da00,#c8ffa7);background-image:-webkit-gradient(linear,0 0,0 100%,from(#50da00),to(#c8ffa7));background-image:-webkit-linear-gradient(top,#50da00,#c8ffa7);background-image:-o-linear-gradient(top,#50da00,#c8ffa7);background-image:linear-gradient(to bottom,#50da00,#c8ffa7);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff50da00', endColorstr='#ffc8ffa7', GradientType=0)}#navbar .navbar-inner.green .nav>li>a:hover{background-color:#8ae655;background-image:-moz-linear-gradient(top,#b8ff8e,#47c100);background-image:-webkit-gradient(linear,0 0,0 100%,from(#b8ff8e),to(#47c100));background-image:-webkit-linear-gradient(top,#b8ff8e,#47c100);background-image:-o-linear-gradient(top,#b8ff8e,#47c100);background-image:linear-gradient(to bottom,#b8ff8e,#47c100);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffb8ff8e', endColorstr='#ff47c100', GradientType=0)}#navbar .navbar-inner.green.transparent{background-color:rgba(152,240,100,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(200,255,167,.6),rgba(80,218,0,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(200,255,167,.6)),to(rgba(80,218,0,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(200,255,167,.6),rgba(80,218,0,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(200,255,167,.6),rgba(80,218,0,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(200,255,167,.6),rgba(80,218,0,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99c8ffa7', endColorstr='#9950da00', GradientType=0)}#navbar .navbar-inner.green.transparent .brand,#navbar .navbar-inner.green.transparent .nav>li>a{text-shadow:0 1px 0 #50da00;color:#333}#navbar .navbar-inner.green.transparent .brand .caret,#navbar .navbar-inner.green.transparent .nav>li>a .caret{border-bottom-color:#55992e;border-top-color:#55992e}#navbar .navbar-inner.green.transparent .brand:focus .caret,#navbar .navbar-inner.green.transparent .brand:hover .caret,#navbar .navbar-inner.green.transparent .nav>li>a:focus .caret,#navbar .navbar-inner.green.transparent .nav>li>a:hover .caret{border-bottom-color:#446630;border-top-color:#446630}#navbar .navbar-inner.green.transparent .brand span{background-image:url(../img/tentacle-20x20.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.green.transparent .brand span{background-image:url(../img/tentacle-20x20@2x.png)}}#navbar .navbar-inner.green.transparent .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.green.transparent .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.green.transparent .nav>li.dropdown.open>.dropdown-toggle{background-color:rgba(128,233,67,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(80,218,0,.6),rgba(200,255,167,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(80,218,0,.6)),to(rgba(200,255,167,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(80,218,0,.6),rgba(200,255,167,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(80,218,0,.6),rgba(200,255,167,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(80,218,0,.6),rgba(200,255,167,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#9950da00', endColorstr='#99c8ffa7', GradientType=0)}#navbar .navbar-inner.green.transparent .nav>li>a:hover{background-color:rgba(138,230,85,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(184,255,142,.6),rgba(71,193,0,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(184,255,142,.6)),to(rgba(71,193,0,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(184,255,142,.6),rgba(71,193,0,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(184,255,142,.6),rgba(71,193,0,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(184,255,142,.6),rgba(71,193,0,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99b8ff8e', endColorstr='#9947c100', GradientType=0)}#navbar .navbar-inner.blue{background-color:#2e63cc;background-image:-moz-linear-gradient(top,#4d88ff,#002b80);background-image:-webkit-gradient(linear,0 0,0 100%,from(#4d88ff),to(#002b80));background-image:-webkit-linear-gradient(top,#4d88ff,#002b80);background-image:-o-linear-gradient(top,#4d88ff,#002b80);background-image:linear-gradient(to bottom,#4d88ff,#002b80);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff4d88ff', endColorstr='#ff002b80', GradientType=0)}#navbar .navbar-inner.blue .brand,#navbar .navbar-inner.blue .nav>li>a{text-shadow:0 1px 0 #1a66ff;color:#f2f2f2}#navbar .navbar-inner.blue .brand .caret,#navbar .navbar-inner.blue .nav>li>a .caret{border-bottom-color:#799bdf;border-top-color:#799bdf}#navbar .navbar-inner.blue .brand:focus .caret,#navbar .navbar-inner.blue .brand:hover .caret,#navbar .navbar-inner.blue .nav>li>a:focus .caret,#navbar .navbar-inner.blue .nav>li>a:hover .caret{border-bottom-color:#b6c7e9;border-top-color:#b6c7e9}#navbar .navbar-inner.blue .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.blue .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.blue .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.blue .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.blue .nav>li.dropdown.open>.dropdown-toggle{background-color:#1f50b3;background-image:-moz-linear-gradient(top,#002b80,#4d88ff);background-image:-webkit-gradient(linear,0 0,0 100%,from(#002b80),to(#4d88ff));background-image:-webkit-linear-gradient(top,#002b80,#4d88ff);background-image:-o-linear-gradient(top,#002b80,#4d88ff);background-image:linear-gradient(to bottom,#002b80,#4d88ff);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff002b80', endColorstr='#ff4d88ff', GradientType=0)}#navbar .navbar-inner.blue .nav>li>a:hover{background-color:#1f55c2;background-image:-moz-linear-gradient(top,#37f,#026);background-image:-webkit-gradient(linear,0 0,0 100%,from(#37f),to(#026));background-image:-webkit-linear-gradient(top,#37f,#026);background-image:-o-linear-gradient(top,#37f,#026);background-image:linear-gradient(to bottom,#37f,#026);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3377ff', endColorstr='#ff002266', GradientType=0)}#navbar .navbar-inner.blue.transparent{background-color:rgba(46,99,204,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(77,136,255,.6),rgba(0,43,128,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(77,136,255,.6)),to(rgba(0,43,128,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(77,136,255,.6),rgba(0,43,128,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(77,136,255,.6),rgba(0,43,128,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(77,136,255,.6),rgba(0,43,128,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#994d88ff', endColorstr='#99002b80', GradientType=0)}#navbar .navbar-inner.blue.transparent .brand,#navbar .navbar-inner.blue.transparent .nav>li>a{text-shadow:0 1px 0 #1a66ff;color:#f2f2f2}#navbar .navbar-inner.blue.transparent .brand .caret,#navbar .navbar-inner.blue.transparent .nav>li>a .caret{border-bottom-color:#799bdf;border-top-color:#799bdf}#navbar .navbar-inner.blue.transparent .brand:focus .caret,#navbar .navbar-inner.blue.transparent .brand:hover .caret,#navbar .navbar-inner.blue.transparent .nav>li>a:focus .caret,#navbar .navbar-inner.blue.transparent .nav>li>a:hover .caret{border-bottom-color:#b6c7e9;border-top-color:#b6c7e9}#navbar .navbar-inner.blue.transparent .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.blue.transparent .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.blue.transparent .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.blue.transparent .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.blue.transparent .nav>li.dropdown.open>.dropdown-toggle{background-color:rgba(31,80,179,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(0,43,128,.6),rgba(77,136,255,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(0,43,128,.6)),to(rgba(77,136,255,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(0,43,128,.6),rgba(77,136,255,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(0,43,128,.6),rgba(77,136,255,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(0,43,128,.6),rgba(77,136,255,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99002b80', endColorstr='#994d88ff', GradientType=0)}#navbar .navbar-inner.blue.transparent .nav>li>a:hover{background-color:rgba(31,85,194,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(51,119,255,.6),rgba(0,34,102,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(51,119,255,.6)),to(rgba(0,34,102,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(51,119,255,.6),rgba(0,34,102,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(51,119,255,.6),rgba(0,34,102,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(51,119,255,.6),rgba(0,34,102,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#993377ff', endColorstr='#99002266', GradientType=0)}#navbar .navbar-inner.violet{background-color:#9864f0;background-image:-moz-linear-gradient(top,#c8a7ff,#5000da);background-image:-webkit-gradient(linear,0 0,0 100%,from(#c8a7ff),to(#5000da));background-image:-webkit-linear-gradient(top,#c8a7ff,#5000da);background-image:-o-linear-gradient(top,#c8a7ff,#5000da);background-image:linear-gradient(to bottom,#c8a7ff,#5000da);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffc8a7ff', endColorstr='#ff5000da', GradientType=0)}#navbar .navbar-inner.violet .brand,#navbar .navbar-inner.violet .nav>li>a{text-shadow:0 1px 0 #a774ff;color:#f2f2f2}#navbar .navbar-inner.violet .brand .caret,#navbar .navbar-inner.violet .nav>li>a .caret{border-bottom-color:#b58df9;border-top-color:#b58df9}#navbar .navbar-inner.violet .brand:focus .caret,#navbar .navbar-inner.violet .brand:hover .caret,#navbar .navbar-inner.violet .nav>li>a:focus .caret,#navbar .navbar-inner.violet .nav>li>a:hover .caret{border-bottom-color:#d3bff5;border-top-color:#d3bff5}#navbar .navbar-inner.violet .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.violet .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.violet .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.violet .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.violet .nav>li.dropdown.open>.dropdown-toggle{background-color:#8043e9;background-image:-moz-linear-gradient(top,#5000da,#c8a7ff);background-image:-webkit-gradient(linear,0 0,0 100%,from(#5000da),to(#c8a7ff));background-image:-webkit-linear-gradient(top,#5000da,#c8a7ff);background-image:-o-linear-gradient(top,#5000da,#c8a7ff);background-image:linear-gradient(to bottom,#5000da,#c8a7ff);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5000da', endColorstr='#ffc8a7ff', GradientType=0)}#navbar .navbar-inner.violet .nav>li>a:hover{background-color:#8a55e6;background-image:-moz-linear-gradient(top,#b88eff,#4700c1);background-image:-webkit-gradient(linear,0 0,0 100%,from(#b88eff),to(#4700c1));background-image:-webkit-linear-gradient(top,#b88eff,#4700c1);background-image:-o-linear-gradient(top,#b88eff,#4700c1);background-image:linear-gradient(to bottom,#b88eff,#4700c1);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffb88eff', endColorstr='#ff4700c1', GradientType=0)}#navbar .navbar-inner.violet.transparent{background-color:rgba(152,100,240,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(200,167,255,.6),rgba(80,0,218,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(200,167,255,.6)),to(rgba(80,0,218,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(200,167,255,.6),rgba(80,0,218,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(200,167,255,.6),rgba(80,0,218,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(200,167,255,.6),rgba(80,0,218,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99c8a7ff', endColorstr='#995000da', GradientType=0)}#navbar .navbar-inner.violet.transparent .brand,#navbar .navbar-inner.violet.transparent .nav>li>a{text-shadow:0 1px 0 #a774ff;color:#f2f2f2}#navbar .navbar-inner.violet.transparent .brand .caret,#navbar .navbar-inner.violet.transparent .nav>li>a .caret{border-bottom-color:#b58df9;border-top-color:#b58df9}#navbar .navbar-inner.violet.transparent .brand:focus .caret,#navbar .navbar-inner.violet.transparent .brand:hover .caret,#navbar .navbar-inner.violet.transparent .nav>li>a:focus .caret,#navbar .navbar-inner.violet.transparent .nav>li>a:hover .caret{border-bottom-color:#d3bff5;border-top-color:#d3bff5}#navbar .navbar-inner.violet.transparent .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.violet.transparent .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.violet.transparent .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.violet.transparent .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.violet.transparent .nav>li.dropdown.open>.dropdown-toggle{background-color:rgba(128,67,233,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(80,0,218,.6),rgba(200,167,255,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(80,0,218,.6)),to(rgba(200,167,255,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(80,0,218,.6),rgba(200,167,255,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(80,0,218,.6),rgba(200,167,255,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(80,0,218,.6),rgba(200,167,255,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#995000da', endColorstr='#99c8a7ff', GradientType=0)}#navbar .navbar-inner.violet.transparent .nav>li>a:hover{background-color:rgba(138,85,230,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(184,142,255,.6),rgba(71,0,193,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(184,142,255,.6)),to(rgba(71,0,193,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(184,142,255,.6),rgba(71,0,193,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(184,142,255,.6),rgba(71,0,193,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(184,142,255,.6),rgba(71,0,193,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99b88eff', endColorstr='#994700c1', GradientType=0)}#navbar .navbar-inner.black{background-color:#4f4f4f;background-image:-moz-linear-gradient(top,#787878,#121212);background-image:-webkit-gradient(linear,0 0,0 100%,from(#787878),to(#121212));background-image:-webkit-linear-gradient(top,#787878,#121212);background-image:-o-linear-gradient(top,#787878,#121212);background-image:linear-gradient(to bottom,#787878,#121212);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff787878', endColorstr='#ff121212', GradientType=0)}#navbar .navbar-inner.black .brand,#navbar .navbar-inner.black .nav>li>a{text-shadow:0 1px 0 #5e5e5e;color:#f2f2f2}#navbar .navbar-inner.black .brand .caret,#navbar .navbar-inner.black .nav>li>a .caret{border-bottom-color:#959595;border-top-color:#959595}#navbar .navbar-inner.black .brand:focus .caret,#navbar .navbar-inner.black .brand:hover .caret,#navbar .navbar-inner.black .nav>li>a:focus .caret,#navbar .navbar-inner.black .nav>li>a:hover .caret{border-bottom-color:#c4c4c4;border-top-color:#c4c4c4}#navbar .navbar-inner.black .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.black .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.black .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.black .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.black .nav>li.dropdown.open>.dropdown-toggle{background-color:#3b3b3b;background-image:-moz-linear-gradient(top,#121212,#787878);background-image:-webkit-gradient(linear,0 0,0 100%,from(#121212),to(#787878));background-image:-webkit-linear-gradient(top,#121212,#787878);background-image:-o-linear-gradient(top,#121212,#787878);background-image:linear-gradient(to bottom,#121212,#787878);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff121212', endColorstr='#ff787878', GradientType=0)}#navbar .navbar-inner.black .nav>li>a:hover{background-color:#424242;background-image:-moz-linear-gradient(top,#6b6b6b,#050505);background-image:-webkit-gradient(linear,0 0,0 100%,from(#6b6b6b),to(#050505));background-image:-webkit-linear-gradient(top,#6b6b6b,#050505);background-image:-o-linear-gradient(top,#6b6b6b,#050505);background-image:linear-gradient(to bottom,#6b6b6b,#050505);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff6b6b6b', endColorstr='#ff050505', GradientType=0)}#navbar .navbar-inner.black.transparent{background-color:rgba(79,79,79,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(120,120,120,.6),rgba(18,18,18,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(120,120,120,.6)),to(rgba(18,18,18,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(120,120,120,.6),rgba(18,18,18,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(120,120,120,.6),rgba(18,18,18,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(120,120,120,.6),rgba(18,18,18,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99787878', endColorstr='#99121212', GradientType=0)}#navbar .navbar-inner.black.transparent .brand,#navbar .navbar-inner.black.transparent .nav>li>a{text-shadow:0 1px 0 #5e5e5e;color:#f2f2f2}#navbar .navbar-inner.black.transparent .brand .caret,#navbar .navbar-inner.black.transparent .nav>li>a .caret{border-bottom-color:#959595;border-top-color:#959595}#navbar .navbar-inner.black.transparent .brand:focus .caret,#navbar .navbar-inner.black.transparent .brand:hover .caret,#navbar .navbar-inner.black.transparent .nav>li>a:focus .caret,#navbar .navbar-inner.black.transparent .nav>li>a:hover .caret{border-bottom-color:#c4c4c4;border-top-color:#c4c4c4}#navbar .navbar-inner.black.transparent .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.black.transparent .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.black.transparent .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.black.transparent .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.black.transparent .nav>li.dropdown.open>.dropdown-toggle{background-color:rgba(59,59,59,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(18,18,18,.6),rgba(120,120,120,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(18,18,18,.6)),to(rgba(120,120,120,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(18,18,18,.6),rgba(120,120,120,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(18,18,18,.6),rgba(120,120,120,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(18,18,18,.6),rgba(120,120,120,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99121212', endColorstr='#99787878', GradientType=0)}#navbar .navbar-inner.black.transparent .nav>li>a:hover{background-color:rgba(66,66,66,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(107,107,107,.6),rgba(5,5,5,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(107,107,107,.6)),to(rgba(5,5,5,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(107,107,107,.6),rgba(5,5,5,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(107,107,107,.6),rgba(5,5,5,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(107,107,107,.6),rgba(5,5,5,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#996b6b6b', endColorstr='#99050505', GradientType=0)}#navbar .navbar-inner.white{background-color:#e9e9e9;background-image:-moz-linear-gradient(top,#fff,#c8c8c8);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#c8c8c8));background-image:-webkit-linear-gradient(top,#fff,#c8c8c8);background-image:-o-linear-gradient(top,#fff,#c8c8c8);background-image:linear-gradient(to bottom,#fff,#c8c8c8);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffc8c8c8', GradientType=0)}#navbar .navbar-inner.white .brand,#navbar .navbar-inner.white .nav>li>a{text-shadow:0 1px 0 #c8c8c8;color:#333}#navbar .navbar-inner.white .brand .caret,#navbar .navbar-inner.white .nav>li>a .caret{border-bottom-color:#919191;border-top-color:#919191}#navbar .navbar-inner.white .brand:focus .caret,#navbar .navbar-inner.white .brand:hover .caret,#navbar .navbar-inner.white .nav>li>a:focus .caret,#navbar .navbar-inner.white .nav>li>a:hover .caret{border-bottom-color:#626262;border-top-color:#626262}#navbar .navbar-inner.white .brand span{background-image:url(../img/tentacle-20x20.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.white .brand span{background-image:url(../img/tentacle-20x20@2x.png)}}#navbar .navbar-inner.white .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.white .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.white .nav>li.dropdown.open>.dropdown-toggle{background-color:#dedede;background-image:-moz-linear-gradient(top,#c8c8c8,#fff);background-image:-webkit-gradient(linear,0 0,0 100%,from(#c8c8c8),to(#fff));background-image:-webkit-linear-gradient(top,#c8c8c8,#fff);background-image:-o-linear-gradient(top,#c8c8c8,#fff);background-image:linear-gradient(to bottom,#c8c8c8,#fff);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffc8c8c8', endColorstr='#ffffffff', GradientType=0)}#navbar .navbar-inner.white .nav>li>a:hover{background-color:#dcdcdc;background-image:-moz-linear-gradient(top,#f2f2f2,#bbb);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f2f2f2),to(#bbb));background-image:-webkit-linear-gradient(top,#f2f2f2,#bbb);background-image:-o-linear-gradient(top,#f2f2f2,#bbb);background-image:linear-gradient(to bottom,#f2f2f2,#bbb);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2f2f2', endColorstr='#ffbbbbbb', GradientType=0)}#navbar .navbar-inner.white.transparent{background-color:rgba(233,233,233,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(255,255,255,.6),rgba(200,200,200,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(255,255,255,.6)),to(rgba(200,200,200,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(255,255,255,.6),rgba(200,200,200,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(255,255,255,.6),rgba(200,200,200,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(255,255,255,.6),rgba(200,200,200,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99ffffff', endColorstr='#99c8c8c8', GradientType=0)}#navbar .navbar-inner.white.transparent .brand,#navbar .navbar-inner.white.transparent .nav>li>a{text-shadow:0 1px 0 #c8c8c8;color:#333}#navbar .navbar-inner.white.transparent .brand .caret,#navbar .navbar-inner.white.transparent .nav>li>a .caret{border-bottom-color:#919191;border-top-color:#919191}#navbar .navbar-inner.white.transparent .brand:focus .caret,#navbar .navbar-inner.white.transparent .brand:hover .caret,#navbar .navbar-inner.white.transparent .nav>li>a:focus .caret,#navbar .navbar-inner.white.transparent .nav>li>a:hover .caret{border-bottom-color:#626262;border-top-color:#626262}#navbar .navbar-inner.white.transparent .brand span{background-image:url(../img/tentacle-20x20.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.white.transparent .brand span{background-image:url(../img/tentacle-20x20@2x.png)}}#navbar .navbar-inner.white.transparent .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.white.transparent .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.white.transparent .nav>li.dropdown.open>.dropdown-toggle{background-color:rgba(222,222,222,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(200,200,200,.6),rgba(255,255,255,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(200,200,200,.6)),to(rgba(255,255,255,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(200,200,200,.6),rgba(255,255,255,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(200,200,200,.6),rgba(255,255,255,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(200,200,200,.6),rgba(255,255,255,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99c8c8c8', endColorstr='#99ffffff', GradientType=0)}#navbar .navbar-inner.white.transparent .nav>li>a:hover{background-color:rgba(220,220,220,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(242,242,242,.6),rgba(187,187,187,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(242,242,242,.6)),to(rgba(187,187,187,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(242,242,242,.6),rgba(187,187,187,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(242,242,242,.6),rgba(187,187,187,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(242,242,242,.6),rgba(187,187,187,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99f2f2f2', endColorstr='#99bbbbbb', GradientType=0)}#navbar .navbar-inner .brand{padding:10px 20px 6px}#navbar .navbar-inner .brand span{padding-left:26px;background-size:20px 20px;background-repeat:no-repeat;display:inline-block;max-width:250px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;vertical-align:top;line-height:20px;height:24px}#navbar_login a.dropdown-toggle span{display:inline-block;max-width:100px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;vertical-align:top}.octoprint-container{margin-top:20px}.octoprint-container .tab-content{padding:9px 15px;border-left:1px solid #DDD;border-right:1px solid #DDD;border-bottom:1px solid #DDD;-webkit-border-bottom-right-radius:4px;-moz-border-radius-bottomright:4px;border-bottom-right-radius:4px;-webkit-border-bottom-left-radius:4px;-moz-border-radius-bottomleft:4px;border-bottom-left-radius:4px}.octoprint-container .nav{margin-bottom:0}.octoprint-container .tab-content h1{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:40px;color:#333;border:0;border-bottom:1px solid #E5E5E5;font-weight:400}.octoprint-container .accordion-heading .accordion-heading-button{float:right}.octoprint-container .accordion-heading .accordion-heading-button a{display:inline-block;padding:8px 15px;font-size:14px;line-height:20px;color:#000;text-decoration:none;background:0 0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.octoprint-container .accordion-heading a.accordion-toggle{display:inline-block}.octoprint-container .accordion-heading [class*=" icon-"],.octoprint-container .accordion-heading [class^=icon-]{color:#000}.print-control .btn{padding-left:4px;padding-right:4px}.upload-buttons .btn{margin-right:0}table{table-layout:fixed}table .popover-title{text-overflow:ellipsis;word-break:break-all}table td,table th{overflow:hidden}table td.gcode_files_name,table th.gcode_files_name{text-overflow:ellipsis;text-align:left;white-space:nowrap}table td.gcode_files_action,table th.gcode_files_action{width:90px;text-align:center;white-space:nowrap}table td.gcode_files_action a,table th.gcode_files_action a{text-decoration:none;color:#000}table td.gcode_files_action a.disabled,table th.gcode_files_action a.disabled{color:#ccc;cursor:default}table td.timelapse_files_checkbox,table td.timelapse_unrendered_checkbox,table th.timelapse_files_checkbox,table th.timelapse_unrendered_checkbox{text-align:center;width:10px}table td.timelapse_files_checkbox input[type=checkbox],table td.timelapse_unrendered_checkbox input[type=checkbox],table th.timelapse_files_checkbox input[type=checkbox],table th.timelapse_unrendered_checkbox input[type=checkbox]{margin-top:0}table td.timelapse_files_name,table td.timelapse_unrendered_name,table th.timelapse_files_name,table th.timelapse_unrendered_name{text-overflow:ellipsis;text-align:left}table td.timelapse_files_size,table td.timelapse_unrendered_size,table th.timelapse_files_size,table th.timelapse_unrendered_size{text-align:right;width:55px}table td.timelapse_unrendered_count,table th.timelapse_unrendered_count{text-align:right;width:45px}table td.timelapse_files_action,table td.timelapse_unrendered_action,table th.timelapse_files_action,table th.timelapse_unrendered_action{width:45px;text-align:center;white-space:nowrap}table td.timelapse_files_action a,table td.timelapse_unrendered_action a,table th.timelapse_files_action a,table th.timelapse_unrendered_action a{text-decoration:none;color:#000}table td.timelapse_files_action a.disabled,table td.timelapse_unrendered_action a.disabled,table th.timelapse_files_action a.disabled,table th.timelapse_unrendered_action a.disabled{color:#ccc;cursor:default}table td.settings_users_name,table th.settings_users_name{text-overflow:ellipsis;text-align:left}table td.settings_users_active,table td.settings_users_admin,table th.settings_users_active,table th.settings_users_admin{text-align:center;width:55px}table td.settings_users_actions,table th.settings_users_actions{width:60px;text-align:center;white-space:nowrap}table td.settings_users_actions a,table th.settings_users_actions a{text-decoration:none;color:#000}table td.settings_users_actions a.disabled,table th.settings_users_actions a.disabled{color:#ccc;cursor:default}table td.settings_logs_name,table th.settings_logs_name{text-overflow:ellipsis;text-align:left}table td.settings_logs_size,table th.settings_logs_size{text-align:right;width:70px}table td.settings_logs_date,table th.settings_logs_date{text-align:left;width:130px}table td.settings_logs_action,table th.settings_logs_action{width:70px;text-align:center;white-space:nowrap}table td.settings_logs_action a,table th.settings_logs_action a{text-decoration:none;color:#000}table td.settings_logs_action a.disabled,table th.settings_logs_action a.disabled{color:#ccc;cursor:default}table td.settings_printerProfiles_profiles_name,table th.settings_printerProfiles_profiles_name{text-overflow:ellipsis;text-align:left}table td.settings_printerProfiles_profiles_model,table th.settings_printerProfiles_profiles_model{text-align:left;width:250px}table td.settings_printerProfiles_profiles_action,table th.settings_printerProfiles_profiles_action{width:80px;text-align:center;white-space:nowrap}table td.settings_printerProfiles_profiles_action a,table th.settings_printerProfiles_profiles_action a{text-decoration:none;color:#000}table td.settings_printerProfiles_profiles_action a.disabled,table th.settings_printerProfiles_profiles_action a.disabled{color:#ccc;cursor:default}#temperature-graph{height:350px;width:100%;background:url(../img/graph-background.png) center no-repeat}#temperature-table{table-layout:fixed;width:100%;margin-top:20px}#temperature-table td.temperature_actual,#temperature-table td.temperature_offset,#temperature-table td.temperature_target,#temperature-table td.temperature_tool,#temperature-table th.temperature_actual,#temperature-table th.temperature_offset,#temperature-table th.temperature_target,#temperature-table th.temperature_tool{vertical-align:middle;text-align:center}#temperature-table td.temperature_actual form,#temperature-table td.temperature_offset form,#temperature-table td.temperature_target form,#temperature-table td.temperature_tool form,#temperature-table th.temperature_actual form,#temperature-table th.temperature_offset form,#temperature-table th.temperature_target form,#temperature-table th.temperature_tool form{margin:0}#temperature-table td.temperature_actual .dropdown-menu,#temperature-table td.temperature_offset .dropdown-menu,#temperature-table td.temperature_target .dropdown-menu,#temperature-table td.temperature_tool .dropdown-menu,#temperature-table th.temperature_actual .dropdown-menu,#temperature-table th.temperature_offset .dropdown-menu,#temperature-table th.temperature_target .dropdown-menu,#temperature-table th.temperature_tool .dropdown-menu{text-align:left}#temperature-table td.temperature_tool,#temperature-table th.temperature_tool{width:16%;text-align:left;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}#temperature-table td.temperature_actual,#temperature-table th.temperature_actual{width:12%}#temperature-table td.temperature_target,#temperature-table th.temperature_target{width:42%;overflow:visible}#temperature-table td.temperature_offset,#temperature-table th.temperature_offset{width:30%}.tab-content,.tab-pane{overflow:visible}#speed_fill,#speed_innerWall,#speed_outerWall,#speed_support,#temp_newBedTemp,#temp_newTemp,#webcam_timelapse_fps,#webcam_timelapse_interval,#webcam_timelapse_postRoll,#webcam_timelapse_retractionZHop{text-align:right}ul.dropdown-menu li a{cursor:pointer}#connection_baudrates,#connection_ports,#connection_printers{width:100%}#offline_overlay,#reloadui_overlay{position:fixed;top:0;left:0;width:100%;height:100%;display:none}#offline_overlay{z-index:10002}#reloadui_overlay{z-index:10001}#offline_overlay_background,#reloadui_overlay_background{position:fixed;top:0;left:0;width:100%;height:100%;background-color:#000;filter:alpha(opacity=50);-moz-opacity:.5;-khtml-opacity:.5;opacity:.5}#offline_overlay_wrapper,#reloadui_overlay_wrapper{position:absolute;top:0;bottom:0;left:0;right:0;padding-top:60px}#offline_overlay_wrapper .container,#reloadui_overlay_wrapper .container{margin:auto}#webcam_container{width:100%;position:relative;outline:0;background-color:#000}#webcam_container .keycontrol_overlay{position:absolute;left:10px;right:10px;bottom:10px;background:rgba(0,0,0,.5);font-size:85%;color:#fff;padding:0}#webcam_container .keycontrol_overlay kbd{border:1px solid #eee;border-radius:3px;margin-left:2px;margin-right:2px;font-size:90%;padding:2px;min-width:1em}#webcam_container .keycontrol_overlay .keycontrol_overlay_heading{position:relative;padding:10px;font-weight:700}#webcam_container .keycontrol_overlay .keycontrol_overlay_column{position:relative;width:45%;padding:10px;float:left}#webcam_container .nowebcam{position:absolute;top:0;left:0;right:0;bottom:0}#webcam_container .nowebcam .text{color:#fff;text-align:center;position:relative;margin:auto;width:80%;top:50%;transform:translateY(-50%);display:block}#webcam_container .nowebcam .text.webcam_loading{animation:pulsate 3s ease-out;animation-iteration-count:infinite}#webcam_container .webcam_rotated{position:relative;width:100%;padding-bottom:100%;pointer-events:none}#webcam_container .webcam_rotated .webcam_fixed_ratio{position:absolute;transform:rotate(-90deg);top:0;bottom:0;pointer-events:none}#webcam_container .webcam_rotated .webcam_fixed_ratio .webcam_fixed_ratio_inner{width:100%;height:100%;pointer-events:none}#webcam_container .webcam_unrotated .webcam_fixed_ratio{width:100%;pointer-events:none;padding-bottom:100%;position:relative}#webcam_container .webcam_unrotated .webcam_fixed_ratio.ratio43{padding-bottom:75%}#webcam_container .webcam_unrotated .webcam_fixed_ratio.ratio169{padding-bottom:56.25%}#webcam_container .webcam_unrotated .webcam_fixed_ratio.ratio1610{padding-bottom:62.5%}#webcam_container .webcam_unrotated .webcam_fixed_ratio .webcam_fixed_ratio_inner{position:absolute;top:0;bottom:0;left:0;right:0;pointer-events:none}#webcam_container img{width:100%;height:100%;object-fit:contain}#state_wrapper hr{margin:5px 0}#files .gcode_files{padding-right:12px}#files .gcode_files .entry{padding:5px;line-height:20px;border-bottom:1px solid #ddd;position:relative}#files .gcode_files .entry:hover{background-color:#f5f5f5}#files .gcode_files .entry .title{text-overflow:ellipsis;word-break:break-all}#files .gcode_files .entry .additionalInfo,#files .gcode_files .entry .internal,#files .gcode_files .entry .size,#files .gcode_files .entry .uploaded{font-size:85%;color:#999}#files .gcode_files .entry .internal{word-break:break-all}#files .gcode_files .entry .action-buttons{position:absolute;bottom:5px;right:5px}#files .gcode_files .entry .additionalInfo{padding-bottom:22px}@keyframes highlightframes{0%{background:#ff0}100%{background:0 0}}#files .gcode_files .entry.highlight{animation:highlightframes 2s}#files .gcode_files .back .back-path{white-space:nowrap}#files .gcode_files .back .back-path span{word-wrap:break-word;white-space:pre-line}#files .upload-buttons{margin-top:10px}#files .form-search{text-align:center;margin-bottom:5px!important}#control{overflow:hidden}#control .jog-panel{float:left;margin-right:19px}#control h1{text-align:left}#control .jog-panel>div{text-align:center}#control .jog-panel>div.distance{text-align:left}#control .jog-panel .slider{margin-bottom:10px}#control .box{width:30px;height:30px;margin-right:10px;margin-bottom:10px;padding-left:8px}#control .control-box{display:block;height:30px;margin-bottom:10px}#control .btn-group{margin-bottom:10px}#control .btn-group.distance>.btn{width:43px;padding:3px 0;height:30px}#control .slider-handle{width:14px;height:14px;margin-left:-7px;margin-top:-3px}#control .custom_section h1{cursor:pointer}#control .custom_section_horizontal>.custom_control{display:inline-block}#control .custom_section_vertical>.custom_control{display:block}#control .custom_control .slider{margin-left:10px;margin-right:10px;margin-bottom:2px}#gcode .progress{width:588px}#gcode .progress .bar{-webkit-transition:width 0s linear;-moz-transition:width 0s linear;-o-transition:width 0s linear;transition:width 0s linear}#gcode .canvas_container{position:relative}#gcode .canvas_container:active,#gcode .canvas_container:hover{outline:0}#gcode .layer-buttons{padding-top:5px;padding-bottom:7px}#gcode #gcode_layer_slider{position:absolute;right:0;top:0;height:568px;float:right}#gcode #gcode_layer_slider .slider-handle{width:14px;height:14px;margin-left:-3px;margin-top:-7px}#gcode #gcode_command_slider .slider-handle{width:14px;height:14px;margin-left:-7px;margin-top:-3px}#term .terminal{margin-bottom:30px}#term .terminal #terminal-output,#term .terminal #terminal-output-lowfi{min-height:340px;margin-bottom:5px}#settings_dialog .aboutlink{float:left}#settings_dialog_menu,#wizard_dialog_menu{margin-left:0}#wizard_firstrun_acl .acl_decision{margin-top:1em}#wizard_firstrun_end p,#wizard_firstrun_start p{margin-bottom:1.5em;line-height:1.5}#settings_appearance_managelanguagesdialog_emptylist{overflow:hidden;width:100%;height:300px;text-align:center;display:table}#settings_appearance_managelanguagesdialog_emptylist div{display:table-cell;vertical-align:middle}.footer ul{margin:0}.footer ul li{display:inline;margin-left:1em;font-size:85%}.footer ul li:first-child{margin-left:0}.footer ul li a{color:#555}.footer #footer_link,.footer #footer_version{max-width:50%}.ui-pnotify .alert a{color:#c09853}.ui-pnotify .alert-danger a,.ui-pnotify .alert-error a{color:#b94a48}.ui-pnotify .alert-success a{color:#468847}.ui-pnotify .alert-info a{color:#3a87ad}.pnotify_additional_info .pnotify_more{font-size:85%}.text-right{text-align:right}.text-center{text-align:center}.text-block{display:block}.overflow_visible{overflow:visible!important}.clickable{cursor:pointer}.border_box{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.hidden{display:none}textarea.block{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;width:100%}@keyframes pulsate{0%{opacity:.5}50%{opacity:1}100%{opacity:.5}}#drop_overlay{position:fixed;top:0;left:0;width:100%;height:100%;z-index:10000;display:none}#drop_overlay.in{display:block}#drop_overlay #drop_overlay_background{position:fixed;top:0;left:0;width:100%;height:100%;background-color:#000;filter:alpha(opacity=50);-moz-opacity:.5;-khtml-opacity:.5;opacity:.5}#drop_overlay #drop_overlay_wrapper{position:absolute;top:0;bottom:0;left:0;right:0;padding-top:60px}#drop_overlay #drop_overlay_wrapper #drop,#drop_overlay #drop_overlay_wrapper #drop_background{position:absolute;top:0;left:0;margin-left:0;width:100%}#drop_overlay #drop_overlay_wrapper #drop_locally,#drop_overlay #drop_overlay_wrapper #drop_locally_background{position:absolute;top:0;left:50%;margin-left:-50%;width:50%;border-right:2px dashed #ccc}#drop_overlay #drop_overlay_wrapper #drop_sd,#drop_overlay #drop_overlay_wrapper #drop_sd_background{position:absolute;top:0;left:50%;margin-left:0;width:50%;border-left:2px dashed #ccc}#drop_overlay #drop_overlay_wrapper .dropzone{height:100%;z-index:10001;color:#fff;font-size:30px}#drop_overlay #drop_overlay_wrapper .dropzone i{font-size:50px}#drop_overlay #drop_overlay_wrapper .dropzone .text{display:block;text-align:center;line-height:40px;position:absolute;width:100%;bottom:5%;filter:alpha(opacity=100);-moz-opacity:1;-khtml-opacity:1;opacity:1}#drop_overlay #drop_overlay_wrapper .dropzone_background{width:50%;height:100%;background-color:#000;filter:alpha(opacity=25);-moz-opacity:.25;-khtml-opacity:.25;opacity:.25}#drop_overlay #drop_overlay_wrapper .dropzone_background.hover{background-color:#000;filter:alpha(opacity=50);-moz-opacity:.5;-khtml-opacity:.5;opacity:.5}#drop_overlay #drop_overlay_wrapper .dropzone_background.fade{-webkit-transition:all .3s ease-out;-moz-transition:all .3s ease-out;-ms-transition:all .3s ease-out;-o-transition:all .3s ease-out;transition:all .3s ease-out;opacity:1}.icon-sd-black-14{background:url(../img/icon-sd-black-14.png) 0 3px no-repeat;width:11px;height:17px;display:inline-block!important}.center{float:none;margin-left:auto;margin-right:auto}.flipH{-webkit-transform:scaleX(-1);-moz-transform:scaleX(-1);-ms-transform:scaleX(-1);transform:scaleX(-1)}.flipV{-webkit-transform:scaleY(-1);-moz-transform:scaleY(-1);-ms-transform:scaleY(-1);transform:scaleY(-1)}.flipH.flipV{-webkit-transform:scaleX(-1) scaleY(-1);-moz-transform:scaleX(-1) scaleY(-1);-ms-transform:scaleX(-1) scaleY(-1);transform:scaleX(-1) scaleY(-1)}.rotate90{-webkit-transform:rotate(-90deg);transform:rotate(-90deg)}.ui-pnotify a{text-decoration:underline}.btn-mini .caret,.btn-small .caret{margin-top:8px}.dropdown-menu-right{right:0;left:auto}.slider .slider-selection{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,.25);background-color:#006dcc;background-image:-moz-linear-gradient(top,#08c,#04c);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#04c));background-image:-webkit-linear-gradient(top,#08c,#04c);background-image:-o-linear-gradient(top,#08c,#04c);background-image:linear-gradient(to bottom,#08c,#04c);background-repeat:repeat-x;border-color:#04c #04c #002a80;border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);*background-color:#04c;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.slider .slider-selection.active,.slider .slider-selection.disabled,.slider .slider-selection:active,.slider .slider-selection:focus,.slider .slider-selection:hover,.slider .slider-selection[disabled]{color:#fff;background-color:#04c;*background-color:#003bb3}.slider .slider-selection.active,.slider .slider-selection:active{background-color:#039 \9}.slider.slider-disabled .slider-selection{background-image:none;opacity:.65;filter:alpha(opacity=65);-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.slider .slider-track{background-color:#f5f5f5;border:1px solid #e3e3e3;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05)}.slider.slider-disabled .slider-track{background-image:none;opacity:.65;filter:alpha(opacity=65);-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.slider .slider-handle{display:inline-block;*display:inline;*zoom:1;font-size:14px;line-height:20px;text-align:center;vertical-align:middle;cursor:pointer;color:#333;text-shadow:0 1px 1px rgba(255,255,255,.75);background-color:#f5f5f5;background-image:-moz-linear-gradient(top,#fff,#e6e6e6);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#e6e6e6));background-image:-webkit-linear-gradient(top,#fff,#e6e6e6);background-image:-o-linear-gradient(top,#fff,#e6e6e6);background-image:linear-gradient(to bottom,#fff,#e6e6e6);background-repeat:repeat-x;*background-color:#e6e6e6;border:1px solid #ccc;*border:0;border-bottom-color:#b3b3b3;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;*margin-left:.3em;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05);padding:0;margin-bottom:0;opacity:1;filter:alpha(opacity=100)}.slider .slider-handle.active,.slider .slider-handle.disabled,.slider .slider-handle:active,.slider .slider-handle:focus,.slider .slider-handle:hover,.slider .slider-handle[disabled]{color:#333;background-color:#e6e6e6;*background-color:#d9d9d9}.slider .slider-handle.active,.slider .slider-handle:active{background-color:#ccc \9}.slider .slider-handle:first-child{*margin-left:0}.slider .slider-handle:focus,.slider .slider-handle:hover{color:#333;text-decoration:none;background-position:0 -15px;-webkit-transition:background-position .1s linear;-moz-transition:background-position .1s linear;-o-transition:background-position .1s linear;transition:background-position .1s linear}.slider .slider-handle:focus{outline:#333 dotted thin;outline:-webkit-focus-ring-color auto 5px;outline-offset:-2px}.slider .slider-handle.active,.slider .slider-handle:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,.15),0 1px 2px rgba(0,0,0,.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,.15),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 2px 4px rgba(0,0,0,.15),0 1px 2px rgba(0,0,0,.05)}.slider .slider-handle.disabled,.slider .slider-handle[disabled]{cursor:default;background-image:none;opacity:.65;filter:alpha(opacity=65);-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.slider .slider-handle.hide{display:none}.slider .slider-handle.round{-webkit-border-radius:50%;-moz-border-radius:50%;border-radius:50%}.modal.large{width:975px;margin-left:-487px}.full-sized-box{position:absolute;bottom:0;left:0;right:0;top:0;padding:15px}.full-sized-box .row-fluid{height:100%}@media (max-width:979px){.full-sized-box{position:static}}:root .full-sized-box,_::-webkit-full-page-media,_:future{position:static}.scrollable{height:100%;overflow:auto;-webkit-overflow-scrolling:touch}.pre-output span{display:block}.input-append .add-on.add-on-limited,.input-prepend .add-on.add-on-limited{overflow-x:hidden;text-overflow:ellipsis;width:inherit}.input-append .btn-group:first-child .btn:first-child,.input-prepend .btn-group:first-child .btn:first-child{-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.input-append .btn-group .btn:first-child,.input-prepend .btn-group .btn:first-child{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.input-append.input-block-level,.input-prepend.input-block-level{display:table}.input-append.input-block-level .add-on,.input-prepend.input-block-level .add-on{display:table-cell;width:1%}.input-append.input-block-level>input,.input-prepend.input-block-level>input{box-sizing:border-box;display:table;min-height:inherit;width:100%}.input-append.input-block-level :not(:last-child),.input-prepend.input-block-level :not(:last-child){border-right:0}.control-group.error .input-append .fileinput-button,.control-group.error .input-prepend .fileinput-button{border-color:#b94a48}.control-text{padding-top:5px;cursor:default}input[type=number]{text-align:right}input[type=number].input-nospin::-webkit-inner-spin-button,input[type=number].input-nospin::-webkit-outer-spin-button{-webkit-appearance:none;margin:0}input[type=number].input-nospin{-moz-appearance:textfield}.progress-text,.progress-text-centered{position:relative}.progress-text .progress-text-back,.progress-text .progress-text-front,.progress-text-centered .progress-text-back,.progress-text-centered .progress-text-front{white-space:nowrap}.progress-text .progress-text-front,.progress-text-centered .progress-text-front{box-sizing:border-box;padding:0 10px;width:100%;display:block}.progress-text .progress-text-back,.progress-text-centered .progress-text-back{position:absolute;font-size:12px;line-height:20px;display:block;box-sizing:border-box;text-align:center;padding:0 10px}.progress-text .bar,.progress-text-centered .bar{position:absolute;overflow:hidden}.progress-text-centered .progress-text-front{position:absolute;font-size:12px;line-height:20px;display:block;text-align:center;color:#fff}.progress-text-centered .progress-text-back{width:100%}#navbar_login:not(.open) #login_dropdown_loggedout{display:block;z-index:-1;height:0;width:0;padding:0!important;overflow:hidden;border:0;box-shadow:none;left:-9999px}#navbar_login:not(.open) #login_dropdown_loggedout.hide{display:none}#loginForm{margin:0}#loginForm button{margin-top:20px} \ No newline at end of file diff --git a/src/octoprint/static/less/octoprint.less b/src/octoprint/static/less/octoprint.less index cadb3c803c..eb2af3dbe1 100644 --- a/src/octoprint/static/less/octoprint.less +++ b/src/octoprint/static/less/octoprint.less @@ -942,6 +942,11 @@ ul.dropdown-menu li a { } } + + #footer_version, + #footer_link { + max-width: 50%; + } } /** Notifications */ diff --git a/src/octoprint/templates/index.jinja2 b/src/octoprint/templates/index.jinja2 index 2065b505ec..cca0f0214b 100644 --- a/src/octoprint/templates/index.jinja2 +++ b/src/octoprint/templates/index.jinja2 @@ -110,14 +110,14 @@ {% endif %}
-
-
- -
- - {{ _('Use this to define additional serial port baud rates to list for connecting with, e.g. 123456. Comma separated.')|format(glob_url="http://docs.python.org/2/library/glob.html") }} -
-
-
-
- + + +
+
+
+ {{ _('Connection') }} +
+ +
+ +
+
+
+ +
+ +
+
+
+
+ +
+
+
+ +
+ + {{ _('Use this to define additional glob patterns matching serial ports to list for connecting against, e.g. /dev/ttyAMA*. One entry per line.')|format(glob_url="http://docs.python.org/2/library/glob.html") }} +
+
+
+ +
+ + {{ _('Use this to define additional serial port baud rates to list for connecting with, e.g. 123456. Comma separated.')|format(glob_url="http://docs.python.org/2/library/glob.html") }} +
+
+
+
+ {{ _('Serial logging') }} +
+
+ +
+
+
-
-
-
- +
+
+ {{ _('Query intervals') }} +
+ +
+
+ + s +
+ {{ _('When printing or idle')}} +
+
+
+ + s +
+ {{ _('When a target temperature is set')}} +
+
+
+ +
+
+ + s +
+ {{ _('Autoreport interval to request from firmware')}} +
+
+
+ +
+
+ + s +
+
+
+
+
+ {{ _('Timeouts') }} +
+ +
+
+ + s +
+ {{ _('busy protocol support not detected')}} +
+
+
+ + s +
+ {{ _('busy protocol support detected')}} +
+
+
+ +
+
+ + s +
+
+
+
+ +
+
+ + s +
+
+
+
+ +
+
+ +
+ + {{ _('Set to 0 to disable consecutive timeout detection and handling.') }} +
+
+
+ +
+ + {{ _('Set to 0 to disable consecutive timeout detection and handling.') }} +
+
+
+ +
+ + {{ _('Set to 0 to disable consecutive timeout detection and handling.') }} +
+
+
+
+
-
- -
- -
-
-
- +
+
+ {{ _('Firmware specific settings') }} +
+
+ +
-
-
-
- +
+
+
+ +
+
+
+
+ +
+
+
+
+ +
+
+
+
+ +
+
+
+
+ +
+
+
+
+ +
+
+
+ +
+ + + +
+
-
-
- -
- - {{ _('Use this to specify a different command than the default M110 to send to the printer on initial connection to trigger a communication handshake.') }} +
+ +
+
+
+ +
+
+
+
+ +
+
+
+ +
+
+ +
+
-
-
- -
- - {{ _('Use this to specify the commands known to take a long time to complete without output from your printer and hence might cause timeout issues. Just the G or M code, comma separated.') }} + +
+ {{ _('Protocol fine tuning') }} +
+
+ +
-
-
- -
- - {{ _('Use this to specify which commands always need to be sent with a checksum. Comma separated list.') }} +
+ +
+
+ +
+ + {{ _('Use this to specify a different command than the default M110 to send to the printer on initial connection to trigger a communication handshake.') }} +
+
+
+ +
+ + {{ _('Use this to specify the commands known to take a long time to complete without output from your printer and hence might cause timeout issues. Just the G or M code, comma separated.') }} +
+
+
+ +
+ + {{ _('Use this to specify which commands always need to be sent with a checksum. Comma separated list.') }} +
+
+
-
-
- -
- - {{ _('Use this to specify the commands that should not have their parameters automatically uppercased in the terminal tab. Just the G or M code, comma separated.') }} -
-
-
- -
-
- -
-
- -
-
- -
-
- -
- - {{ _('Set to 0 to disable consecutive timeout detection and handling.') }} + +
+
+
+ {{ _('Error handling') }} +
+
+ +
+
+
+
+ +
-
-
- + +
+ {{ _('Event based position logging') }}
- - {{ _('Set to 0 to disable consecutive timeout detection and handling.') }} +
-
-
-
- - {{ _('Set to 0 to disable consecutive timeout detection and handling.') }} +
-
+
- From eadb4a5bb6b6a44d3d08cff86d65bb3eaf387ba7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Thu, 15 Feb 2018 17:10:15 +0100 Subject: [PATCH 214/333] [Docs] document new capabilities config settings --- docs/configuration/config_yaml.rst | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/docs/configuration/config_yaml.rst b/docs/configuration/config_yaml.rst index d3d76de2be..7205ee1498 100644 --- a/docs/configuration/config_yaml.rst +++ b/docs/configuration/config_yaml.rst @@ -763,6 +763,10 @@ Use the following settings to configure the serial connection to the printer: # Defaults to 30 sec communication: 30 + # Timeout during serial communication when busy protocol support is detected, in seconds. + # Defaults to 3 sec + communicationBusy: 3 + # Timeout after which to query temperature when no target is set temperature: 5 @@ -892,6 +896,14 @@ Use the following settings to configure the serial connection to the printer: # the responds skips on the ok) triggerOkForM29: true + capabilities: + + # Whether to enable temperature autoreport in the firmware if its support is detected + autoreport_temp: true + + # Whether to shorten the communication timeout if the firmware seems to support the busy protocol + busy_protocol: true + .. _sec-configuration-config_yaml-server: Server From 1587ee64027be418ccfc2f5fc9cc8ec8221121dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Fri, 16 Feb 2018 10:24:13 +0100 Subject: [PATCH 215/333] Forgot to adjust migrated settings locs in comm layer --- src/octoprint/util/comm.py | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/octoprint/util/comm.py b/src/octoprint/util/comm.py index ae1d8ed211..f1f4844cbc 100644 --- a/src/octoprint/util/comm.py +++ b/src/octoprint/util/comm.py @@ -403,13 +403,13 @@ def __init__(self, port = None, baudrate=None, callbackObject=None, printerProfi self._hello_command = settings().get(["serial", "helloCommand"]) - self._alwaysSendChecksum = settings().getBoolean(["feature", "alwaysSendChecksum"]) - self._neverSendChecksum = settings().getBoolean(["feature", "neverSendChecksum"]) - self._sendChecksumWithUnknownCommands = settings().getBoolean(["feature", "sendChecksumWithUnknownCommands"]) - self._unknownCommandsNeedAck = settings().getBoolean(["feature", "unknownCommandsNeedAck"]) - self._sdAlwaysAvailable = settings().getBoolean(["feature", "sdAlwaysAvailable"]) - self._sdRelativePath = settings().getBoolean(["feature", "sdRelativePath"]) - self._blockWhileDwelling = settings().getBoolean(["feature", "blockWhileDwelling"]) + self._alwaysSendChecksum = settings().getBoolean(["serial", "alwaysSendChecksum"]) + self._neverSendChecksum = settings().getBoolean(["serial", "neverSendChecksum"]) + self._sendChecksumWithUnknownCommands = settings().getBoolean(["serial", "sendChecksumWithUnknownCommands"]) + self._unknownCommandsNeedAck = settings().getBoolean(["serial", "unknownCommandsNeedAck"]) + self._sdAlwaysAvailable = settings().getBoolean(["serial", "sdAlwaysAvailable"]) + self._sdRelativePath = settings().getBoolean(["serial", "sdRelativePath"]) + self._blockWhileDwelling = settings().getBoolean(["serial", "blockWhileDwelling"]) self._currentLine = 1 self._line_mutex = threading.RLock() self._resendDelta = None @@ -423,10 +423,10 @@ def __init__(self, port = None, baudrate=None, callbackObject=None, printerProfi self._lastCommError = None self._lastResendNumber = None self._currentResendCount = 0 - self._resendSwallowRepetitions = settings().getBoolean(["feature", "ignoreIdenticalResends"]) + self._resendSwallowRepetitions = settings().getBoolean(["serial", "ignoreIdenticalResends"]) self._resendSwallowRepetitionsCounter = 0 - self._firmware_detection = settings().getBoolean(["feature", "firmwareDetection"]) + self._firmware_detection = settings().getBoolean(["serial", "firmwareDetection"]) self._firmware_info_received = not self._firmware_detection self._firmware_info = dict() self._firmware_capabilities = dict() @@ -1222,7 +1222,7 @@ def _monitor(self): feedback_errors = [] pause_triggers = convert_pause_triggers(settings().get(["printerParameters", "pauseTriggers"])) - disable_external_heatup_detection = not settings().getBoolean(["feature", "externalHeatupDetection"]) + disable_external_heatup_detection = not settings().getBoolean(["serial", "externalHeatupDetection"]) self._consecutive_timeouts = 0 @@ -1230,7 +1230,7 @@ def _monitor(self): if not self._openSerial(): return - try_hello = not settings().getBoolean(["feature", "waitForStartOnConnect"]) + try_hello = not settings().getBoolean(["serial", "waitForStartOnConnect"]) self._log("Connected to: %s, starting monitor" % self._serial) if self._baudrate == 0: @@ -1246,8 +1246,8 @@ def _monitor(self): self._ok_timeout = get_new_timeout("communicationBusy" if self._busy_protocol_detected else "communication", self._timeout_intervals) startSeen = False - supportRepetierTargetTemp = settings().getBoolean(["feature", "repetierTargetTemp"]) - supportWait = settings().getBoolean(["feature", "supportWait"]) + supportRepetierTargetTemp = settings().getBoolean(["serial", "repetierTargetTemp"]) + supportWait = settings().getBoolean(["serial", "supportWait"]) connection_timeout = settings().getFloat(["serial", "timeout", "connection"]) detection_timeout = settings().getFloat(["serial", "timeout", "detection"]) @@ -2256,7 +2256,7 @@ def _handleResendRequest(self, line): self._resendDelta = resendDelta self._lastResendNumber = lineToResend self._currentResendCount = 0 - self._resendSwallowRepetitionsCounter = settings().getInt(["feature", "identicalResendsCountdown"]) + self._resendSwallowRepetitionsCounter = settings().getInt(["serial", "identicalResendsCountdown"]) if self._resendDelta > len(self._lastLines) or len(self._lastLines) == 0 or self._resendDelta < 0: self._errorValue = "Printer requested line %d but no sufficient history is available, can't resend" % lineToResend @@ -3661,7 +3661,7 @@ def gcode_and_subcode_for_cmd(cmd): gcode = values["codeGM"] elif "codeT" in values and values["codeT"]: gcode = values["codeT"] - elif settings().getBoolean(["feature", "supportFAsCommand"]) and "codeF" in values and values["codeF"]: + elif settings().getBoolean(["serial", "supportFAsCommand"]) and "codeF" in values and values["codeF"]: gcode = values["codeF"] else: # this should never happen From 00e6a3856f0a67e0062a882663134c687b89280a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Fri, 16 Feb 2018 16:30:20 +0100 Subject: [PATCH 216/333] Allow tagging of GCODE commands throughout their lifecycle Commands injected/extended/rewritten through one of the phase hooks or sent by a plugin through self._printer.commands will also be tagged with "plugin:", allowing other code to detect these tags and handle such commands differently. That will be interesting for plugins that need to differentiate between commands originating from the printed file vs the user vs the system vs other plugins. --- docs/plugins/hooks.rst | 41 +++- src/octoprint/printer/__init__.py | 108 ++++---- src/octoprint/printer/standard.py | 139 ++++++----- src/octoprint/server/__init__.py | 31 ++- src/octoprint/server/api/files.py | 4 +- src/octoprint/server/api/job.py | 14 +- src/octoprint/server/api/printer.py | 36 ++- src/octoprint/util/comm.py | 369 +++++++++++++++++++--------- 8 files changed, 491 insertions(+), 251 deletions(-) diff --git a/docs/plugins/hooks.rst b/docs/plugins/hooks.rst index 1a320573de..f9c8d76176 100644 --- a/docs/plugins/hooks.rst +++ b/docs/plugins/hooks.rst @@ -420,7 +420,7 @@ This describes actually four hooks: * ``octoprint.comm.protocol.gcode.sending`` * ``octoprint.comm.protocol.gcode.sent`` -.. py:function:: protocol_gcodephase_hook(comm_instance, phase, cmd, cmd_type, gcode, subcode=None, *args, **kwargs) +.. py:function:: protocol_gcodephase_hook(comm_instance, phase, cmd, cmd_type, gcode, subcode=None, tags=None, *args, **kwargs) Pre- and postprocess commands as they progress through the various phases of being sent to the printer. The phases are the following: @@ -443,7 +443,39 @@ This describes actually four hooks: the communication layer or before they are actually sent over the serial port, or to react to the queuing or sending of commands after the fact. The hook handler will be called with the processing ``phase``, the ``cmd`` to be sent to the printer as well as the ``cmd_type`` parameter used for enqueuing (OctoPrint will make sure that the send queue - will never contain more than one line with the same ``cmd_type``) and the detected gcode command (if it is one). + will never contain more than one line with the same ``cmd_type``) and the detected ``gcode`` command (if it is one) + as well as its ``subcode`` (if it has one). OctoPrint will also provide any ``tags`` attached to the command throughout + its lifecycle. + + Tags are arbitrary strings that can be attached to a command as it moves through the various phases and can be used to e.g. + distinguish between commands that originated in a printed file (``source:file``) vs. a configured GCODE script + (``source:script``) vs. an API call (``source:api``) vs. a plugin (``source:plugin`` or ``source:rewrite`` and + ``plugin:``). If during development you want to get an idea of the various possible tags, set + the logger ``octoprint.util.comm.command_phases`` to ``DEBUG``, connect to a printer (real or virtual) and take a + look at your ``octoprint.log`` during serial traffic: + + .. code-block:: plain + + 2018-02-16 18:20:31,213 - octoprint.util.comm.command_phases - DEBUG - phase: queuing | command: T0 | gcode: T | tags: [ api:printer.command, source:api, trigger:printer.commands ] + 2018-02-16 18:20:31,216 - octoprint.util.comm.command_phases - DEBUG - phase: queued | command: M117 Before T! | gcode: M117 | tags: [ api:printer.command, phase:queuing, plugin:multi_gcode_test, source:api, source:rewrite, trigger:printer.commands ] + 2018-02-16 18:20:31,217 - octoprint.util.comm.command_phases - DEBUG - phase: sending | command: M117 Before T! | gcode: M117 | tags: [ api:printer.command, phase:queuing, plugin:multi_gcode_test, source:api, source:rewrite, trigger:printer.commands ] + 2018-02-16 18:20:31,217 - octoprint.util.comm.command_phases - DEBUG - phase: queued | command: T0 | gcode: T | tags: [ api:printer.command, source:api, trigger:printer.commands ] + 2018-02-16 18:20:31,219 - octoprint.util.comm.command_phases - DEBUG - phase: queued | command: M117 After T! | gcode: M117 | tags: [ api:printer.command, phase:queuing, plugin:multi_gcode_test, source:api, source:rewrite, trigger:printer.commands ] + 2018-02-16 18:20:31,220 - octoprint.util.comm.command_phases - DEBUG - phase: sent | command: M117 Before T! | gcode: M117 | tags: [ api:printer.command, phase:queuing, plugin:multi_gcode_test, source:api, source:rewrite, trigger:printer.commands ] + 2018-02-16 18:20:31,230 - tornado.access - INFO - 204 POST /api/printer/command (127.0.0.1) 23.00ms + 2018-02-16 18:20:31,232 - tornado.access - INFO - 200 POST /api/printer/command (127.0.0.1) 25.00ms + 2018-02-16 18:20:31,232 - octoprint.util.comm.command_phases - DEBUG - phase: sending | command: T0 | gcode: T | tags: [ api:printer.command, source:api, trigger:printer.commands ] + 2018-02-16 18:20:31,234 - octoprint.util.comm.command_phases - DEBUG - phase: sent | command: T0 | gcode: T | tags: [ api:printer.command, source:api, trigger:printer.commands ] + 2018-02-16 18:20:31,242 - octoprint.util.comm.command_phases - DEBUG - phase: sending | command: M117 After T! | gcode: M117 | tags: [ api:printer.command, phase:queuing, plugin:multi_gcode_test, source:api, source:rewrite, trigger:printer.commands ] + 2018-02-16 18:20:31,243 - octoprint.util.comm.command_phases - DEBUG - phase: sent | command: M117 After T! | gcode: M117 | tags: [ api:printer.command, phase:queuing, plugin:multi_gcode_test, source:api, source:rewrite, trigger:printer.commands ] + 2018-02-16 18:20:38,552 - octoprint.util.comm.command_phases - DEBUG - phase: queuing | command: G91 | gcode: G91 | tags: [ api:printer.printhead, source:api, trigger:printer.commands, trigger:printer.jog ] + 2018-02-16 18:20:38,552 - octoprint.util.comm.command_phases - DEBUG - phase: queued | command: G91 | gcode: G91 | tags: [ api:printer.printhead, source:api, trigger:printer.commands, trigger:printer.jog ] + 2018-02-16 18:20:38,553 - octoprint.util.comm.command_phases - DEBUG - phase: sending | command: G91 | gcode: G91 | tags: [ api:printer.printhead, source:api, trigger:printer.commands, trigger:printer.jog ] + 2018-02-16 18:20:38,553 - octoprint.util.comm.command_phases - DEBUG - phase: queuing | command: G1 X10 F6000 | gcode: G1 | tags: [ api:printer.printhead, source:api, trigger:printer.commands, trigger:printer.jog ] + 2018-02-16 18:20:38,555 - octoprint.util.comm.command_phases - DEBUG - phase: queued | command: G1 X10 F6000 | gcode: G1 | tags: [ api:printer.printhead, source:api, trigger:printer.commands, trigger:printer.jog ] + 2018-02-16 18:20:38,556 - octoprint.util.comm.command_phases - DEBUG - phase: sent | command: G91 | gcode: G91 | tags: [ api:printer.printhead, source:api, trigger:printer.commands, trigger:printer.jog ] + 2018-02-16 18:20:38,556 - octoprint.util.comm.command_phases - DEBUG - phase: queuing | command: G90 | gcode: G90 | tags: [ api:printer.printhead, source:api, trigger:printer.commands, trigger:printer.jog ] + 2018-02-16 18:20:38,558 - octoprint.util.comm.command_phases - DEBUG - phase: queued | command: G90 | gcode: G90 | tags: [ api:printer.printhead, source:api, trigger:printer.commands, trigger:printer.jog ] Defining a ``cmd_type`` other than None will make sure OctoPrint takes care of only having one command of that type in its sending queue. Predefined types are ``temperature_poll`` for temperature polling via ``M105`` and @@ -482,6 +514,8 @@ This describes actually four hooks: should use this option. * A 2-tuple consisting of a rewritten version of the ``cmd`` and the ``cmd_type``, e.g. ``return "M105", "temperature_poll"``. Handlers which wish to rewrite both the command and the command type should use this option. + * A 3-tuple consisting of a rewritten version of the ``cmd``, the ``cmd_type`` and any additional ``tags`` you might + want to attach to the lifecycle of the command, e.g. ``return "M105", "temperature_poll", "my_custom_tag" * **"queuing" phase only**: A list of any of the above to allow for expanding one command into many. The following example shows how any queued command could be turned into a sequence of a temperature query, line number reset, display of the ``gcode`` on the printer's display and finally the actual command (this example @@ -489,7 +523,7 @@ This describes actually four hooks: .. code-block:: python - def rewrite_foo(self, comm_instance, phase, cmd, cmd_type, gcode, *args, **kwargs): + def rewrite_foo(self, comm_instance, phase, cmd, cmd_type, gcode, subcode=None, tags=None *args, **kwargs): if gcode or not cmd.startswith("@foo"): return @@ -524,6 +558,7 @@ This describes actually four hooks: :param str gcode: Parsed GCODE command, e.g. ``G0`` or ``M110``, may also be None if no known command could be parsed :param str subcode: Parsed subcode of the GCODE command, e.g. ``1`` for ``M80.1``. Will be None if no subcode was provided or no command could be parsed. + :param set tags: Tags attached to the :return: None, 1-tuple, 2-tuple or string, see the description above for details. .. _sec-plugins-hook-comm-protocol-gcode-received: diff --git a/src/octoprint/printer/__init__.py b/src/octoprint/printer/__init__.py index 11ef17db07..84a892afb1 100644 --- a/src/octoprint/printer/__init__.py +++ b/src/octoprint/printer/__init__.py @@ -51,7 +51,7 @@ class PrinterInterface(object): """Regex for valid heater identifiers.""" @classmethod - def get_connection_options(cls): + def get_connection_options(cls, *args, **kwargs): """ Retrieves the available ports, baudrates, preferred port and baudrate for connecting to the printer. @@ -75,7 +75,7 @@ def get_connection_options(cls): "autoconnect": settings().getBoolean(["serial", "autoconnect"]) } - def connect(self, port=None, baudrate=None, profile=None): + def connect(self, port=None, baudrate=None, profile=None, *args, **kwargs): """ Connects to the printer, using the specified serial ``port``, ``baudrate`` and printer ``profile``. If a connection is already established, that connection will be closed prior to connecting anew with the provided @@ -89,13 +89,13 @@ def connect(self, port=None, baudrate=None, profile=None): """ pass - def disconnect(self): + def disconnect(self, *args, **kwargs): """ Disconnects from the printer. Does nothing if no connection is currently established. """ raise NotImplementedError() - def get_transport(self): + def get_transport(self, *args, **kwargs): """ Returns the communication layer's transport object, if a connection is currently established. @@ -107,7 +107,7 @@ def get_transport(self): """ raise NotImplementedError() - def fake_ack(self): + def fake_ack(self, *args, **kwargs): """ Fakes an acknowledgment for the communication layer. If the communication between OctoPrint and the printer gets stuck due to lost "ok" responses from the server due to communication issues, this can be used to get @@ -115,17 +115,18 @@ def fake_ack(self): """ raise NotImplementedError() - def commands(self, commands): + def commands(self, commands, tags=None, *args, **kwargs): """ Sends the provided ``commands`` to the printer. Arguments: commands (str, list): The commands to send. Might be a single command provided just as a string or a list of multiple commands to send in order. + tags (set of str): An optional set of tags to attach to the command(s) throughout their lifecycle """ raise NotImplementedError() - def script(self, name, context=None): + def script(self, name, context=None, tags=None, *args, **kwargs): """ Sends the GCODE script ``name`` to the printer. @@ -137,13 +138,14 @@ def script(self, name, context=None): Arguments: name (str): The name of the GCODE script to render. context (dict): An optional context of additional template variables to provide to the renderer. + tags (set of str): An optional set of tags to attach to the command(s) throughout their lifecycle Raises: UnknownScriptException: There is no GCODE script with name ``name`` """ raise NotImplementedError() - def jog(self, axes, relative=True, speed=None, *args, **kwargs): + def jog(self, axes, relative=True, speed=None, tags=None, *args, **kwargs): """ Jogs the specified printer ``axis`` by the specified ``amount`` in mm. @@ -154,38 +156,42 @@ def jog(self, axes, relative=True, speed=None, *args, **kwargs): speed (int, bool or None): Speed at which to jog (F parameter). If set to ``False`` no speed will be set specifically. If set to ``None`` (or left out) the minimum of all involved axes speeds from the printer profile will be used. + tags (set of str): An optional set of tags to attach to the command(s) throughout their lifecycle """ raise NotImplementedError() - def home(self, axes): + def home(self, axes, tags=None, *args, **kwargs): """ Homes the specified printer ``axes``. Arguments: axes (str, list): The axis or axes to home, each of which must converted to lower case must match one of "x", "y", "z" and "e" + tags (set of str): An optional set of tags to attach to the command(s) throughout their lifecycle """ raise NotImplementedError() - def extrude(self, amount): + def extrude(self, amount, tags=None, *args, **kwargs): """ Extrude ``amount`` millimeters of material from the tool. Arguments: amount (int, float): The amount of material to extrude in mm + tags (set of str): An optional set of tags to attach to the command(s) throughout their lifecycle """ raise NotImplementedError() - def change_tool(self, tool): + def change_tool(self, tool, tags=None, *args, **kwargs): """ Switch the currently active ``tool`` (for which extrude commands will apply). Arguments: tool (str): The tool to switch to, matching the regex "tool[0-9]+" (e.g. "tool0", "tool1", ...) + tags (set of str): An optional set of tags to attach to the command(s) throughout their lifecycle """ raise NotImplementedError() - def set_temperature(self, heater, value): + def set_temperature(self, heater, value, tags=None, *args, **kwargs): """ Sets the target temperature on the specified ``heater`` to the given ``value`` in celsius. @@ -194,10 +200,11 @@ def set_temperature(self, heater, value): temperature or something matching the regular expression "tool[0-9]+" (e.g. "tool0", "tool1", ...) for the hotends of the printer value (int, float): The temperature in celsius to set the target temperature to. + tags (set of str): An optional set of tags to attach to the command(s) throughout their lifecycle """ raise NotImplementedError() - def set_temperature_offset(self, offsets=None): + def set_temperature_offset(self, offsets=None, tags=None, *args, **kwargs): """ Sets the temperature ``offsets`` to apply to target temperatures read from a GCODE file while printing. @@ -205,30 +212,33 @@ def set_temperature_offset(self, offsets=None): offsets (dict): A dictionary specifying the offsets to apply. Keys must match the format for the ``heater`` parameter to :func:`set_temperature`, so "bed" for the offset for the bed target temperature and "tool[0-9]+" for the offsets to the hotend target temperatures. + tags (set of str): An optional set of tags to attach to the command(s) throughout their lifecycle """ raise NotImplementedError() - def feed_rate(self, factor): + def feed_rate(self, factor, tags=None, *args, **kwargs): """ Sets the ``factor`` for the printer's feed rate. Arguments: factor (int, float): The factor for the feed rate to send to the firmware. Percentage expressed as either an int between 0 and 100 or a float between 0 and 1. + tags (set of str): An optional set of tags to attach to the command(s) throughout their lifecycle """ raise NotImplementedError() - def flow_rate(self, factor): + def flow_rate(self, factor, tags=None, *args, **kwargs): """ Sets the ``factor`` for the printer's flow rate. Arguments: factor (int, float): The factor for the flow rate to send to the firmware. Percentage expressed as either an int between 0 and 100 or a float between 0 and 1. + tags (set of str): An optional set of tags to attach to the command(s) throughout their lifecycle """ raise NotImplementedError() - def can_modify_file(self, path, sd): + def can_modify_file(self, path, sd, *args, **kwargs): """ Determines whether the ``path`` (on the printer's SD if ``sd`` is True) may be modified (updated or deleted) or not. @@ -254,7 +264,7 @@ def can_modify_file(self, path, sd): """ return not (self.is_current_file(path, sd) and (self.is_printing() or self.is_paused())) - def is_current_file(self, path, sd): + def is_current_file(self, path, sd, *args, **kwargs): """ Returns whether the provided ``path`` (on the printer's SD if ``sd`` is True) is the currently selected file for printing. @@ -286,7 +296,7 @@ def is_current_file(self, path, sd): return False - def select_file(self, path, sd, printAfterSelect=False, pos=None): + def select_file(self, path, sd, printAfterSelect=False, pos=None, tags=None, *args, **kwargs): """ Selects the specified ``path`` for printing, specifying if the file is to be found on the ``sd`` or not. Optionally can also directly start the print after selecting the file. @@ -297,6 +307,7 @@ def select_file(self, path, sd, printAfterSelect=False, pos=None): sd (boolean): Indicates whether the file is on the printer's SD card or not. printAfterSelect (boolean): Indicates whether a print should be started after the file is selected. + tags (set of str): An optional set of tags to attach to the command(s) throughout their lifecycle Raises: InvalidFileType: if the file is not a machinecode file and hence cannot be printed @@ -305,53 +316,68 @@ def select_file(self, path, sd, printAfterSelect=False, pos=None): """ raise NotImplementedError() - def unselect_file(self): + def unselect_file(self, *args, **kwargs): """ Unselects and currently selected file. """ raise NotImplementedError() - def start_print(self): + def start_print(self, tags=None, *args, **kwargs): """ Starts printing the currently selected file. If no file is currently selected, does nothing. + + Arguments: + tags (set of str): An optional set of tags to attach to the command(s) throughout their lifecycle """ raise NotImplementedError() - def pause_print(self): + def pause_print(self, tags=None, *args, **kwargs): """ Pauses the current print job if it is currently running, does nothing otherwise. + + Arguments: + tags (set of str): An optional set of tags to attach to the command(s) throughout their lifecycle """ raise NotImplementedError() - def resume_print(self): + def resume_print(self, tags=None, *args, **kwargs): """ Resumes the current print job if it is currently paused, does nothing otherwise. + + Arguments: + tags (set of str): An optional set of tags to attach to the command(s) throughout their lifecycle """ raise NotImplementedError() - def toggle_pause_print(self): + def toggle_pause_print(self, tags=None, *args, **kwargs): """ Pauses the current print job if it is currently running or resumes it if it is currently paused. + + Arguments: + tags (set of str): An optional set of tags to attach to the command(s) throughout their lifecycle """ if self.is_printing(): - self.pause_print() + self.pause_print(tags=tags) elif self.is_paused(): - self.resume_print() + self.resume_print(tags=tags) - def cancel_print(self): + def cancel_print(self, tags=None, *args, **kwargs): """ Cancels the current print job. + + Arguments: + tags (set of str): An optional set of tags to attach to the command(s) throughout their lifecycle """ raise NotImplementedError() - def get_state_string(self): + def get_state_string(self, *args, **kwargs): """ Returns: (str) A human readable string corresponding to the current communication state. """ raise NotImplementedError() - def get_state_id(self): + def get_state_id(self, *args, **kwargs): """ Identifier of the current communication state. @@ -376,35 +402,35 @@ def get_state_id(self): (str) A unique identifier corresponding to the current communication state. """ - def get_current_data(self): + def get_current_data(self, *args, **kwargs): """ Returns: (dict) The current state data. """ raise NotImplementedError() - def get_current_job(self): + def get_current_job(self, *args, **kwargs): """ Returns: (dict) The data of the current job. """ raise NotImplementedError() - def get_current_temperatures(self): + def get_current_temperatures(self, *args, **kwargs): """ Returns: (dict) The current temperatures. """ raise NotImplementedError() - def get_temperature_history(self): + def get_temperature_history(self, *args, **kwargs): """ Returns: (list) The temperature history. """ raise NotImplementedError() - def get_current_connection(self): + def get_current_connection(self, *args, **kwargs): """ Returns: (tuple) The current connection information as a 4-tuple ``(connection_string, port, baudrate, printer_profile)``. @@ -412,49 +438,49 @@ def get_current_connection(self): """ raise NotImplementedError() - def is_closed_or_error(self): + def is_closed_or_error(self, *args, **kwargs): """ Returns: (boolean) Whether the printer is currently disconnected and/or in an error state. """ raise NotImplementedError() - def is_operational(self): + def is_operational(self, *args, **kwargs): """ Returns: (boolean) Whether the printer is currently connected and available. """ raise NotImplementedError() - def is_printing(self): + def is_printing(self, *args, **kwargs): """ Returns: (boolean) Whether the printer is currently printing. """ raise NotImplementedError() - def is_paused(self): + def is_paused(self, *args, **kwargs): """ Returns: (boolean) Whether the printer is currently paused. """ raise NotImplementedError() - def is_error(self): + def is_error(self, *args, **kwargs): """ Returns: (boolean) Whether the printer is currently in an error state. """ raise NotImplementedError() - def is_ready(self): + def is_ready(self, *args, **kwargs): """ Returns: (boolean) Whether the printer is currently operational and ready for new print jobs (not printing). """ raise NotImplementedError() - def register_callback(self, callback): + def register_callback(self, callback, *args, **kwargs): """ Registers a :class:`PrinterCallback` with the instance. @@ -463,7 +489,7 @@ def register_callback(self, callback): """ raise NotImplementedError() - def unregister_callback(self, callback): + def unregister_callback(self, callback, *args, **kwargs): """ Unregisters a :class:`PrinterCallback` from the instance. diff --git a/src/octoprint/printer/standard.py b/src/octoprint/printer/standard.py index 81fe12ac8b..620fde17e4 100644 --- a/src/octoprint/printer/standard.py +++ b/src/octoprint/printer/standard.py @@ -126,14 +126,14 @@ def __init__(self, fileManager, analysisQueue, printerProfileManager): #~~ handling of PrinterCallbacks - def register_callback(self, callback): + def register_callback(self, callback, *args, **kwargs): if not isinstance(callback, PrinterCallback): self._logger.warn("Registering an object as printer callback which doesn't implement the PrinterCallback interface") self._callbacks.append(callback) self._sendInitialStateUpdate(callback) - def unregister_callback(self, callback): + def unregister_callback(self, callback, *args, **kwargs): if callback in self._callbacks: self._callbacks.remove(callback) @@ -196,7 +196,7 @@ def call_plugins(storage, filename, progress): #~~ PrinterInterface implementation - def connect(self, port=None, baudrate=None, profile=None): + def connect(self, port=None, baudrate=None, profile=None, *args, **kwargs): """ Connects to the printer. If port and/or baudrate is provided, uses these settings, otherwise autodetection will be attempted. @@ -215,7 +215,7 @@ def connect(self, port=None, baudrate=None, profile=None): self._comm = comm.MachineCom(port, baudrate, callbackObject=self, printerProfileManager=self._printerProfileManager) - def disconnect(self): + def disconnect(self, *args, **kwargs): """ Closes the connection to the printer. """ @@ -225,7 +225,7 @@ def disconnect(self): else: eventManager().fire(Events.DISCONNECTED) - def get_transport(self): + def get_transport(self, *args, **kwargs): if self._comm is None: return None @@ -233,13 +233,13 @@ def get_transport(self): return self._comm.getTransport() getTransport = util.deprecated("getTransport has been renamed to get_transport", since="1.2.0-dev-590", includedoc="Replaced by :func:`get_transport`") - def fake_ack(self): + def fake_ack(self, *args, **kwargs): if self._comm is None: return self._comm.fakeOk() - def commands(self, commands): + def commands(self, commands, *args, **kwargs): """ Sends one or more gcode commands to the printer. """ @@ -250,16 +250,18 @@ def commands(self, commands): commands = [commands] for command in commands: - self._comm.sendCommand(command) + self._comm.sendCommand(command, tags=kwargs.get("tags", set()) | {"trigger:printer.commands"}) - def script(self, name, context=None, must_be_set=True): + def script(self, name, context=None, must_be_set=True, *args, **kwargs): if self._comm is None: return if name is None or not name: raise ValueError("name must be set") - result = self._comm.sendGcodeScript(name, replacements=context) + result = self._comm.sendGcodeScript(name, + replacements=context, + tags=kwargs.get("tags", set()) | {"trigger:printer.script"}) if not result and must_be_set: raise UnknownScript(name) @@ -298,9 +300,9 @@ def jog(self, axes, relative=True, speed=None, *args, **kwargs): else: commands = ["G90", command] - self.commands(commands) + self.commands(commands, tags=kwargs.get("tags", set()) | {"trigger:printer.jog"}) - def home(self, axes): + def home(self, axes, *args, **kwargs): if not isinstance(axes, (list, tuple)): if isinstance(axes, (str, unicode)): axes = [axes] @@ -311,24 +313,26 @@ def home(self, axes): if len(axes) != len(validated_axes): raise ValueError("axes contains invalid axes: {axes}".format(axes=axes)) - self.commands(["G91", "G28 %s" % " ".join(map(lambda x: "%s0" % x.upper(), validated_axes)), "G90"]) + self.commands(["G91", "G28 %s" % " ".join(map(lambda x: "%s0" % x.upper(), validated_axes)), "G90"], + tags=kwargs.get("tags", set) | {"trigger:printer.home"}) - def extrude(self, amount): + def extrude(self, amount, *args, **kwargs): if not isinstance(amount, (int, long, float)): raise ValueError("amount must be a valid number: {amount}".format(amount=amount)) printer_profile = self._printerProfileManager.get_current_or_default() extrusion_speed = printer_profile["axes"]["e"]["speed"] - self.commands(["G91", "G1 E%s F%d" % (amount, extrusion_speed), "G90"]) + self.commands(["G91", "G1 E%s F%d" % (amount, extrusion_speed), "G90"], + tags=kwargs.get("tags", set()) | {"trigger:printer.extrude"}) - def change_tool(self, tool): + def change_tool(self, tool, *args, **kwargs): if not PrinterInterface.valid_tool_regex.match(tool): raise ValueError("tool must match \"tool[0-9]+\": {tool}".format(tool=tool)) tool_num = int(tool[len("tool"):]) - self.commands("T%d" % tool_num) + self.commands("T%d" % tool_num, tags=kwargs.get("tags", set()) | {"trigger:printer.change_tool"}) - def set_temperature(self, heater, value): + def set_temperature(self, heater, value, *args, **kwargs): if not PrinterInterface.valid_heater_regex.match(heater): raise ValueError("heater must match \"tool[0-9]+\" or \"bed\": {heater}".format(heater=heater)) @@ -341,14 +345,17 @@ def set_temperature(self, heater, value): shared_nozzle = printer_profile["extruder"]["sharedNozzle"] if extruder_count > 1 and not shared_nozzle: toolNum = int(heater[len("tool"):]) - self.commands("M104 T{} S{}".format(toolNum, value)) + self.commands("M104 T{} S{}".format(toolNum, value), + tags=kwargs.get("tags", set()) | {"trigger:printer.set_temperature"}) else: - self.commands("M104 S{}".format(value)) + self.commands("M104 S{}".format(value), + tags=kwargs.get("tags", set()) | {"trigger:printer.set_temperature"}) elif heater == "bed": - self.commands("M140 S{}".format(value)) + self.commands("M140 S{}".format(value), + tags=kwargs.get("tags", set()) | {"trigger:printer.set_temperature"}) - def set_temperature_offset(self, offsets=None): + def set_temperature_offset(self, offsets=None, *args, **kwargs): if offsets is None: offsets = dict() @@ -381,15 +388,17 @@ def _convert_rate_value(self, factor, min=0, max=200): return factor - def feed_rate(self, factor): + def feed_rate(self, factor, *args, **kwargs): factor = self._convert_rate_value(factor, min=50, max=200) - self.commands("M220 S%d" % factor) + self.commands("M220 S%d" % factor, + tags=kwargs.get("tags", set()) | {"trigger:printer.feed_rate"}) - def flow_rate(self, factor): + def flow_rate(self, factor, *args, **kwargs): factor = self._convert_rate_value(factor, min=75, max=125) - self.commands("M221 S%d" % factor) + self.commands("M221 S%d" % factor, + tags=kwargs.get("tags", set()) | {"trigger:printer.flow_rate"}) - def select_file(self, path, sd, printAfterSelect=False, pos=None): + def select_file(self, path, sd, printAfterSelect=False, pos=None, *args, **kwargs): if self._comm is None or (self._comm.isBusy() or self._comm.isStreaming()): self._logger.info("Cannot load file: printer not connected or currently busy") return @@ -408,11 +417,12 @@ def select_file(self, path, sd, printAfterSelect=False, pos=None): self._printAfterSelect = printAfterSelect self._posAfterSelect = pos - self._comm.selectFile("/" + path if sd else path, sd) + self._comm.selectFile("/" + path if sd else path, sd, + tags=kwargs.get("tags", set()) | {"trigger:printer.select_file"}) self._updateProgressData() self._setCurrentZ(None) - def unselect_file(self): + def unselect_file(self, *args, **kwargs): if self._comm is not None and (self._comm.isBusy() or self._comm.isStreaming()): return @@ -430,7 +440,7 @@ def get_file_position(self): return self._comm.getFilePosition() - def start_print(self, pos=None): + def start_print(self, pos=None, *args, **kwargs): """ Starts the currently loaded print job. Only starts if the printer is connected and operational, not currently printing and a printjob is loaded @@ -463,9 +473,10 @@ def start_print(self, pos=None): self._lastProgressReport = None self._updateProgressData() self._setCurrentZ(None) - self._comm.startPrint(pos=pos) + self._comm.startPrint(pos=pos, + tags=kwargs.get("tags", set()) | {"trigger:printer.start_print"}) - def pause_print(self): + def pause_print(self, *args, **kwargs): """ Pause the current printjob. """ @@ -475,9 +486,9 @@ def pause_print(self): if self._comm.isPaused(): return - self._comm.setPause(True) + self._comm.setPause(True, tags=kwargs.get("tags", set()) | {"trigger:printer.pause_print"}) - def resume_print(self): + def resume_print(self, *args, **kwargs): """ Resume the current printjob. """ @@ -487,9 +498,9 @@ def resume_print(self): if not self._comm.isPaused(): return - self._comm.setPause(False) + self._comm.setPause(False, tags=kwargs.get("tags", set()) | {"trigger:printer.resume_print"}) - def cancel_print(self): + def cancel_print(self, *args, **kwargs): """ Cancel the current printjob. """ @@ -498,28 +509,28 @@ def cancel_print(self): # tell comm layer to cancel - will also trigger our cancelled handler # for further processing - self._comm.cancelPrint() + self._comm.cancelPrint(tags=kwargs.get("tags", set()) | {"trigger:printer.cancel_print"}) - def get_state_string(self, state=None): + def get_state_string(self, state=None, *args, **kwargs): if self._comm is None: return "Offline" else: return self._comm.getStateString(state=state) - def get_state_id(self, state=None): + def get_state_id(self, state=None, *args, **kwargs): if self._comm is None: return "OFFLINE" else: return self._comm.getStateId(state=state) - def get_current_data(self): + def get_current_data(self, *args, **kwargs): return self._stateMonitor.get_current_data() - def get_current_job(self): + def get_current_job(self, *args, **kwargs): currentData = self._stateMonitor.get_current_data() return currentData["job"] - def get_current_temperatures(self): + def get_current_temperatures(self, *args, **kwargs): if self._comm is not None: offsets = self._comm.getOffsets() else: @@ -542,10 +553,10 @@ def get_current_temperatures(self): return result - def get_temperature_history(self): + def get_temperature_history(self, *args, **kwargs): return self._temps - def get_current_connection(self): + def get_current_connection(self, *args, **kwargs): if self._comm is None: return "Closed", None, None, None @@ -553,25 +564,25 @@ def get_current_connection(self): printer_profile = self._printerProfileManager.get_current_or_default() return self._comm.getStateString(), port, baudrate, printer_profile - def is_closed_or_error(self): + def is_closed_or_error(self, *args, **kwargs): return self._comm is None or self._comm.isClosedOrError() - def is_operational(self): + def is_operational(self, *args, **kwargs): return self._comm is not None and self._comm.isOperational() - def is_printing(self): + def is_printing(self, *args, **kwargs): return self._comm is not None and self._comm.isPrinting() - def is_paused(self): + def is_paused(self, *args, **kwargs): return self._comm is not None and self._comm.isPaused() - def is_error(self): + def is_error(self, *args, **kwargs): return self._comm is not None and self._comm.isError() - def is_ready(self): + def is_ready(self, *args, **kwargs): return self.is_operational() and not self.is_printing() and not self._comm.isStreaming() - def is_sd_ready(self): + def is_sd_ready(self, *args, **kwargs): if not settings().getBoolean(["feature", "sdSupport"]) or self._comm is None: return False else: @@ -579,12 +590,12 @@ def is_sd_ready(self): #~~ sd file handling - def get_sd_files(self): + def get_sd_files(self, *args, **kwargs): if self._comm is None or not self._comm.isSdReady(): return [] return map(lambda x: (x[0][1:], x[1]), self._comm.getSdFiles()) - def add_sd_file(self, filename, absolutePath, on_success=None, on_failure=None): + def add_sd_file(self, filename, absolutePath, on_success=None, on_failure=None, *args, **kwargs): if not self._comm or self._comm.isBusy() or not self._comm.isSdReady(): self._logger.error("No connection to printer or printer is busy") return @@ -604,26 +615,28 @@ def add_sd_file(self, filename, absolutePath, on_success=None, on_failure=None): # probably something else added through a plugin, use it's basename as-is remoteName = os.path.basename(filename) self._timeEstimationData = TimeEstimationHelper() - self._comm.startFileTransfer(absolutePath, filename, "/" + remoteName, special=not valid_file_type(filename, "gcode")) + self._comm.startFileTransfer(absolutePath, filename, "/" + remoteName, + special=not valid_file_type(filename, "gcode"), + tags=kwargs.get("tags", set()) | {"trigger:printer.add_sd_file"}) return remoteName - def delete_sd_file(self, filename): + def delete_sd_file(self, filename, *args, **kwargs): if not self._comm or not self._comm.isSdReady(): return - self._comm.deleteSdFile("/" + filename) + self._comm.deleteSdFile("/" + filename, tags=kwargs.get("tags", set()) | {"trigger:printer.delete_sd_file"}) - def init_sd_card(self): + def init_sd_card(self, *args, **kwargs): if not self._comm or self._comm.isSdReady(): return - self._comm.initSdCard() + self._comm.initSdCard(tags=kwargs.get("tags", set()) | {"trigger:printer.init_sd_card"}) - def release_sd_card(self): + def release_sd_card(self, *args, **kwargs): if not self._comm or not self._comm.isSdReady(): return - self._comm.releaseSdCard() + self._comm.releaseSdCard(tags=kwargs.get("tags", set()) | {"trigger:printer.release_sd_card"}) - def refresh_sd_files(self, blocking=False): + def refresh_sd_files(self, blocking=False, *args, **kwargs): """ Refreshes the list of file stored on the SD card attached to printer (if available and printer communication available). Optional blocking parameter allows making the method block (max 10s) until the file list has been @@ -632,9 +645,9 @@ def refresh_sd_files(self, blocking=False): if not self._comm or not self._comm.isSdReady(): return self._sdFilelistAvailable.clear() - self._comm.refreshSdFiles() + self._comm.refreshSdFiles(tags=kwargs.get("tags", set()) | {"trigger:printer.refresh_sd_files"}) if blocking: - self._sdFilelistAvailable.wait(10000) + self._sdFilelistAvailable.wait(kwargs.get("timeout", 10000)) #~~ state monitoring diff --git a/src/octoprint/server/__init__.py b/src/octoprint/server/__init__.py index 8f90b6ea07..ae624cf33e 100644 --- a/src/octoprint/server/__init__.py +++ b/src/octoprint/server/__init__.py @@ -350,8 +350,37 @@ def octoprint_plugin_inject_factory(name, implementation): """Factory for injections for all OctoPrintPlugins""" if not isinstance(implementation, octoprint.plugin.OctoPrintPlugin): return None + + components_copy = dict(components) + if "printer" in components: + import wrapt + import functools + + def tagwrap(f): + @functools.wraps(f) + def wrapper(*args, **kwargs): + tags = kwargs.get("tags", set()) | {"source:plugin", + "plugin:{}".format(name)} + kwargs["tags"] = tags + return f(*args, **kwargs) + setattr(wrapper, "__tagwrapped__", True) + return wrapper + + class TaggedFuncsPrinter(wrapt.ObjectProxy): + def __getattribute__(self, attr): + __wrapped__ = super(TaggedFuncsPrinter, self).__getattribute__("__wrapped__") + item = getattr(__wrapped__, attr) + if callable(item) \ + and ("tags" in item.__code__.co_varnames or "kwargs" in item.__code__.co_varnames) \ + and not getattr(item, "__tagwrapped__", False): + return tagwrap(item) + else: + return item + + components_copy["printer"] = TaggedFuncsPrinter(components["printer"]) + props = dict() - props.update(components) + props.update(components_copy) props.update(dict( data_folder=os.path.join(self._settings.getBaseFolder("data"), name) )) diff --git a/src/octoprint/server/api/files.py b/src/octoprint/server/api/files.py index 4ba185f40e..00d6b3a67b 100644 --- a/src/octoprint/server/api/files.py +++ b/src/octoprint/server/api/files.py @@ -325,7 +325,7 @@ def fileProcessingFinished(filename, absFilename, destination): """ if destination == FileDestinations.SDCARD and octoprint.filemanager.valid_file_type(filename, "machinecode"): - return filename, printer.add_sd_file(filename, absFilename, selectAndOrPrint) + return filename, printer.add_sd_file(filename, absFilename, selectAndOrPrint, tags={"source:api", "api:files.sd"}) else: selectAndOrPrint(filename, absFilename, destination) return filename @@ -743,7 +743,7 @@ def deleteGcodeFile(filename, target): # delete it if target == FileDestinations.SDCARD: - printer.delete_sd_file(filename) + printer.delete_sd_file(filename, tags={"source:api", "api:files.sd"}) else: fileManager.remove_file(target, filename) diff --git a/src/octoprint/server/api/job.py b/src/octoprint/server/api/job.py index 8f9d1a71a1..f9adb42f63 100644 --- a/src/octoprint/server/api/job.py +++ b/src/octoprint/server/api/job.py @@ -32,30 +32,32 @@ def controlJob(): activePrintjob = printer.is_printing() or printer.is_paused() + tags = {"source:api", "api:job"} + if command == "start": if activePrintjob: return make_response("Printer already has an active print job, did you mean 'restart'?", 409) - printer.start_print() + printer.start_print(tags=tags) elif command == "restart": if not printer.is_paused(): return make_response("Printer does not have an active print job or is not paused", 409) - printer.start_print() + printer.start_print(tags=tags) elif command == "pause": if not activePrintjob: return make_response("Printer is neither printing nor paused, 'pause' command cannot be performed", 409) action = data.get("action", "toggle") if action == "toggle": - printer.toggle_pause_print() + printer.toggle_pause_print(tags=tags) elif action == "pause": - printer.pause_print() + printer.pause_print(tags=tags) elif action == "resume": - printer.resume_print() + printer.resume_print(tags=tags) else: return make_response("Unknown action '{}', allowed values for action parameter are 'pause', 'resume' and 'toggle'".format(action), 400) elif command == "cancel": if not activePrintjob: return make_response("Printer is neither printing nor paused, 'cancel' command cannot be performed", 409) - printer.cancel_print() + printer.cancel_print(tags=tags) return NO_CONTENT diff --git a/src/octoprint/server/api/printer.py b/src/octoprint/server/api/printer.py index b6e81b220d..0452e9e03f 100644 --- a/src/octoprint/server/api/printer.py +++ b/src/octoprint/server/api/printer.py @@ -75,6 +75,8 @@ def printerToolCommand(): validation_regex = re.compile("tool\d+") + tags = {"source:api", "api:printer.tool"} + ##~~ tool selection if command == "select": tool = data["tool"] @@ -83,7 +85,7 @@ def printerToolCommand(): if not tool.startswith("tool"): return make_response("Invalid tool for selection: %s" % tool, 400) - printer.change_tool(tool) + printer.change_tool(tool, tags=tags) ##~~ temperature elif command == "target": @@ -100,7 +102,7 @@ def printerToolCommand(): # perform the actual temperature commands for tool in validated_values.keys(): - printer.set_temperature(tool, validated_values[tool]) + printer.set_temperature(tool, validated_values[tool], tags=tags) ##~~ temperature offset elif command == "offset": @@ -129,14 +131,14 @@ def printerToolCommand(): amount = data["amount"] if not isinstance(amount, (int, long, float)): return make_response("Not a number for extrusion amount: %r" % amount, 400) - printer.extrude(amount) + printer.extrude(amount, tags=tags) elif command == "flowrate": factor = data["factor"] if not isinstance(factor, (int, long, float)): return make_response("Not a number for flow rate: %r" % factor, 400) try: - printer.flow_rate(factor) + printer.flow_rate(factor, tags=tags) except ValueError as e: return make_response("Invalid value for flow rate: %s" % str(e), 400) @@ -171,6 +173,8 @@ def printerBedCommand(): if response is not None: return response + tags = {"source:api", "api:printer.bed"} + ##~~ temperature if command == "target": target = data["target"] @@ -180,7 +184,7 @@ def printerBedCommand(): return make_response("Not a number: %r" % target, 400) # perform the actual temperature command - printer.set_temperature("bed", target) + printer.set_temperature("bed", target, tags=tags) ##~~ temperature offset elif command == "offset": @@ -232,6 +236,8 @@ def printerPrintheadCommand(): # do not jog when a print job is running or we don't have a connection return make_response("Printer is not operational or currently printing", 409) + tags = {"source:api", "api:printer.printhead"} + valid_axes = ["x", "y", "z"] ##~~ jog command if command == "jog": @@ -248,7 +254,7 @@ def printerPrintheadCommand(): speed = data.get("speed", None) # execute the jog commands - printer.jog(validated_values, relative=not absolute, speed=speed) + printer.jog(validated_values, relative=not absolute, speed=speed, tags=tags) ##~~ home command elif command == "home": @@ -260,14 +266,14 @@ def printerPrintheadCommand(): validated_values.append(axis) # execute the home command - printer.home(validated_values) + printer.home(validated_values, tags=tags) elif command == "feedrate": factor = data["factor"] if not isinstance(factor, (int, long, float)): return make_response("Not a number for feed rate: %r" % factor, 400) try: - printer.feed_rate(factor) + printer.feed_rate(factor, tags=tags) except ValueError as e: return make_response("Invalid value for feed rate: %s" % str(e), 400) @@ -295,12 +301,14 @@ def printerSdCommand(): if response is not None: return response + tags = {"source:api", "api:printer.sd"} + if command == "init": - printer.init_sd_card() + printer.init_sd_card(tags=tags) elif command == "refresh": - printer.refresh_sd_files() + printer.refresh_sd_files(tags=tags) elif command == "release": - printer.release_sd_card() + printer.release_sd_card(tags=tags) return NO_CONTENT @@ -341,6 +349,8 @@ def printerCommand(): if "parameters" in data: parameters = data["parameters"] + tags = {"source:api", "api:printer.command"} + if "command" in data or "commands" in data: if "command" in data: commands = [data["command"]] @@ -356,7 +366,7 @@ def printerCommand(): commandToSend = command % parameters commandsToSend.append(commandToSend) - printer.commands(commandsToSend) + printer.commands(commandsToSend, tags=tags) elif "script" in data: script_name = data["script"] @@ -365,7 +375,7 @@ def printerCommand(): context["context"] = data["context"] try: - printer.script(script_name, context=context) + printer.script(script_name, context=context, tags=tags) except UnknownScript: return make_response("Unknown script: {script_name}".format(**locals()), 404) diff --git a/src/octoprint/util/comm.py b/src/octoprint/util/comm.py index f1f4844cbc..234efd2286 100644 --- a/src/octoprint/util/comm.py +++ b/src/octoprint/util/comm.py @@ -342,6 +342,7 @@ class MachineCom(object): def __init__(self, port = None, baudrate=None, callbackObject=None, printerProfileManager=None): self._logger = logging.getLogger(__name__) self._serialLogger = logging.getLogger("SERIAL") + self._phaseLogger = logging.getLogger(__name__ + ".command_phases") if port == None: port = settings().get(["serial", "port"]) @@ -787,7 +788,7 @@ def setTemperatureOffset(self, offsets): def fakeOk(self): self._handle_ok() - def sendCommand(self, cmd, cmd_type=None, processed=False, force=False, on_sent=None): + def sendCommand(self, cmd, cmd_type=None, processed=False, force=False, on_sent=None, tags=None): cmd = to_unicode(cmd, errors="replace") if not processed: cmd = process_gcode_line(cmd) @@ -796,13 +797,13 @@ def sendCommand(self, cmd, cmd_type=None, processed=False, force=False, on_sent= if self.isPrinting() and not self.isSdFileSelected(): try: - self._command_queue.put((cmd, cmd_type, on_sent), item_type=cmd_type) + self._command_queue.put((cmd, cmd_type, on_sent, tags), item_type=cmd_type) return True except TypeAlreadyInQueue as e: self._logger.debug("Type already in command queue: " + e.type) return False elif self.isOperational() or force: - return self._sendCommand(cmd, cmd_type=cmd_type, on_sent=on_sent) + return self._sendCommand(cmd, cmd_type=cmd_type, on_sent=on_sent, tags=tags) def _getGcodeScript(self, scriptName, replacements=None): context = dict() @@ -870,13 +871,16 @@ def to_list(data): scriptLines)) - def sendGcodeScript(self, scriptName, replacements=None): + def sendGcodeScript(self, scriptName, replacements=None, tags=None): + if tags is None: + tags = set() + scriptLines = self._getGcodeScript(scriptName, replacements=replacements) for line in scriptLines: - self.sendCommand(line) + self.sendCommand(line, tags=tags | {"trigger:comm.send_gcode_script", "source:script", "script:{}".format(scriptName)}) return "\n".join(scriptLines) - def startPrint(self, pos=None): + def startPrint(self, pos=None, tags=None): if not self.isOperational() or self.isPrinting(): return @@ -888,13 +892,16 @@ def startPrint(self, pos=None): self._pauseWaitStartTime = 0 self._pauseWaitTimeLost = 0.0 + if tags is None: + tags = set() + try: with self._jobLock: self._currentFile.start() self._changeState(self.STATE_PRINTING) - self.resetLineNumbers() + self.resetLineNumbers(tags={"trigger:comm.start_print"}) self._callback.on_comm_print_job_started() @@ -904,14 +911,15 @@ def startPrint(self, pos=None): # make sure to ignore the "file selected" later on, otherwise we'll reset our progress data self._ignore_select = True - self.sendCommand("M23 {filename}".format(filename=self._currentFile.getFilename())) + self.sendCommand("M23 {filename}".format(filename=self._currentFile.getFilename()), + tags=tags | {"trigger:comm.start_print",}) if pos is not None and isinstance(pos, int) and pos > 0: self._currentFile.setFilepos(pos) - self.sendCommand("M26 S{}".format(pos)) + self.sendCommand("M26 S{}".format(pos), tags=tags | {"trigger:comm.start_print",}) else: self._currentFile.setFilepos(0) - self.sendCommand("M24") + self.sendCommand("M24", tags=tags | {"trigger:comm.start_print",}) self._sd_status_timer = RepeatedTimer(self._timeout_intervals.get("sdStatus", 1.0), self._poll_sd_status, run_first=True) self._sd_status_timer.start() @@ -921,7 +929,7 @@ def startPrint(self, pos=None): line = self._getNext() if line is not None: - self.sendCommand(line) + self.sendCommand(line, tags={"trigger:comm.start_print", "source:file"}) # now make sure we actually do something, up until now we only filled up the queue self._sendFromQueue() @@ -931,13 +939,16 @@ def startPrint(self, pos=None): self._changeState(self.STATE_ERROR) eventManager().fire(Events.ERROR, {"error": self.getErrorString()}) - def startFileTransfer(self, filename, localFilename, remoteFilename, special=False): + def startFileTransfer(self, filename, localFilename, remoteFilename, special=False, tags=None): if not self.isOperational() or self.isBusy(): self._logger.info("Printer is not operational or busy") return + if tags is None: + tags = set() + with self._jobLock: - self.resetLineNumbers() + self.resetLineNumbers(tags={"trigger:comm.start_file_transfer"}) if special: self._currentFile = SpecialStreamingGcodeFileInformation(filename, localFilename, remoteFilename) @@ -945,22 +956,25 @@ def startFileTransfer(self, filename, localFilename, remoteFilename, special=Fal self._currentFile = StreamingGcodeFileInformation(filename, localFilename, remoteFilename) self._currentFile.start() - self.sendCommand("M28 %s" % remoteFilename) + self.sendCommand("M28 %s" % remoteFilename, tags=tags | {"trigger:comm.start_file_transfer",}) eventManager().fire(Events.TRANSFER_STARTED, {"local": localFilename, "remote": remoteFilename}) self._callback.on_comm_file_transfer_started(remoteFilename, self._currentFile.getFilesize()) - def cancelFileTransfer(self): + def cancelFileTransfer(self, tags=None): if not self.isOperational() or not self.isStreaming(): self._logger.info("Printer is not operational or not streaming") return - self._finishFileTransfer(failed=True) + self._finishFileTransfer(failed=True, tags=tags) + + def _finishFileTransfer(self, failed=False, tags=None): + if tags is None: + tags = set() - def _finishFileTransfer(self, failed=False): with self._jobLock: remote = self._currentFile.getRemoteFilename() - self._sendCommand("M29") + self._sendCommand("M29", tags=tags | {"trigger:comm.finish_file_transfer",}) if failed: self.deleteSdFile(remote) @@ -980,12 +994,15 @@ def _finishFileTransfer(self, failed=False): self._callback.on_comm_file_transfer_done(remote) eventManager().fire(Events.TRANSFER_DONE, payload) - self.refreshSdFiles() + self.refreshSdFiles(tags={"trigger:comm.finish_file_transfer",}) - def selectFile(self, filename, sd): + def selectFile(self, filename, sd, tags=None): if self.isBusy(): return + if tags is None: + tags = set() + if sd: if not self.isOperational(): # printer is not connected, can't use SD @@ -995,7 +1012,7 @@ def selectFile(self, filename, sd): filename = filename[1:] self._sdFileToSelect = filename - self.sendCommand("M23 %s" % filename) + self.sendCommand("M23 %s" % filename, tags=tags | {"trigger:comm.select_file",}) else: self._currentFile = PrintingGcodeFileInformation(filename, offsets_callback=self.getOffsets, current_tool_callback=self.getCurrentTool) self._callback.on_comm_file_selected(filename, self._currentFile.getFilesize(), False) @@ -1011,7 +1028,7 @@ def _cancel_preparation_done(self): self._recordFilePosition() self._callback.on_comm_print_job_cancelled() - def cancelPrint(self, firmware_error=None, disable_log_position=False): + def cancelPrint(self, firmware_error=None, disable_log_position=False, tags=None): if not self.isOperational(): return @@ -1024,20 +1041,23 @@ def cancelPrint(self, firmware_error=None, disable_log_position=False): self.cancelFileTransfer() return + if tags is None: + tags = set() + def _on_M400_sent(): # we don't call on_print_job_cancelled on our callback here # because we do this only after our M114 has been answered # by the firmware self._record_cancel_data = True - self.sendCommand("M114") + self.sendCommand("M114", tags=tags | {"trigger:comm.cancel", "trigger:record_position"}) with self._jobLock: self._changeState(self.STATE_OPERATIONAL) if self.isSdFileSelected(): - self.sendCommand("M25") # pause print - self.sendCommand("M27") # get current byte position in file - self.sendCommand("M26 S0") # reset position in file to byte 0 + self.sendCommand("M25", tags=tags | {"trigger:comm.cancel",}) # pause print + self.sendCommand("M27", tags=tags | {"trigger:comm.cancel",}) # get current byte position in file + self.sendCommand("M26 S0", tags=tags | {"trigger:comm.cancel",}) # reset position in file to byte 0 if self._sd_status_timer is not None: try: self._sd_status_timer.cancel() @@ -1045,20 +1065,23 @@ def _on_M400_sent(): pass if self._log_position_on_cancel and not disable_log_position: - self.sendCommand("M400", on_sent=_on_M400_sent) + self.sendCommand("M400", on_sent=_on_M400_sent, tags=tags | {"trigger:comm.cancel", "trigger:record_position"}) else: self._cancel_preparation_done() def _pause_preparation_done(self): self._callback.on_comm_print_job_paused() - def setPause(self, pause): + def setPause(self, pause, tags=None): if self.isStreaming(): return if not self._currentFile: return + if tags is None: + tags = set() + with self._jobLock: if not pause and self.isPaused(): if self._pauseWaitStartTime: @@ -1069,12 +1092,17 @@ def setPause(self, pause): self._callback.on_comm_print_job_resumed() if self.isSdFileSelected(): - self.sendCommand("M24") - self.sendCommand("M27") + self.sendCommand("M24", tags=tags | {"trigger:comm.set_pause","trigger:resume"}) + self.sendCommand("M27", tags=tags | {"trigger:comm.set_pause", "trigger:resume"}) else: line = self._getNext() if line is not None: - self.sendCommand(line) + if not tags: + tags_to_use = set() + else: + tags_to_use = set(tags) + tags_to_use.add("file") + self.sendCommand(line, tags=tags | {"trigger:comm.set_pause", "trigger:resume", "source:file"}) # now make sure we actually do something, up until now we only filled up the queue self._sendFromQueue() @@ -1085,59 +1113,68 @@ def setPause(self, pause): self._changeState(self.STATE_PAUSED) if self.isSdFileSelected(): - self.sendCommand("M25") # pause print + self.sendCommand("M25", tags=tags | {"trigger:comm.set_pause", "trigger:pause"}) # pause print def _on_M400_sent(): # we don't call on_print_job_paused on our callback here # because we do this only after our M114 has been answered # by the firmware self._record_pause_data = True - self.sendCommand("M114") + self.sendCommand("M114", tags=tags | {"trigger:comm.set_pause", "trigger:pause", "trigger:record_position"}) if self._log_position_on_pause: - self.sendCommand("M400", on_sent=_on_M400_sent) + self.sendCommand("M400", on_sent=_on_M400_sent, tags=tags | {"trigger:comm.set_pause", "trigger:pause", "trigger:record_position"}) else: self._pause_preparation_done() def getSdFiles(self): return self._sdFiles - def deleteSdFile(self, filename): + def deleteSdFile(self, filename, tags=None): if not self._sdEnabled: return + if tags is None: + tags = set() + if not self.isOperational() or (self.isBusy() and isinstance(self._currentFile, PrintingSdFileInformation) and self._currentFile.getFilename() == filename): # do not delete a file from sd we are currently printing from return - self.sendCommand("M30 %s" % filename.lower()) + self.sendCommand("M30 %s" % filename.lower(), tags=tags | {"trigger:comm.delete_sd_file",}) self.refreshSdFiles() - def refreshSdFiles(self): + def refreshSdFiles(self, tags=None): if not self._sdEnabled: return if not self.isOperational() or self.isBusy(): return - self.sendCommand("M20") + if tags is None: + tags = set() - def initSdCard(self): + self.sendCommand("M20", tags=tags | {"trigger:comm.refresh_sd_files",}) + + def initSdCard(self, tags=None): if not self._sdEnabled: return if not self.isOperational(): return - self.sendCommand("M21") + if tags is None: + tags = set() + + self.sendCommand("M21", tags=tags | {"trigger:comm.init_sd_card"}) if self._sdAlwaysAvailable: self._sdAvailable = True self.refreshSdFiles() self._callback.on_comm_sd_state_change(self._sdAvailable) - def releaseSdCard(self): + def releaseSdCard(self, tags=None): if not self._sdEnabled: return @@ -1145,22 +1182,31 @@ def releaseSdCard(self): # do not release the sd card if we are currently printing from it return - self.sendCommand("M22") + if tags is None: + tags = set() + + self.sendCommand("M22", tags=tags | {"trigger:comm.release_sd_card",}) self._sdAvailable = False self._sdFiles = [] self._callback.on_comm_sd_state_change(self._sdAvailable) self._callback.on_comm_sd_files(self._sdFiles) - def sayHello(self): - self.sendCommand(self._hello_command, force=True) + def sayHello(self, tags=None): + if tags is None: + tags = set() + + self.sendCommand(self._hello_command, force=True, tags=tags | {"trigger:comm.say_hello",}) self._clear_to_send.set() - def resetLineNumbers(self, number=0): + def resetLineNumbers(self, number=0, tags=None): if not self.isOperational(): return - self.sendCommand("M110 N%d" % number) + if tags is None: + tags = set() + + self.sendCommand("M110 N%d" % number, tags=tags | {"trigger:comm.reset_line_numbers",}) ##~~ record aborted file positions @@ -1640,7 +1686,7 @@ def convert_line(line): self._baudrateDetectRetry -= 1 self._serial.write('\n') self._log("Baudrate test retry: %d" % (self._baudrateDetectRetry)) - self.sayHello() + self.sayHello(tags="trigger:baudrate_detection") elif len(self._baudrateDetectList) > 0: baudrate = self._baudrateDetectList.pop(0) try: @@ -1651,7 +1697,7 @@ def convert_line(line): self._baudrateDetectRetry = 5 self._timeout = get_new_timeout("communicationBusy" if self._busy_protocol_detected else "communication", self._timeout_intervals) self._serial.write('\n') - self.sayHello() + self.sayHello(tags="trigger:baudrate_detection") except: self._log("Unexpected error while setting baudrate {}: {}".format(baudrate, get_exception_string())) self._logger.exception("Unexpceted error while setting baudrate {}".format(baudrate)) @@ -1792,7 +1838,7 @@ def _handle_timeout(self): message = "Communication timeout while printing, trying to trigger response from printer." self._logger.info(message) self._log(message + " " + general_message) - if self._sendCommand("M105", cmd_type="temperature"): + if self._sendCommand("M105", cmd_type="temperature", tags={"trigger:comm.handle_timeout"}): self._clear_to_send.set() elif self._clear_to_send.blocked(): @@ -1873,7 +1919,7 @@ def _poll_temperature(self): """ if self.isOperational() and not self._temperature_autoreporting and not self._connection_closing and not self.isStreaming() and not self._long_running_command and not self._heating and not self._dwelling_until and not self._manualStreaming: - self.sendCommand("M105", cmd_type="temperature_poll") + self.sendCommand("M105", cmd_type="temperature_poll", tags={"trigger:comm.poll_temperature"}) def _poll_sd_status(self): """ @@ -1884,7 +1930,7 @@ def _poll_sd_status(self): """ if self.isOperational() and not self._connection_closing and self.isSdPrinting() and not self._long_running_command and not self._dwelling_until and not self._heating: - self.sendCommand("M27", cmd_type="sd_status_poll") + self.sendCommand("M27", cmd_type="sd_status_poll", tags={"trigger:comm.poll_sd_status"}) def _set_autoreport_temperature_interval(self, interval=None): if interval is None: @@ -1892,7 +1938,7 @@ def _set_autoreport_temperature_interval(self, interval=None): interval = int(self._timeout_intervals.get("temperatureAutoreport", 2)) except: interval = 2 - self.sendCommand("M155 S{}".format(interval)) + self.sendCommand("M155 S{}".format(interval), tags={"trigger:comm.set_autoreport_temperature_interval"}) def _set_busy_protocol_interval(self, interval=None): if interval is None: @@ -1900,7 +1946,7 @@ def _set_busy_protocol_interval(self, interval=None): interval = max(int(self._timeout_intervals.get("communicationBusy", 3)) - 1, 1) except: interval = 2 - self.sendCommand("M113 S{}".format(interval)) + self.sendCommand("M113 S{}".format(interval), tags={"trigger:comm.set_busy_protocol_interval"}) def _onConnected(self): self._serial.timeout = settings().getFloat(["serial", "timeout", "communication"]) @@ -1909,21 +1955,20 @@ def _onConnected(self): self._changeState(self.STATE_OPERATIONAL) - self.resetLineNumbers() - if self._firmware_detection: - self.sendCommand("M115") + self.resetLineNumbers(tags={"trigger:comm.on_connected",}) + self.sendCommand("M115", tags={"trigger:comm.on_connected",}) if self._sdAvailable: - self.refreshSdFiles() + self.refreshSdFiles(tags={"trigger:comm.on_connected",}) else: - self.initSdCard() + self.initSdCard(tags={"trigger:comm.on_connected"}) payload = dict(port=self._port, baudrate=self._baudrate) eventManager().fire(Events.CONNECTED, payload) self.sendGcodeScript("afterPrinterConnected", replacements=dict(event=payload)) def _onExternalReset(self): - self.resetLineNumbers() + self.resetLineNumbers(tags={"trigger:comm.on_external_reset"}) if self._temperature_autoreporting: self._set_autoreport_temperature_interval() @@ -1965,17 +2010,18 @@ def _sendFromQueue(self): try: if isinstance(entry, tuple): - if not len(entry) == 3: + if not len(entry) == 4: # something with that entry is broken, ignore it and fetch # the next one continue - cmd, cmd_type, callback = entry + cmd, cmd_type, callback, tags = entry else: cmd = entry cmd_type = None callback = None + tags = None - if self._sendCommand(cmd, cmd_type=cmd_type, on_sent=callback): + if self._sendCommand(cmd, cmd_type=cmd_type, on_sent=callback, tags=tags): # we actually did add this cmd to the send queue, so let's # return, we are done here return True @@ -2202,7 +2248,7 @@ def _sendNext(self): # end of file, return false return False - result = self._sendCommand(line) + result = self._sendCommand(line, tags={"source:file"}) self._callback.on_comm_progress() if result: # line sent, return true @@ -2327,7 +2373,7 @@ def _resendNextCommand(self, again=False): return result - def _sendCommand(self, cmd, cmd_type=None, on_sent=None): + def _sendCommand(self, cmd, cmd_type=None, on_sent=None, tags=None): # Make sure we are only handling one sending job at a time with self._sendingLock: if self._serial is None: @@ -2337,16 +2383,16 @@ def _sendCommand(self, cmd, cmd_type=None, on_sent=None): if not self.isStreaming(): # trigger the "queuing" phase only if we are not streaming to sd right now - results = self._process_command_phase("queuing", cmd, command_type=cmd_type, gcode=gcode, subcode=subcode) + results = self._process_command_phase("queuing", cmd, command_type=cmd_type, gcode=gcode, subcode=subcode, tags=tags) if not results: # command is no more, return return False else: - results = [(cmd, cmd_type, gcode, subcode)] + results = [(cmd, cmd_type, gcode, subcode, tags)] # process helper - def process(cmd, cmd_type, gcode, subcode, on_sent=None): + def process(cmd, cmd_type, gcode, subcode, on_sent=None, tags=None): if cmd is None: # no command, next entry return False @@ -2356,10 +2402,10 @@ def process(cmd, cmd_type, gcode, subcode, on_sent=None): eventManager().fire(gcodeToEvent[gcode]) # actually enqueue the command for sending - if self._enqueue_for_sending(cmd, command_type=cmd_type, on_sent=on_sent): + if self._enqueue_for_sending(cmd, command_type=cmd_type, on_sent=on_sent, tags=tags): if not self.isStreaming(): # trigger the "queued" phase only if we are not streaming to sd right now - self._process_command_phase("queued", cmd, cmd_type, gcode=gcode, subcode=subcode) + self._process_command_phase("queued", cmd, cmd_type, gcode=gcode, subcode=subcode, tags=tags) return True else: return False @@ -2376,18 +2422,18 @@ def process(cmd, cmd_type, gcode, subcode, on_sent=None): enqueued_something = False # process all but the last ... - for (cmd, cmd_type, gcode, subcode) in results: - enqueued_something = process(cmd, cmd_type, gcode, subcode) or enqueued_something + for (cmd, cmd_type, gcode, subcode, tags) in results: + enqueued_something = process(cmd, cmd_type, gcode, subcode, tags=tags) or enqueued_something # ... and then process the last one with the on_sent callback attached - cmd, cmd_type, gcode, subcode = last_command - enqueued_something = process(cmd, cmd_type, gcode, subcode, on_sent=on_sent) or enqueued_something + cmd, cmd_type, gcode, subcode, tags = last_command + enqueued_something = process(cmd, cmd_type, gcode, subcode, on_sent=on_sent, tags=tags) or enqueued_something return enqueued_something ##~~ send loop handling - def _enqueue_for_sending(self, command, linenumber=None, command_type=None, on_sent=None, resend=False): + def _enqueue_for_sending(self, command, linenumber=None, command_type=None, on_sent=None, resend=False, tags=None): """ Enqueues a command and optional linenumber to use for it in the send queue. @@ -2398,6 +2444,8 @@ def _enqueue_for_sending(self, command, linenumber=None, command_type=None, on_s command_type (str): Optional command type, if set and command type is already in the queue the command won't be enqueued on_sent (callable): Optional callable to call after command has been sent to printer. + resend (bool): Whether this is a resent command + tags (set of str or None): Tags to attach to this command """ try: @@ -2405,7 +2453,7 @@ def _enqueue_for_sending(self, command, linenumber=None, command_type=None, on_s if resend: target = "resend" - self._send_queue.put((command, linenumber, command_type, on_sent, False), item_type=command_type, target=target) + self._send_queue.put((command, linenumber, command_type, on_sent, False, tags), item_type=command_type, target=target) return True except TypeAlreadyInQueue as e: self._logger.debug("Type already in send queue: " + e.type) @@ -2436,7 +2484,7 @@ def _send_loop(self): self._dwelling_until = False # fetch command, command type and optional linenumber and sent callback from queue - command, linenumber, command_type, on_sent, processed = entry + command, linenumber, command_type, on_sent, processed, tags = entry # some firmwares (e.g. Smoothie) might support additional in-band communication that will not # stick to the acknowledgement behaviour of GCODE, so we check here if we have a GCODE command @@ -2451,7 +2499,10 @@ def _send_loop(self): else: if not processed: # trigger "sending" phase if we didn't so far - results = self._process_command_phase("sending", command, command_type, gcode=gcode, subcode=subcode) + results = self._process_command_phase("sending", command, command_type, + gcode=gcode, + subcode=subcode, + tags=tags) if not results: # No, we are not going to send this, that was a last-minute bail. @@ -2471,7 +2522,7 @@ def _send_loop(self): assert len(results) == 1 # we only use the first (and only!) entry here - command, _, gcode, subcode = results[0] + command, _, gcode, subcode, tags = results[0] if command.strip() == "": self._logger.info("Refusing to send an empty line to the printer") @@ -2499,7 +2550,7 @@ def _send_loop(self): if on_sent is not None and callable(on_sent): # we have a sent callback for this specific command, let's execute it now on_sent() - self._process_command_phase("sent", command, command_type, gcode=gcode, subcode=subcode) + self._process_command_phase("sent", command, command_type, gcode=gcode, subcode=subcode, tags=tags) # we only need to use up a clear if the command we just sent was either a gcode command or if we also # require ack's for unknown commands @@ -2527,10 +2578,28 @@ def _send_loop(self): self._logger.exception("Caught an exception in the send loop") self._log("Closing down send loop") - def _process_command_phase(self, phase, command, command_type=None, gcode=None, subcode=None): + def _log_command_phase(self, phase, command, *args, **kwargs): + if self._phaseLogger.isEnabledFor(logging.DEBUG): + output_parts = [u"phase: {}".format(phase), + u"command: {}".format(command)] + + if kwargs.get("command_type"): + output_parts.append(u"command_type: {}".format(kwargs["command_type"])) + if kwargs.get("gcode"): + output_parts.append(u"gcode: {}".format(kwargs["gcode"])) + if kwargs.get("subcode"): + output_parts.append(u"subcode: {}".format(kwargs["subcode"])) + if kwargs.get("tags"): + output_parts.append(u"tags: [ {} ]".format(", ".join(sorted(kwargs["tags"])))) + + self._phaseLogger.debug(" | ".join(output_parts)) + + def _process_command_phase(self, phase, command, command_type=None, gcode=None, subcode=None, tags=None): if gcode is None: gcode, subcode = gcode_and_subcode_for_cmd(command) - results = [(command, command_type, gcode, subcode)] + results = [(command, command_type, gcode, subcode, tags)] + + self._log_command_phase(phase, command, command_type=command_type, gcode=gcode, subcode=subcode, tags=tags) if (self.isStreaming() and self.isPrinting()) or phase not in ("queuing", "queued", "sending", "sent"): return results @@ -2538,18 +2607,22 @@ def _process_command_phase(self, phase, command, command_type=None, gcode=None, # send it through the phase specific handlers provided by plugins for name, hook in self._gcode_hooks[phase].items(): new_results = [] - for command, command_type, gcode, subcode in results: + for command, command_type, gcode, subcode, tags in results: try: - hook_results = hook(self, phase, command, command_type, gcode, subcode=subcode) + hook_results = hook(self, phase, command, command_type, gcode, subcode=subcode, tags=tags) except: self._logger.exception("Error while processing hook {name} for phase {phase} and command {command}:".format(**locals())) else: - normalized = _normalize_command_handler_result(command, command_type, gcode, subcode, hook_results) + normalized = _normalize_command_handler_result(command, command_type, gcode, subcode, tags, + hook_results, + tags_to_add={"source:rewrite", + "phase:{}".format(phase), + "plugin:{}".format(name)}) # make sure we don't allow multi entry results in anything but the queuing phase if not phase in ("queuing",) and len(normalized) > 1: self._logger.error("Error while processing hook {name} for phase {phase} and command {command}: Hook returned multi-entry result for phase {phase} and command {command}. That's not supported, if you need to do multi expansion of commands you need to do this in the queuing phase. Ignoring hook result and sending command as-is.".format(**locals())) - new_results.append((command, command_type, gcode, subcode)) + new_results.append((command, command_type, gcode, subcode, tags)) else: new_results += normalized if not new_results: @@ -2560,15 +2633,19 @@ def _process_command_phase(self, phase, command, command_type=None, gcode=None, # if it's a gcode command send it through the specific handler if it exists new_results = [] modified = False - for command, command_type, gcode, subcode in results: + for command, command_type, gcode, subcode, tags in results: if gcode is not None: gcode_handler = "_gcode_" + gcode + "_" + phase if hasattr(self, gcode_handler): - handler_results = getattr(self, gcode_handler)(command, cmd_type=command_type, subcode=subcode) - new_results += _normalize_command_handler_result(command, command_type, gcode, subcode, handler_results) + handler_results = getattr(self, gcode_handler)(command, + cmd_type=command_type, + subcode=subcode, + tags=tags) + new_results += _normalize_command_handler_result(command, command_type, gcode, subcode, tags, + handler_results) modified = True else: - new_results.append((command, command_type, gcode, subcode)) + new_results.append((command, command_type, gcode, subcode, tags)) modified = True if modified: if not new_results: @@ -2581,9 +2658,14 @@ def _process_command_phase(self, phase, command, command_type=None, gcode=None, command_phase_handler = "_command_phase_" + phase if hasattr(self, command_phase_handler): new_results = [] - for command, command_type, gcode, subcode in results: - handler_results = getattr(self, command_phase_handler)(command, cmd_type=command_type, gcode=gcode, subcode=subcode) - new_results += _normalize_command_handler_result(command, command_type, gcode, subcode, handler_results) + for command, command_type, gcode, subcode, tags in results: + handler_results = getattr(self, command_phase_handler)(command, + cmd_type=command_type, + gcode=gcode, + subcode=subcode, + tags=tags) + new_results += _normalize_command_handler_result(command, command_type, gcode, subcode, tags, + handler_results) results = new_results # finally return whatever we resulted on @@ -3670,7 +3752,7 @@ def gcode_and_subcode_for_cmd(cmd): return gcode, values.get("subcode", None) -def _normalize_command_handler_result(command, command_type, gcode, subcode, handler_results): +def _normalize_command_handler_result(command, command_type, gcode, subcode, tags, handler_results, tags_to_add=None): """ Normalizes a command handler result. @@ -3678,14 +3760,15 @@ def _normalize_command_handler_result(command, command_type, gcode, subcode, han entries. ``None`` results are ignored, the provided ``command``, ``command_type``, - ``gcode`` and ``subcode`` are returned in that case (as single-entry list with - one 4-tuple as entry). + ``gcode``, ``subcode`` and ``tags`` are returned in that case (as single-entry list with + one 5-tuple as entry). Single result entries are either: * a single string defining a replacement ``command`` * a 1-tuple defining a replacement ``command`` * a 2-tuple defining a replacement ``command`` and ``command_type`` + * a 3-tuple defining a replacement ``command`` and ``command_type`` and additional ``tags`` to set A ``command`` that is ``None`` will lead to the entry being ignored for the normalized result. @@ -3696,26 +3779,32 @@ def _normalize_command_handler_result(command, command_type, gcode, subcode, han be empty in which case the command is to be suppressed. Examples: - >>> _normalize_command_handler_result("M105", None, "M105", None, None) - [('M105', None, 'M105', None)] - >>> _normalize_command_handler_result("M105", None, "M105", None, "M110") - [('M110', None, 'M110', None)] - >>> _normalize_command_handler_result("M105", None, "M105", None, ["M110"]) - [('M110', None, 'M110', None)] - >>> _normalize_command_handler_result("M105", None, "M105", None, ["M110", "M117 Foobar"]) - [('M110', None, 'M110', None), ('M117 Foobar', None, 'M117', None)] - >>> _normalize_command_handler_result("M105", None, "M105", None, [("M110",), "M117 Foobar"]) - [('M110', None, 'M110', None), ('M117 Foobar', None, 'M117', None)] - >>> _normalize_command_handler_result("M105", None, "M105", None, [("M110", "lineno_reset"), "M117 Foobar"]) - [('M110', 'lineno_reset', 'M110', None), ('M117 Foobar', None, 'M117', None)] - >>> _normalize_command_handler_result("M105", None, "M105", None, []) + >>> _normalize_command_handler_result("M105", None, "M105", None, None, None) + [('M105', None, 'M105', None, None)] + >>> _normalize_command_handler_result("M105", None, "M105", None, None, "M110") + [('M110', None, 'M110', None, None)] + >>> _normalize_command_handler_result("M105", None, "M105", None, None, ["M110"]) + [('M110', None, 'M110', None, None)] + >>> _normalize_command_handler_result("M105", None, "M105", None, None, ["M110", "M117 Foobar"]) + [('M110', None, 'M110', None, None), ('M117 Foobar', None, 'M117', None, None)] + >>> _normalize_command_handler_result("M105", None, "M105", None, None, [("M110",), "M117 Foobar"]) + [('M110', None, 'M110', None, None), ('M117 Foobar', None, 'M117', None, None)] + >>> _normalize_command_handler_result("M105", None, "M105", None, None, [("M110", "lineno_reset"), "M117 Foobar"]) + [('M110', 'lineno_reset', 'M110', None, None), ('M117 Foobar', None, 'M117', None, None)] + >>> _normalize_command_handler_result("M105", None, "M105", None, None, []) [] - >>> _normalize_command_handler_result("M105", None, "M105", None, ["M110", None]) - [('M110', None, 'M110', None)] - >>> _normalize_command_handler_result("M105", None, "M105", None, [("M110",), (None, "ignored")]) - [('M110', None, 'M110', None)] - >>> _normalize_command_handler_result("M105", None, "M105", None, [("M110",), ("M117 Foobar", "display_message"), ("tuple", "of unexpected", "length"), ("M110", "lineno_reset")]) - [('M110', None, 'M110', None), ('M117 Foobar', 'display_message', 'M117', None), ('M110', 'lineno_reset', 'M110', None)] + >>> _normalize_command_handler_result("M105", None, "M105", None, None, ["M110", None]) + [('M110', None, 'M110', None, None)] + >>> _normalize_command_handler_result("M105", None, "M105", None, None, [("M110",), (None, "ignored")]) + [('M110', None, 'M110', None, None)] + >>> _normalize_command_handler_result("M105", None, "M105", None, None, [("M110",), ("M117 Foobar", "display_message"), ("tuple", "of", "unexpected", "length"), ("M110", "lineno_reset")]) + [('M110', None, 'M110', None, None), ('M117 Foobar', 'display_message', 'M117', None, None), ('M110', 'lineno_reset', 'M110', None, None)] + >>> _normalize_command_handler_result("M105", None, "M105", None, {"tag1", "tag2"}, ["M110", "M117 Foobar"]) + [('M110', None, 'M110', None, set(['tag1', 'tag2'])), ('M117 Foobar', None, 'M117', None, set(['tag1', 'tag2']))] + >>> _normalize_command_handler_result("M105", None, "M105", None, {"tag1", "tag2"}, ["M110", "M105", "M117 Foobar"], tags_to_add={"tag3"}) + [('M110', None, 'M110', None, set(['tag1', 'tag2', 'tag3'])), ('M105', None, 'M105', None, set(['tag1', 'tag2'])), ('M117 Foobar', None, 'M117', None, set(['tag1', 'tag2', 'tag3']))] + >>> _normalize_command_handler_result("M105", None, "M105", None, {"tag1", "tag2"}, ["M110", ("M105", "temperature_poll"), "M117 Foobar"], tags_to_add={"tag3"}) + [('M110', None, 'M110', None, set(['tag1', 'tag2', 'tag3'])), ('M105', 'temperature_poll', 'M105', None, set(['tag1', 'tag2', 'tag3'])), ('M117 Foobar', None, 'M117', None, set(['tag1', 'tag2', 'tag3']))] Arguments: command (str or None): The command for which the handler result was @@ -3726,16 +3815,20 @@ def _normalize_command_handler_result(command, command_type, gcode, subcode, han generated subcode (str or None): The GCODE subcode for which the handler result was generated + tags (set of str or None): The tags associated with the GCODE for which + the handler result was generated handler_results: The handler result(s) to normalized. Can be either a single result entry or a list of result entries. + tags_to_add (set of str or None): List of tags to add to expanded result + entries Returns: (list) - A list of normalized handler result entries, which are - 4-tuples consisting of ``command``, ``command_type``, ``gcode`` - and ``subcode``, the latter three of which may be ``None``. + 5-tuples consisting of ``command``, ``command_type``, ``gcode`` + ``subcode`` and ``tags``, the latter three of which may be ``None``. """ - original = (command, command_type, gcode, subcode) + original = (command, command_type, gcode, subcode, tags) if handler_results is None: # handler didn't return anything, we'll just continue @@ -3753,21 +3846,39 @@ def _normalize_command_handler_result(command, command_type, gcode, subcode, han # entry is None, we'll ignore that entry and continue continue + if tags: + # copy the tags + tags = set(tags) + if isinstance(handler_result, basestring): # entry is just a string, replace command with it command = handler_result - gcode, subcode = gcode_and_subcode_for_cmd(command) - result.append((command, command_type, gcode, subcode)) + + if command != original[0]: + # command changed, re-extract gcode and subcode and add tags if necessary + gcode, subcode = gcode_and_subcode_for_cmd(command) + + if tags_to_add and isinstance(tags_to_add, set) and command != original[0]: + if tags is None: + tags = set() + tags |= tags_to_add + + result.append((command, command_type, gcode, subcode, tags)) elif isinstance(handler_result, tuple): # entry is a tuple, extract command and command_type hook_result_length = len(handler_result) + handler_tags = None + if hook_result_length == 1: # handler returned just the command command, = handler_result elif hook_result_length == 2: # handler returned command and command_type command, command_type = handler_result + elif hook_result_length == 3: + # handler returned command, command type and additional tags + command, command_type, handler_tags = handler_result else: # handler returned a tuple of an unexpected length, ignore # and continue @@ -3777,11 +3888,25 @@ def _normalize_command_handler_result(command, command_type, gcode, subcode, han # command is None, ignore it and continue continue - gcode, subcode = gcode_and_subcode_for_cmd(command) - result.append((command, command_type, gcode, subcode)) + if command != original[0] or command_type != original[2]: + # command or command_type changed, re-extract gcode and subcode and add tags if necessary + gcode, subcode = gcode_and_subcode_for_cmd(command) + + if tags_to_add and isinstance(tags_to_add, set): + if tags is None: + tags = set() + tags |= tags_to_add + + if handler_tags and isinstance(handler_tags, set): + # handler provided additional tags, add them + if tags is None: + tags = set() + tags |= handler_tags + + result.append((command, command_type, gcode, subcode, tags)) # reset to original - command, command_type, gcode, subcode = original + command, command_type, gcode, subcode, tags = original return result From d166bafa8df4defd4f6da3f172a0aa5a1137dc9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Fri, 16 Feb 2018 22:03:45 +0100 Subject: [PATCH 217/333] New context "job_on_hold" on printer object Can be used to do some stuff while printing while ensuring that the file won't continue to be streamed. Should be used sparingly and only for very limited reasons since it can severely affect print results. --- src/octoprint/printer/__init__.py | 27 +++++++++++++++++++++++++++ src/octoprint/printer/standard.py | 5 +++++ src/octoprint/util/comm.py | 23 +++++++++++++++++++++-- 3 files changed, 53 insertions(+), 2 deletions(-) diff --git a/src/octoprint/printer/__init__.py b/src/octoprint/printer/__init__.py index 84a892afb1..038aa1547b 100644 --- a/src/octoprint/printer/__init__.py +++ b/src/octoprint/printer/__init__.py @@ -107,6 +107,33 @@ def get_transport(self, *args, **kwargs): """ raise NotImplementedError() + def job_on_hold(self, blocking=True, *args, **kwargs): + """ + Contextmanager that allows executing code while printing while making sure that no commands from the file + being printed are continued to be sent to the printer. Note that this will only work for local files, + NOT SD files. + + Example: + + .. code-block:: python + + with printer.job_on_hold(): + park_printhead() + take_snapshot() + send_printhead_back() + + It should be used sparingly and only for very specific situations (such as parking the print head somewhere, + taking a snapshot from the webcam, then continuing). If you abuse this, you WILL cause print quality issues! + + A lock is in place that ensures that the context can only actually be held by one thread at a time. If you + don't want to block on acquire, be sure to set ``blocking`` to ``False`` and catch the ``RuntimeException`` thrown + if the lock can't be acquired. + + Args: + blocking (bool): Whether to block while attempting to acquire the lock (default) or not + """ + raise NotImplementedError() + def fake_ack(self, *args, **kwargs): """ Fakes an acknowledgment for the communication layer. If the communication between OctoPrint and the printer diff --git a/src/octoprint/printer/standard.py b/src/octoprint/printer/standard.py index 620fde17e4..1284ac2be4 100644 --- a/src/octoprint/printer/standard.py +++ b/src/octoprint/printer/standard.py @@ -233,6 +233,11 @@ def get_transport(self, *args, **kwargs): return self._comm.getTransport() getTransport = util.deprecated("getTransport has been renamed to get_transport", since="1.2.0-dev-590", includedoc="Replaced by :func:`get_transport`") + def job_on_hold(self, blocking=None, *args, **kwargs): + if self._comm is None: + raise RuntimeError("No connection to the printer") + return self._comm.job_on_hold(blocking=blocking) + def fake_ack(self, *args, **kwargs): if self._comm is None: return diff --git a/src/octoprint/util/comm.py b/src/octoprint/util/comm.py index 234efd2286..372ed7fac9 100644 --- a/src/octoprint/util/comm.py +++ b/src/octoprint/util/comm.py @@ -10,6 +10,7 @@ import time import re import threading +import contextlib try: import queue @@ -502,6 +503,8 @@ def __init__(self, port = None, baudrate=None, callbackObject=None, printerProfi # print job self._currentFile = None + self._job_on_hold = False + self._job_on_hold_mutex = threading.RLock() # multithreading locks self._jobLock = threading.RLock() @@ -696,6 +699,19 @@ def getTransport(self): ##~~ external interface + @contextlib.contextmanager + def job_on_hold(self, blocking=True): + if not self._job_on_hold_mutex.acquire(blocking=blocking): + raise RuntimeError("Could not acquire job_on_hold lock") + + self._job_on_hold = True + try: + yield + finally: + self._job_on_hold = False + self._job_on_hold_mutex.release() + self._continue_sending() + def close(self, is_error=False, wait=True, timeout=10.0, *args, **kwargs): """ Closes the connection to the printer. @@ -795,7 +811,7 @@ def sendCommand(self, cmd, cmd_type=None, processed=False, force=False, on_sent= if not cmd: return False - if self.isPrinting() and not self.isSdFileSelected(): + if self.isPrinting() and not self.isSdFileSelected() and not self._job_on_hold: try: self._command_queue.put((cmd, cmd_type, on_sent, tags), item_type=cmd_type) return True @@ -1421,7 +1437,7 @@ def convert_line(line): handled = True # process timeouts - elif ((line == "" and now > self._timeout) or (self.isPrinting() and now > self._ok_timeout)) \ + elif ((line == "" and now > self._timeout) or (self.isPrinting() and not self._job_on_hold and now > self._ok_timeout)) \ and (not self._blockWhileDwelling or not self._dwelling_until or now > self._dwelling_until): # We have two timeout variants: # @@ -1869,6 +1885,9 @@ def _continue_sending(self): # we found something in the queue to send return True + elif self._job_on_hold: + return False + elif self._sendNext(): # we sent the next line from the file return True From 55f5c472a013f5b7fab81b37a814a5dcc80c6938 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Fri, 16 Feb 2018 22:03:54 +0100 Subject: [PATCH 218/333] [Docs] Some fixes --- docs/bundledplugins/logging.rst | 4 ++-- docs/plugins/hooks.rst | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/bundledplugins/logging.rst b/docs/bundledplugins/logging.rst index 0fccc9e1a0..67e0a923ed 100644 --- a/docs/bundledplugins/logging.rst +++ b/docs/bundledplugins/logging.rst @@ -8,7 +8,7 @@ The OctoPrint Logging plugin comes bundled with OctoPrint starting with version It implements the log management functionality that was formerly part of the core application and adds features to configure logging levels for sub modules through the included settings dialog. -.. _fig-bundledplugins-pluginmanager-mainscreen: +.. _fig-bundledplugins-logging-settings: .. figure:: ../images/bundledplugins-logging-settings.png :align: center :alt: Logging plugin @@ -34,7 +34,7 @@ Retrieve a list of available log files Retrieve information regarding all log files currently available and regarding the disk space still available in the system on the location the log files are being stored. - Returns a :ref:`Logfile Retrieve response `. + Returns a :ref:`Logfile Retrieve response `. **Example** diff --git a/docs/plugins/hooks.rst b/docs/plugins/hooks.rst index f9c8d76176..6a397fe31a 100644 --- a/docs/plugins/hooks.rst +++ b/docs/plugins/hooks.rst @@ -454,7 +454,7 @@ This describes actually four hooks: the logger ``octoprint.util.comm.command_phases`` to ``DEBUG``, connect to a printer (real or virtual) and take a look at your ``octoprint.log`` during serial traffic: - .. code-block:: plain + .. code-block:: none 2018-02-16 18:20:31,213 - octoprint.util.comm.command_phases - DEBUG - phase: queuing | command: T0 | gcode: T | tags: [ api:printer.command, source:api, trigger:printer.commands ] 2018-02-16 18:20:31,216 - octoprint.util.comm.command_phases - DEBUG - phase: queued | command: M117 Before T! | gcode: M117 | tags: [ api:printer.command, phase:queuing, plugin:multi_gcode_test, source:api, source:rewrite, trigger:printer.commands ] @@ -515,7 +515,7 @@ This describes actually four hooks: * A 2-tuple consisting of a rewritten version of the ``cmd`` and the ``cmd_type``, e.g. ``return "M105", "temperature_poll"``. Handlers which wish to rewrite both the command and the command type should use this option. * A 3-tuple consisting of a rewritten version of the ``cmd``, the ``cmd_type`` and any additional ``tags`` you might - want to attach to the lifecycle of the command, e.g. ``return "M105", "temperature_poll", "my_custom_tag" + want to attach to the lifecycle of the command in a set, e.g. ``return "M105", "temperature_poll", {"my_custom_tag"}`` * **"queuing" phase only**: A list of any of the above to allow for expanding one command into many. The following example shows how any queued command could be turned into a sequence of a temperature query, line number reset, display of the ``gcode`` on the printer's display and finally the actual command (this example @@ -558,7 +558,7 @@ This describes actually four hooks: :param str gcode: Parsed GCODE command, e.g. ``G0`` or ``M110``, may also be None if no known command could be parsed :param str subcode: Parsed subcode of the GCODE command, e.g. ``1`` for ``M80.1``. Will be None if no subcode was provided or no command could be parsed. - :param set tags: Tags attached to the + :param set tags: Tags attached to the command :return: None, 1-tuple, 2-tuple or string, see the description above for details. .. _sec-plugins-hook-comm-protocol-gcode-received: From 33147b03059cc06f426423b75b3a4fb141232a3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Mon, 19 Feb 2018 11:27:19 +0100 Subject: [PATCH 219/333] Make terminal logging to log unicode safe Closes #2426 --- src/octoprint/util/comm.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/octoprint/util/comm.py b/src/octoprint/util/comm.py index 372ed7fac9..52c3b5773c 100644 --- a/src/octoprint/util/comm.py +++ b/src/octoprint/util/comm.py @@ -561,9 +561,9 @@ def _log(self, message): self._serialLogger.debug(message) def _to_logfile_with_terminal(self, message=None, level=logging.INFO): - log = "Last lines in terminal:\n" + "\n".join(map(lambda x: "| " + x, list(self._terminal_log))) + log = u"Last lines in terminal:\n" + u"\n".join(map(lambda x: u"| {}".format(x), list(self._terminal_log))) if message is not None: - log = message + "\n| " + log + log = message + u"\n| " + log self._logger.log(level, log) def _addToLastLines(self, cmd): @@ -2188,7 +2188,7 @@ def _handle_errors(self, line): elif not self.isError(): # handle everything else error_text = line[6:] if lower_line.startswith("error:") else line[2:] - self._to_logfile_with_terminal("Received an error from the printer's firmware: {}".format(error_text), + self._to_logfile_with_terminal(u"Received an error from the printer's firmware: {}".format(error_text), level=logging.WARN) if not self._ignore_errors: @@ -2348,7 +2348,8 @@ def _handleResendRequest(self, line): self._log_resends_rate_start = now self._log_resends_rate_count = 0 - self._to_logfile_with_terminal("Got a resend request from the printer: requested line = {}, current line = {}".format(lineToResend, self._currentLine)) + self._to_logfile_with_terminal(u"Got a resend request from the printer: requested line = {}, " + u"current line = {}".format(lineToResend, self._currentLine)) self._log_resends_rate_count += 1 self._send_queue.resend_active = True From 541844ce3c69164a74671a1970f77c89fcbaad8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Mon, 19 Feb 2018 11:37:57 +0100 Subject: [PATCH 220/333] Add action:cancel Closes #2367 --- docs/features/action_commands.rst | 10 +++++++--- src/octoprint/util/comm.py | 5 ++++- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/docs/features/action_commands.rst b/docs/features/action_commands.rst index 82591cf9eb..502f068b83 100644 --- a/docs/features/action_commands.rst +++ b/docs/features/action_commands.rst @@ -21,16 +21,20 @@ Action commands are a feature defined for the GCODE based RepRap communication p OctoPrint out of the box supports handling of the above mentioned commands: +cancel + When this command is received from the printer, OctoPrint will cancel a current print job like if the + "Cancel" button had been clicked. + pause - When this command is received from the printer, OctoPrint will pause streaming of a current print job just like if the + When this command is received from the printer, OctoPrint will pause a current print job just like if the "Pause" button had been clicked. resume - When this command is received from the printer, OctoPrint will resume streaming of a current print job just like if + When this command is received from the printer, OctoPrint will resume a current print job just like if the "Resume" button had been clicked. disconnect - When this command is Received from the printer, OctoPrint will immediately disconnect from it. + When this command is received from the printer, OctoPrint will immediately disconnect from it. Support for additional commands may be added by plugins by implementing a handler for the :ref:`octoprint.comm.protocol.action ` hook. diff --git a/src/octoprint/util/comm.py b/src/octoprint/util/comm.py index 52c3b5773c..05babc2956 100644 --- a/src/octoprint/util/comm.py +++ b/src/octoprint/util/comm.py @@ -1361,7 +1361,10 @@ def _monitor(self): if debugging_output.startswith("action:"): action_command = debugging_output[len("action:"):].strip() - if action_command == "pause": + if action_command == "cancel": + self._log("Cancelling on request of the printer...") + self.cancelPrint() + elif action_command == "pause": self._log("Pausing on request of the printer...") self.setPause(True) elif action_command == "resume": From 40082456c1a9bad8a52b393313af3d1469cf45b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Fri, 23 Feb 2018 16:57:46 +0100 Subject: [PATCH 221/333] Don't even import disabled plugins We want to avoid disabled plugins interfering with our application in any way at all. Hence disabled plugins are now treated the same way as blacklisted ones. The system will only create a dummy entry for them and not import them at all. As a consequence, a restart of the platform will be necessary for disabling plugins that were disabled. The Plugin Manager has been adjusted accordingly. Note that by no longer even importing the base file, plugin name, version, description and other such metadata defined through control properties won't be accessible. Closes #2442 --- src/octoprint/__init__.py | 7 +- src/octoprint/plugin/core.py | 69 +++++++++++-------- .../plugins/pluginmanager/__init__.py | 21 +++--- .../pluginmanager/static/js/pluginmanager.js | 9 +-- 4 files changed, 60 insertions(+), 46 deletions(-) diff --git a/src/octoprint/__init__.py b/src/octoprint/__init__.py index f04a963f16..c67c05280b 100644 --- a/src/octoprint/__init__.py +++ b/src/octoprint/__init__.py @@ -322,12 +322,9 @@ def init_pluginsystem(settings, safe_mode=False, ignore_blacklist=True, connecti plugin_validators = [] if safe_mode: def validator(phase, plugin_info): - if phase == "after_load": + if phase in ("before_import", "before_load", "before_enable"): setattr(plugin_info, "safe_mode_victim", not plugin_info.bundled) - setattr(plugin_info, "safe_mode_enabled", False) - elif phase == "before_enable": if not plugin_info.bundled: - setattr(plugin_info, "safe_mode_enabled", True) return False return True plugin_validators.append(validator) @@ -440,7 +437,7 @@ def process_blacklist(entries): result.append((entry["plugin"], version)) else: logger.debug("Blacklisted plugin: {}".format(entry["plugin"])) - result.append(entry["key"]) + result.append(entry["plugin"]) return result diff --git a/src/octoprint/plugin/core.py b/src/octoprint/plugin/core.py index 2cce7fe1ab..1ceffcc367 100644 --- a/src/octoprint/plugin/core.py +++ b/src/octoprint/plugin/core.py @@ -144,6 +144,7 @@ def __init__(self, key, location, instance, name=None, version=None, description self.origin = None self.enabled = True self.blacklisted = False + self.forced_disabled = False self.bundled = False self.loaded = False self.managable = True @@ -159,7 +160,10 @@ def __init__(self, key, location, instance, name=None, version=None, description def validate(self, phase, additional_validators=None): result = True - if phase == "before_load": + if phase == "before_import": + result = not self.forced_disabled and not self.blacklisted and result + + elif phase == "before_load": # if the plugin still uses __plugin_init__, log a deprecation warning and move it to __plugin_load__ if hasattr(self.instance, self.__class__.attr_init): if not hasattr(self.instance, self.__class__.attr_load): @@ -193,7 +197,7 @@ def validate(self, phase, additional_validators=None): if additional_validators is not None: for validator in additional_validators: - result = result and validator(phase, self) + result = validator(phase, self) and result return result @@ -602,14 +606,13 @@ def _find_plugins_from_folders(self, folders, existing, ignored_uninstalled=True # plugin is already defined, ignore it continue - plugin = self._import_plugin_from_module(key, folder=folder) + bundled = flagged_readonly + + plugin = self._import_plugin_from_module(key, folder=folder, bundled=bundled) if plugin: plugin.origin = FolderOrigin("folder", folder) plugin.managable = not flagged_readonly and not actual_readonly - plugin.bundled = flagged_readonly - plugin.enabled = False - result[key] = plugin except: self.logger.exception("Error processing folder entry {!r} from folder {}".format(entry, folder)) @@ -676,6 +679,7 @@ def wrapped(gen): plugin = self._import_plugin_from_module(key, **kwargs) if plugin: plugin.origin = EntryPointOrigin("entry_point", group, module_name, package_name, version) + plugin.enabled = False # plugin is manageable if its location is writable and OctoPrint # is either not running from a virtual env or the plugin is @@ -685,19 +689,19 @@ def wrapped(gen): plugin.managable = os.access(plugin.location, os.W_OK) \ and (not self._python_virtual_env or is_sub_path_of(plugin.location, self._python_prefix) - or is_editable_install(self._python_install_dir, - package_name, - module_name, - plugin.location)) + or is_editable_install(self._python_install_dir, + package_name, + module_name, + plugin.location)) - plugin.enabled = False result[key] = plugin except: self.logger.exception("Error processing entry point {!r} for group {}".format(entry_point, group)) return result - def _import_plugin_from_module(self, key, folder=None, module_name=None, name=None, version=None, summary=None, author=None, url=None, license=None): + def _import_plugin_from_module(self, key, folder=None, module_name=None, name=None, version=None, summary=None, + author=None, url=None, license=None, bundled=False): # TODO error handling try: if folder: @@ -710,31 +714,42 @@ def _import_plugin_from_module(self, key, folder=None, module_name=None, name=No self.logger.warn("Could not locate plugin {key}".format(key=key)) return None + # Create a simple dummy entry first ... + plugin = PluginInfo(key, module[1], None, name=name, version=version, description=summary, author=author, + url=url, license=license) + plugin.bundled = bundled + + if self._is_plugin_disabled(key): + self.logger.info("Plugin {} is disabled.".format(plugin)) + plugin.forced_disabled = True + if self._is_plugin_blacklisted(key) or (version is not None and self._is_plugin_version_blacklisted(key, version)): - plugin = PluginInfo(key, module[1], None, name=name, version=version, description=summary, author=author, url=url, license=license) + self.logger.warn("Plugin {} is blacklisted.".format(plugin)) plugin.blacklisted = True - self.logger.warn("Plugin {} is blacklisted. Not importing it, only registering a dummy entry.".format(plugin)) - return plugin - - plugin = self._import_plugin(key, *module, name=name, version=version, summary=summary, author=author, url=url, license=license) - if plugin is None: - return None - if plugin.check(): + if not plugin.validate("before_import", additional_validators=self.plugin_validators): return plugin - else: - self.logger.warn("Plugin \"{plugin}\" did not pass check".format(plugin=str(plugin))) - return None + # ... then create and return the real one + return self._import_plugin(key, *module, + name=name, version=version, summary=summary, author=author, url=url, + license=license, bundled=bundled) - def _import_plugin(self, key, f, filename, description, name=None, version=None, summary=None, author=None, url=None, license=None): + def _import_plugin(self, key, f, filename, description, name=None, version=None, summary=None, author=None, url=None, license=None, bundled=False): try: instance = imp.load_module(key, f, filename, description) - return PluginInfo(key, filename, instance, name=name, version=version, description=summary, author=author, url=url, license=license) + plugin = PluginInfo(key, filename, instance, name=name, version=version, description=summary, author=author, url=url, license=license) + plugin.bundled = bundled except: self.logger.exception("Error loading plugin {key}".format(key=key)) return None + if plugin.check(): + return plugin + else: + self.logger.warn("Plugin \"{plugin}\" did not pass check".format(plugin=str(plugin))) + return None + def _is_plugin_disabled(self, key): return key in self.plugin_disabled_list or key.endswith('disabled') @@ -765,7 +780,7 @@ def reload_plugins(self, startup=False, initialize_implementations=True, force_r # 1st pass: loading the plugins for name, plugin in plugins.items(): try: - if not plugin.blacklisted: + if not plugin.blacklisted and not plugin.forced_disabled: self.load_plugin(name, plugin, startup=startup, initialize_implementation=initialize_implementations) except PluginNeedsRestart: pass @@ -779,7 +794,7 @@ def reload_plugins(self, startup=False, initialize_implementations=True, force_r # 2nd pass: enabling those plugins that need enabling for name, plugin in plugins.items(): try: - if plugin.loaded and not self._is_plugin_disabled(name): + if plugin.loaded and not plugin.forced_disabled: if plugin.blacklisted: self.logger.warn("Plugin {} is blacklisted. Not enabling it.".format(plugin)) continue diff --git a/src/octoprint/plugins/pluginmanager/__init__.py b/src/octoprint/plugins/pluginmanager/__init__.py index 2ee271bb44..e15c987f92 100644 --- a/src/octoprint/plugins/pluginmanager/__init__.py +++ b/src/octoprint/plugins/pluginmanager/__init__.py @@ -561,13 +561,14 @@ def command_toggle(self, plugin, command): if plugin.key == "pluginmanager": return make_response("Can't enable/disable Plugin Manager", 400) + pending = ((command == "disable" and plugin.key in self._pending_enable) or (command == "enable" and plugin.key in self._pending_disable)) + safe_mode_victim = getattr(plugin, "safe_mode_victim", False) + needs_restart = self._plugin_manager.is_restart_needing_plugin(plugin) needs_refresh = plugin.implementation and isinstance(plugin.implementation, octoprint.plugin.ReloadNeedingPlugin) needs_reconnect = self._plugin_manager.has_any_of_hooks(plugin, self._reconnect_hooks) and self._printer.is_operational() - pending = ((command == "disable" and plugin.key in self._pending_enable) or (command == "enable" and plugin.key in self._pending_disable)) - safe_mode_victim = getattr(plugin, "safe_mode_victim", False) - needs_restart_api = (needs_restart or safe_mode_victim) and not pending + needs_restart_api = (needs_restart or safe_mode_victim or plugin.forced_disabled) and not pending needs_refresh_api = needs_refresh and not pending needs_reconnect_api = needs_reconnect and not pending @@ -677,12 +678,12 @@ def _mark_plugin_enabled(self, plugin, needs_restart=False): self._settings.global_set(["plugins", "_disabled"], disabled_list) self._settings.save(force=True) - if not needs_restart and not getattr(plugin, "safe_mode_victim", False): + if not needs_restart and not plugin.forced_disabled and not getattr(plugin, "safe_mode_victim", False): self._plugin_manager.enable_plugin(plugin.key) else: if plugin.key in self._pending_disable: self._pending_disable.remove(plugin.key) - elif (not plugin.enabled and not getattr(plugin, "safe_mode_enabled", False)) and plugin.key not in self._pending_enable: + elif not plugin.enabled and plugin.key not in self._pending_enable: self._pending_enable.add(plugin.key) def _mark_plugin_disabled(self, plugin, needs_restart=False): @@ -692,12 +693,12 @@ def _mark_plugin_disabled(self, plugin, needs_restart=False): self._settings.global_set(["plugins", "_disabled"], disabled_list) self._settings.save(force=True) - if not needs_restart and not getattr(plugin, "safe_mode_victim", False): + if not needs_restart and not plugin.forced_disabled and not getattr(plugin, "safe_mode_victim", False): self._plugin_manager.disable_plugin(plugin.key) else: if plugin.key in self._pending_enable: self._pending_enable.remove(plugin.key) - elif (plugin.enabled or getattr(plugin, "safe_mode_enabled", False)) and plugin.key not in self._pending_disable: + elif (plugin.enabled or plugin.forced_disabled or getattr(plugin, "safe_mode_victim", False)) and plugin.key not in self._pending_disable: self._pending_disable.add(plugin.key) def _fetch_all_data(self, async=False): @@ -920,10 +921,10 @@ def _to_external_plugin(self, plugin): managable=plugin.managable, enabled=plugin.enabled, blacklisted=plugin.blacklisted, + forced_disabled=plugin.forced_disabled, safe_mode_victim=getattr(plugin, "safe_mode_victim", False), - safe_mode_enabled=getattr(plugin, "safe_mode_enabled", False), - pending_enable=(not plugin.enabled and not getattr(plugin, "safe_mode_enabled", False) and plugin.key in self._pending_enable), - pending_disable=((plugin.enabled or getattr(plugin, "safe_mode_enabled", False)) and plugin.key in self._pending_disable), + pending_enable=(not plugin.enabled and not getattr(plugin, "safe_mode_victim", False) and plugin.key in self._pending_enable), + pending_disable=((plugin.enabled or getattr(plugin, "safe_mode_victim", False)) and plugin.key in self._pending_disable), pending_install=(self._plugin_manager.is_plugin_marked(plugin.key, "installed")), pending_uninstall=(self._plugin_manager.is_plugin_marked(plugin.key, "uninstalled")), origin=plugin.origin.type, diff --git a/src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js b/src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js index d658aa1d0f..19842dcb6d 100644 --- a/src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js +++ b/src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js @@ -222,7 +222,7 @@ $(function() { self.enableToggle = function(data) { var command = self._getToggleCommand(data); - var not_safemode_victim = !data.safe_mode_victim || data.safe_mode_enabled; + var not_safemode_victim = !data.safe_mode_victim; var not_blacklisted = !data.blacklisted; return self.enableManagement() && (command == "disable" || (not_safemode_victim && not_blacklisted)) && data.key != 'pluginmanager'; }; @@ -462,7 +462,7 @@ $(function() { }; if (self._getToggleCommand(data) == "enable") { - if (data.safe_mode_victim && !data.safe_mode_enabled) return; + if (data.safe_mode_victim) return; OctoPrint.plugins.pluginmanager.enable(data.key) .done(onSuccess) .fail(onError); @@ -857,7 +857,8 @@ $(function() { }; self._getToggleCommand = function(data) { - var disable = (data.enabled || data.pending_enable || (data.safe_mode_victim && data.safe_mode_enabled)) && !data.pending_disable; + var disable = (data.enabled || (data.safe_mode_victim && !data.forced_disabled) || data.pending_enable) + && !data.pending_disable; return disable ? "disable" : "enable"; }; @@ -873,7 +874,7 @@ $(function() { if (command == "enable") { if (data.blacklisted) { return gettext("Blacklisted"); - } else if (data.safe_mode_victim && !data.safe_mode_enabled) { + } else if (data.safe_mode_victim) { return gettext("Disabled due to active safe mode"); } else { return gettext("Enable Plugin"); From b108fc939cf56cead2d4e358e66fe6738d2dca27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Fri, 23 Feb 2018 17:02:52 +0100 Subject: [PATCH 222/333] Extract control property values from disabled plugins By parsing the main plugin file into a Python AST we can still extract the control property values (as long as they are simple string assignments or calls to gettext with a string argument) without having to execute them in any way. --- src/octoprint/plugin/core.py | 84 ++++++++++++++++++++++++++++++++---- 1 file changed, 76 insertions(+), 8 deletions(-) diff --git a/src/octoprint/plugin/core.py b/src/octoprint/plugin/core.py index 1ceffcc367..a6f26661ce 100644 --- a/src/octoprint/plugin/core.py +++ b/src/octoprint/plugin/core.py @@ -157,6 +157,8 @@ def __init__(self, key, location, instance, name=None, version=None, description self._url = url self._license = license + self._cached_parsed_metadata = None + def validate(self, phase, additional_validators=None): result = True @@ -292,7 +294,9 @@ def name(self): Returns: str: Name of the plugin, fallback is the plugin's identifier. """ - return self._get_instance_attribute(self.__class__.attr_name, defaults=(self._name, self.key)) + return self._get_instance_attribute(self.__class__.attr_name, + defaults=(self._name, self.key), + incl_metadata=True) @property def description(self): @@ -304,7 +308,9 @@ def description(self): Returns: str or None: Description of the plugin. """ - return self._get_instance_attribute(self.__class__.attr_description, default=self._description) + return self._get_instance_attribute(self.__class__.attr_description, + default=self._description, + incl_metadata=True) @property def disabling_discouraged(self): @@ -328,7 +334,9 @@ def version(self): Returns: str or None: Version of the plugin. """ - return self._version if self._version is not None else self._get_instance_attribute(self.__class__.attr_version, default=self._version) + return self._version if self._version is not None else self._get_instance_attribute(self.__class__.attr_version, + default=self._version, + incl_metadata=True) @property def author(self): @@ -339,7 +347,9 @@ def author(self): Returns: str or None: Author of the plugin. """ - return self._get_instance_attribute(self.__class__.attr_author, default=self._author) + return self._get_instance_attribute(self.__class__.attr_author, + default=self._author, + incl_metadata=True) @property def url(self): @@ -350,7 +360,9 @@ def url(self): Returns: str or None: Website URL for the plugin. """ - return self._get_instance_attribute(self.__class__.attr_url, default=self._url) + return self._get_instance_attribute(self.__class__.attr_url, + default=self._url, + incl_metadata=True) @property def license(self): @@ -361,7 +373,9 @@ def license(self): Returns: str or None: License of the plugin. """ - return self._get_instance_attribute(self.__class__.attr_license, default=self._license) + return self._get_instance_attribute(self.__class__.attr_license, + default=self._license, + incl_metadata=True) @property def hooks(self): @@ -452,15 +466,69 @@ def disable(self): """ return self._get_instance_attribute(self.__class__.attr_disable, default=lambda: True) - def _get_instance_attribute(self, attr, default=None, defaults=None): + def _get_instance_attribute(self, attr, default=None, defaults=None, incl_metadata=False): if self.instance is None or not hasattr(self.instance, attr): - if defaults is not None: + if incl_metadata and attr in self._parsed_metadata: + return self._parsed_metadata[attr] + + elif defaults is not None: for value in defaults: + if callable(value): + value = value() if value is not None: return value + return default + return getattr(self.instance, attr) + @property + def _parsed_metadata(self): + if self._cached_parsed_metadata is None: + self._cached_parsed_metadata = self._parse_metadata() + return self._cached_parsed_metadata + + def _parse_metadata(self): + result = dict() + try: + import ast + + path = self.location + if os.path.isdir(path): + path = os.path.join(self.location, "__init__.py") + + if not os.path.isfile(path): + return result + + with open(path, "rb") as f: + root = ast.parse(f.read()) + + assignments = filter(lambda x: isinstance(x, ast.Assign) and x.targets, + root.body) + + def extract_target_ids(node): + return map(lambda x: x.id, + filter(lambda x: isinstance(x, ast.Name), node.targets)) + + for key in (self.__class__.attr_name, self.__class__.attr_version, self.__class__.attr_author, + self.__class__.attr_description, self.__class__.attr_url, self.__class__.attr_license): + for a in reversed(assignments): + targets = extract_target_ids(a) + if key in targets: + if isinstance(a.value, ast.Str): + result[key] = a.value.s + + elif isinstance(a.value, ast.Call) and hasattr(a.value, "func") \ + and a.value.func.id == "gettext" and a.value.args \ + and isinstance(a.value.args[0], ast.Str): + result[key] = a.value.args[0].s + + break + except: + pass + + return result + class PluginManager(object): """ From 9a8b8d86d5ed6dd2d59a5794947af1887f0fb730 Mon Sep 17 00:00:00 2001 From: electr0sheep Date: Fri, 23 Feb 2018 18:00:54 -0700 Subject: [PATCH 223/333] Extended "customControls_controlTemplate_command" --- src/octoprint/templates/tabs/control.jinja2 | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/octoprint/templates/tabs/control.jinja2 b/src/octoprint/templates/tabs/control.jinja2 index a3c8cc6331..0cf7e741f2 100644 --- a/src/octoprint/templates/tabs/control.jinja2 +++ b/src/octoprint/templates/tabs/control.jinja2 @@ -173,6 +173,11 @@ From a3ba266a6b37b9632d430ce1552ee1a4b68ba496 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Sat, 24 Feb 2018 14:54:58 +0100 Subject: [PATCH 224/333] Some more work on the AST parsing Also use version parsed from AST for blacklist check too. --- src/octoprint/plugin/core.py | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/src/octoprint/plugin/core.py b/src/octoprint/plugin/core.py index a6f26661ce..5bf24a060f 100644 --- a/src/octoprint/plugin/core.py +++ b/src/octoprint/plugin/core.py @@ -137,7 +137,7 @@ class PluginInfo(object): attr_disable = '__plugin_disable__' """ Module attribute which to call when disabling the plugin. """ - def __init__(self, key, location, instance, name=None, version=None, description=None, author=None, url=None, license=None): + def __init__(self, key, location, instance, name=None, version=None, description=None, author=None, url=None, license=None, parsed_metadata=None): self.key = key self.location = location self.instance = instance @@ -157,7 +157,9 @@ def __init__(self, key, location, instance, name=None, version=None, description self._url = url self._license = license - self._cached_parsed_metadata = None + self._logger = logging.getLogger(__name__) + + self._cached_parsed_metadata = parsed_metadata def validate(self, phase, additional_validators=None): result = True @@ -468,8 +470,8 @@ def disable(self): def _get_instance_attribute(self, attr, default=None, defaults=None, incl_metadata=False): if self.instance is None or not hasattr(self.instance, attr): - if incl_metadata and attr in self._parsed_metadata: - return self._parsed_metadata[attr] + if incl_metadata and attr in self.parsed_metadata: + return self.parsed_metadata[attr] elif defaults is not None: for value in defaults: @@ -483,12 +485,14 @@ def _get_instance_attribute(self, attr, default=None, defaults=None, incl_metada return getattr(self.instance, attr) @property - def _parsed_metadata(self): + def parsed_metadata(self): if self._cached_parsed_metadata is None: self._cached_parsed_metadata = self._parse_metadata() return self._cached_parsed_metadata def _parse_metadata(self): + self._logger.debug("Parsing plugin metadata for {} from AST".format(self.key)) + result = dict() try: import ast @@ -791,7 +795,7 @@ def _import_plugin_from_module(self, key, folder=None, module_name=None, name=No self.logger.info("Plugin {} is disabled.".format(plugin)) plugin.forced_disabled = True - if self._is_plugin_blacklisted(key) or (version is not None and self._is_plugin_version_blacklisted(key, version)): + if self._is_plugin_blacklisted(key) or (plugin.version is not None and self._is_plugin_version_blacklisted(key, plugin.version)): self.logger.warn("Plugin {} is blacklisted.".format(plugin)) plugin.blacklisted = True @@ -801,12 +805,19 @@ def _import_plugin_from_module(self, key, folder=None, module_name=None, name=No # ... then create and return the real one return self._import_plugin(key, *module, name=name, version=version, summary=summary, author=author, url=url, - license=license, bundled=bundled) + license=license, bundled=bundled, parsed_metadata=plugin.parsed_metadata) - def _import_plugin(self, key, f, filename, description, name=None, version=None, summary=None, author=None, url=None, license=None, bundled=False): + def _import_plugin(self, key, f, filename, description, name=None, version=None, summary=None, author=None, url=None, license=None, bundled=False, parsed_metadata=None): try: instance = imp.load_module(key, f, filename, description) - plugin = PluginInfo(key, filename, instance, name=name, version=version, description=summary, author=author, url=url, license=license) + plugin = PluginInfo(key, filename, instance, + name=name, + version=version, + description=summary, + author=author, + url=url, + license=license, + parsed_metadata=parsed_metadata) plugin.bundled = bundled except: self.logger.exception("Error loading plugin {key}".format(key=key)) @@ -919,14 +930,8 @@ def load_plugin(self, name, plugin=None, startup=False, initialize_implementatio plugin.load() plugin.validate("after_load", additional_validators=self.plugin_validators) self.on_plugin_loaded(name, plugin) - plugin.loaded = True - # we might only now have a version, so check again if we are blacklisted - if not plugin.blacklisted and plugin.version and self._is_plugin_version_blacklisted(plugin.key, - plugin.version): - plugin.blacklisted = True - self.logger.debug("Loaded plugin {name}: {plugin}".format(**locals())) except PluginLifecycleException as e: raise e From c3ba9c84532279289f8e5a6673f422b696398dfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Sat, 24 Feb 2018 14:59:30 +0100 Subject: [PATCH 225/333] Fix upload_cli --- src/octoprint/util/comm.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/octoprint/util/comm.py b/src/octoprint/util/comm.py index 05babc2956..d81800d1ab 100644 --- a/src/octoprint/util/comm.py +++ b/src/octoprint/util/comm.py @@ -3978,6 +3978,7 @@ def __init__(self, path, target): self._path = path self._target = target + self._state = None def on_comm_file_transfer_started(self, filename, filesize): # transfer started, report @@ -3990,6 +3991,8 @@ def on_comm_file_transfer_done(self, filename): self.finished.set() def on_comm_state_change(self, state): + self._state = state + if state in (MachineCom.STATE_ERROR, MachineCom.STATE_CLOSED_WITH_ERROR): # report and exit on errors logger.error("Error/closed with error, exiting.") @@ -3997,14 +4000,22 @@ def on_comm_state_change(self, state): self.finished.set() elif state in (MachineCom.STATE_OPERATIONAL,) and not self.started: - # start transfer once we are operational - self.comm.startFileTransfer(self._path, os.path.basename(self._path), self._target) + def run(): + logger.info("Looks like we are operational, waiting a bit for everything to settle") + time.sleep(15) + if self._state in (MachineCom.STATE_OPERATIONAL,) and not self.started: + # start transfer once we are operational + self.comm.startFileTransfer(self._path, os.path.basename(self._path), self._target) + + thread = threading.Thread(target=run) + thread.daemon = True + thread.start() callback = MyMachineComCallback(path, target) # mock printer profile manager profile = dict(heatedBed=False, - extruder=dict(count=1)) + extruder=dict(count=1, sharedNozzle=False)) printer_profile_manager = Object() printer_profile_manager.get_current_or_default = lambda: profile From 763888e83aeeae9ad79ed5dfc9d8a8b00fd04df7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Sat, 24 Feb 2018 22:30:12 +0100 Subject: [PATCH 226/333] Allow slicers to manage their profiles themselves --- src/octoprint/plugin/types.py | 41 +++++++++++++++++++++++++++++++ src/octoprint/slicing/__init__.py | 20 ++++----------- 2 files changed, 46 insertions(+), 15 deletions(-) diff --git a/src/octoprint/plugin/types.py b/src/octoprint/plugin/types.py index b39dcac91a..9067ee596b 100644 --- a/src/octoprint/plugin/types.py +++ b/src/octoprint/plugin/types.py @@ -1861,6 +1861,47 @@ def get_slicer_properties(self): destination_extensions=["gco", "gcode", "g"] ) + def get_slicer_profiles(self, profile_path): + """ + Fetch all :class:`~octoprint.slicing.SlicingProfile`s stored for this slicer. + + For compatibility reasons with existing slicing plugins this method defaults to returning profiles parsed from + .profile files in the plugin's ``profile_path``, utilizing the :func:`SlicingPlugin.get_slicer_profile` method + of the plugin implementation. + + Arguments: + profile_path (str): The base folder where OctoPrint stores this slicer plugin's profiles + """ + + try: + from os import scandir + except ImportError: + from scandir import scandir + + import octoprint.util + + profiles = dict() + for entry in scandir(profile_path): + if not entry.name.endswith(".profile") or octoprint.util.is_hidden_path(entry.name): + # we are only interested in profiles and no hidden files + continue + + profile_name = entry.name[:-len(".profile")] + profiles[profile_name] = self.get_slicer_profile(entry.path) + return profiles + + # noinspection PyMethodMayBeStatic + def get_slicer_profiles_lastmodified(self, profile_path): + import os + try: + from os import scandir + except ImportError: + from scandir import scandir + + lms = [os.stat(profile_path).st_mtime] + lms += [os.stat(entry.path).st_mtime for entry in scandir(profile_path) if entry.name.endswith(".profile")] + return max(lms) + # noinspection PyMethodMayBeStatic def get_slicer_default_profile(self): """ diff --git a/src/octoprint/slicing/__init__.py b/src/octoprint/slicing/__init__.py index 9174cc52fd..f14ecc1998 100644 --- a/src/octoprint/slicing/__init__.py +++ b/src/octoprint/slicing/__init__.py @@ -377,9 +377,9 @@ def save_profile(self, slicer, name, profile, overrides=None, allow_overwrite=Tr If it's a :class:`dict`, a new :class:`SlicingProfile` instance will be created with the supplied meta data and the profile data as the :attr:`~SlicingProfile.data` attribute. - + .. note:: - + If the profile is the first profile to be saved for the slicer, it will automatically be marked as default. Arguments: @@ -432,7 +432,7 @@ def save_profile(self, slicer, name, profile, overrides=None, allow_overwrite=Tr profile=name) event = octoprint.events.Events.SLICING_PROFILE_MODIFIED if is_overwrite else octoprint.events.Events.SLICING_PROFILE_ADDED octoprint.events.eventManager().fire(event, payload) - + if first_profile: # enforce the first profile we add for this slicer is set as default self.set_default_profile(slicer, name) @@ -557,16 +557,8 @@ def all_profiles(self, slicer, require_configured=False): if require_configured and not slicer in self.configured_slicers: raise SlicerNotConfigured(slicer) - profiles = dict() slicer_profile_path = self.get_slicer_profile_path(slicer) - for entry in scandir(slicer_profile_path): - if not entry.name.endswith(".profile") or octoprint.util.is_hidden_path(entry.name): - # we are only interested in profiles and no hidden files - continue - - profile_name = entry.name[:-len(".profile")] - profiles[profile_name] = self._load_profile_from_path(slicer, entry.path, require_configured=require_configured) - return profiles + return self.get_slicer(slicer).get_slicer_profiles(slicer_profile_path) def profiles_last_modified(self, slicer): """ @@ -583,9 +575,7 @@ def profiles_last_modified(self, slicer): raise UnknownSlicer(slicer) slicer_profile_path = self.get_slicer_profile_path(slicer) - lms = [os.stat(slicer_profile_path).st_mtime] - lms += [os.stat(entry.path).st_mtime for entry in scandir(slicer_profile_path) if entry.name.endswith(".profile")] - return max(lms) + return self.get_slicer(slicer).get_slicer_profiles_lastmodified(slicer_profile_path) def get_slicer_profile_path(self, slicer): """ From b3fff6e95f53f93811360844cdcb53690b02fcda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Mon, 26 Feb 2018 12:47:46 +0100 Subject: [PATCH 227/333] Fix firmware error reporting on job cancel Closes #2364 --- docs/events/index.rst | 12 ++++- src/octoprint/events.py | 1 + src/octoprint/printer/standard.py | 7 +++ src/octoprint/static/js/app/dataupdater.js | 63 +++++++++++++++++----- src/octoprint/util/comm.py | 23 ++++---- 5 files changed, 82 insertions(+), 24 deletions(-) diff --git a/docs/events/index.rst b/docs/events/index.rst index 4e675c8f8f..7086ccecdc 100644 --- a/docs/events/index.rst +++ b/docs/events/index.rst @@ -363,8 +363,18 @@ PrintDone Still available for reasons of backwards compatibility. Will be removed with 1.4.0. +PrintCancelling + The print is about to be cancelled. + + Payload: + + * ``name``: the file's name + * ``path``: the file's path within its storage location + * ``origin``: the origin storage location of the file, either ``local`` or ``sdcard`` + * ``firmwareError``: the firmware error that caused cancelling the print job, if any + PrintCancelled - The print has been cancelled via the cancel button. + The print has been cancelled. Payload: diff --git a/src/octoprint/events.py b/src/octoprint/events.py index b789f05272..476e6d1825 100644 --- a/src/octoprint/events.py +++ b/src/octoprint/events.py @@ -69,6 +69,7 @@ class Events(object): PRINT_STARTED = "PrintStarted" PRINT_DONE = "PrintDone" PRINT_FAILED = "PrintFailed" + PRINT_CANCELLING = "PrintCancelling" PRINT_CANCELLED = "PrintCancelled" PRINT_PAUSED = "PrintPaused" PRINT_RESUMED = "PrintResumed" diff --git a/src/octoprint/printer/standard.py b/src/octoprint/printer/standard.py index 1284ac2be4..4b6707fe60 100644 --- a/src/octoprint/printer/standard.py +++ b/src/octoprint/printer/standard.py @@ -1163,6 +1163,13 @@ def on_comm_print_job_failed(self): payload = self._payload_for_print_job_event() eventManager().fire(Events.PRINT_FAILED, payload) + def on_comm_print_job_cancelling(self, firmware_error=None): + payload = self._payload_for_print_job_event() + if payload: + if firmware_error: + payload["firmwareError"] = firmware_error + eventManager().fire(Events.PRINT_CANCELLING, payload) + def on_comm_print_job_cancelled(self): self._setCurrentZ(None) self._updateProgressData() diff --git a/src/octoprint/static/js/app/dataupdater.js b/src/octoprint/static/js/app/dataupdater.js index 06a15c1227..406df46900 100644 --- a/src/octoprint/static/js/app/dataupdater.js +++ b/src/octoprint/static/js/app/dataupdater.js @@ -225,28 +225,63 @@ function DataUpdater(allViewModels, connectCallback, disconnectCallback) { log.debug("Got event " + type + " with payload: " + JSON.stringify(payload)); - if (type == "PrintCancelled") { - if (payload.firmwareError) { - new PNotify({ - title: gettext("Unhandled communication error"), - text: _.sprintf(gettext("There was an unhandled error while talking to the printer. Due to that the ongoing print job was cancelled. Error: %(firmwareError)s"), payload), - type: "error", - hide: false - }); + if (type === "PrintCancelling" && payload.firmwareError) { + new PNotify({ + title: gettext("Error reported by printer"), + text: _.sprintf(gettext("Your printer's firmware reported an error. Due to that the ongoing print job will be cancelled. Reported error: %(firmwareError)s"), payload), + type: "error", + hide: false + }); + } else if (type === "Error" && payload.error) { + var title = undefined, + text = undefined; + + switch (payload.reason) { + case "firmware": { + title = gettext("Error reported by printer"); + text = _.sprintf(gettext("Your printer's firmware reported an error. Due to that OctoPrint will disconnect. Reported error: %(error)s"), payload); + break; + } + case "resend": + case "timeout": { + title = gettext("Communication error"); + text = gettext("There was a communication error while talking to your printer. Please consult the terminal output and octoprint.log for details. Error: %(error)s", payload); + break; + } + case "connection": { + title = gettext("Error connecting to printer"); + text = gettext("There was an error while trying to connect to your printer. Error: %(error)s", payload); + break; + } + case "start_print": { + title = gettext("Error starting a print"); + text = gettext("There was an error while trying to start a print job. Error: %(error)s", payload); + break; + } + case "autodetect_port": + case "autodetect_baudrate": { + // ignore + break; + } + default: { + title = gettext("Unknown error"); + text = gettext("There was an unknown error while talking to your printer. Please consult the terminal output and octoprint.log for details. Error: %(error)s", payload); + break; + } } - } else if (type == "Error") { - if (payload.error && payload.error.indexOf("autodetect") == -1) { // ignore "failed to autodetect" + + if (title && text) { new PNotify({ - title: gettext("Unhandled communication error"), - text: _.sprintf(gettext("There was an unhandled error while talking to the printer. Due to that OctoPrint disconnected. Error: %(error)s"), payload), + title: title, + text: text, type: "error", hide: false }); } - } else if (type == "PrinterReset") { + } else if (type === "PrinterReset") { new PNotify({ title: gettext("Printer reset detected"), - text: gettext("It looks like the printer was reset while a connection was active. If this was intentional you may safely ignore this message. Otherwise you should investigate why your printer reset itself, since this will interrupt prints and also file transfers to your printer's SD."), + text: gettext("It looks like your printer reset while a connection was active. If this was intentional you may safely ignore this message. Otherwise you should investigate why your printer reset itself, since this will interrupt prints and also file transfers to your printer's SD."), hide: false }); } diff --git a/src/octoprint/util/comm.py b/src/octoprint/util/comm.py index d81800d1ab..a9070dd9a4 100644 --- a/src/octoprint/util/comm.py +++ b/src/octoprint/util/comm.py @@ -953,7 +953,7 @@ def startPrint(self, pos=None, tags=None): self._logger.exception("Error while trying to start printing") self._errorValue = get_exception_string() self._changeState(self.STATE_ERROR) - eventManager().fire(Events.ERROR, {"error": self.getErrorString()}) + eventManager().fire(Events.ERROR, {"error": self.getErrorString(), "reason": "start_print"}) def startFileTransfer(self, filename, localFilename, remoteFilename, special=False, tags=None): if not self.isOperational() or self.isBusy(): @@ -1040,7 +1040,7 @@ def unselectFile(self): self._currentFile = None self._callback.on_comm_file_selected(None, None, False) - def _cancel_preparation_done(self): + def _cancel_preparation_done(self, firmware_error=None): self._recordFilePosition() self._callback.on_comm_print_job_cancelled() @@ -1067,6 +1067,8 @@ def _on_M400_sent(): self._record_cancel_data = True self.sendCommand("M114", tags=tags | {"trigger:comm.cancel", "trigger:record_position"}) + self._callback.on_comm_print_job_cancelling(firmware_error=firmware_error) + with self._jobLock: self._changeState(self.STATE_OPERATIONAL) @@ -1724,7 +1726,7 @@ def convert_line(line): self.close(wait=False) self._errorValue = "No more baudrates to test, and no suitable baudrate found." self._changeState(self.STATE_ERROR) - eventManager().fire(Events.ERROR, {"error": self.getErrorString()}) + eventManager().fire(Events.ERROR, {"error": self.getErrorString(), "reason": "autodetect_baudrate"}) elif 'start' in line or 'ok' in line: self._onConnected() if 'start' in line: @@ -1774,7 +1776,7 @@ def convert_line(line): errorMsg = "See octoprint.log for details" self._log(errorMsg) self._errorValue = errorMsg - eventManager().fire(Events.ERROR, {"error": self.getErrorString()}) + eventManager().fire(Events.ERROR, {"error": self.getErrorString(), "reason": "crash"}) self.close(is_error=True) self._log("Connection closed, closing down monitor") @@ -1831,7 +1833,7 @@ def _handle_timeout(self): self._logger.info(message) self._log(message + " " + general_message) self._errorValue = "Too many consecutive timeouts, printer still connected and alive?" - eventManager().fire(Events.ERROR, {"error": self._errorValue}) + eventManager().fire(Events.ERROR, {"error": self._errorValue, "reason": "timeout"}) self.close(is_error=True) elif self._resendActive: @@ -2092,7 +2094,7 @@ def default(_, port, baudrate, read_timeout): if port is None: self._errorValue = 'Failed to autodetect serial port, please set it manually.' self._changeState(self.STATE_ERROR) - eventManager().fire(Events.ERROR, {"error": self.getErrorString()}) + eventManager().fire(Events.ERROR, {"error": self.getErrorString(), "reason": "autodetect_port"}) self._log("Failed to autodetect serial port, please set it manually.") return None @@ -2125,7 +2127,7 @@ def default(_, port, baudrate, read_timeout): exception_string = get_exception_string() self._errorValue = "Connection error, see Terminal tab" self._changeState(self.STATE_ERROR) - eventManager().fire(Events.ERROR, {"error": self.getErrorString()}) + eventManager().fire(Events.ERROR, {"error": self.getErrorString(), "reason": "connection"}) error_message = "Unexpected error while connecting to serial port: %s %s (hook %s)" % (self._port, exception_string, name) self._log(error_message) @@ -2198,7 +2200,7 @@ def _handle_errors(self, line): if self._disconnect_on_errors: self._errorValue = error_text self._changeState(self.STATE_ERROR) - eventManager().fire(Events.ERROR, {"error": self.getErrorString()}) + eventManager().fire(Events.ERROR, {"error": self.getErrorString(), "reason": "firmware"}) elif self.isPrinting(): self.cancelPrint(firmware_error=error_text) self._clear_to_send.set() @@ -2333,7 +2335,7 @@ def _handleResendRequest(self, line): if self.isPrinting(): # abort the print, there's nothing we can do to rescue it now self._changeState(self.STATE_ERROR) - eventManager().fire(Events.ERROR, {"error": self.getErrorString()}) + eventManager().fire(Events.ERROR, {"error": self.getErrorString(), "reason": "resend"}) else: # reset resend delta, we can't do anything about it self._resendDelta = None @@ -3013,6 +3015,9 @@ def on_comm_print_job_failed(self): def on_comm_print_job_done(self): pass + def on_comm_print_job_cancelling(self, firmware_error=None): + pass + def on_comm_print_job_cancelled(self): pass From ec86b3328eaeb5f3dd58f270156eea32b8a25592 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Mon, 26 Feb 2018 12:51:38 +0100 Subject: [PATCH 228/333] Remove left-over parameter --- src/octoprint/util/comm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/octoprint/util/comm.py b/src/octoprint/util/comm.py index a9070dd9a4..390faa176f 100644 --- a/src/octoprint/util/comm.py +++ b/src/octoprint/util/comm.py @@ -1040,7 +1040,7 @@ def unselectFile(self): self._currentFile = None self._callback.on_comm_file_selected(None, None, False) - def _cancel_preparation_done(self, firmware_error=None): + def _cancel_preparation_done(self): self._recordFilePosition() self._callback.on_comm_print_job_cancelled() From 18ad764ef59a276c5ce9f5454c2b7cd73ffad8a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Mon, 26 Feb 2018 16:04:27 +0100 Subject: [PATCH 229/333] Also detect plugins only provided as pyc Implements #2448 --- src/octoprint/plugin/core.py | 47 ++++++++++++++++++++++++------------ 1 file changed, 32 insertions(+), 15 deletions(-) diff --git a/src/octoprint/plugin/core.py b/src/octoprint/plugin/core.py index 5bf24a060f..c86b117cdb 100644 --- a/src/octoprint/plugin/core.py +++ b/src/octoprint/plugin/core.py @@ -491,18 +491,26 @@ def parsed_metadata(self): return self._cached_parsed_metadata def _parse_metadata(self): - self._logger.debug("Parsing plugin metadata for {} from AST".format(self.key)) - result = dict() - try: - import ast - path = self.location - if os.path.isdir(path): - path = os.path.join(self.location, "__init__.py") + path = self.location + if not path: + return result + + if os.path.isdir(path): + path = os.path.join(self.location, "__init__.py") + + if not os.path.isfile(path): + return result + + if not path.endswith(".py"): + # we only support parsing plain text source files + return result + + self._logger.debug("Parsing plugin metadata for {} from AST of {}".format(self.key, path)) - if not os.path.isfile(path): - return result + try: + import ast with open(path, "rb") as f: root = ast.parse(f.read()) @@ -663,15 +671,24 @@ def _find_plugins_from_folders(self, folders, existing, ignored_uninstalled=True for entry in scandir(folder): try: - if entry.is_dir() and os.path.isfile(os.path.join(entry.path, "__init__.py")): + if entry.is_dir(): + init_py = os.path.join(entry.path, "__init__.py") + init_pyc = os.path.join(entry.path, "__init__.pyc") + + if not os.path.isfile(init_py) and not os.path.isfile(init_pyc): + # neither does exist, we ignore this + continue + key = entry.name - elif entry.is_file() and entry.name.endswith(".py"): - key = entry.name[:-3] # strip off the .py extension - if key.startswith("__"): - # might be an __init__.py in our plugins folder, or something else we don't want - # to handle + + elif entry.is_file(): + key, ext = os.path.splitext(entry.name) + if ext not in (".py", ".pyc") or key.startswith("__"): + # neither py nor pyc, or starts with __ (like __init__), we ignore this continue + else: + # whatever this is, we ignore it continue if key in existing or key in result or (ignored_uninstalled and key in self.marked_plugins["uninstalled"]): From 296af89b024679a667542c14d05706a35a49ea85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Mon, 26 Feb 2018 16:52:00 +0100 Subject: [PATCH 230/333] Fix two buggy sets of tags --- src/octoprint/util/comm.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/octoprint/util/comm.py b/src/octoprint/util/comm.py index 390faa176f..f65480dcae 100644 --- a/src/octoprint/util/comm.py +++ b/src/octoprint/util/comm.py @@ -1707,7 +1707,7 @@ def convert_line(line): self._baudrateDetectRetry -= 1 self._serial.write('\n') self._log("Baudrate test retry: %d" % (self._baudrateDetectRetry)) - self.sayHello(tags="trigger:baudrate_detection") + self.sayHello(tags={"trigger:baudrate_detection",}) elif len(self._baudrateDetectList) > 0: baudrate = self._baudrateDetectList.pop(0) try: @@ -1718,7 +1718,7 @@ def convert_line(line): self._baudrateDetectRetry = 5 self._timeout = get_new_timeout("communicationBusy" if self._busy_protocol_detected else "communication", self._timeout_intervals) self._serial.write('\n') - self.sayHello(tags="trigger:baudrate_detection") + self.sayHello(tags={"trigger:baudrate_detection",}) except: self._log("Unexpected error while setting baudrate {}: {}".format(baudrate, get_exception_string())) self._logger.exception("Unexpceted error while setting baudrate {}".format(baudrate)) From 2c87133f3de3c22c8f45f7c188461533befb8e7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Mon, 26 Feb 2018 17:05:18 +0100 Subject: [PATCH 231/333] Virtual: Report invalid extruders on serial For testing #2302 --- src/octoprint/plugins/virtual_printer/virtual.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/octoprint/plugins/virtual_printer/virtual.py b/src/octoprint/plugins/virtual_printer/virtual.py index 5866f1b150..63767ea46d 100644 --- a/src/octoprint/plugins/virtual_printer/virtual.py +++ b/src/octoprint/plugins/virtual_printer/virtual.py @@ -353,9 +353,11 @@ def _processIncoming(self): def _gcode_T(self, code, data): t = int(code) - if 0 <= t <= self.extruderCount: + if 0 <= t < self.extruderCount: self.currentExtruder = t self._send("Active Extruder: %d" % self.currentExtruder) + else: + self._send("echo:T{} Invalid extruder ".format(t)) def _gcode_F(self, code, data): if self._supportF: From 551d6aab68abfddcd1a212ab215b38051294e74d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Mon, 26 Feb 2018 17:25:42 +0100 Subject: [PATCH 232/333] Detect invalid tools according to firmware Also don't send invalid tool commands (based on printer profile and former responses from the firmware on the ongoing connection). Closes #2302 --- src/octoprint/util/comm.py | 63 ++++++++++++++++++++++++++++++++------ 1 file changed, 53 insertions(+), 10 deletions(-) diff --git a/src/octoprint/util/comm.py b/src/octoprint/util/comm.py index f65480dcae..3320dbb305 100644 --- a/src/octoprint/util/comm.py +++ b/src/octoprint/util/comm.py @@ -374,7 +374,9 @@ def __init__(self, port = None, baudrate=None, callbackObject=None, printerProfi self._pauseWaitStartTime = None self._pauseWaitTimeLost = 0.0 self._currentTool = 0 - self._formerTool = None + self._toolBeforeChange = None + self._toolBeforeHeatup = None + self._knownInvalidTools = set() self._long_running_command = False self._heating = False @@ -1466,7 +1468,7 @@ def convert_line(line): if handled and self._state not in (self.STATE_CONNECTING, self.STATE_DETECT_BAUDRATE): continue - # position report processing + ##~~ position report processing if 'X:' in line and 'Y:' in line and 'Z:' in line: parsed = parse_position_line(line) if parsed: @@ -1509,7 +1511,7 @@ def convert_line(line): self._callback.on_comm_position_update(self.last_position.as_dict(), reason=reason) - # temperature processing + ##~~ temperature processing elif ' T:' in line or line.startswith('T:') or ' T0:' in line or line.startswith('T0:') \ or ((' B:' in line or line.startswith('B:')) and not 'A:' in line): @@ -1612,6 +1614,33 @@ def convert_line(line): self._logger.info("Firmware states that it supports temperature autoreporting") self._set_autoreport_temperature_interval() + ##~~ invalid extruder + elif 'invalid extruder' in lower_line: + tool = None + + match = regexes_parameters["intT"].search(line) + if match: + try: + tool = int(match.group("value")) + except ValueError: + pass # should never happen + + if tool is None or tool == self._currentTool: + if self._toolBeforeChange is not None: + fallback_tool = self._toolBeforeChange + else: + fallback_tool = 0 + + invalid_tool = self._currentTool + + # log to terminal and remember as invalid + self._log("T{} reported as invalid, reverting to T{}".format(invalid_tool, fallback_tool)) + self._knownInvalidTools.add(invalid_tool) + + # we actually do send a T command here instead of just settings self._currentTool just in case + # we had any scripts or plugins modify stuff due to the prior tool change + self.sendCommand("T{}".format(fallback_tool), tags={"trigger:revert_invalid_tool",}) + ##~~ SD Card handling elif 'SD init fail' in line or 'volume.init failed' in line or 'openRoot failed' in line: self._sdAvailable = False @@ -1788,9 +1817,9 @@ def _handle_ok(self): self._long_running_command = False - if self._formerTool is not None: - self._currentTool = self._formerTool - self._formerTool = None + if self._toolBeforeHeatup is not None: + self._currentTool = self._toolBeforeHeatup + self._toolBeforeHeatup = None self._finish_heatup() @@ -2782,6 +2811,11 @@ def _gcode_T_queuing(self, cmd, cmd_type=None, gcode=None, subcode=None, *args, current_tool = self._currentTool new_tool = int(toolMatch.group("value")) + if not self._validate_tool(new_tool): + self._log("Not queuing T{}, that tool doesn't exist according to the printer profile or " + "was reported as invalid by the firmware".format(new_tool)) + return None + before = self._getGcodeScript("beforeToolChange", replacements=dict(tool=dict(old=current_tool, new=new_tool))) after = self._getGcodeScript("afterToolChange", replacements=dict(tool=dict(old=current_tool, new=new_tool))) @@ -2790,9 +2824,15 @@ def _gcode_T_queuing(self, cmd, cmd_type=None, gcode=None, subcode=None, *args, def _gcode_T_sent(self, cmd, cmd_type=None, gcode=None, subcode=None, *args, **kwargs): toolMatch = regexes_parameters["intT"].search(cmd) if toolMatch: - old = self._currentTool - self._currentTool = int(toolMatch.group("value")) - eventManager().fire(Events.TOOL_CHANGE, dict(old=old, new=self._currentTool)) + new_tool = int(toolMatch.group("value")) + if not self._validate_tool(new_tool): + self._log("Not sending T{}, that tool doesn't exist according to the printer profile or " + "was reported as invalid by the firmware".format(new_tool)) + return None + + self._toolBeforeChange = self._currentTool + self._currentTool = new_tool + eventManager().fire(Events.TOOL_CHANGE, dict(old=self._toolBeforeChange, new=self._currentTool)) def _gcode_G0_sent(self, cmd, cmd_type=None, gcode=None, subcode=None, *args, **kwargs): if "Z" in cmd or "F" in cmd: @@ -2862,7 +2902,7 @@ def _gcode_M104_sent(self, cmd, cmd_type=None, gcode=None, subcode=None, wait=Fa toolNum = int(toolMatch.group("value")) if wait: - self._formerTool = self._currentTool + self._toolBeforeHeatup = self._currentTool self._currentTool = toolNum match = regexes_parameters["floatS"].search(cmd) @@ -2979,6 +3019,9 @@ def _gcode_G4_sent(self, cmd, cmd_type=None, gcode=None, subcode=None, *args, ** self._timeout = get_new_timeout("communicationBusy" if self._busy_protocol_detected else "communication", self._timeout_intervals) + _timeout self._dwelling_until = time.time() + _timeout + def _validate_tool(self, tool): + return tool < self._printerProfileManager.get_current_or_default()["extruder"]["count"] and not tool in self._knownInvalidTools + ##~~ command phase handlers def _command_phase_sending(self, cmd, cmd_type=None, gcode=None, subcode=None, *args, **kwargs): From d42c74d34adc22c13fa671c2c933b8d02d22474d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Wed, 28 Feb 2018 16:50:08 +0100 Subject: [PATCH 233/333] Hook for custom error handling by plugins Implements #2208 --- docs/plugins/hooks.rst | 20 ++++++++++++++++++++ src/octoprint/util/comm.py | 17 +++++++++++++++-- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/docs/plugins/hooks.rst b/docs/plugins/hooks.rst index 6a397fe31a..d6d79fb52c 100644 --- a/docs/plugins/hooks.rst +++ b/docs/plugins/hooks.rst @@ -587,6 +587,26 @@ octoprint.comm.protocol.gcode.received :return: The received line or in any case, a modified version of it. :rtype: str +.. _sec-plugins-hook-comm-protocol-gcode-error: + +octoprint.comm.protocol.gcode.error +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. py:function:: gcode_received_hook(comm_instance, error_message, *args, **kwargs) + + Get the messages of any errors messages sent by the printer, with the leading ``Error:`` or ``!!`` already + stripped. Handlers should return True if they handled that error internally and it should not be processed by + the system further. Normal processing of these kinds of errors - depending on the configuration of error + handling - involves canceling the ongoing print and possibly also disconnecting. + + Plugins might utilize this hook to handle errors generated by the printer that are recoverable in one way or + the other and should not trigger the normal handling that assumes the worst. + + :param MachineCom comm_instance: The :class:`~octoprint.util.comm.MachineCom` instance which triggered the hook. + :param str error_message: The error message received from the printer. + :return: True if the error was handled in the plugin and should not be processed further, False (or None) otherwise. + :rtype: bool + .. _sec-plugins-hook-comm-protocol-scripts: octoprint.comm.protocol.scripts diff --git a/src/octoprint/util/comm.py b/src/octoprint/util/comm.py index 3320dbb305..746f05b984 100644 --- a/src/octoprint/util/comm.py +++ b/src/octoprint/util/comm.py @@ -137,7 +137,7 @@ Groups will be as follows: * ``id``: id of the extruder or which the position is reported - * ``value``: reported position value + * ``value``: reported position value """ regex_firmware_splitter = re.compile("\s*([A-Z0-9_]+):") @@ -473,6 +473,7 @@ def __init__(self, port = None, baudrate=None, callbackObject=None, printerProfi sent=self._pluginManager.get_hooks("octoprint.comm.protocol.gcode.sent") ) self._received_message_hooks = self._pluginManager.get_hooks("octoprint.comm.protocol.gcode.received") + self._error_message_hooks = self._pluginManager.get_hooks("octoprint.comm.protocol.gcode.error") self._printer_action_hooks = self._pluginManager.get_hooks("octoprint.comm.protocol.action") self._gcodescript_hooks = self._pluginManager.get_hooks("octoprint.comm.protocol.scripts") @@ -2200,6 +2201,9 @@ def _handle_errors(self, line): if regex_minMaxError.match(line): # special delivery for firmware that goes "Error:x\n: Extruder switched off. MAXTEMP triggered !\n" line = line.rstrip() + self._readline() + lower_line = line.lower() + + stripped_error = (line[6:] if lower_line.startswith("error:") else line[2:]).strip() if any(map(lambda x: x in lower_line, self._recoverable_communication_errors)): # manually trigger an ack for comm errors the printer doesn't send a resend request for but @@ -2209,7 +2213,7 @@ def _handle_errors(self, line): elif any(map(lambda x: x in lower_line, self._resend_request_communication_errors)): # skip comm errors that the printer sends a resend request for anyhow - self._lastCommError = line[6:] if lower_line.startswith("error:") else line[2:] + self._lastCommError = stripped_error elif any(map(lambda x: x in lower_line, self._sd_card_errors)): # skip errors with the SD card @@ -2221,6 +2225,15 @@ def _handle_errors(self, line): elif not self.isError(): # handle everything else + for name, hook in self._error_message_hooks.items(): + try: + ret = hook(self, stripped_error) + except: + self._logger.exception("Error while processing hook {name}:".format(**locals())) + else: + if ret: + return line + error_text = line[6:] if lower_line.startswith("error:") else line[2:] self._to_logfile_with_terminal(u"Received an error from the printer's firmware: {}".format(error_text), level=logging.WARN) From 2db2532c2fa35c5316b200abd67116b5b8cabf9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Wed, 28 Feb 2018 17:07:53 +0100 Subject: [PATCH 234/333] Fix unit tests broken by implementation of #2208 --- tests/util/test_comm.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/util/test_comm.py b/tests/util/test_comm.py index 0c3618da19..3ff2d99696 100644 --- a/tests/util/test_comm.py +++ b/tests/util/test_comm.py @@ -19,6 +19,7 @@ def setUp(self): self._comm._lastCommError = None self._comm._errorValue = None self._comm._clear_to_send = mock.Mock() + self._comm._error_message_hooks = dict() # settings self._comm._ignore_errors = False @@ -113,6 +114,16 @@ def test_unknown_command(self, line): self.assertEqual(line, result) self.assert_nop() + @ddt.data("Error: This should get handled", "!! This should also get handled") + def test_unknown_handled(self, line): + """Should pass""" + def handler(comm, message, *args, **kwargs): + return "handled" in message + self._comm._error_message_hooks["test"] = handler + result = self._comm._handle_errors(line) + self.assertEqual(line, result) + self.assert_nop() + @ddt.data("Error: Printer on fire") def test_other_error_disconnect(self, line): """Should trigger escalation""" From b8e4ac21d0d338066d956ca50001d8c27f1ff39e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Wed, 28 Feb 2018 17:14:06 +0100 Subject: [PATCH 235/333] Example and copy-paste-fix for new comm error handler hook --- docs/plugins/hooks.rst | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/docs/plugins/hooks.rst b/docs/plugins/hooks.rst index d6d79fb52c..3e1801ae6b 100644 --- a/docs/plugins/hooks.rst +++ b/docs/plugins/hooks.rst @@ -592,7 +592,7 @@ octoprint.comm.protocol.gcode.received octoprint.comm.protocol.gcode.error ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.. py:function:: gcode_received_hook(comm_instance, error_message, *args, **kwargs) +.. py:function:: gcode_error_hook(comm_instance, error_message, *args, **kwargs) Get the messages of any errors messages sent by the printer, with the leading ``Error:`` or ``!!`` already stripped. Handlers should return True if they handled that error internally and it should not be processed by @@ -602,6 +602,16 @@ octoprint.comm.protocol.gcode.error Plugins might utilize this hook to handle errors generated by the printer that are recoverable in one way or the other and should not trigger the normal handling that assumes the worst. + **Example:** + + Looks for error messages containing "fan error" or "bed missing" (ignoring case) and marks them as handled by the + plugin. + + .. onlineinclude:: https://raw.githubusercontent.com/OctoPrint/Plugin-Examples/master/comm_error_handler_test.py + :linenos: + :tab-width: 4 + :caption: `comm_error_handler_test.py `_ + :param MachineCom comm_instance: The :class:`~octoprint.util.comm.MachineCom` instance which triggered the hook. :param str error_message: The error message received from the printer. :return: True if the error was handled in the plugin and should not be processed further, False (or None) otherwise. From 59cbfaf8ff193c4ecf5af5f12793234f27374939 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Mon, 5 Mar 2018 12:33:23 +0100 Subject: [PATCH 236/333] Introduce Cancelling/Pausing states before full cancel/pause We so far switched immediately to "Operational" state, even while the M400/M114 combo and/or any post switch scripts were still being executed. That was confusing and as witnessed in #2321 and also #2449 also could cause issues. --- docs/api/datamodel.rst | 8 + docs/api/printer.rst | 4 + src/octoprint/printer/__init__.py | 14 ++ src/octoprint/printer/standard.py | 8 + .../static/js/app/viewmodels/printerstate.js | 10 +- src/octoprint/util/comm.py | 167 +++++++++++++----- 6 files changed, 164 insertions(+), 47 deletions(-) diff --git a/docs/api/datamodel.rst b/docs/api/datamodel.rst index 92e32050f5..74d56dc573 100644 --- a/docs/api/datamodel.rst +++ b/docs/api/datamodel.rst @@ -44,6 +44,14 @@ Printer State - 1 - Boolean - ``true`` if the printer is currently printing, ``false`` otherwise + * - ``flags.pausing`` + - 1 + - Boolean + - ``true`` if the printer is currently printing and in the process of pausing, ``false`` otherwise + * - ``flags.cancelling`` + - 1 + - Boolean + - ``true`` if the printer is currently printing and in the process of pausing, ``false`` otherwise * - ``flags.sdReady`` - 1 - Boolean diff --git a/docs/api/printer.rst b/docs/api/printer.rst index 200f421082..23d002afe5 100644 --- a/docs/api/printer.rst +++ b/docs/api/printer.rst @@ -149,6 +149,8 @@ Retrieve the current printer state "operational": true, "paused": false, "printing": false, + "cancelling": false, + "pausing": false, "sdReady": true, "error": false, "ready": true, @@ -179,6 +181,8 @@ Retrieve the current printer state "operational": true, "paused": false, "printing": false, + "cancelling": false, + "pausing": false, "sdReady": true, "error": false, "ready": true, diff --git a/src/octoprint/printer/__init__.py b/src/octoprint/printer/__init__.py index 038aa1547b..6a090bcfdd 100644 --- a/src/octoprint/printer/__init__.py +++ b/src/octoprint/printer/__init__.py @@ -486,6 +486,20 @@ def is_printing(self, *args, **kwargs): """ raise NotImplementedError() + def is_cancelling(self, *args, **kwargs): + """ + Returns: + (boolean) Whether the printer is currently cancelling a print. + """ + raise NotImplementedError() + + def is_pausing(self, *args, **kwargs): + """ + Returns: + (boolean) Whether the printer is currently pausing a print. + """ + raise NotImplementedError() + def is_paused(self, *args, **kwargs): """ Returns: diff --git a/src/octoprint/printer/standard.py b/src/octoprint/printer/standard.py index 4b6707fe60..0d06497834 100644 --- a/src/octoprint/printer/standard.py +++ b/src/octoprint/printer/standard.py @@ -578,6 +578,12 @@ def is_operational(self, *args, **kwargs): def is_printing(self, *args, **kwargs): return self._comm is not None and self._comm.isPrinting() + def is_cancelling(self, *args, **kwargs): + return self._comm is not None and self._comm.isCancelling() + + def is_pausing(self, *args, **kwargs): + return self._comm is not None and self._comm.isPausing() + def is_paused(self, *args, **kwargs): return self._comm is not None and self._comm.isPaused() @@ -1001,6 +1007,8 @@ def _getStateFlags(self): return { "operational": self.is_operational(), "printing": self.is_printing(), + "cancelling": self.is_cancelling(), + "pausing": self.is_pausing(), "closedOrError": self.is_closed_or_error(), "error": self.is_error(), "paused": self.is_paused(), diff --git a/src/octoprint/static/js/app/viewmodels/printerstate.js b/src/octoprint/static/js/app/viewmodels/printerstate.js index 5b4b905cd1..9028291c9b 100644 --- a/src/octoprint/static/js/app/viewmodels/printerstate.js +++ b/src/octoprint/static/js/app/viewmodels/printerstate.js @@ -9,6 +9,8 @@ $(function() { self.isErrorOrClosed = ko.observable(undefined); self.isOperational = ko.observable(undefined); self.isPrinting = ko.observable(undefined); + self.isCancelling = ko.observable(undefined); + self.isPausing = ko.observable(undefined); self.isPaused = ko.observable(undefined); self.isError = ko.observable(undefined); self.isReady = ko.observable(undefined); @@ -16,13 +18,13 @@ $(function() { self.isSdReady = ko.observable(undefined); self.enablePrint = ko.pureComputed(function() { - return self.isOperational() && self.isReady() && !self.isPrinting() && self.loginState.isUser() && self.filename() != undefined; + return self.isOperational() && self.isReady() && !self.isPrinting() && !self.isCancelling() && !self.isPausing() && self.loginState.isUser() && self.filename() != undefined; }); self.enablePause = ko.pureComputed(function() { - return self.isOperational() && (self.isPrinting() || self.isPaused()) && self.loginState.isUser(); + return self.isOperational() && (self.isPrinting() || self.isPaused()) && !self.isCancelling() && !self.isPausing() && self.loginState.isUser(); }); self.enableCancel = ko.pureComputed(function() { - return self.isOperational() && (self.isPrinting() || self.isPaused()) && self.loginState.isUser(); + return self.isOperational() && (self.isPrinting() || self.isPaused()) && !self.isCancelling() && !self.isPausing() && self.loginState.isUser(); }); self.filename = ko.observable(undefined); @@ -193,6 +195,8 @@ $(function() { self.isOperational(data.flags.operational); self.isPaused(data.flags.paused); self.isPrinting(data.flags.printing); + self.isCancelling(data.flags.cancelling); + self.isPausing(data.flags.pausing); self.isError(data.flags.error); self.isReady(data.flags.ready); self.isSdReady(data.flags.sdReady); diff --git a/src/octoprint/util/comm.py b/src/octoprint/util/comm.py index 746f05b984..6cde33f9dd 100644 --- a/src/octoprint/util/comm.py +++ b/src/octoprint/util/comm.py @@ -332,6 +332,8 @@ class MachineCom(object): STATE_ERROR = 9 STATE_CLOSED_WITH_ERROR = 10 STATE_TRANSFERING_FILE = 11 + STATE_CANCELLING = 12 + STATE_PAUSING = 13 CAPABILITY_AUTOREPORT_TEMP = "AUTOREPORT_TEMP" CAPABILITY_BUSY_PROTOCOL = "BUSY_PROTOCOL" @@ -594,32 +596,36 @@ def getStateString(self, state=None): if state == self.STATE_NONE: return "Offline" - if state == self.STATE_OPEN_SERIAL: + elif state == self.STATE_OPEN_SERIAL: return "Opening serial port" - if state == self.STATE_DETECT_SERIAL: + elif state == self.STATE_DETECT_SERIAL: return "Detecting serial port" - if state == self.STATE_DETECT_BAUDRATE: + elif state == self.STATE_DETECT_BAUDRATE: return "Detecting baudrate" - if state == self.STATE_CONNECTING: + elif state == self.STATE_CONNECTING: return "Connecting" - if state == self.STATE_OPERATIONAL: + elif state == self.STATE_OPERATIONAL: return "Operational" - if state == self.STATE_PRINTING: + elif state == self.STATE_PRINTING: if self.isSdFileSelected(): return "Printing from SD" elif self.isStreaming(): return "Sending file to SD" else: return "Printing" - if state == self.STATE_PAUSED: + elif state == self.STATE_CANCELLING: + return "Cancelling" + elif state == self.STATE_PAUSING: + return "Pausing" + elif state == self.STATE_PAUSED: return "Paused" - if state == self.STATE_CLOSED: + elif state == self.STATE_CLOSED: return "Offline" - if state == self.STATE_ERROR: + elif state == self.STATE_ERROR: return "Error: %s" % (self.getErrorString()) - if state == self.STATE_CLOSED_WITH_ERROR: + elif state == self.STATE_CLOSED_WITH_ERROR: return "Offline: %s" % (self.getErrorString()) - if state == self.STATE_TRANSFERING_FILE: + elif state == self.STATE_TRANSFERING_FILE: return "Transferring file to SD" return "Unknown State (%d)" % (self._state) @@ -627,16 +633,23 @@ def getErrorString(self): return self._errorValue def isClosedOrError(self): - return self._state == self.STATE_ERROR or self._state == self.STATE_CLOSED_WITH_ERROR or self._state == self.STATE_CLOSED + return self._state in (self.STATE_ERROR, self.STATE_CLOSED, self.STATE_CLOSED_WITH_ERROR) def isError(self): - return self._state == self.STATE_ERROR or self._state == self.STATE_CLOSED_WITH_ERROR + return self._state in (self.STATE_ERROR, self.STATE_CLOSED_WITH_ERROR) def isOperational(self): - return self._state == self.STATE_OPERATIONAL or self._state == self.STATE_PRINTING or self._state == self.STATE_PAUSED or self._state == self.STATE_TRANSFERING_FILE + return self._state in (self.STATE_OPERATIONAL, self.STATE_PRINTING, self.STATE_CANCELLING, self.STATE_PAUSING, + self.STATE_PAUSED, self.STATE_TRANSFERING_FILE) def isPrinting(self): - return self._state == self.STATE_PRINTING + return self._state in (self.STATE_PRINTING, self.STATE_CANCELLING, self.STATE_PAUSING) + + def isCancelling(self): + return self._state == self.STATE_CANCELLING + + def isPausing(self): + return self._state == self.STATE_PAUSING def isSdPrinting(self): return self.isSdFileSelected() and self.isPrinting() @@ -645,13 +658,13 @@ def isSdFileSelected(self): return self._currentFile is not None and isinstance(self._currentFile, PrintingSdFileInformation) def isStreaming(self): - return self._currentFile is not None and isinstance(self._currentFile, StreamingGcodeFileInformation) + return self._currentFile is not None and isinstance(self._currentFile, StreamingGcodeFileInformation) and not self._currentFile.done def isPaused(self): return self._state == self.STATE_PAUSED def isBusy(self): - return self.isPrinting() or self.isPaused() + return self.isPrinting() or self.isPaused() or self._state in (self.STATE_CANCELLING, self.STATE_PAUSING) def isSdReady(self): return self._sdAvailable @@ -808,11 +821,12 @@ def fakeOk(self): self._handle_ok() def sendCommand(self, cmd, cmd_type=None, processed=False, force=False, on_sent=None, tags=None): - cmd = to_unicode(cmd, errors="replace") - if not processed: - cmd = process_gcode_line(cmd) - if not cmd: - return False + if not isinstance(cmd, QueueMarker): + cmd = to_unicode(cmd, errors="replace") + if not processed: + cmd = process_gcode_line(cmd) + if not cmd: + return False if self.isPrinting() and not self.isSdFileSelected() and not self._job_on_hold: try: @@ -1003,17 +1017,19 @@ def _finishFileTransfer(self, failed=False, tags=None): "time": self.getPrintTime() } - self._currentFile = None - self._changeState(self.STATE_OPERATIONAL) + def finalize(): + self._currentFile = None + self._changeState(self.STATE_OPERATIONAL) - if failed: - self._callback.on_comm_file_transfer_failed(remote) - eventManager().fire(Events.TRANSFER_FAILED, payload) - else: - self._callback.on_comm_file_transfer_done(remote) - eventManager().fire(Events.TRANSFER_DONE, payload) + if failed: + self._callback.on_comm_file_transfer_failed(remote) + eventManager().fire(Events.TRANSFER_FAILED, payload) + else: + self._callback.on_comm_file_transfer_done(remote) + eventManager().fire(Events.TRANSFER_DONE, payload) - self.refreshSdFiles(tags={"trigger:comm.finish_file_transfer",}) + self.refreshSdFiles(tags={"trigger:comm.finish_file_transfer",}) + self._sendCommand(SendQueueMarker(finalize)) def selectFile(self, filename, sd, tags=None): if self.isBusy(): @@ -1047,6 +1063,11 @@ def _cancel_preparation_done(self): self._recordFilePosition() self._callback.on_comm_print_job_cancelled() + def finalize(): + self._changeState(self.STATE_OPERATIONAL) + self.sendCommand(SendQueueMarker(finalize)) + self._continue_sending() + def cancelPrint(self, firmware_error=None, disable_log_position=False, tags=None): if not self.isOperational(): return @@ -1073,7 +1094,7 @@ def _on_M400_sent(): self._callback.on_comm_print_job_cancelling(firmware_error=firmware_error) with self._jobLock: - self._changeState(self.STATE_OPERATIONAL) + self._changeState(self.STATE_CANCELLING) if self.isSdFileSelected(): self.sendCommand("M25", tags=tags | {"trigger:comm.cancel",}) # pause print @@ -1093,6 +1114,11 @@ def _on_M400_sent(): def _pause_preparation_done(self): self._callback.on_comm_print_job_paused() + def finalize(): + self._changeState(self.STATE_PAUSED) + self.sendCommand(SendQueueMarker(finalize)) + self._continue_sending() + def setPause(self, pause, tags=None): if self.isStreaming(): return @@ -1132,7 +1158,7 @@ def setPause(self, pause, tags=None): if not self._pauseWaitStartTime: self._pauseWaitStartTime = time.time() - self._changeState(self.STATE_PAUSED) + self._changeState(self.STATE_PAUSING) if self.isSdFileSelected(): self.sendCommand("M25", tags=tags | {"trigger:comm.set_pause", "trigger:pause"}) # pause print @@ -1690,7 +1716,8 @@ def convert_line(line): self._changeState(self.STATE_PRINTING) elif 'Done printing file' in line and self.isSdPrinting(): # printer is reporting file finished printing - self._sdFilePos = 0 + self._currentFile.done = True + self._currentFile.setFilepos(0) self._callback.on_comm_print_job_done() self._changeState(self.STATE_OPERATIONAL) if self._sd_status_timer is not None: @@ -1824,7 +1851,7 @@ def _handle_ok(self): self._finish_heatup() - if not self._state in (self.STATE_PRINTING, self.STATE_OPERATIONAL, self.STATE_PAUSED): + if not self._state in (self.STATE_PRINTING, self.STATE_OPERATIONAL, self.STATE_PAUSED, self.STATE_CANCELLING, self.STATE_PAUSING): return # process queues ongoing resend requests and queues if we are operational @@ -1908,11 +1935,7 @@ def _finish_heatup(self): def _continue_sending(self): while self._active: - if self._state == self.STATE_OPERATIONAL or self._state == self.STATE_PAUSED or self.isSdPrinting(): - # just send stuff from the command queue and be done with it - return self._sendFromQueue() - - elif self._state == self.STATE_PRINTING: + if self._state == self.STATE_PRINTING and not (self._currentFile is None or self._currentFile.done or self.isSdPrinting()): # we are printing, we really want to send either something from the command # queue or the next line from our file, so we only return here if we actually DO # send something @@ -1928,6 +1951,9 @@ def _continue_sending(self): return True self._logger.debug("No command sent on ok while printing, doing another iteration") + else: + # just send stuff from the command queue and be done with it + return self._sendFromQueue() def _process_registered_message(self, line, feedback_matcher, feedback_controls, feedback_errors): feedback_match = feedback_matcher.search(line) @@ -2294,11 +2320,13 @@ def _getNext(self): line = self._currentFile.getNext() if line is None: - if self.isStreaming(): + if isinstance(self._currentFile, StreamingGcodeFileInformation): self._finishFileTransfer() else: self._callback.on_comm_print_job_done() - self._changeState(self.STATE_OPERATIONAL) + def finalize(): + self._changeState(self.STATE_OPERATIONAL) + return SendQueueMarker(finalize) return line def _sendNext(self): @@ -2310,7 +2338,15 @@ def _sendNext(self): return False line = self._getNext() - if line is None: + if isinstance(line, QueueMarker): + self.sendCommand(line) + self._callback.on_comm_progress() + + # end of file, return false so that the next round in continue_sending will process + # what we just enqueued (any scripts + marker) + return False + + elif line is None: # end of file, return false return False @@ -2446,6 +2482,13 @@ def _sendCommand(self, cmd, cmd_type=None, on_sent=None, tags=None): if self._serial is None: return False + if isinstance(cmd, QueueMarker): + if isinstance(cmd, SendQueueMarker): + self._enqueue_for_sending(cmd) + return True + else: + return False + gcode, subcode = gcode_and_subcode_for_cmd(cmd) if not self.isStreaming(): @@ -2505,7 +2548,7 @@ def _enqueue_for_sending(self, command, linenumber=None, command_type=None, on_s Enqueues a command and optional linenumber to use for it in the send queue. Arguments: - command (str): The command to send. + command (str or SendQueueMarker): The command to send. linenumber (int): The line number with which to send the command. May be ``None`` in which case the command will be sent without a line number and checksum. command_type (str): Optional command type, if set and command type is already in the queue the @@ -2553,6 +2596,11 @@ def _send_loop(self): # fetch command, command type and optional linenumber and sent callback from queue command, linenumber, command_type, on_sent, processed, tags = entry + if isinstance(command, SendQueueMarker): + command.run() + self._continue_sending() + continue + # some firmwares (e.g. Smoothie) might support additional in-band communication that will not # stick to the acknowledgement behaviour of GCODE, so we check here if we have a GCODE command # at hand here and only clear our clear_to_send flag later if that's the case @@ -3128,6 +3176,7 @@ def __init__(self, filename): self._pos = 0 self._size = None self._start_time = None + self._done = False def getStartTime(self): return self._start_time @@ -3164,6 +3213,7 @@ def start(self): Marks the print job as started and remembers the start time. """ self._start_time = time.time() + self._done = False def close(self): """ @@ -3171,6 +3221,10 @@ def close(self): """ pass + @property + def done(self): + return self._done + class PrintingSdFileInformation(PrintingFileInformation): """ Encapsulates information regarding an ongoing print from SD. @@ -3191,6 +3245,14 @@ def setFilepos(self, pos): def getFileLocation(self): return FileDestinations.SDCARD + @property + def done(self): + return self._done + + @done.setter + def done(self, value): + self._done = value + class PrintingGcodeFileInformation(PrintingFileInformation): """ Encapsulates information regarding an ongoing direct print. Takes care of the needed file handle and ensures @@ -3268,6 +3330,7 @@ def getNext(self): if self._handle is None: # file got closed just now self._pos = self._size + self._done = True self._report_stats() return None @@ -3995,6 +4058,22 @@ def _normalize_command_handler_result(command, command_type, gcode, subcode, tag return result +class QueueMarker(object): + + def __init__(self, callback): + self.callback = callback + + def run(self): + if callable(self.callback): + try: + self.callback() + except: + logging.getLogger(__name__).exception("Error while running callback of QueueMarker") + +class SendQueueMarker(QueueMarker): + pass + + # --- Test code for speed testing the comm layer via command line follows From 01d37fc9ee99b9cb247685e2a00954ad41968ecd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Mon, 5 Mar 2018 17:25:15 +0100 Subject: [PATCH 237/333] PMGR: Reduce notification spam --- .../pluginmanager/static/js/pluginmanager.js | 413 +++++++----------- 1 file changed, 152 insertions(+), 261 deletions(-) diff --git a/src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js b/src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js index 19842dcb6d..b30ab59718 100644 --- a/src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js +++ b/src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js @@ -205,6 +205,16 @@ $(function() { self.hiddenNoticeNotifications = {}; self.noticeCount = ko.observable(0); + self.notification = undefined; + self.logContents = { + steps: [], + action: { + reload: false, + refresh: false, + reconnect: false + } + }; + self.noticeCountText = ko.pureComputed(function() { var count = self.noticeCount(); if (count == 0) { @@ -701,133 +711,138 @@ $(function() { return self.isCompatible(data) ? (self.installed(data) ? gettext("Reinstall") : gettext("Install")) : (data.disabled ? gettext("Disabled") : gettext("Incompatible")); }; - self._displayNotification = function(response, action, titleSuccess, textSuccess, textRestart, textReload, textReconnect, titleError, textError) { - var notification; + self._displayPluginManagementNotification = function(response, action, plugin) { + self.logContents.action.restart = self.logContents.action.restart || response.needs_restart; + self.logContents.action.refresh = self.logContents.action.refresh || response.needs_refresh; + self.logContents.action_reconnect = self.logContents.action.reconnect || response.needs_reconnect; + self.logContents.steps.push({action: action, plugin: plugin, result: response.result}); + + var title = gettext("Plugin management log"); + var text = "

    "; + + var steps = self.logContents.steps; + if (steps.length > 5) { + var count = steps.length - 5; + var line; + if (count > 1) { + line = gettext("%(count)d earlier actions..."); + } else { + line = gettext("%(count)d earlier action"); + } + text += "
  • " + _.sprintf(line, {count: count}) + "
  • "; + steps = steps.slice(steps.length - 5); + } - var beforeClose = function(notification) { - self.notifications = _.without(self.notifications, notification); - }; + _.each(steps, function(step) { + var line = undefined; - if (response.result) { - if (action == "install" && response.plugin && response.plugin.blacklisted) { - notification = new PNotify({ - title: titleSuccess, - text: textSuccess, - type: "warning", - callbacks: { - before_close: beforeClose - }, - hide: false - }) - } else if (response.needs_restart) { - var options = { - title: titleSuccess, - text: textRestart, - buttons: { - closer: true, - sticker: false - }, - callbacks: { - before_close: beforeClose - }, - hide: false - }; + switch (step.action) { + case "install": { + line = gettext("Install %(plugin)s: %(result)s"); + break; + } + case "uninstall": { + line = gettext("Uninstall %(plugin)s: %(result)s"); + break; + } + case "enable": { + line = gettext("Enable %(plugin)s: %(result)s"); + break; + } + case "disable": { + line = gettext("Disable %(plugin)s: %(result)s"); + break; + } + default: { + return; + } + } + text += "
  • " + + _.sprintf(line, {plugin: step.plugin, result: step.result ? "" : ""}) + + "
  • "; + }); + text += "

"; + + var confirm = undefined; + var type = "success"; + if (self.logContents.action.restart) { + text += "

" + gettext("A restart is needed for the changes to take effect.") + "

"; + type = "warning"; + + if (self.restartCommandSpec) { var restartClicked = false; - if (self.restartCommandSpec) { - options.confirm = { - confirm: true, - buttons: [{ - text: gettext("Restart now"), - click: function (notice) { - if (restartClicked) return; - restartClicked = true; - showConfirmationDialog({ - message: gettext("This will restart your OctoPrint server.

This action may disrupt any ongoing print jobs (depending on your printer's controller and general setup that might also apply to prints run directly from your printer's internal storage)."), - onproceed: function() { - OctoPrint.system.executeCommand("core", "restart") - .done(function() { - notice.remove(); - new PNotify({ - title: gettext("Restart in progress"), - text: gettext("The server is now being restarted in the background") - }) + confirm = { + confirm: true, + buttons: [{ + text: gettext("Restart now"), + click: function (notice) { + if (restartClicked) return; + restartClicked = true; + showConfirmationDialog({ + message: gettext("This will restart your OctoPrint server.

This action may disrupt any ongoing print jobs (depending on your printer's controller and general setup that might also apply to prints run directly from your printer's internal storage)."), + onproceed: function() { + OctoPrint.system.executeCommand("core", "restart") + .done(function() { + notice.remove(); + new PNotify({ + title: gettext("Restart in progress"), + text: gettext("The server is now being restarted in the background") }) - .fail(function() { - new PNotify({ - title: gettext("Something went wrong"), - text: gettext("Trying to restart the server produced an error, please check octoprint.log for details. You'll have to restart manually.") - }) - }); - }, - onclose: function() { - restartClicked = false; - } - }); - } - }] - } + }) + .fail(function() { + new PNotify({ + title: gettext("Something went wrong"), + text: gettext("Trying to restart the server produced an error, please check octoprint.log for details. You'll have to restart manually.") + }) + }); + }, + onclose: function() { + restartClicked = false; + } + }); + } + }] } + } + } else if (self.logContents.action.refresh) { + text += "

" + gettext("A refresh is needed for the changes to take effect.") + "

"; + type = "warning"; - notification = PNotify.singleButtonNotify(options); - } else if (response.needs_refresh) { - var refreshClicked = false; - notification = PNotify.singleButtonNotify({ - title: titleSuccess, - text: textReload, - confirm: { - confirm: true, - buttons: [{ - text: gettext("Reload now"), - click: function () { - if (refreshClicked) return; - refreshClicked = true; - location.reload(true); - } - }] - }, - buttons: { - closer: true, - sticker: false - }, - callbacks: { - before_close: beforeClose - }, - hide: false - }) - } else if (response.needs_reconnect) { - notification = new PNotify({ - title: titleSuccess, - text: textReconnect, - callbacks: { - before_close: beforeClose - }, - hide: false - }) - } else { - notification = new PNotify({ - title: titleSuccess, - text: textSuccess, - type: "success", - callbacks: { - before_close: beforeClose - }, - hide: false - }) + var refreshClicked = false; + confirm = { + confirm: true, + buttons: [{ + text: gettext("Reload now"), + click: function () { + if (refreshClicked) return; + refreshClicked = true; + location.reload(true); + } + }] } - } else { - notification = new PNotify({ - title: titleError, - text: textError, - type: "error", - callbacks: { - before_close: beforeClose - }, - hide: false - }); + } else if (self.logContents.action_reconnect) { + text += "

" + gettext("A reconnect to the printer is needed for the changes to take effect.") + "

"; + type = "warning"; + } + + var options = { + title: title, + text: text, + type: type + }; + + if (self.logNotification !== undefined) { + self.logNotification.remove(); } - self.notifications.push(notification); + if (confirm !== undefined) { + options.confirm = confirm; + options.hide = false; + self.logNotification = PNotify.singleButtonNotify(options); + } else { + self.logNotification = new PNotify(options); + } }; self._markWorking = function(title, line) { @@ -1068,14 +1083,26 @@ $(function() { }; self.onUserLoggedOut = function() { - self._closeAllNotifications(); + self._resetNotifications(); }; self.onEventConnectivityChanged = function(payload) { self.requestData({eval_notices: true}); }; + self._resetNotifications = function() { + self._closeAllNotifications(); + self.logContents.action.restart + = self.logContents.action.reload + = self.logContents.action.reconnect + = false; + self.logContents.steps = []; + }; + self._closeAllNotifications = function() { + if (self.logNotification) { + self.logNotification.remove(); + } if (self.notifications) { _.each(self.notifications, function(notification) { notification.remove(); @@ -1084,7 +1111,7 @@ $(function() { }; self.onServerDisconnect = function() { - self._closeAllNotifications(); + self._resetNotifications(); return true; }; @@ -1095,7 +1122,7 @@ $(function() { }; self.onDataUpdaterPluginMessage = function(plugin, data) { - if (plugin != "pluginmanager") { + if (plugin !== "pluginmanager") { return; } @@ -1109,157 +1136,21 @@ $(function() { var messageType = data.type; - if (messageType == "loglines" && self.working()) { + if (messageType === "loglines" && self.working()) { _.each(data.loglines, function(line) { self.loglines.push(self._preprocessLine(line)); }); self._scrollWorkingOutputToEnd(); - } else if (messageType == "result") { - var titleSuccess, textSuccess, textRestart, textReload, textReconnect, titleError, textError; + } else if (messageType === "result") { var action = data.action; - var name = "Unknown"; - if (action == "install") { - var unknown = false; - - if (data.hasOwnProperty("plugin")) { - if (data.plugin == "unknown") { - unknown = true; - } else { - name = data.plugin.name; - } - } - - if (unknown) { - titleSuccess = _.sprintf(gettext("Plugin installed")); - textSuccess = gettext("A plugin was installed successfully, however it was impossible to detect which one. Please Restart OctoPrint to make sure everything will be registered properly"); - textRestart = textSuccess; - textReload = textSuccess; - textReconnect = textSuccess; - } else if (data.plugin && data.plugin.blacklisted) { - if (data.was_reinstalled) { - titleSuccess = _.sprintf(gettext("Plugin \"%(name)s\" reinstalled"), {name: name}); - textSuccess = gettext("The plugin was reinstalled successfully, however it is blacklisted and therefore won't be loaded."); - } else { - titleSuccess = _.sprintf(gettext("Plugin \"%(name)s\" installed"), {name: name}); - textSuccess = gettext("The plugin was installed successfully, however it is blacklisted and therefore won't be loaded."); - } - textRestart = textSuccess; - textReload = textSuccess; - textReconnect = textSuccess; - } else if (data.was_reinstalled) { - titleSuccess = _.sprintf(gettext("Plugin \"%(name)s\" reinstalled"), {name: name}); - textSuccess = gettext("The plugin was reinstalled successfully"); - textRestart = gettext("The plugin was reinstalled successfully, however a restart of OctoPrint is needed for that to take effect."); - textReload = gettext("The plugin was reinstalled successfully, however a reload of the page is needed for that to take effect."); - textReconnect = gettext("The plugin was reinstalled successfully, however a reconnect to the printer is needed for that to take effect."); - } else { - titleSuccess = _.sprintf(gettext("Plugin \"%(name)s\" installed"), {name: name}); - textSuccess = gettext("The plugin was installed successfully"); - textRestart = gettext("The plugin was installed successfully, however a restart of OctoPrint is needed for that to take effect."); - textReload = gettext("The plugin was installed successfully, however a reload of the page is needed for that to take effect."); - textReconnect = gettext("The plugin was installed successfully, however a reconnect to the printer is needed for that to take effect."); - } - - titleError = gettext("Something went wrong"); - var source = "unknown"; - if (data.hasOwnProperty("source")) { - source = data.source; - } - var sourceType = "unknown"; - if (data.hasOwnProperty("source_type")) { - sourceType = data.source_type; - } - - if (data.hasOwnProperty("reason")) { - if (data.was_reinstalled) { - if (sourceType == "path") { - textError = _.sprintf(gettext("Reinstalling the plugin from file failed: %(reason)s"), {reason: data.reason}); - } else { - textError = _.sprintf(gettext("Reinstalling the plugin from \"%(source)s\" failed: %(reason)s"), {reason: data.reason, source: source}); - } - } else { - if (sourceType == "path") { - textError = _.sprintf(gettext("Installing the plugin from file failed: %(reason)s"), {reason: data.reason}); - } else { - textError = _.sprintf(gettext("Installing the plugin from \"%(source)s\" failed: %(reason)s"), {reason: data.reason, source: source}); - } - } - } else { - if (data.was_reinstalled) { - if (sourceType == "path") { - textError = gettext("Reinstalling the plugin from file failed, please see the log for details."); - } else { - textError = _.sprintf(gettext("Reinstalling the plugin from \"%(source)s\" failed, please see the log for details."), {source: source}); - } - } else { - if (sourceType == "path") { - textError = gettext("Installing the plugin from file failed, please see the log for details."); - } else { - textError = _.sprintf(gettext("Installing the plugin from \"%(source)s\" failed, please see the log for details."), {source: source}); - } - } - } - - } else if (action == "uninstall") { - if (data.hasOwnProperty("plugin")) { - name = data.plugin.name; - } - - titleSuccess = _.sprintf(gettext("Plugin \"%(name)s\" uninstalled"), {name: name}); - textSuccess = gettext("The plugin was uninstalled successfully"); - textRestart = gettext("The plugin was uninstalled successfully, however a restart of OctoPrint is needed for that to take effect."); - textReload = gettext("The plugin was uninstalled successfully, however a reload of the page is needed for that to take effect."); - textReconnect = gettext("The plugin was uninstalled successfully, however a reconnect to the printer is needed for that to take effect."); - - titleError = gettext("Something went wrong"); - if (data.hasOwnProperty("reason")) { - textError = _.sprintf(gettext("Uninstalling the plugin failed: %(reason)s"), {reason: data.reason}); - } else { - textError = gettext("Uninstalling the plugin failed, please see the log for details."); - } - - } else if (action == "enable") { - if (data.hasOwnProperty("plugin")) { + if (data.hasOwnProperty("plugin")) { + if (data.plugin !== "unknown") { name = data.plugin.name; } - - titleSuccess = _.sprintf(gettext("Plugin \"%(name)s\" enabled"), {name: name}); - textSuccess = gettext("The plugin was enabled successfully."); - textRestart = gettext("The plugin was enabled successfully, however a restart of OctoPrint is needed for that to take effect."); - textReload = gettext("The plugin was enabled successfully, however a reload of the page is needed for that to take effect."); - textReconnect = gettext("The plugin was enabled successfully, however a reconnect to the printer is needed for that to take effect."); - - titleError = gettext("Something went wrong"); - if (data.hasOwnProperty("reason")) { - textError = _.sprintf(gettext("Toggling the plugin failed: %(reason)s"), {reason: data.reason}); - } else { - textError = gettext("Toggling the plugin failed, please see the log for details."); - } - - } else if (action == "disable") { - if (data.hasOwnProperty("plugin")) { - name = data.plugin.name; - } - - titleSuccess = _.sprintf(gettext("Plugin \"%(name)s\" disabled"), {name: name}); - textSuccess = gettext("The plugin was disabled successfully."); - textRestart = gettext("The plugin was disabled successfully, however a restart of OctoPrint is needed for that to take effect."); - textReload = gettext("The plugin was disabled successfully, however a reload of the page is needed for that to take effect."); - textReconnect = gettext("The plugin was disabled successfully, however a reconnect to the printer is needed for that to take effect."); - - titleError = gettext("Something went wrong"); - if (data.hasOwnProperty("reason")) { - textError = _.sprintf(gettext("Toggling the plugin failed: %(reason)s"), {reason: data.reason}); - } else { - textError = gettext("Toggling the plugin failed, please see the log for details."); - } - - } else { - return; } - self._displayNotification(data, action, titleSuccess, textSuccess, textRestart, textReload, textReconnect, titleError, textError); + self._displayPluginManagementNotification(data, action, name); self.requestData(); } }; From 11eb731b3d139327ea96a775f2a5cb09067fbcff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Tue, 6 Mar 2018 12:43:48 +0100 Subject: [PATCH 238/333] Virtual printer: simulate broken resends w/o ok --- src/octoprint/plugins/virtual_printer/virtual.py | 4 +++- src/octoprint/settings.py | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/octoprint/plugins/virtual_printer/virtual.py b/src/octoprint/plugins/virtual_printer/virtual.py index 63767ea46d..725a690195 100644 --- a/src/octoprint/plugins/virtual_printer/virtual.py +++ b/src/octoprint/plugins/virtual_printer/virtual.py @@ -119,6 +119,7 @@ def __init__(self, seriallog_handler=None, read_timeout=5.0, write_timeout=10.0) self._echoOnM117 = settings().getBoolean(["devel", "virtualPrinter", "echoOnM117"]) self._brokenM29 = settings().getBoolean(["devel", "virtualPrinter", "brokenM29"]) + self._brokenResend = settings().getBoolean(["devel", "virtualPrinter", "brokenResend"]) self._m115FormatString = settings().get(["devel", "virtualPrinter", "m115FormatString"]) self._firmwareName = settings().get(["devel", "virtualPrinter", "firmwareName"]) @@ -583,7 +584,8 @@ def _triggerResend(self, expected=None, actual=None, checksum=None): def request_resend(): self._send("Resend:%d" % expected) - self._sendOk() + if not self._brokenResend: + self._sendOk() if settings().getBoolean(["devel", "virtualPrinter", "repetierStyleResends"]): request_resend() diff --git a/src/octoprint/settings.py b/src/octoprint/settings.py index 64eb8e8295..9b6606737f 100644 --- a/src/octoprint/settings.py +++ b/src/octoprint/settings.py @@ -385,6 +385,7 @@ def settings(init=False, basedir=None, configfile=None): "supportM112": True, "echoOnM117": True, "brokenM29": True, + "brokenResend": False, "supportF": False, "firmwareName": "Virtual Marlin 1.0", "sharedNozzle": False, From ee36e2abd37d1aa4f5f884007f2c75c459e429a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Tue, 6 Mar 2018 12:48:24 +0100 Subject: [PATCH 239/333] Allow detection of firmware w/ broken resend If firmware doesn't send an ok immediately after a resend request, we will now be able to detect this (if enabled to do so) by starting a timer that will simulate an ok after .5s if none has been received. This should help with issues like prusa3d/Prusa-Firmware#331 where an ok is only missing sometimes and not always, making the existing workaround of always simulating an ok after a resend cause additional problems. --- src/octoprint/server/api/settings.py | 7 +++-- src/octoprint/settings.py | 24 ++++++++++++-- .../dialogs/settings/serialconnection.jinja2 | 31 +++++++++++++------ src/octoprint/util/comm.py | 18 +++++++++-- 4 files changed, 62 insertions(+), 18 deletions(-) diff --git a/src/octoprint/server/api/settings.py b/src/octoprint/server/api/settings.py index 0ea1899079..ff578813ab 100644 --- a/src/octoprint/server/api/settings.py +++ b/src/octoprint/server/api/settings.py @@ -148,7 +148,7 @@ def getSettings(): "triggerOkForM29": s.getBoolean(["serial", "triggerOkForM29"]), "logPositionOnPause": s.getBoolean(["serial", "logPositionOnPause"]), "logPositionOnCancel": s.getBoolean(["serial", "logPositionOnCancel"]), - "supportResendsWithoutOk": s.getBoolean(["serial", "supportResendsWithoutOk"]), + "supportResendsWithoutOk": s.get(["serial", "supportResendsWithoutOk"]), "waitForStart": s.getBoolean(["serial", "waitForStartOnConnect"]), "alwaysSendChecksum": s.getBoolean(["serial", "alwaysSendChecksum"]), "neverSendChecksum": s.getBoolean(["serial", "neverSendChecksum"]), @@ -374,7 +374,10 @@ def _saveSettings(data): if "ignoreErrorsFromFirmware" in data["serial"]: s.setBoolean(["serial", "ignoreErrorsFromFirmware"], data["serial"]["ignoreErrorsFromFirmware"]) if "disconnectOnErrors" in data["serial"]: s.setBoolean(["serial", "disconnectOnErrors"], data["serial"]["disconnectOnErrors"]) if "triggerOkForM29" in data["serial"]: s.setBoolean(["serial", "triggerOkForM29"], data["serial"]["triggerOkForM29"]) - if "supportResendsWithoutOk" in data["serial"]: s.setBoolean(["serial", "supportResendsWithoutOk"], data["serial"]["supportResendsWithoutOk"]) + if "supportResendsWithoutOk" in data["serial"]: + value = data["serial"]["supportResendsWithoutOk"] + if value in ("always", "detect", "never"): + s.set(["serial", "supportResendsWithoutOk"], value) if "waitForStart" in data["serial"]: s.setBoolean(["serial", "waitForStartOnConnect"], data["serial"]["waitForStart"]) if "alwaysSendChecksum" in data["serial"]: s.setBoolean(["serial", "alwaysSendChecksum"], data["serial"]["alwaysSendChecksum"]) if "neverSendChecksum" in data["serial"]: s.setBoolean(["serial", "neverSendChecksum"], data["serial"]["neverSendChecksum"]) diff --git a/src/octoprint/settings.py b/src/octoprint/settings.py index 9b6606737f..6ed1d1bb72 100644 --- a/src/octoprint/settings.py +++ b/src/octoprint/settings.py @@ -98,7 +98,8 @@ def settings(init=False, basedir=None, configfile=None): "temperature": 5, "temperatureTargetSet": 2, "temperatureAutoreport": 2, - "sdStatus": 1 + "sdStatus": 1, + "resendOk": .5 }, "maxCommunicationTimeouts": { "idle": 2, @@ -114,7 +115,7 @@ def settings(init=False, basedir=None, configfile=None): "disconnectOnErrors": True, "ignoreErrorsFromFirmware": False, "logResends": True, - "supportResendsWithoutOk": False, + "supportResendsWithoutOk": "detect", "logPositionOnPause": True, "logPositionOnCancel": True, "waitForStartOnConnect": False, @@ -910,7 +911,8 @@ def _migrate_config(self, config=None, persist=False): self._migrate_printer_parameters, self._migrate_gcode_scripts, self._migrate_core_system_commands, - self._migrate_serial_features + self._migrate_serial_features, + self._migrate_resend_without_ok ) for migrate in migrators: @@ -1217,6 +1219,22 @@ def migrate_key(key, source, target): return changed + def _migrate_resend_without_ok(self, config): + """ + Migrates supportResendsWithoutOk flag from boolean to ("always", "detect", "never") value range. + + True gets migrated to "always", False to "detect" (which is the new default). + """ + if "serial" in config and "supportResendsWithoutOk" in config["serial"] \ + and config["serial"]["supportResendsWithoutOk"] not in ("always", "detect", "never"): + value = config["serial"]["supportResendsWithoutOk"] + if value: + config["serial"]["supportResendsWithoutOk"] = "always" + else: + config["serial"]["supportResendsWithoutOk"] = "detect" + return True + return False + def backup(self, suffix, path=None): import shutil diff --git a/src/octoprint/templates/dialogs/settings/serialconnection.jinja2 b/src/octoprint/templates/dialogs/settings/serialconnection.jinja2 index 5b40eaab8f..355c8a0238 100644 --- a/src/octoprint/templates/dialogs/settings/serialconnection.jinja2 +++ b/src/octoprint/templates/dialogs/settings/serialconnection.jinja2 @@ -250,17 +250,28 @@
-
- +
+
+ +
-
- +
+ +
+ + + + {{ _('Some Marlin forks lack an acknowledging ok with their resend requests. Set this to "always" or "if detected as necessary" if you run into communication stalls on resend requests.') }} +
diff --git a/src/octoprint/util/comm.py b/src/octoprint/util/comm.py index 6cde33f9dd..d697de8e86 100644 --- a/src/octoprint/util/comm.py +++ b/src/octoprint/util/comm.py @@ -440,7 +440,8 @@ def __init__(self, port = None, baudrate=None, callbackObject=None, printerProfi self._temperature_autoreporting = False self._busy_protocol_detected = False - self._supportResendsWithoutOk = settings().getBoolean(["serial", "supportResendsWithoutOk"]) + self._trigger_ok_after_resend = settings().get(["serial", "supportResendsWithoutOk"]) + self._resend_ok_timer = None self._resendActive = False @@ -1838,6 +1839,10 @@ def convert_line(line): self._log("Connection closed, closing down monitor") def _handle_ok(self): + if self._resend_ok_timer: + self._resend_ok_timer.cancel() + self._resend_ok_timer = None + self._ok_timeout = get_new_timeout("communicationBusy" if self._busy_protocol_detected else "communication", self._timeout_intervals) self._clear_to_send.set() @@ -2436,11 +2441,18 @@ def _handleResendRequest(self, line): self._log_resends_rate_count += 1 self._send_queue.resend_active = True + return True finally: - if self._supportResendsWithoutOk: - # simulate an ok if our flags indicate that the printer needs that for resend requests to work + if self._trigger_ok_after_resend == "always": self._handle_ok() + elif self._trigger_ok_after_resend == "detect": + def process(): + self._resend_ok_timer = None + self._handle_ok() + self._logger.info("Firmware didn't send an 'ok' with their resend request. That's a known bug with some firmware variants out there. Simulating an ok to continue...") + self._resend_ok_timer = threading.Timer(self._timeout_intervals.get("resendOk", 1.0), process) + self._resend_ok_timer.start() def _resendSameCommand(self): return self._resendNextCommand(again=True) From de0660f6782c01b1532f211468aa50afe9b304a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Tue, 6 Mar 2018 13:28:40 +0100 Subject: [PATCH 240/333] Force disconnect on error state Fixes #2461 --- src/octoprint/util/comm.py | 49 ++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 26 deletions(-) diff --git a/src/octoprint/util/comm.py b/src/octoprint/util/comm.py index d697de8e86..f477216793 100644 --- a/src/octoprint/util/comm.py +++ b/src/octoprint/util/comm.py @@ -623,12 +623,12 @@ def getStateString(self, state=None): elif state == self.STATE_CLOSED: return "Offline" elif state == self.STATE_ERROR: - return "Error: %s" % (self.getErrorString()) + return "Error: {}".format(self.getErrorString()) elif state == self.STATE_CLOSED_WITH_ERROR: - return "Offline: %s" % (self.getErrorString()) + return "Offline (Error: {})".format(self.getErrorString()) elif state == self.STATE_TRANSFERING_FILE: return "Transferring file to SD" - return "Unknown State (%d)" % (self._state) + return "Unknown State ({})".format(self._state) def getErrorString(self): return self._errorValue @@ -969,9 +969,7 @@ def startPrint(self, pos=None, tags=None): self._sendFromQueue() except: self._logger.exception("Error while trying to start printing") - self._errorValue = get_exception_string() - self._changeState(self.STATE_ERROR) - eventManager().fire(Events.ERROR, {"error": self.getErrorString(), "reason": "start_print"}) + self._trigger_error(get_exception_string(), "start_print") def startFileTransfer(self, filename, localFilename, remoteFilename, special=False, tags=None): if not self.isOperational() or self.isBusy(): @@ -1781,10 +1779,8 @@ def convert_line(line): self._log("Unexpected error while setting baudrate {}: {}".format(baudrate, get_exception_string())) self._logger.exception("Unexpceted error while setting baudrate {}".format(baudrate)) else: - self.close(wait=False) - self._errorValue = "No more baudrates to test, and no suitable baudrate found." - self._changeState(self.STATE_ERROR) - eventManager().fire(Events.ERROR, {"error": self.getErrorString(), "reason": "autodetect_baudrate"}) + error_text = "No more baudrates to test, and no suitable baudrate found." + self._trigger_error(error_text, "autodetect_baudrate") elif 'start' in line or 'ok' in line: self._onConnected() if 'start' in line: @@ -2153,10 +2149,9 @@ def default(_, port, baudrate, read_timeout): self._changeState(self.STATE_DETECT_SERIAL) port = self._detect_port() if port is None: - self._errorValue = 'Failed to autodetect serial port, please set it manually.' - self._changeState(self.STATE_ERROR) - eventManager().fire(Events.ERROR, {"error": self.getErrorString(), "reason": "autodetect_port"}) - self._log("Failed to autodetect serial port, please set it manually.") + error_text = "Failed to autodetect serial port, please set it manually." + self._trigger_error(error_text, "autodetect_port") + self._log(error_text) return None # connect to regular serial port @@ -2186,9 +2181,7 @@ def default(_, port, baudrate, read_timeout): serial_obj = factory(self, self._port, self._baudrate, settings().getFloat(["serial", "timeout", "connection"])) except: exception_string = get_exception_string() - self._errorValue = "Connection error, see Terminal tab" - self._changeState(self.STATE_ERROR) - eventManager().fire(Events.ERROR, {"error": self.getErrorString(), "reason": "connection"}) + self._trigger_error("Connection error, see Terminal tab", "connection") error_message = "Unexpected error while connecting to serial port: %s %s (hook %s)" % (self._port, exception_string, name) self._log(error_message) @@ -2271,9 +2264,7 @@ def _handle_errors(self, line): if not self._ignore_errors: if self._disconnect_on_errors: - self._errorValue = error_text - self._changeState(self.STATE_ERROR) - eventManager().fire(Events.ERROR, {"error": self.getErrorString(), "reason": "firmware"}) + self._trigger_error(error_text, "firmware") elif self.isPrinting(): self.cancelPrint(firmware_error=error_text) self._clear_to_send.set() @@ -2284,6 +2275,13 @@ def _handle_errors(self, line): # finally return the line return line + def _trigger_error(self, text, reason, close=True): + self._errorValue = text + self._changeState(self.STATE_ERROR) + eventManager().fire(Events.ERROR, {"error": self.getErrorString(), "reason": reason}) + if close: + self.close(is_error=True) + def _readline(self): if self._serial is None: return None @@ -2412,13 +2410,12 @@ def _handleResendRequest(self, line): self._resendSwallowRepetitionsCounter = settings().getInt(["serial", "identicalResendsCountdown"]) if self._resendDelta > len(self._lastLines) or len(self._lastLines) == 0 or self._resendDelta < 0: - self._errorValue = "Printer requested line %d but no sufficient history is available, can't resend" % lineToResend - self._log(self._errorValue) - self._logger.warn(self._errorValue + ". Printer requested line {}, current line is {}, line history has {} entries.".format(lineToResend, self._currentLine, len(self._lastLines))) + error_text = "Printer requested line %d but no sufficient history is available, can't resend" % lineToResend + self._log(error_text) + self._logger.warn(error_text + ". Printer requested line {}, current line is {}, line history has {} entries.".format(lineToResend, self._currentLine, len(self._lastLines))) if self.isPrinting(): - # abort the print, there's nothing we can do to rescue it now - self._changeState(self.STATE_ERROR) - eventManager().fire(Events.ERROR, {"error": self.getErrorString(), "reason": "resend"}) + # abort the print & disconnect, there's nothing we can do to rescue it + self._trigger_error(error_text, "resend") else: # reset resend delta, we can't do anything about it self._resendDelta = None From c84f49e852670a9eeaa8251eab76541e1efc3b30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Tue, 6 Mar 2018 14:49:30 +0100 Subject: [PATCH 241/333] Change presentation of serial error handling config A set of radio controls is less confusing than two distinct checkboxes. --- .../static/js/app/viewmodels/settings.js | 13 ++++++++----- .../dialogs/settings/serialconnection.jinja2 | 17 +++++++++-------- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/src/octoprint/static/js/app/viewmodels/settings.js b/src/octoprint/static/js/app/viewmodels/settings.js index 08e3159437..96abb943f4 100644 --- a/src/octoprint/static/js/app/viewmodels/settings.js +++ b/src/octoprint/static/js/app/viewmodels/settings.js @@ -162,8 +162,7 @@ $(function() { self.serial_longRunningCommands = ko.observable(undefined); self.serial_checksumRequiringCommands = ko.observable(undefined); self.serial_helloCommand = ko.observable(undefined); - self.serial_ignoreErrorsFromFirmware = ko.observable(undefined); - self.serial_disconnectOnErrors = ko.observable(undefined); + self.serial_serialErrorBehaviour = ko.observable(undefined); self.serial_triggerOkForM29 = ko.observable(undefined); self.serial_waitForStart = ko.observable(undefined); self.serial_sendChecksum = ko.observable("print"); @@ -694,8 +693,10 @@ $(function() { longRunningCommands: function() { return splitTextToArray(self.serial_longRunningCommands(), ",", true) }, checksumRequiringCommands: function() { return splitTextToArray(self.serial_checksumRequiringCommands(), ",", true) }, externalHeatupDetection: function() { return !self.serial_disableExternalHeatupDetection()}, - alwaysSendChecksum: function() { return self.serial_sendChecksum() == "always"}, - neverSendChecksum: function() { return self.serial_sendChecksum() == "never"} + alwaysSendChecksum: function() { return self.serial_sendChecksum() === "always"}, + neverSendChecksum: function() { return self.serial_sendChecksum() === "never"}, + ignoreErrorsFromFirmware: function() { return self.serial_serialErrorBehaviour() === "ignore"}, + disconnectOnErrors: function() { return self.serial_serialErrorBehaviour() === "disconnect" } }, scripts: { gcode: function() { @@ -812,7 +813,9 @@ $(function() { checksumRequiringCommands: function(value) { self.serial_checksumRequiringCommands(value.join(", "))}, externalHeatupDetection: function(value) { self.serial_disableExternalHeatupDetection(!value) }, alwaysSendChecksum: function(value) { if (value) { self.serial_sendChecksum("always")}}, - neverSendChecksum: function(value) { if (value) { self.serial_sendChecksum("never")}} + neverSendChecksum: function(value) { if (value) { self.serial_sendChecksum("never")}}, + ignoreErrorsFromFirmware: function(value) { if (value) {self.serial_serialErrorBehaviour("ignore")}}, + disconnectOnErrors: function(value) { if (value) {self.serial_serialErrorBehaviour("disconnect")}} }, terminalFilters: function(value) { self.terminalFilters($.extend(true, [], value)) }, temperature: { diff --git a/src/octoprint/templates/dialogs/settings/serialconnection.jinja2 b/src/octoprint/templates/dialogs/settings/serialconnection.jinja2 index 355c8a0238..39912dd897 100644 --- a/src/octoprint/templates/dialogs/settings/serialconnection.jinja2 +++ b/src/octoprint/templates/dialogs/settings/serialconnection.jinja2 @@ -318,16 +318,17 @@
{{ _('Error handling') }}
+
-
-
-
-
-
From 2abff3b589f5bfc6184ebf9601680180b0d1e5f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Tue, 6 Mar 2018 15:05:25 +0100 Subject: [PATCH 242/333] Fix broken unit test --- tests/util/test_comm.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/util/test_comm.py b/tests/util/test_comm.py index 3ff2d99696..babeb202cb 100644 --- a/tests/util/test_comm.py +++ b/tests/util/test_comm.py @@ -13,6 +13,7 @@ def setUp(self): # mocks self._comm._handle_errors = lambda *args, **kwargs: octoprint.util.comm.MachineCom._handle_errors(self._comm, *args, **kwargs) + self._comm._trigger_error = lambda *args, **kwargs: octoprint.util.comm.MachineCom._trigger_error(self._comm, *args, **kwargs) self._comm._recoverable_communication_errors = octoprint.util.comm.MachineCom._recoverable_communication_errors self._comm._resend_request_communication_errors = octoprint.util.comm.MachineCom._resend_request_communication_errors self._comm._sd_card_errors = octoprint.util.comm.MachineCom._sd_card_errors @@ -206,11 +207,13 @@ def assert_not_last_comm_error(self): def assert_disconnected(self): self.assertIsNotNone(self._comm._errorValue) - self._comm._changeState.assert_called_once() + self._comm._changeState.assert_called_with(self._comm.STATE_ERROR) + self._comm.close.assert_called_once_with(is_error=True) def assert_not_disconnected(self): self.assertIsNone(self._comm._errorValue) self._comm._changeState.assert_not_called() + self._comm.close.assert_not_called() def assert_print_cancelled(self): self._comm.cancelPrint.assert_called_once() From 6a3f1d28fb2381c5f6264bdfa6099211c2a68d77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Tue, 6 Mar 2018 15:08:41 +0100 Subject: [PATCH 243/333] Reduce code duplication in error handling --- src/octoprint/util/comm.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/octoprint/util/comm.py b/src/octoprint/util/comm.py index f477216793..7655715737 100644 --- a/src/octoprint/util/comm.py +++ b/src/octoprint/util/comm.py @@ -2258,18 +2258,18 @@ def _handle_errors(self, line): if ret: return line - error_text = line[6:] if lower_line.startswith("error:") else line[2:] - self._to_logfile_with_terminal(u"Received an error from the printer's firmware: {}".format(error_text), + self._to_logfile_with_terminal(u"Received an error from the printer's firmware: {}".format(stripped_error), level=logging.WARN) if not self._ignore_errors: if self._disconnect_on_errors: - self._trigger_error(error_text, "firmware") + self._trigger_error(stripped_error, "firmware") elif self.isPrinting(): - self.cancelPrint(firmware_error=error_text) + self.cancelPrint(firmware_error=stripped_error) self._clear_to_send.set() else: - self._log("WARNING! Received an error from the printer's firmware, ignoring that as configured but you might want to investigate what happened here! Error: {}".format(error_text)) + self._log("WARNING! Received an error from the printer's firmware, ignoring that as configured " + "but you might want to investigate what happened here! Error: {}".format(stripped_error)) self._clear_to_send.set() # finally return the line From fb6ff04013716b460252d63c95e95e75915ccc6c Mon Sep 17 00:00:00 2001 From: ntoff Date: Wed, 7 Mar 2018 22:04:09 +1000 Subject: [PATCH 244/333] Add a confirmation to disconnect while printing Fullfills a feature request: https://github.com/foosel/OctoPrint/issues/2287 Add a confirmation dialog if a user attempts to disconnect while a print is in progress --- .../static/js/app/viewmodels/connection.js | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/octoprint/static/js/app/viewmodels/connection.js b/src/octoprint/static/js/app/viewmodels/connection.js index bfdf03bfb4..2c8bf90b7b 100644 --- a/src/octoprint/static/js/app/viewmodels/connection.js +++ b/src/octoprint/static/js/app/viewmodels/connection.js @@ -125,8 +125,24 @@ $(function() { self.settings.printerProfiles.requestData(); }); } else { - self.requestData(); - OctoPrint.connection.disconnect(); + if (!self.isPrinting() && !self.isPaused()) { + self.requestData(); + OctoPrint.connection.disconnect(); + } else { + showConfirmationDialog({ + title: gettext("Are you sure?"), + message: gettext("

You are about to disconnect from the printer while a print is in progress.

\ +

Disconnecting while a print is in progress will prevent OctoPrint from completing the print. If you're printing from an SD card attached directly to the printer, any attempt to restart OctoPrint or reconnect to the printer could interrupt the print.

"), + question: gettext("Are you sure you want to disconnect from the printer?"), + cancel: gettext("Stay Connected"), + proceed: gettext("Disconnect"), + onproceed: function() { + self.requestData(); + OctoPrint.connection.disconnect(); + } + }) + } + } }; From 283c44e35ee4d70d8be75199f16128b541cc1564 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Wed, 7 Mar 2018 13:23:35 +0100 Subject: [PATCH 245/333] Better logging of printer callback errors --- src/octoprint/printer/standard.py | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/src/octoprint/printer/standard.py b/src/octoprint/printer/standard.py index 0d06497834..5ad114b439 100644 --- a/src/octoprint/printer/standard.py +++ b/src/octoprint/printer/standard.py @@ -139,23 +139,31 @@ def unregister_callback(self, callback, *args, **kwargs): def _sendAddTemperatureCallbacks(self, data): for callback in self._callbacks: - try: callback.on_printer_add_temperature(data) - except: self._logger.exception("Exception while adding temperature data point") + try: + callback.on_printer_add_temperature(data) + except: + self._logger.exception(u"Exception while adding temperature data point to callback {}".format(callback)) def _sendAddLogCallbacks(self, data): for callback in self._callbacks: - try: callback.on_printer_add_log(data) - except: self._logger.exception("Exception while adding communication log entry") + try: + callback.on_printer_add_log(data) + except: + self._logger.exception(u"Exception while adding communication log entry to callback {}".format(callback)) def _sendAddMessageCallbacks(self, data): for callback in self._callbacks: - try: callback.on_printer_add_message(data) - except: self._logger.exception("Exception while adding printer message") + try: + callback.on_printer_add_message(data) + except: + self._logger.exception(u"Exception while adding printer message to callback {}".format(callback)) def _sendCurrentDataCallbacks(self, data): for callback in self._callbacks: - try: callback.on_printer_send_current_data(copy.deepcopy(data)) - except: self._logger.exception("Exception while pushing current data") + try: + callback.on_printer_send_current_data(copy.deepcopy(data)) + except: + self._logger.exception(u"Exception while pushing current data to callback {}".format(callback)) #~~ callback from metadata analysis event From 3c8d2f2df5e271553c997d8d4512144d4c62517e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Wed, 7 Mar 2018 13:46:24 +0100 Subject: [PATCH 246/333] Remove a blank line so @ntoff can sleep again Also reformatted the block of text a bit in code. --- src/octoprint/static/js/app/viewmodels/connection.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/octoprint/static/js/app/viewmodels/connection.js b/src/octoprint/static/js/app/viewmodels/connection.js index 2c8bf90b7b..aaf646d6eb 100644 --- a/src/octoprint/static/js/app/viewmodels/connection.js +++ b/src/octoprint/static/js/app/viewmodels/connection.js @@ -131,8 +131,12 @@ $(function() { } else { showConfirmationDialog({ title: gettext("Are you sure?"), - message: gettext("

You are about to disconnect from the printer while a print is in progress.

\ -

Disconnecting while a print is in progress will prevent OctoPrint from completing the print. If you're printing from an SD card attached directly to the printer, any attempt to restart OctoPrint or reconnect to the printer could interrupt the print.

"), + message: gettext("

You are about to disconnect from the printer while a print " + + "is in progress.

" + + "

Disconnecting while a print is in progress will prevent OctoPrint from " + + "completing the print. If you're printing from an SD card attached directly " + + "to the printer, any attempt to restart OctoPrint or reconnect to the printer " + + "could interrupt the print.

"), question: gettext("Are you sure you want to disconnect from the printer?"), cancel: gettext("Stay Connected"), proceed: gettext("Disconnect"), @@ -142,7 +146,6 @@ $(function() { } }) } - } }; From 90aa5ed3d4684be0d0e59e54f1b697662b5b56ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Wed, 7 Mar 2018 14:00:04 +0100 Subject: [PATCH 247/333] Custom json encoding for flask & sockjs That will allow us to support encoding non-built in types by simply adding them through octoprint.util.json.JsonEncoding.add_encoder --- src/octoprint/server/__init__.py | 4 +++- src/octoprint/server/util/flask.py | 8 ++++++++ src/octoprint/server/util/sockjs.py | 24 ++++++++++++++++++++++++ src/octoprint/util/json/__init__.py | 23 +++++++++++++++++++++++ 4 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 src/octoprint/util/json/__init__.py diff --git a/src/octoprint/server/__init__.py b/src/octoprint/server/__init__.py index ae624cf33e..ede0b10cc2 100644 --- a/src/octoprint/server/__init__.py +++ b/src/octoprint/server/__init__.py @@ -793,12 +793,14 @@ def _get_locale(self): return Locale.parse(request.accept_languages.best_match(LANGUAGES)) def _setup_app(self, app): - from octoprint.server.util.flask import ReverseProxiedEnvironment, OctoPrintFlaskRequest, OctoPrintFlaskResponse + from octoprint.server.util.flask import ReverseProxiedEnvironment, OctoPrintFlaskRequest, OctoPrintFlaskResponse, OctoPrintJsonEncoder s = settings() app.debug = self._debug + app.json_encoder = OctoPrintJsonEncoder + secret_key = s.get(["server", "secretKey"]) if not secret_key: import string diff --git a/src/octoprint/server/util/flask.py b/src/octoprint/server/util/flask.py index 58dc8b4488..4085e88aa2 100644 --- a/src/octoprint/server/util/flask.py +++ b/src/octoprint/server/util/flask.py @@ -8,6 +8,7 @@ import tornado.web import flask +import flask.json import flask.ext.login import flask.ext.principal import flask.ext.assets @@ -29,6 +30,7 @@ import octoprint.plugin from octoprint.util import DefaultOrderedDict +from octoprint.util.json import JsonEncoding from werkzeug.contrib.cache import BaseCache @@ -1399,3 +1401,9 @@ def asset_exists(category, asset): break return assets + +##~~ JSON encoding + +class OctoPrintJsonEncoder(flask.json.JSONEncoder): + def default(self, obj): + return JsonEncoding.encode(obj) diff --git a/src/octoprint/server/util/sockjs.py b/src/octoprint/server/util/sockjs.py index bcd027a4ba..dbd3e8600d 100644 --- a/src/octoprint/server/util/sockjs.py +++ b/src/octoprint/server/util/sockjs.py @@ -9,6 +9,8 @@ import threading import sockjs.tornado import sockjs.tornado.session +import sockjs.tornado.proto +import sockjs.tornado.util import time import octoprint.timelapse @@ -18,9 +20,13 @@ from octoprint.events import Events from octoprint.settings import settings +from octoprint.util.json import JsonEncoding import octoprint.printer +import wrapt +import json + class ThreadSafeSession(sockjs.tornado.session.Session): def __init__(self, conn, server, session_id, expiry=None): @@ -50,8 +56,26 @@ def remove_handler(self, handler): return result +class JsonEncodingSessionWrapper(wrapt.ObjectProxy): + + def send_message(self, msg, stats=True, binary=False): + """Send or queue outgoing message + + `msg` + Message to send + `stats` + If set to True, will update statistics after operation completes + """ + self.send_jsonified(json.dumps(sockjs.tornado.util.bytes_to_str(msg), + separators=(',', ':'), + default=JsonEncoding.encode), + stats) + + class PrinterStateConnection(sockjs.tornado.SockJSConnection, octoprint.printer.PrinterCallback): def __init__(self, printer, fileManager, analysisQueue, userManager, eventManager, pluginManager, session): + session = JsonEncodingSessionWrapper(session) + sockjs.tornado.SockJSConnection.__init__(self, session) self._logger = logging.getLogger(__name__) diff --git a/src/octoprint/util/json/__init__.py b/src/octoprint/util/json/__init__.py new file mode 100644 index 0000000000..f8fb31a7fb --- /dev/null +++ b/src/octoprint/util/json/__init__.py @@ -0,0 +1,23 @@ +# coding=utf-8 +from __future__ import absolute_import, division, print_function + +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' +__copyright__ = "Copyright (C) 2018 The OctoPrint Project - Released under terms of the AGPLv3 License" + + +import collections + +class JsonEncoding(object): + + encoders = collections.OrderedDict() + + @classmethod + def add_encoder(cls, type, encoder): + cls.encoders[type] = encoder + + @classmethod + def encode(cls, obj): + for type, encoder in cls.encoders.items(): + if isinstance(obj, type): + return encoder(obj) + raise TypeError From 088b4a8059ecc172969971be1809b3e453f77f79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Wed, 7 Mar 2018 14:04:20 +0100 Subject: [PATCH 248/333] Use frozendict for printer state Attempt to narrow down on #1951. To be on the safe side, the use of frozendict here can be disabled by settings devel.useFrozenDictForPrinterState to False in the config. --- setup.py | 3 +- src/octoprint/printer/standard.py | 159 +++++++++++++--------------- src/octoprint/settings.py | 1 + src/octoprint/util/json/__init__.py | 3 + 4 files changed, 77 insertions(+), 89 deletions(-) diff --git a/setup.py b/setup.py index 12066905be..643e82b96d 100644 --- a/setup.py +++ b/setup.py @@ -52,7 +52,8 @@ "wrapt>=1.10.10,<1.11", "futures>=3.1.1,<3.2", "emoji>=0.4.5,<0.5", - "monotonic>=1.3,<1.4" + "monotonic>=1.3,<1.4", + "frozendict>=1.2,<1.3" ] if sys.platform == "darwin": diff --git a/src/octoprint/printer/standard.py b/src/octoprint/printer/standard.py index 5ad114b439..cf17870e94 100644 --- a/src/octoprint/printer/standard.py +++ b/src/octoprint/printer/standard.py @@ -17,6 +17,8 @@ from past.builtins import basestring +from frozendict import frozendict + from octoprint import util as util from octoprint.events import eventManager, Events from octoprint.filemanager import FileDestinations, NoSuchStorage, valid_file_type @@ -40,6 +42,8 @@ def __init__(self, fileManager, analysisQueue, printerProfileManager): self._logger = logging.getLogger(__name__) + self._dict = frozendict if settings().getBoolean(["devel", "useFrozenDictForPrinterState"]) else dict + self._analysisQueue = analysisQueue self._fileManager = fileManager self._printerProfileManager = printerProfileManager @@ -100,25 +104,23 @@ def __init__(self, fileManager, analysisQueue, printerProfileManager): on_get_progress=self._updateProgressDataCallback ) self._stateMonitor.reset( - state={"text": self.get_state_string(), "flags": self._getStateFlags()}, - job_data={ - "file": { - "name": None, - "path": None, - "size": None, - "origin": None, - "date": None - }, - "estimatedPrintTime": None, - "lastPrintTime": None, - "filament": { - "length": None, - "volume": None - } - }, - progress={"completion": None, "filepos": None, "printTime": None, "printTimeLeft": None}, + state=self._dict(text=self.get_state_string(), flags=self._getStateFlags()), + job_data=self._dict(file=self._dict(name=None, + path=None, + size=None, + origin=None, + date=None), + estimatedPrintTime=None, + lastPrintTime=None, + filament=self._dict(length=None, + volume=None)), + progress=self._dict(completion=None, + filepos=None, + printTime=None, + printTimeLeft=None, + printTimeOrigin=None), current_z=None, - offsets=dict() + offsets=self._dict() ) eventManager().subscribe(Events.METADATA_ANALYSIS_FINISHED, self._on_event_MetadataAnalysisFinished) @@ -682,7 +684,7 @@ def _setState(self, state, state_string=None): state_string = self.get_state_string() self._state = state - self._stateMonitor.set_state({"text": state_string, "flags": self._getStateFlags()}) + self._stateMonitor.set_state(self._dict(text=state_string, flags=self._getStateFlags())) payload = dict( state_id=self.get_state_id(self._state), @@ -712,11 +714,12 @@ def _estimateTotalPrintTime(self, progress, printTime): return result - def _updateProgressData(self, completion=None, filepos=None, printTime=None, printTimeLeft=None): - self._stateMonitor.set_progress(dict(completion=int(completion * 100) if completion is not None else None, - filepos=filepos, - printTime=int(printTime) if printTime is not None else None, - printTimeLeft=int(printTimeLeft) if printTimeLeft is not None else None)) + def _updateProgressData(self, completion=None, filepos=None, printTime=None, printTimeLeft=None, printTimeLeftOrigin=None): + self._stateMonitor.set_progress(self._dict(completion=int(completion * 100) if completion is not None else None, + filepos=filepos, + printTime=int(printTime) if printTime is not None else None, + printTimeLeft=int(printTimeLeft) if printTimeLeft is not None else None, + printTimeLeftOrigin=printTimeLeftOrigin)) def _updateProgressDataCallback(self): if self._comm is None: @@ -746,11 +749,11 @@ def _updateProgressDataCallback(self): self._lastProgressReport = progress_int self._reportPrintProgressToPlugins(progress_int) - return dict(completion=progress * 100 if progress is not None else None, - filepos=filepos, - printTime=int(printTime) if printTime is not None else None, - printTimeLeft=int(printTimeLeft) if printTimeLeft is not None else None, - printTimeLeftOrigin=printTimeLeftOrigin) + return self._dict(completion=progress * 100 if progress is not None else None, + filepos=filepos, + printTime=int(printTime) if printTime is not None else None, + printTimeLeft=int(printTimeLeft) if printTimeLeft is not None else None, + printTimeLeftOrigin=printTimeLeftOrigin) def _estimatePrintTimeLeft(self, progress, printTime, cleanedPrintTime, statisticalTotalPrintTime, statisticalTotalPrintTimeType): """ @@ -872,28 +875,18 @@ def _addTemperatureData(self, tools=None, bed=None): if tools is None: tools = dict() - currentTimeUtc = int(time.time()) - - data = { - "time": currentTimeUtc - } + data = dict(time=int(time.time())) for tool in tools.keys(): - data["tool%d" % tool] = { - "actual": tools[tool][0], - "target": tools[tool][1] - } + data["tool%d" % tool] = self._dict(actual=tools[tool][0], target=tools[tool][1]) if bed is not None and isinstance(bed, tuple): - data["bed"] = { - "actual": bed[0], - "target": bed[1] - } + data["bed"] = self._dict(actual=bed[0], target=bed[1]) self._temps.append(data) self._temp = tools self._bedTemp = bed - self._stateMonitor.add_temperature(data) + self._stateMonitor.add_temperature(self._dict(**data)) def _validateJob(self, filename, sd): if not valid_file_type(filename, type="machinecode"): @@ -929,20 +922,16 @@ def _setJobData(self, filename, filesize, sd): } else: self._selectedFile = None - self._stateMonitor.set_job_data({ - "file": { - "name": None, - "path": None, - "display": None, - "origin": None, - "size": None, - "date": None - }, - "estimatedPrintTime": None, - "averagePrintTime": None, - "lastPrintTime": None, - "filament": None, - }) + self._stateMonitor.set_job_data(self._dict(file=self._dict(name=None, + path=None, + display=None, + origin=None, + size=None, + date=None), + estimatedPrintTime=None, + averagePrintTime=None, + lastPrintTime=None, + filament=None)) return estimatedPrintTime = None @@ -984,20 +973,16 @@ def _setJobData(self, filename, filesize, sd): self._selectedFile["estimatedPrintTime"] = estimatedPrintTime self._selectedFile["estimatedPrintTimeType"] = "analysis" - self._stateMonitor.set_job_data({ - "file": { - "name": name_in_storage, - "path": path_in_storage, - "display": display_name, - "origin": FileDestinations.SDCARD if sd else FileDestinations.LOCAL, - "size": filesize, - "date": date - }, - "estimatedPrintTime": estimatedPrintTime, - "averagePrintTime": averagePrintTime, - "lastPrintTime": lastPrintTime, - "filament": filament, - }) + self._stateMonitor.set_job_data(self._dict(file=self._dict(name=name_in_storage, + path=path_in_storage, + display=display_name, + origin=FileDestinations.SDCARD if sd else FileDestinations.LOCAL, + size=filesize, + date=date), + estimatedPrintTime=estimatedPrintTime, + averagePrintTime=averagePrintTime, + lastPrintTime=lastPrintTime, + filament=filament)) def _sendInitialStateUpdate(self, callback): try: @@ -1012,17 +997,15 @@ def _sendInitialStateUpdate(self, callback): self._logger.exception("Error while trying to send initial state update") def _getStateFlags(self): - return { - "operational": self.is_operational(), - "printing": self.is_printing(), - "cancelling": self.is_cancelling(), - "pausing": self.is_pausing(), - "closedOrError": self.is_closed_or_error(), - "error": self.is_error(), - "paused": self.is_paused(), - "ready": self.is_ready(), - "sdReady": self.is_sd_ready() - } + return self._dict(operational=self.is_operational(), + printing=self.is_printing(), + cancelling=self.is_cancelling(), + pausing=self.is_pausing(), + closedOrError=self.is_closed_or_error(), + error=self.is_error(), + paused=self.is_paused(), + ready=self.is_ready(), + sdReady=self.is_sd_ready()) #~~ comm.MachineComPrintCallback implementation @@ -1112,7 +1095,7 @@ def on_comm_z_change(self, newZ): self._setCurrentZ(newZ) def on_comm_sd_state_change(self, sdReady): - self._stateMonitor.set_state({"text": self.get_state_string(), "flags": self._getStateFlags()}) + self._stateMonitor.set_state(self._dict(text=self.get_state_string(), flags=self._getStateFlags())) def on_comm_sd_files(self, files): eventManager().fire(Events.UPDATED_FILES, {"type": "gcode"}) @@ -1127,7 +1110,7 @@ def on_comm_file_selected(self, full_path, size, sd): eventManager().fire(Events.FILE_DESELECTED) self._setJobData(full_path, size, sd) - self._stateMonitor.set_state({"text": self.get_state_string(), "flags": self._getStateFlags()}) + self._stateMonitor.set_state(self._dict(text=self.get_state_string(), flags=self._getStateFlags())) if self._printAfterSelect: self._printAfterSelect = False @@ -1151,7 +1134,7 @@ def on_comm_print_job_done(self): filepos=payload["size"], printTime=payload["time"], printTimeLeft=0) - self._stateMonitor.set_state({"text": self.get_state_string(), "flags": self._getStateFlags()}) + self._stateMonitor.set_state(self._dict(text=self.get_state_string(), flags=self._getStateFlags())) eventManager().fire(Events.PRINT_DONE, payload) self.script("afterPrintDone", @@ -1172,7 +1155,7 @@ def log_print(): else: self._updateProgressData() - self._stateMonitor.set_state({"text": self.get_state_string(), "flags": self._getStateFlags()}) + self._stateMonitor.set_state(self._dict(text=self.get_state_string(), flags=self._getStateFlags())) def on_comm_print_job_failed(self): @@ -1233,7 +1216,7 @@ def on_comm_file_transfer_started(self, filename, filesize): self._setJobData(filename, filesize, True) self._updateProgressData(completion=0.0, filepos=0, printTime=0) - self._stateMonitor.set_state({"text": self.get_state_string(), "flags": self._getStateFlags()}) + self._stateMonitor.set_state(self._dict(text=self.get_state_string(), flags=self._getStateFlags())) def on_comm_file_transfer_done(self, filename, failed=False): self._sdStreaming = False @@ -1250,7 +1233,7 @@ def on_comm_file_transfer_done(self, filename, failed=False): self._setCurrentZ(None) self._setJobData(None, None, None) self._updateProgressData() - self._stateMonitor.set_state({"text": self.get_state_string(), "flags": self._getStateFlags()}) + self._stateMonitor.set_state(self._dict(text=self.get_state_string(), flags=self._getStateFlags())) def on_comm_file_transfer_failed(self, filename): self.on_comm_file_transfer_done(filename, failed=True) diff --git a/src/octoprint/settings.py b/src/octoprint/settings.py index 6ed1d1bb72..ca497ec591 100644 --- a/src/octoprint/settings.py +++ b/src/octoprint/settings.py @@ -363,6 +363,7 @@ def settings(init=False, basedir=None, configfile=None): "bundle": True, "clean_on_startup": True }, + "useFrozenDictForPrinterState": True, "virtualPrinter": { "enabled": False, "okAfterResend": False, diff --git a/src/octoprint/util/json/__init__.py b/src/octoprint/util/json/__init__.py index f8fb31a7fb..7d0621b110 100644 --- a/src/octoprint/util/json/__init__.py +++ b/src/octoprint/util/json/__init__.py @@ -6,6 +6,7 @@ import collections +import frozendict class JsonEncoding(object): @@ -21,3 +22,5 @@ def encode(cls, obj): if isinstance(obj, type): return encoder(obj) raise TypeError + +JsonEncoding.add_encoder(frozendict.frozendict, lambda obj: dict(obj)) From eada72a4d9fef3082ea5ae3a4642b9f83b371ac8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Wed, 7 Mar 2018 14:05:10 +0100 Subject: [PATCH 249/333] Add __str__ for sockjs connection --- src/octoprint/server/util/sockjs.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/octoprint/server/util/sockjs.py b/src/octoprint/server/util/sockjs.py index dbd3e8600d..07783ffd34 100644 --- a/src/octoprint/server/util/sockjs.py +++ b/src/octoprint/server/util/sockjs.py @@ -106,6 +106,12 @@ def _getRemoteAddress(self, info): return forwardedFor.split(",")[0] return info.ip + def __str__(self): + if self._remoteAddress: + return "{!r} connected to {}".format(self, self._remoteAddress) + else: + return "Unconnected {!r}".format(self) + def on_open(self, info): self._remoteAddress = self._getRemoteAddress(info) self._logger.info("New connection from client: %s" % self._remoteAddress) @@ -156,7 +162,6 @@ def on_open(self, info): self._emit("event", {"type": Events.MOVIE_RENDERING, "payload": octoprint.timelapse.current_render_job}) def on_close(self): - self._logger.info("Client connection closed: %s" % self._remoteAddress) self._printer.unregister_callback(self) self._fileManager.unregister_slicingprogress_callback(self) octoprint.timelapse.unregister_callback(self) @@ -166,6 +171,9 @@ def on_close(self): for event in octoprint.events.all_events(): self._eventManager.unsubscribe(event, self._onEvent) + self._logger.info("Client connection closed: %s" % self._remoteAddress) + self._remoteAddress = None + def on_message(self, message): try: import json From bab4782b8c04447bbcb309b4c71ce619a75e4403 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Wed, 7 Mar 2018 14:09:55 +0100 Subject: [PATCH 250/333] Log comm states to application log Should allow to better debug some things in the future --- src/octoprint/util/comm.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/octoprint/util/comm.py b/src/octoprint/util/comm.py index 7655715737..c0100b0a91 100644 --- a/src/octoprint/util/comm.py +++ b/src/octoprint/util/comm.py @@ -554,7 +554,10 @@ def _changeState(self, newState): oldState = self.getStateString() self._state = newState - self._log('Changing monitoring state from \'%s\' to \'%s\'' % (oldState, self.getStateString())) + + text = "Changing monitoring state from \"{}\" to \"{}\"".format(oldState, self.getStateString()) + self._log(text) + self._logger.info(text) self._callback.on_comm_state_change(newState) def _dual_log(self, message, level=logging.ERROR): From 52167298342accb6f4284e88473ca64619ee6842 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Wed, 7 Mar 2018 14:24:38 +0100 Subject: [PATCH 251/333] Add a regular server heartbeat to the log --- src/octoprint/server/__init__.py | 13 +++++++++++++ src/octoprint/settings.py | 1 + 2 files changed, 14 insertions(+) diff --git a/src/octoprint/server/__init__.py b/src/octoprint/server/__init__.py index ede0b10cc2..9f37e2edf7 100644 --- a/src/octoprint/server/__init__.py +++ b/src/octoprint/server/__init__.py @@ -218,6 +218,7 @@ def run(self): safe_mode = self._safe_mode self._logger = logging.getLogger(__name__) + self._setup_heartbeat_logging() pluginManager = self._plugin_manager # monkey patch a bunch of stuff @@ -792,6 +793,18 @@ def _get_locale(self): return Locale.parse(request.accept_languages.best_match(LANGUAGES)) + def _setup_heartbeat_logging(self): + logger = logging.getLogger(__name__ + ".heartbeat") + + def log_heartbeat(): + logger.info("Server heartbeat <3") + + interval = settings().getFloat(["server", "heartbeat"]) + logger.info("Starting server heartbeat, {}s interval".format(interval)) + + timer = octoprint.util.RepeatedTimer(interval, log_heartbeat) + timer.start() + def _setup_app(self, app): from octoprint.server.util.flask import ReverseProxiedEnvironment, OctoPrintFlaskRequest, OctoPrintFlaskResponse, OctoPrintJsonEncoder diff --git a/src/octoprint/settings.py b/src/octoprint/settings.py index ca497ec591..2b29b93484 100644 --- a/src/octoprint/settings.py +++ b/src/octoprint/settings.py @@ -150,6 +150,7 @@ def settings(init=False, basedir=None, configfile=None): "startOnceInSafeMode": False, "seenWizards": {}, "secretKey": None, + "heartbeat": 15 * 60, # 15 min "reverseProxy": { "prefixHeader": None, "schemeHeader": None, From f1401d3f1ecf5e7edb2bf4f425f1054278589d88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Wed, 7 Mar 2018 14:33:33 +0100 Subject: [PATCH 252/333] Gcode viewer: Don't hiccup on GCODE subcodes Implements #2378 --- src/octoprint/static/gcodeviewer/js/Worker.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/octoprint/static/gcodeviewer/js/Worker.js b/src/octoprint/static/gcodeviewer/js/Worker.js index 6afb77d773..f5c0f42242 100644 --- a/src/octoprint/static/gcodeviewer/js/Worker.js +++ b/src/octoprint/static/gcodeviewer/js/Worker.js @@ -335,7 +335,7 @@ var doParse = function () { var log = false; - if (/^(?:G0|G1|G2|G3)\s/i.test(line)) { + if (/^(?:G0|G1|G2|G3)(\.\d+)?\s/i.test(line)) { args = line.split(/\s/); for (j = 0; j < args.length; j++) { @@ -426,25 +426,25 @@ var doParse = function () { addToModel = true; move = true; } - } else if (/^(?:M82)/i.test(line)) { + } else if (/^(?:M82)(\.\d+)?/i.test(line)) { relativeE = false; - } else if (/^(?:G91)/i.test(line)) { + } else if (/^(?:G91)(\.\d+)?/i.test(line)) { relativeMode = true; if (g90InfluencesExtruder) { relativeE = true; } - } else if (/^(?:G90)/i.test(line)) { + } else if (/^(?:G90)(\.\d+)?/i.test(line)) { relativeMode = false; if (g90InfluencesExtruder) { relativeE = false; } - } else if (/^(?:M83)/i.test(line)) { + } else if (/^(?:M83)(\.\d+)?/i.test(line)) { relativeE = true; - } else if (/^(?:M101)/i.test(line)) { + } else if (/^(?:M101)(\.\d+)?/i.test(line)) { dcExtrude = true; - } else if (/^(?:M103)/i.test(line)) { + } else if (/^(?:M103)(\.\d+)?/i.test(line)) { dcExtrude = false; - } else if (/^(?:G92)/i.test(line)) { + } else if (/^(?:G92)(\.\d+)?/i.test(line)) { args = line.split(/\s/); for (j = 0; j < args.length; j++) { @@ -494,7 +494,7 @@ var doParse = function () { move = false; } - } else if (/^(?:G28)/i.test(line)) { + } else if (/^(?:G28)(\.\d+)?/i.test(line)) { args = line.split(/\s/); if (args.length === 1) { From 1f9c9bed42ddef19b4fa515870a3fe2e8213e323 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Wed, 7 Mar 2018 14:56:10 +0100 Subject: [PATCH 253/333] Timelapse: Remove post roll capturing from timed timelapses As discussed in #2317 this option was confusing and frustrating too many people. Instead of an actual capture we'll now just do the same as for z-based timelapses and use the last image repeatedly. No, using the live webstream and capturing real time image is NOT an option and not going to happen here, see the discussion in #2317 on this as well. --- src/octoprint/server/api/timelapse.py | 11 +---- .../static/js/app/viewmodels/timelapse.js | 12 ------ src/octoprint/templates/tabs/timelapse.jinja2 | 11 +---- src/octoprint/timelapse.py | 43 +++---------------- 4 files changed, 7 insertions(+), 70 deletions(-) diff --git a/src/octoprint/server/api/timelapse.py b/src/octoprint/server/api/timelapse.py index 2c5af7336f..2c54a5c865 100644 --- a/src/octoprint/server/api/timelapse.py +++ b/src/octoprint/server/api/timelapse.py @@ -43,8 +43,7 @@ def _config_for_timelapse(timelapse): return dict(type="timed", postRoll=timelapse.post_roll, fps=timelapse.fps, - interval=timelapse.interval, - capturePostRoll=timelapse.capture_post_roll) + interval=timelapse.interval) else: return dict(type="off") @@ -216,14 +215,6 @@ def setTimelapseConfig(): else: return make_response("Invalid value for interval: %d" % interval, 400) - if "capturePostRoll" in data: - try: - capturePostRoll = bool(data["capturePostRoll"]) - except ValueError: - return make_response("Invalid value for capturePostRoll: %r" % data["capturePostRoll"], 400) - else: - config["options"]["capturePostRoll"] = capturePostRoll - if "retractionZHop" in data: try: retractionZHop = float(data["retractionZHop"]) diff --git a/src/octoprint/static/js/app/viewmodels/timelapse.js b/src/octoprint/static/js/app/viewmodels/timelapse.js index 5599f7f123..74c1bf22f0 100644 --- a/src/octoprint/static/js/app/viewmodels/timelapse.js +++ b/src/octoprint/static/js/app/viewmodels/timelapse.js @@ -11,7 +11,6 @@ $(function() { self.defaultInterval = 10; self.defaultRetractionZHop = 0; self.defaultMinDelay = 5.0; - self.defaultCapturePostRoll = true; self.timelapseType = ko.observable(undefined); self.timelapseTimedInterval = ko.observable(self.defaultInterval); @@ -19,7 +18,6 @@ $(function() { self.timelapseFps = ko.observable(self.defaultFps); self.timelapseRetractionZHop = ko.observable(self.defaultRetractionZHop); self.timelapseMinDelay = ko.observable(self.defaultMinDelay); - self.timelapseCapturePostRoll = ko.observable(self.defaultCapturePostRoll); self.serverConfig = ko.observable(); @@ -76,9 +74,6 @@ $(function() { self.timelapseMinDelay.subscribe(function() { self.isDirty(true); }); - self.timelapseCapturePostRoll.subscribe(function() { - self.isDirty(true); - }); self.persist.subscribe(function() { self.isDirty(true); }); @@ -176,12 +171,6 @@ $(function() { self.timelapseTimedInterval(self.defaultInterval); } - if (config.type === "timed" && config.capturePostRoll !== undefined){ - self.timelapseCapturePostRoll(config.capturePostRoll); - } else { - self.timelapseCapturePostRoll(self.defaultCapturePostRoll); - } - if (config.type === "zchange" && config.retractionZHop !== undefined && config.retractionZHop > 0) { self.timelapseRetractionZHop(config.retractionZHop); } else { @@ -386,7 +375,6 @@ $(function() { if (self.timelapseType() === "timed") { payload["interval"] = self.timelapseTimedInterval(); - payload["capturePostRoll"] = self.timelapseCapturePostRoll(); } if (self.timelapseType() === "zchange") { diff --git a/src/octoprint/templates/tabs/timelapse.jinja2 b/src/octoprint/templates/tabs/timelapse.jinja2 index 3c2cbf4952..c47778de5a 100644 --- a/src/octoprint/templates/tabs/timelapse.jinja2 +++ b/src/octoprint/templates/tabs/timelapse.jinja2 @@ -56,16 +56,7 @@ {{ _('sec') }}

- {{ _('OctoPrint will take additional pictures to add this many seconds to the end of your rendered timelapse.') }} -
-
- -
-
- + {{ _('OctoPrint will use the final picture to add this many seconds to the end of your rendered timelapse.') }}
diff --git a/src/octoprint/timelapse.py b/src/octoprint/timelapse.py index e8ae50c3f4..8ecd40fb2e 100644 --- a/src/octoprint/timelapse.py +++ b/src/octoprint/timelapse.py @@ -333,11 +333,7 @@ def configure_timelapse(config=None, persist=False): if "options" in config and "interval" in config["options"] and config["options"]["interval"] > 0: interval = config["options"]["interval"] - capture_post_roll = True - if "options" in config and "capturePostRoll" in config["options"] and isinstance(config["options"]["capturePostRoll"], bool): - capture_post_roll = config["options"]["capturePostRoll"] - - current = TimedTimelapse(post_roll=postRoll, interval=interval, fps=fps, capture_post_roll=capture_post_roll) + current = TimedTimelapse(post_roll=postRoll, interval=interval, fps=fps) notify_callbacks(current) @@ -685,13 +681,11 @@ def _on_z_change(self, event, payload): class TimedTimelapse(Timelapse): - def __init__(self, interval=1, capture_post_roll=True, post_roll=0, fps=25): + def __init__(self, interval=1, post_roll=0, fps=25): Timelapse.__init__(self, post_roll=post_roll, fps=fps) self._interval = interval if self._interval < 1: self._interval = 1 # force minimum interval of 1s - self._capture_post_roll = capture_post_roll - self._postroll_captures = 0 self._timer = None self._logger.debug("TimedTimelapse initialized") @@ -699,16 +693,11 @@ def __init__(self, interval=1, capture_post_roll=True, post_roll=0, fps=25): def interval(self): return self._interval - @property - def capture_post_roll(self): - return self._capture_post_roll - def config_data(self): return { "type": "timed", "options": { - "interval": self._interval, - "capture_post_roll": self._capture_post_roll + "interval": self._interval } } @@ -724,40 +713,18 @@ def on_print_started(self, event, payload): on_finish=self._on_timer_finished) self._timer.start() - def on_print_done(self, event, payload): - if self._capture_post_roll: - self._postroll_captures = self._post_roll * self._fps - else: - self._postroll_captures = 0 - Timelapse.on_print_done(self, event, payload) - - def calculate_post_roll(self): - if self._capture_post_roll: - return self._post_roll * self._fps * self._interval - else: - return Timelapse.calculate_post_roll(self) - def process_post_roll(self): - if self._capture_post_roll: - return - - # we only use the final image as post roll if we - # are not supposed to capture it + # we only use the final image as post roll self._copying_postroll() self.post_roll_finished() def _timer_active(self): - return self._in_timelapse or self._postroll_captures > 0 + return self._in_timelapse def _timer_task(self): self.capture_image() - if self._postroll_captures > 0: - self._postroll_captures -= 1 def _on_timer_finished(self): - if self._capture_post_roll: - self.post_roll_finished() - # timer is done, delete it self._timer = None From 3fcd5adfbd264b28f985c7cfd9d5b0792199ddaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Wed, 7 Mar 2018 15:20:46 +0100 Subject: [PATCH 254/333] Timelapse: Add snapshot imeout & SSL validation settings Implements #2455 & #2362 --- docs/api/util.rst | 2 ++ src/octoprint/server/api/__init__.py | 6 +++++- src/octoprint/server/api/settings.py | 4 ++++ src/octoprint/settings.py | 2 ++ src/octoprint/static/js/app/viewmodels/settings.js | 9 ++++++++- src/octoprint/templates/dialogs/settings/webcam.jinja2 | 2 ++ .../settings/webcam/webcamSnapshotSslValidation.jinja2 | 7 +++++++ .../settings/webcam/webcamSnapshotTimeout.jinja2 | 9 +++++++++ src/octoprint/timelapse.py | 7 ++++++- 9 files changed, 45 insertions(+), 3 deletions(-) create mode 100644 src/octoprint/templates/snippets/settings/webcam/webcamSnapshotSslValidation.jinja2 create mode 100644 src/octoprint/templates/snippets/settings/webcam/webcamSnapshotTimeout.jinja2 diff --git a/docs/api/util.rst b/docs/api/util.rst index 1ee12ff22c..e01664d201 100644 --- a/docs/api/util.rst +++ b/docs/api/util.rst @@ -45,6 +45,8 @@ Test paths or URLs * ``method``: The request method to use for the test. Optional, defaults to ``HEAD``. * ``timeout``: A timeout for the request, in seconds. If no reply from the tested URL has been received within this time frame, the check will be considered a failure. Optional, defaults to 3 seconds. + * ``validSsl``: Whether to validate the SSL connection if the ``url`` happens to be an HTTPS URL or not. Optional, + defaults to ``True``. * ``status``: The status code(s) or named status range(s) to test for. Can be either a single value or a list of either HTTP status codes or any of the following named status ranges: diff --git a/src/octoprint/server/api/__init__.py b/src/octoprint/server/api/__init__.py index ea042e3a2c..ed9c164fe9 100644 --- a/src/octoprint/server/api/__init__.py +++ b/src/octoprint/server/api/__init__.py @@ -337,6 +337,7 @@ def as_dict(self): url = data["url"] method = data.get("method", "HEAD") timeout = 3.0 + valid_ssl = True check_status = [status_ranges["normal"]] if "timeout" in data: @@ -345,6 +346,9 @@ def as_dict(self): except: return make_response("{!r} is not a valid value for timeout (must be int or float)".format(data["timeout"]), 400) + if "validSsl" in data: + valid_ssl = data["validSsl"] in valid_boolean_trues + if "status" in data: request_status = data["status"] if not isinstance(request_status, list): @@ -363,7 +367,7 @@ def as_dict(self): check_status.append([code]) try: - response = requests.request(method=method, url=url, timeout=timeout) + response = requests.request(method=method, url=url, timeout=timeout, verify=valid_ssl) status = response.status_code except: status = 0 diff --git a/src/octoprint/server/api/settings.py b/src/octoprint/server/api/settings.py index ff578813ab..c7023f8c73 100644 --- a/src/octoprint/server/api/settings.py +++ b/src/octoprint/server/api/settings.py @@ -101,6 +101,8 @@ def getSettings(): "streamRatio": s.get(["webcam", "streamRatio"]), "streamTimeout": s.getInt(["webcam", "streamTimeout"]), "snapshotUrl": s.get(["webcam", "snapshot"]), + "snapshotTimeout": s.getInt(["webcam", "snapshotTimeout"]), + "snapshotSslValidation": s.getBoolean(["webcam", "snapshotSslValidation"]), "ffmpegPath": s.get(["webcam", "ffmpeg"]), "bitrate": s.get(["webcam", "bitrate"]), "ffmpegThreads": s.get(["webcam", "ffmpegThreads"]), @@ -332,6 +334,8 @@ def _saveSettings(data): if "streamRatio" in data["webcam"] and data["webcam"]["streamRatio"] in ("16:9", "4:3"): s.set(["webcam", "streamRatio"], data["webcam"]["streamRatio"]) if "streamTimeout" in data["webcam"]: s.setInt(["webcam", "streamTimeout"], data["webcam"]["streamTimeout"]) if "snapshotUrl" in data["webcam"]: s.set(["webcam", "snapshot"], data["webcam"]["snapshotUrl"]) + if "snapshotTimeout" in data["webcam"]: s.setInt(["webcam", "snapshotTimeout"], data["webcam"]["snapshotTimeout"]) + if "snapshotSslValidation" in data["webcam"]: s.setBoolean(["webcam", "snapshotSslValidation"], data["webcam"]["snapshotSslValidation"]) if "ffmpegPath" in data["webcam"]: s.set(["webcam", "ffmpeg"], data["webcam"]["ffmpegPath"]) if "bitrate" in data["webcam"]: s.set(["webcam", "bitrate"], data["webcam"]["bitrate"]) if "ffmpegThreads" in data["webcam"]: s.setInt(["webcam", "ffmpegThreads"], data["webcam"]["ffmpegThreads"]) diff --git a/src/octoprint/settings.py b/src/octoprint/settings.py index 2b29b93484..83e60498ca 100644 --- a/src/octoprint/settings.py +++ b/src/octoprint/settings.py @@ -199,6 +199,8 @@ def settings(init=False, basedir=None, configfile=None): "streamRatio": "16:9", "streamTimeout": 5, "snapshot": None, + "snapshotTimeout": 5, + "snapshotSslValidation": True, "ffmpeg": None, "ffmpegThreads": 1, "bitrate": "5000k", diff --git a/src/octoprint/static/js/app/viewmodels/settings.js b/src/octoprint/static/js/app/viewmodels/settings.js index 96abb943f4..86a9ce019d 100644 --- a/src/octoprint/static/js/app/viewmodels/settings.js +++ b/src/octoprint/static/js/app/viewmodels/settings.js @@ -120,6 +120,8 @@ $(function() { self.webcam_streamRatio = ko.observable(undefined); self.webcam_streamTimeout = ko.observable(undefined); self.webcam_snapshotUrl = ko.observable(undefined); + self.webcam_snapshotTimeout = ko.observable(undefined); + self.webcam_snapshotSslValidation = ko.observable(undefined); self.webcam_ffmpegPath = ko.observable(undefined); self.webcam_bitrate = ko.observable(undefined); self.webcam_ffmpegThreads = ko.observable(undefined); @@ -322,7 +324,12 @@ $(function() { var errorTitle = gettext("Snapshot test failed"); self.testWebcamSnapshotUrlBusy(true); - OctoPrint.util.testUrl(self.webcam_snapshotUrl(), {method: "GET", response: "bytes"}) + OctoPrint.util.testUrl(self.webcam_snapshotUrl(), { + method: "GET", + response: "bytes", + timeout: self.webcam_snapshotTimeout(), + validSsl: self.webcam_snapshotSslValidation() + }) .done(function(response) { if (!response.result) { showMessageDialog({ diff --git a/src/octoprint/templates/dialogs/settings/webcam.jinja2 b/src/octoprint/templates/dialogs/settings/webcam.jinja2 index 60a846c90e..7a1330edc7 100644 --- a/src/octoprint/templates/dialogs/settings/webcam.jinja2 +++ b/src/octoprint/templates/dialogs/settings/webcam.jinja2 @@ -24,6 +24,8 @@
{% include "snippets/settings/webcam/ffmpegBitrate.jinja2" %} {% include "snippets/settings/webcam/ffmpegThreads.jinja2" %} + {% include "snippets/settings/webcam/webcamSnapshotTimeout.jinja2" %} + {% include "snippets/settings/webcam/webcamSnapshotSslValidation.jinja2" %}
diff --git a/src/octoprint/templates/snippets/settings/webcam/webcamSnapshotSslValidation.jinja2 b/src/octoprint/templates/snippets/settings/webcam/webcamSnapshotSslValidation.jinja2 new file mode 100644 index 0000000000..c3a4256289 --- /dev/null +++ b/src/octoprint/templates/snippets/settings/webcam/webcamSnapshotSslValidation.jinja2 @@ -0,0 +1,7 @@ +
+
+ +
+
diff --git a/src/octoprint/templates/snippets/settings/webcam/webcamSnapshotTimeout.jinja2 b/src/octoprint/templates/snippets/settings/webcam/webcamSnapshotTimeout.jinja2 new file mode 100644 index 0000000000..8f6e35c407 --- /dev/null +++ b/src/octoprint/templates/snippets/settings/webcam/webcamSnapshotTimeout.jinja2 @@ -0,0 +1,9 @@ +
+ +
+
+ + sec +
+
+
diff --git a/src/octoprint/timelapse.py b/src/octoprint/timelapse.py index 8ecd40fb2e..d18fb2d9af 100644 --- a/src/octoprint/timelapse.py +++ b/src/octoprint/timelapse.py @@ -363,6 +363,8 @@ def __init__(self, post_roll=0, fps=25): self._capture_dir = settings().getBaseFolder("timelapse_tmp") self._movie_dir = settings().getBaseFolder("timelapse") self._snapshot_url = settings().get(["webcam", "snapshot"]) + self._snapshot_timeout = settings().getInt(["webcam", "snapshotTimeout"]) + self._snapshot_validate_ssl = settings().getBoolean(["webcam", "snapshotSslValidation"]) self._fps = fps @@ -578,7 +580,10 @@ def _perform_capture(self, filename, onerror=None): eventManager().fire(Events.CAPTURE_START, dict(file=filename)) try: self._logger.debug("Going to capture {} from {}".format(filename, self._snapshot_url)) - r = requests.get(self._snapshot_url, stream=True, timeout=5) + r = requests.get(self._snapshot_url, + stream=True, + timeout=self._snapshot_timeout, + verify=self._snapshot_validate_ssl) r.raise_for_status() with open (filename, "wb") as f: From 88b622e28291f1ee11a1433dbaef1dbd405e03f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Wed, 7 Mar 2018 15:37:16 +0100 Subject: [PATCH 255/333] customClass => additionalClasses --- docs/features/custom_controls.rst | 4 ++++ src/octoprint/static/js/app/viewmodels/control.js | 4 ++++ src/octoprint/templates/tabs/control.jinja2 | 7 +------ 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/docs/features/custom_controls.rst b/docs/features/custom_controls.rst index a953617e9e..f130596c17 100644 --- a/docs/features/custom_controls.rst +++ b/docs/features/custom_controls.rst @@ -166,6 +166,10 @@ Controls clicked. This allows to override the direct sending of the command or commands to the printer with more sophisticated behaviour. The JavaScript snippet is ``eval``'d and processed in a context where the control it is part of is provided as local variable ``data`` and the ``ControlViewModel`` is available as ``self``. + * - ``additionalClasses`` + - (Optional) Additional classes to apply to the button of a ``command``, ``commands``, ``script`` or ``javascript`` + control, other than the default ``btn``. Can be used to visually style the button, e.g. set to ``btn-danger`` to + turn the button red. * - ``enabled`` - (Optional) A JavaScript snippet returning either ``true`` or ``false`` determining whether the control should be enabled or not. This allow to override the default logic for this (disabled if printer is offline diff --git a/src/octoprint/static/js/app/viewmodels/control.js b/src/octoprint/static/js/app/viewmodels/control.js index 0f38329dee..0722054146 100644 --- a/src/octoprint/static/js/app/viewmodels/control.js +++ b/src/octoprint/static/js/app/viewmodels/control.js @@ -226,6 +226,10 @@ $(function() { } } + if (control.hasOwnProperty("command") && !control.hasOwnProperty("additionalClasses")) { + control.additionalClasses = ""; + } + control.processed = true; return control; }; diff --git a/src/octoprint/templates/tabs/control.jinja2 b/src/octoprint/templates/tabs/control.jinja2 index 0cf7e741f2..14b6725083 100644 --- a/src/octoprint/templates/tabs/control.jinja2 +++ b/src/octoprint/templates/tabs/control.jinja2 @@ -173,11 +173,6 @@ From e0f8ae898686bb226e16df5f0dae53c5bb83c18a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Tue, 27 Feb 2018 09:46:46 +0100 Subject: [PATCH 256/333] "SD printing byte 0/0" means we ain't printing Newer versions of Marlin apparently now report it like that instead of returning "Not SD printing". --- src/octoprint/util/comm.py | 48 ++++++++++++++++++++++++++------------ 1 file changed, 33 insertions(+), 15 deletions(-) diff --git a/src/octoprint/util/comm.py b/src/octoprint/util/comm.py index c0100b0a91..da961db029 100644 --- a/src/octoprint/util/comm.py +++ b/src/octoprint/util/comm.py @@ -951,10 +951,10 @@ def startPrint(self, pos=None, tags=None): self.sendCommand("M23 {filename}".format(filename=self._currentFile.getFilename()), tags=tags | {"trigger:comm.start_print",}) if pos is not None and isinstance(pos, int) and pos > 0: - self._currentFile.setFilepos(pos) + self._currentFile.pos = pos self.sendCommand("M26 S{}".format(pos), tags=tags | {"trigger:comm.start_print",}) else: - self._currentFile.setFilepos(0) + self._currentFile.pos = 0 self.sendCommand("M24", tags=tags | {"trigger:comm.start_print",}) @@ -1675,11 +1675,6 @@ def convert_line(line): self._sdAvailable = False self._sdFiles = [] self._callback.on_comm_sd_state_change(self._sdAvailable) - elif 'Not SD printing' in line: - if self.isSdFileSelected() and self.isPrinting(): - # something went wrong, printer is reporting that we actually are not printing right now... - self._sdFilePos = 0 - self._changeState(self.STATE_OPERATIONAL) elif 'SD card ok' in line and not self._sdAvailable: self._sdAvailable = True self.refreshSdFiles() @@ -1693,8 +1688,21 @@ def convert_line(line): elif 'SD printing byte' in line and self.isSdPrinting(): # answer to M27, at least on Marlin, Repetier and Sprinter: "SD printing byte %d/%d" match = regex_sdPrintingByte.search(line) - self._currentFile.setFilepos(int(match.group("current"))) - self._callback.on_comm_progress() + if match: + current = int(match.group("current")) + total = int(match.group("total")) + + if current == total == 0: + # apparently not SD printing - newer Marlin reports it like that for some reason + self._changeState(self.STATE_OPERATIONAL) + else: + self._currentFile.pos = current + if self._currentFile.size == 0: + self._currentFile.size = total + self._callback.on_comm_progress() + elif 'Not SD printing' in line and self.isSdFileSelected() and self.isPrinting(): + # something went wrong, printer is reporting that we actually are not printing right now... + self._changeState(self.STATE_OPERATIONAL) elif 'File opened' in line and not self._ignore_select: # answer to M23, at least on Marlin, Repetier and Sprinter: "File opened:%s Size:%d" match = regex_sdFileOpened.search(line) @@ -3248,12 +3256,6 @@ def __init__(self, filename, size): PrintingFileInformation.__init__(self, filename) self._size = size - def setFilepos(self, pos): - """ - Sets the current file position. - """ - self._pos = pos - def getFileLocation(self): return FileDestinations.SDCARD @@ -3265,6 +3267,22 @@ def done(self): def done(self, value): self._done = value + @property + def size(self): + return self._size + + @size.setter + def size(self, value): + self._size = value + + @property + def pos(self): + return self._pos + + @pos.setter + def pos(self, value): + self._pos = value + class PrintingGcodeFileInformation(PrintingFileInformation): """ Encapsulates information regarding an ongoing direct print. Takes care of the needed file handle and ensures From 52bb94e272bc3541a8adf315ee9b94949658bcd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Tue, 27 Feb 2018 10:54:33 +0100 Subject: [PATCH 257/333] Try to detect externally started SD prints Strongly depends on the firmware: * Firmware must send a "File opened: ..." message on start of the print * Firmware must respond to an immediately sent M27 with "SD printing byte /" * Firmware must stay responsive during ongoing print to allow for regular M27 polls (or push those automatically) or M25 to pause/ cancel the print through OctoPrint. --- src/octoprint/util/comm.py | 63 +++++++++++++++++++++++--------------- 1 file changed, 39 insertions(+), 24 deletions(-) diff --git a/src/octoprint/util/comm.py b/src/octoprint/util/comm.py index da961db029..fdc0c77596 100644 --- a/src/octoprint/util/comm.py +++ b/src/octoprint/util/comm.py @@ -917,7 +917,7 @@ def sendGcodeScript(self, scriptName, replacements=None, tags=None): self.sendCommand(line, tags=tags | {"trigger:comm.send_gcode_script", "source:script", "script:{}".format(scriptName)}) return "\n".join(scriptLines) - def startPrint(self, pos=None, tags=None): + def startPrint(self, pos=None, tags=None, external_sd=False): if not self.isOperational() or self.isPrinting(): return @@ -938,25 +938,24 @@ def startPrint(self, pos=None, tags=None): self._changeState(self.STATE_PRINTING) - self.resetLineNumbers(tags={"trigger:comm.start_print"}) + if not self.isSdFileSelected(): + self.resetLineNumbers(tags={"trigger:comm.start_print"}) self._callback.on_comm_print_job_started() if self.isSdFileSelected(): - #self.sendCommand("M26 S0") # setting the sd pos apparently sometimes doesn't work, so we re-select - # the file instead - - # make sure to ignore the "file selected" later on, otherwise we'll reset our progress data - self._ignore_select = True - self.sendCommand("M23 {filename}".format(filename=self._currentFile.getFilename()), - tags=tags | {"trigger:comm.start_print",}) - if pos is not None and isinstance(pos, int) and pos > 0: - self._currentFile.pos = pos - self.sendCommand("M26 S{}".format(pos), tags=tags | {"trigger:comm.start_print",}) - else: - self._currentFile.pos = 0 + if not external_sd: + # make sure to ignore the "file selected" later on, otherwise we'll reset our progress data + self._ignore_select = True + self.sendCommand("M23 {filename}".format(filename=self._currentFile.getFilename()), + tags=tags | {"trigger:comm.start_print",}) + if pos is not None and isinstance(pos, int) and pos > 0: + self._currentFile.pos = pos + self.sendCommand("M26 S{}".format(pos), tags=tags | {"trigger:comm.start_print",}) + else: + self._currentFile.pos = 0 - self.sendCommand("M24", tags=tags | {"trigger:comm.start_print",}) + self.sendCommand("M24", tags=tags | {"trigger:comm.start_print",}) self._sd_status_timer = RepeatedTimer(self._timeout_intervals.get("sdStatus", 1.0), self._poll_sd_status, run_first=True) self._sd_status_timer.start() @@ -1070,7 +1069,7 @@ def finalize(): self.sendCommand(SendQueueMarker(finalize)) self._continue_sending() - def cancelPrint(self, firmware_error=None, disable_log_position=False, tags=None): + def cancelPrint(self, firmware_error=None, disable_log_position=False, tags=None, external_sd=False): if not self.isOperational(): return @@ -1099,9 +1098,10 @@ def _on_M400_sent(): self._changeState(self.STATE_CANCELLING) if self.isSdFileSelected(): - self.sendCommand("M25", tags=tags | {"trigger:comm.cancel",}) # pause print - self.sendCommand("M27", tags=tags | {"trigger:comm.cancel",}) # get current byte position in file - self.sendCommand("M26 S0", tags=tags | {"trigger:comm.cancel",}) # reset position in file to byte 0 + if not external_sd: + self.sendCommand("M25", tags=tags | {"trigger:comm.cancel",}) # pause print + self.sendCommand("M27", tags=tags | {"trigger:comm.cancel",}) # get current byte position in file + self.sendCommand("M26 S0", tags=tags | {"trigger:comm.cancel",}) # reset position in file to byte 0 if self._sd_status_timer is not None: try: self._sd_status_timer.cancel() @@ -1685,24 +1685,29 @@ def convert_line(line): elif 'End file list' in line: self._sdFileList = False self._callback.on_comm_sd_files(self._sdFiles) - elif 'SD printing byte' in line and self.isSdPrinting(): + elif 'SD printing byte' in line: # answer to M27, at least on Marlin, Repetier and Sprinter: "SD printing byte %d/%d" match = regex_sdPrintingByte.search(line) if match: current = int(match.group("current")) total = int(match.group("total")) - if current == total == 0: + if self.isSdPrinting() and current == total == 0: # apparently not SD printing - newer Marlin reports it like that for some reason - self._changeState(self.STATE_OPERATIONAL) - else: + self.cancelPrint(external_sd=True) + + elif self.isSdFileSelected(): + if not self.isSdPrinting(): + self.startPrint(external_sd=True) + self._currentFile.pos = current if self._currentFile.size == 0: self._currentFile.size = total self._callback.on_comm_progress() + elif 'Not SD printing' in line and self.isSdFileSelected() and self.isPrinting(): # something went wrong, printer is reporting that we actually are not printing right now... - self._changeState(self.STATE_OPERATIONAL) + self.cancelPrint(external_sd=True) elif 'File opened' in line and not self._ignore_select: # answer to M23, at least on Marlin, Repetier and Sprinter: "File opened:%s Size:%d" match = regex_sdFileOpened.search(line) @@ -1712,10 +1717,20 @@ def convert_line(line): else: name = "Unknown" size = 0 + + expected = False if self._sdFileToSelect: + expected = True name = self._sdFileToSelect self._sdFileToSelect = None + self._currentFile = PrintingSdFileInformation(name, size) + + if not expected: + # It doesn't look like we expected this, so it might be that someone just started a print + # job from SD via the printer's controller. Let's query the printing status and see if that's + # indeed the case and if so switch states accordingly + self.sendCommand("M27", tags={"trigger:unexpected_file_open"}) elif 'File selected' in line: if self._ignore_select: self._ignore_select = False From 98c7ad0c2045d1a962c65097a61912cbc54a7d47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Tue, 27 Feb 2018 10:57:49 +0100 Subject: [PATCH 258/333] If current == total -> not SD printing --- src/octoprint/util/comm.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/octoprint/util/comm.py b/src/octoprint/util/comm.py index fdc0c77596..fc8b54bcad 100644 --- a/src/octoprint/util/comm.py +++ b/src/octoprint/util/comm.py @@ -1473,7 +1473,7 @@ def convert_line(line): handled = True # process timeouts - elif ((line == "" and now > self._timeout) or (self.isPrinting() and not self._job_on_hold and now > self._ok_timeout)) \ + elif ((line == "" and now > self._timeout) or (self.isPrinting() and not self.isSdPrinting() and not self._job_on_hold and now > self._ok_timeout)) \ and (not self._blockWhileDwelling or not self._dwelling_until or now > self._dwelling_until): # We have two timeout variants: # @@ -1697,7 +1697,7 @@ def convert_line(line): self.cancelPrint(external_sd=True) elif self.isSdFileSelected(): - if not self.isSdPrinting(): + if not self.isSdPrinting() and current != total: self.startPrint(external_sd=True) self._currentFile.pos = current @@ -1742,7 +1742,7 @@ def convert_line(line): elif 'Done printing file' in line and self.isSdPrinting(): # printer is reporting file finished printing self._currentFile.done = True - self._currentFile.setFilepos(0) + self._currentFile.pos = 0 self._callback.on_comm_print_job_done() self._changeState(self.STATE_OPERATIONAL) if self._sd_status_timer is not None: From e6ec48b8a1221b4d77cf393767bd4295f0b3b409 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Wed, 7 Mar 2018 17:45:06 +0100 Subject: [PATCH 259/333] Support SD status autoreport --- .../plugins/virtual_printer/virtual.py | 22 +++++++++++++-- src/octoprint/server/api/settings.py | 4 +++ src/octoprint/settings.py | 5 +++- .../static/js/app/viewmodels/settings.js | 2 ++ .../dialogs/settings/serialconnection.jinja2 | 19 ++++++++++++- src/octoprint/util/comm.py | 28 ++++++++++++++++++- 6 files changed, 74 insertions(+), 6 deletions(-) diff --git a/src/octoprint/plugins/virtual_printer/virtual.py b/src/octoprint/plugins/virtual_printer/virtual.py index 725a690195..012bef9104 100644 --- a/src/octoprint/plugins/virtual_printer/virtual.py +++ b/src/octoprint/plugins/virtual_printer/virtual.py @@ -126,9 +126,10 @@ def __init__(self, seriallog_handler=None, read_timeout=5.0, write_timeout=10.0) self._okFormatString = settings().get(["devel", "virtualPrinter", "okFormatString"]) - self._capabilities = settings().get(["devel", "virtualPrinter", "capabilities"]) + self._capabilities = settings().get(["devel", "virtualPrinter", "capabilities"], merged=True) self._temperature_reporter = None + self._sdstatus_reporter = None self.currentLine = 0 self.lastN = 0 @@ -414,8 +415,23 @@ def _gcode_M26(self, data): self._setSdPos(pos) def _gcode_M27(self, data): - if self._sdCardReady: - self._reportSdStatus() + def report(): + if self._sdCardReady: + self._reportSdStatus() + + match = re.search("S([0-9]+)", data) + if match: + interval = int(match.group(1)) + if self._sdstatus_reporter is not None: + self._sdstatus_reporter.cancel() + + if interval > 0: + self._sdstatus_reporter = RepeatedTimer(interval, report) + self._sdstatus_reporter.start() + else: + self._sdstatus_reporter = None + + report() def _gcode_M28(self, data): if self._sdCardReady: diff --git a/src/octoprint/server/api/settings.py b/src/octoprint/server/api/settings.py index c7023f8c73..9ccb461be3 100644 --- a/src/octoprint/server/api/settings.py +++ b/src/octoprint/server/api/settings.py @@ -139,6 +139,7 @@ def getSettings(): "timeoutTemperatureTargetSet": s.getFloat(["serial", "timeout", "temperatureTargetSet"]), "timeoutTemperatureAutoreport": s.getFloat(["serial", "timeout", "temperatureAutoreport"]), "timeoutSdStatus": s.getFloat(["serial", "timeout", "sdStatus"]), + "timeoutSdStatusAutoreport": s.getFloat(["serial", "timeout", "sdStatusAutoreport"]), "log": s.getBoolean(["serial", "log"]), "additionalPorts": s.get(["serial", "additionalPorts"]), "additionalBaudrates": s.get(["serial", "additionalBaudrates"]), @@ -166,6 +167,7 @@ def getSettings(): "maxTimeoutsPrinting": s.getInt(["serial", "maxCommunicationTimeouts", "printing"]), "maxTimeoutsLong": s.getInt(["serial", "maxCommunicationTimeouts", "long"]), "capAutoreportTemp": s.getBoolean(["serial", "capabilities", "autoreport_temp"]), + "capAutoreportSdStatus": s.getBoolean(["serial", "capabilities", "autoreport_sdstatus"]), "capBusyProtocol": s.getBoolean(["serial", "capabilities", "busy_protocol"]) }, "folder": { @@ -370,6 +372,7 @@ def _saveSettings(data): if "timeoutTemperatureTargetSet" in data["serial"]: s.setFloat(["serial", "timeout", "temperatureTargetSet"], data["serial"]["timeoutTemperatureTargetSet"]) if "timeoutTemperatureAutoreport" in data["serial"]: s.setFloat(["serial", "timeout", "temperatureAutoreport"], data["serial"]["timeoutTemperatureAutoreport"]) if "timeoutSdStatus" in data["serial"]: s.setFloat(["serial", "timeout", "sdStatus"], data["serial"]["timeoutSdStatus"]) + if "timeoutSdStatusAutoreport" in data["serial"]: s.setFloat(["serial", "timeout", "sdStatusAutoreport"], data["serial"]["timeoutSdStatusAutoreport"]) if "additionalPorts" in data["serial"] and isinstance(data["serial"]["additionalPorts"], (list, tuple)): s.set(["serial", "additionalPorts"], data["serial"]["additionalPorts"]) if "additionalBaudrates" in data["serial"] and isinstance(data["serial"]["additionalBaudrates"], (list, tuple)): s.set(["serial", "additionalBaudrates"], data["serial"]["additionalBaudrates"]) if "longRunningCommands" in data["serial"] and isinstance(data["serial"]["longRunningCommands"], (list, tuple)): s.set(["serial", "longRunningCommands"], data["serial"]["longRunningCommands"]) @@ -399,6 +402,7 @@ def _saveSettings(data): if "maxTimeoutsPrinting" in data["serial"]: s.setInt(["serial", "maxCommunicationTimeouts", "printing"], data["serial"]["maxTimeoutsPrinting"]) if "maxTimeoutsLong" in data["serial"]: s.setInt(["serial", "maxCommunicationTimeouts", "long"], data["serial"]["maxTimeoutsLong"]) if "capAutoreportTemp" in data["serial"]: s.setBoolean(["serial", "capabilities", "autoreport_temp"], data["serial"]["capAutoreportTemp"]) + if "capAutoreportSdStatus" in data["serial"]: s.setBoolean(["serial", "capabilities", "autoreport_sdstatus"], data["serial"]["capAutoreportSdStatus"]) if "capBusyProtocol" in data["serial"]: s.setBoolean(["serial", "capabilities", "busy_protocol"], data["serial"]["capBusyProtocol"]) oldLog = s.getBoolean(["serial", "log"]) diff --git a/src/octoprint/settings.py b/src/octoprint/settings.py index 83e60498ca..ab9c0f01c2 100644 --- a/src/octoprint/settings.py +++ b/src/octoprint/settings.py @@ -99,6 +99,7 @@ def settings(init=False, basedir=None, configfile=None): "temperatureTargetSet": 2, "temperatureAutoreport": 2, "sdStatus": 1, + "sdStatusAutoreport": 1, "resendOk": .5 }, "maxCommunicationTimeouts": { @@ -137,6 +138,7 @@ def settings(init=False, basedir=None, configfile=None): "capabilities": { "autoreport_temp": True, + "autoreport_sdstatus": True, "busy_protocol": True }, @@ -403,7 +405,8 @@ def settings(init=False, basedir=None, configfile=None): "m115FormatString": "FIRMWARE_NAME: {firmware_name} PROTOCOL_VERSION:1.0", "m115ReportCapabilities": False, "capabilities": { - "AUTOREPORT_TEMP": True + "AUTOREPORT_TEMP": True, + "AUTOREPORT_SD_STATUS": True }, "ambientTemperature": 21.3, "errors": { diff --git a/src/octoprint/static/js/app/viewmodels/settings.js b/src/octoprint/static/js/app/viewmodels/settings.js index 86a9ce019d..c2ee03faf6 100644 --- a/src/octoprint/static/js/app/viewmodels/settings.js +++ b/src/octoprint/static/js/app/viewmodels/settings.js @@ -158,6 +158,7 @@ $(function() { self.serial_timeoutTemperatureTargetSet = ko.observable(undefined); self.serial_timeoutTemperatureAutoreport = ko.observable(undefined); self.serial_timeoutSdStatus = ko.observable(undefined); + self.serial_timeoutSdStatusAutoreport = ko.observable(undefined); self.serial_log = ko.observable(undefined); self.serial_additionalPorts = ko.observable(undefined); self.serial_additionalBaudrates = ko.observable(undefined); @@ -183,6 +184,7 @@ $(function() { self.serial_maxTimeoutsPrinting = ko.observable(undefined); self.serial_maxTimeoutsLong = ko.observable(undefined); self.serial_capAutoreportTemp = ko.observable(undefined); + self.serial_capAutoreportSdStatus = ko.observable(undefined); self.serial_capBusyProtocol = ko.observable(undefined); self.folder_uploads = ko.observable(undefined); diff --git a/src/octoprint/templates/dialogs/settings/serialconnection.jinja2 b/src/octoprint/templates/dialogs/settings/serialconnection.jinja2 index 39912dd897..9cfd520399 100644 --- a/src/octoprint/templates/dialogs/settings/serialconnection.jinja2 +++ b/src/octoprint/templates/dialogs/settings/serialconnection.jinja2 @@ -87,7 +87,7 @@
- +
@@ -95,6 +95,16 @@
+
+ +
+
+ + s +
+ {{ _('Autoreport interval to request from firmware')}} +
+
{{ _('Timeouts') }} @@ -243,6 +253,13 @@
+
+
+ +
+
From faa81748460f89851169140270e32b61d775f384 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Thu, 15 Mar 2018 16:08:02 +0100 Subject: [PATCH 279/333] Link to community forum & new FAQ location Also updated contribution guidelines & templates --- .github/ISSUE_TEMPLATE.md | 82 +++++++---- .github/PULL_REQUEST_TEMPLATE.md | 18 ++- CONTRIBUTING.md | 246 ++++++++++++++++++------------- README.md | 29 ++-- 4 files changed, 219 insertions(+), 156 deletions(-) diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 00bd36a44c..fdf1380d2a 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,29 +1,27 @@ + #### What were you doing? -[Please be as specific as possible here. The maintainers will need to + 1. ... 2. ... 3. ... + #### What did you expect to happen? @@ -59,50 +56,75 @@ to also include a link to a file with which to reproduce the problem.] #### Did the same happen when running OctoPrint in safe mode? -[Try to reproduce your problem in safe mode. You can find information -on how to enable safe mode in the Contribution Guidelines.] + #### Version of OctoPrint -[Can be found in the lower left corner of the web interface. ALWAYS INCLUDE.] + #### Operating System running OctoPrint -[OctoPi, Linux, Windows, MacOS, something else? With version please. -OctoPi's version can be found in /etc/octopi_version] + #### Printer model & used firmware incl. version -[If applicable, always include if unsure.] + -#### Browser and Version of Browser, Operating System running Browser +#### Browser and version of browser, operating system running browser -[If applicable, always include if unsure.] + #### Link to octoprint.log -[On gist.github.com or pastebin.com. ALWAYS INCLUDE and never truncate. -The Contribution Guidelines tell you where to find that.] + #### Link to contents of terminal tab or serial.log -[On gist.github.com or pastebin.com. If applicable, always include if unsure or + #### Link to contents of Javascript console in the browser -[On gist.github.com or pastebin.com or alternatively a screenshot. If applicable - + #### Screenshot(s)/video(s) showing the problem: -[If applicable. Always include if unsure or reporting UI issues.] + I have read the FAQ. diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 4c18b123cf..c9b4d9f224 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,3 +1,4 @@ + * [ ] Your changes are not possible to do through a plugin and relevant to a large audience (ideally all users of OctoPrint) @@ -16,12 +18,13 @@ checklist: Brainstorming ticket * [ ] Your PR targets OctoPrint's devel branch, or maintenance if it's a bug fix for an issue present in the current stable version (no PRs - against master or anything else) + against master or anything else please) * [ ] Your PR was opened from a custom branch on your repository (no PRs from your version of master, maintenance or devel please), - e.g. dev/my_new_feature + e.g. dev/my_new_feature or fix/my_bugfix * [ ] Your PR only contains relevant changes: no unrelated files, - no dead code, ideally only one commit - rebase your PR if necessary! + no dead code, ideally only one commit - rebase and squash your PR + if necessary! * [ ] Your changes follow the existing coding style * [ ] If your changes include style sheets: You have modified the .less source files, not the .css files (those are generated with @@ -32,11 +35,10 @@ checklist: nothing broke * [ ] You have added yourself to the AUTHORS.md file :) -Feel free to delete all this help text, then describe -your PR further. You may use the template provided below to do that. -The more details the better! - ----- + #### What does this PR do and why is it necessary? diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 74b8be4fb5..d730df513b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -21,45 +21,41 @@ or **[creating pull requests](#pull-requests)**. ## Issues, Tickets, however you may call them -Please read the following short instructions fully and follow them. You can +Please read the following instructions fully and follow them. You can help the project tremendously this way: not only do you help the maintainers to **address problems in a timely manner** but also keep it possible for them to **fix bugs, add new and improve on existing functionality** instead of doing nothing but ticket management. -![Ticket flow chart](http://i.imgur.com/qYSZyuw.png) +![Ticket flow chart](https://i.imgur.com/SmU7iCJ.png) -- **[Read the FAQ](https://github.com/foosel/OctoPrint/wiki/FAQ)** +- **[Read the FAQ](http://faq.octoprint.org)** - If you want to report a **bug**, [read "How to file a bug report" below](#how-to-file-a-bug-report) and *[use the provided template](#what-should-i-include-in-a-ticket)*. You do not need to do anything else with your ticket. - If you want to post a **feature request** or a **documentation request**, add `[Request]` to your issue's title (e.g. `[Request] Awesome new feature`). A question on how to run/change/setup something is **not** what qualifies as a request here, use the - [Mailinglist](https://groups.google.com/group/octoprint) or the - [Google+ Community](https://plus.google.com/communities/102771308349328485741) for + [community forum at discourse.octoprint.org](https://discourse.octoprint.org) for such support issues. - If you are a **developer** that wants to brainstorm a pull request or possible changes to the plugin system, add [Brainstorming] to your issue's title (e.g. `[Brainstorming] New plugin hook for doing some cool stuff`). - If you need **support**, have a **question** or some **other reason** that doesn't fit any of the above categories, the issue tracker is not the right place. - Consult the [Mailinglist](https://groups.google.com/group/octoprint) or the - [Google+ Community](https://plus.google.com/communities/102771308349328485741) instead. + Consult the [community forum at discourse.octoprint.org](https://discourse.octoprint.org) instead. No matter what kind of ticket you create, never mix two or more "ticket reasons" into one ticket: One ticket per bug, request, brainstorming thread please. ----- - -**Note**: A bot is in place that monitors new tickets, automatically -categorizes them and checks new bug reports for usage of the provided template. -That bot will only bother you if you open a ticket that appears to be a bug (no -`[Request]` or `[Brainstorming]` in the title) without the template, and it -will do that only to ensure that all information needed to solve the issue is -available for the maintainers to directly start tackling that problem. - ----- +> 👉 **Note** +> +> A bot is in place that monitors new tickets, automatically +> categorizes them and checks new bug reports for usage of the provided template. +> That bot will only bother you if you open a ticket that appears to be a bug (no +> `[Request]` or `[Brainstorming]` in the title) without the complete template, and it +> will do that only to ensure that all information needed to solve the issue is +> available for the maintainers to directly start tackling that problem. ## How to file a bug report @@ -72,22 +68,25 @@ following section *completely* and also follow the instructions in the ### What should I do before submitting a bug report? -1. **Make sure you are at the right location**. This is the Github repository +1. **Make sure you are at the right location**. This is the bug tracker of the official version of OctoPrint, which is the 3D print server and corresponding web interface itself. - - **This is not the Github respository of OctoPi**, which is the preconfigured + + **OctoPrint doesn't manage your network connection or your webcam nor + can it fix your printer not getting detected as a serial interface** - + if you have any kinds of problems with that, get in touch on the + [community forum](https://discourse.octoprint.org). + + **This is not the bug tracker of OctoPi**, which is the preconfigured Raspberry Pi image including OctoPrint among other things - that one can be found - [here](https://github.com/guysoft/OctoPi). Please note that while we do have - some entries regarding OctoPi in the FAQ, any bugs should be reported in the - [proper bug tracker](https://github.com/guysoft/OctoPi/issues) which - again - - is not here. + [here](https://github.com/guysoft/OctoPi). If you have any kind of specific + issue with how the OctoPi system is setup, go there please. - **This is also not the Github repository of any OctoPrint Plugins you + **This is also not the bug tracker of any OctoPrint Plugins you might have installed**. Report any issues with those in their corresponding bug tracker (probably linked to from the plugin's homepage). - Finally, **this is also not the right issue tracker if you are running a + Finally, **this is also not the right bug tracker if you are running a forked version of OctoPrint**. Seek help for such unofficial versions from the people maintaining them instead. @@ -101,7 +100,7 @@ following section *completely* and also follow the instructions in the more about safe mode in the [docs](http://docs.octoprint.org/en/master/features/safemode.html). You might also want to try the current development version of OctoPrint - (if you aren't already). Refer to the [FAQ](https://github.com/foosel/OctoPrint/wiki/FAQ) + (if you aren't already). Refer to the [FAQ](http://faq.octoprint.org) for information on how to do this. 3. The problem still exists? Then please **look through the @@ -111,8 +110,8 @@ following section *completely* and also follow the instructions in the Sorting through duplicates of the same issue sometimes causes more work than fixing it. Take the time to filter through possible duplicates and be really sure that your problem definitely is a new one. Try more than one search query - (e.g. do not only search for "webcam" if you happen to run into an issue - with your webcam, also search for "timelapse" etc). Do not only read the subject lines + (e.g. do not only search for "timelapse" if you happen to run into an issue + with your webcam, also search for "recording" etc). Do not only read the subject lines of tickets that look like they might be related, but also read the ticket itself! **Very important:** Please make absolutely sure that if you find a bug that looks like @@ -126,84 +125,151 @@ following section *completely* and also follow the instructions in the makes it more difficult due to on top of having to figure out the original problem there's now also a [red herring](https://en.wikipedia.org/wiki/Red_herring) interfering - so please be very diligent here! - + +If in doubt about any of the above - get in touch on the [community forum](https://discourse.octoprint.org) +instead of opening a ticket here. If you are actually running into a bug, we'll figure it out together +there. + ### What should I include in a bug report? First of all make sure your use **a descriptive title**. "It doesn't work" and similar unspecific complaints are NOT descriptive titles. -**Always use the following template** (please remove what's within `[...]`, that's -only provided here as some additional information for you), **even if only adding a -"me too" to an existing ticket**: +**Always use the following template, even if only adding a "me too" to an +existing ticket**: + +``` + - [Try to reproduce your problem in safe mode. You can find information - on how to enable safe mode in the Contribution Guidelines.] +#### What were you doing? - #### Version of OctoPrint + - #### Operating System running OctoPrint +1. ... +2. ... +3. ... - [OctoPi, Linux, Windows, MacOS, something else? With version please, - OctoPi's version can be found in /etc/octopi_version] + - #### Printer model & used firmware incl. version +#### What did you expect to happen? - [If applicable, always include if unsure.] +#### What happened instead? - #### Browser and Version of Browser, Operating System running Browser +#### Did the same happen when running OctoPrint in safe mode? - [If applicable, always include if unsure.] + - #### Link to contents of terminal tab or serial.log +#### Version of OctoPrint - [On gist.github.com or pastebin.com. If applicable, always include if unsure or - reporting communication issues. Never truncate. + - serial.log is usually not written due to performance reasons and must be - enabled explicitly. Provide at the very least the FULL contents of your - terminal tab at the time of the bug occurrence, even if you do not have - a serial.log.] +#### Operating System running OctoPrint - #### Link to contents of Javascript console in the browser + - [On gist.github.com or pastebin.com or alternatively a screenshot. If applicable - - always include if unsure or reporting UI issues. +#### Printer model & used firmware incl. version - The Contribution Guidelines tell you where to find that.] + - #### Screenshot(s)/video(s) showing the problem: +#### Browser and version of browser, operating system running browser - [If applicable. Always include if unsure or reporting UI issues.] + - I have read the FAQ. +#### Link to octoprint.log + + + +#### Link to contents of terminal tab or serial.log + + + +#### Link to contents of Javascript console in the browser + + + +#### Screenshot(s)/video(s) showing the problem: + + + +I have read the FAQ. +``` Copy-paste this template **completely** (or use the version that gets pre-filled into the "new issue" form). Do not skip any lines or the bot *will* complain! Provide @@ -226,35 +292,7 @@ current commit. ### Where can I find those log files you keep talking about? -OctoPrint by default provides two log outputs, a third one can be enabled if -more information is needed. - -One is contained in the **"Terminal" tab** within OctoPrint's UI and is a log -of the last 300 lines of communication with the printer. Please copy-paste -this *completely* somewhere (disable auto scroll to make copying the contents easier) - -e.g. http://pastebin.com or http://gist.github.com - and include a link in -your bug report. - -There is also **OctoPrint's application log file** or in short `octoprint.log`, -which is by default located at `~/.octoprint/logs/octoprint.log` on Linux, -`%APPDATA%\OctoPrint\logs\octoprint.log` on Windows and -`~/Library/Application Support/OctoPrint/logs/octoprint.log` on MacOS. You can -also access it directly through OctoPrint via Settings > Logs. Please -copy-paste this *completely* to pastebin or gist as well and include a link in your bug -report. - -It might happen that you are asked to provide a more **thorough log of the -communication with the printer** if you haven't already done so, the `serial.log`. -This is not written by default due to performance reasons, but you can enable -it in the settings dialog. After enabling that log, please reproduce the problem -again (connect to the printer, do whatever triggers it), then copy-paste -`~/.octoprint/logs/serial.log` (Windows: `%APPDATA%\OctoPrint\logs\serial.log`, -MacOS: `~/Library/Application Support/OctoPrint/logs/serial.log`) to pastebin -or gist and include the link in the bug report. - -You might also be asked to provide a log with an increased log level. You can -find information on how to do just that in the -[docs](http://docs.octoprint.org/en/master/configuration/logging_yaml.html). +Please refer to [this FAQ entry](https://discourse.octoprint.org/t/where-can-i-find-octoprints-log-files/299). ### Where can I find my browser's error console? @@ -445,6 +483,8 @@ the local version identifier to allow for an exact determination of the active c by OctoPrint itself and not a misbehaving plugin. * 2017-03-27: Added safe mode section to ticket template. * 2017-11-22: Added note on how to run the unit tests + * 2018-03-15: Link to new community forum and some clarifications re bug + reporting ## Footnotes * [1] - If you are wondering why, the problem is that anything that you add diff --git a/README.md b/README.md index 1571a3308b..dce169a903 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,8 @@ and released under the [GNU Affero General Public License V3](http://www.gnu.org Its website can be found at [octoprint.org](https://octoprint.org/?utm_source=github&utm_medium=readme). +The community forum available at [discourse.octoprint.org](https://discourse.octoprint.org/?utm_source=github&utm_medium=readme). + The documentation is located at [docs.octoprint.org](http://docs.octoprint.org). The official plugin repository can be reached at [plugins.octoprint.org](https://plugins.octoprint.org/?utm_source=github&utm_medium=readme). @@ -19,17 +21,17 @@ If you enjoy OctoPrint, please consider becoming a regular supporter!** You are currently looking at the source code repository of OctoPrint. If you already installed it (e.g. by using the Raspberry Pi targeted distribution [OctoPi](https://github.com/guysoft/OctoPi)) and only want to find out how to use it, [the documentation](http://docs.octoprint.org/) and [the public wiki](https://github.com/foosel/OctoPrint/wiki) -might be of more interest for you. You might also want to subscribe to [the mailing list](https://groups.google.com/group/octoprint) -or the [G+ Community](https://plus.google.com/communities/102771308349328485741) where there are other active users who might be +might be of more interest for you. You might also want to subscribe to join +[the community forum at discourse.octoprint.org](https://discourse.octoprint.org) where there are other active users who might be able to help you with any questions you might have. ## Contributing Contributions of all kinds are welcome, not only in the form of code but also with regards to the -[official documentation](http://docs.octoprint.org/) or [the public wiki](https://github.com/foosel/OctoPrint/wiki), support -of other users in the [bug tracker](https://github.com/foosel/OctoPrint/issues), -[the Mailinglist](https://groups.google.com/group/octoprint) or -[the G+ Community](https://plus.google.com/communities/102771308349328485741) and also [financially](https://octoprint.org/support-octoprint/?utm_source=github&utm_medium=readme). +[official documentation](http://docs.octoprint.org/) or [the public wiki](https://github.com/foosel/OctoPrint/wiki), debugging help +in the [bug tracker](https://github.com/foosel/OctoPrint/issues), support of other users on +[the community forum at discourse.octoprint.org](https://discourse.octoprint.org) +and also [financially](https://octoprint.org/support-octoprint/?utm_source=github&utm_medium=readme). If you think something is bad about OctoPrint or its documentation the way it is, please help in any way to make it better instead of just complaining about it -- this is an Open Source Project @@ -54,23 +56,20 @@ you already have Python 2.7, pip and virtualenv set up on your system: 1. Checkout OctoPrint: `git clone https://github.com/foosel/OctoPrint.git` 2. Change into the OctoPrint folder: `cd OctoPrint` 3. Create a user-owned virtual environment therein: `virtualenv venv` -4. Install OctoPrint *into that virtual environment*: `./venv/bin/python setup.py install` +4. Install OctoPrint *into that virtual environment*: `./venv/bin/pip install .` You may then start the OctoPrint server via `/path/to/OctoPrint/venv/bin/octoprint`, see [Usage](#usage) for details. After installation, please make sure you follow the first-run wizard and set up -access control as necessary. If you want to not only be notified about new -releases, but also be able to automatically upgrade to them from within -OctoPrint, take a look [at the documentation of the Software Update Plugin](https://github.com/foosel/OctoPrint/wiki/Plugin:-Software-Update#making-octoprint-updateable-on-existing-installations) -and at its settings. +access control as necessary. ## Dependencies OctoPrint depends on a few python modules to do its job. Those are automatically installed when installing -OctoPrint via `setup.py`: +OctoPrint via `pip`: - python setup.py install + pip install . You should do this every time after pulling from the repository, since the dependencies may have changed. @@ -78,9 +77,9 @@ OctoPrint currently only supports Python 2.7. ## Usage -Running the `setup.py` script via +Running the pip install via - python setup.py install + pip install . installs the `octoprint` script in your Python installation's scripts folder (which, depending on whether you installed OctoPrint globally or into a virtual env, will be in your `PATH` or not). The From 61769262f6d1e3724b5aaed0019186444cf8d3ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Thu, 15 Mar 2018 16:10:17 +0100 Subject: [PATCH 280/333] New meta git prefix for github meta md updates --- .versioneer-lookup | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.versioneer-lookup b/.versioneer-lookup index 7fd57865ed..f3f420a29b 100644 --- a/.versioneer-lookup +++ b/.versioneer-lookup @@ -7,8 +7,9 @@ # The file is processed from top to bottom, the first matching line wins. If or are left out, # the lookup table does not apply to the matched branches -# master and rc shall not use the lookup table, only tags +# master, meta, rc and prerelease shall not use the lookup table, only tags master +meta/.* rc/.* prerelease From 01f5a58709dd99d3d5b18f2d7d24a939dd44fe42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Thu, 15 Mar 2018 16:20:33 +0100 Subject: [PATCH 281/333] Fix a typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index dce169a903..05481b5cab 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ and released under the [GNU Affero General Public License V3](http://www.gnu.org Its website can be found at [octoprint.org](https://octoprint.org/?utm_source=github&utm_medium=readme). -The community forum available at [discourse.octoprint.org](https://discourse.octoprint.org/?utm_source=github&utm_medium=readme). +The community forum is available at [discourse.octoprint.org](https://discourse.octoprint.org/?utm_source=github&utm_medium=readme). The documentation is located at [docs.octoprint.org](http://docs.octoprint.org). From e8d5449a8fe1f549f8b950058082a6873c6bfe3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Thu, 15 Mar 2018 16:30:54 +0100 Subject: [PATCH 282/333] Adjust issue template for new GH layout Apparently the "big yellow box" hinting the contribution guidelines vanished some time ago and got replaced by a teeny tiny link to the right under "Helpful resources". Ah well. --- .github/ISSUE_TEMPLATE.md | 10 ++++------ CONTRIBUTING.md | 10 ++++------ 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index fdf1380d2a..a17754fad5 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,10 +1,8 @@ #### What were you doing? - #### What were you doing? - #### Version of OctoPrint From 46a0c3057f47c126bd1dd1702a27c32a78dd83f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Mon, 19 Mar 2018 10:11:38 +0100 Subject: [PATCH 291/333] Logging: At missing CSS file Thanks @kantlivelong for the heads-up --- src/octoprint/plugins/logging/static/css/logging.css | 1 + 1 file changed, 1 insertion(+) create mode 100644 src/octoprint/plugins/logging/static/css/logging.css diff --git a/src/octoprint/plugins/logging/static/css/logging.css b/src/octoprint/plugins/logging/static/css/logging.css new file mode 100644 index 0000000000..31603a3408 --- /dev/null +++ b/src/octoprint/plugins/logging/static/css/logging.css @@ -0,0 +1 @@ +table td.settings_logs_name,table th.settings_logs_name{text-overflow:ellipsis;text-align:left}table td.settings_logs_size,table th.settings_logs_size{text-align:right;width:70px}table td.settings_logs_date,table th.settings_logs_date{text-align:left;width:130px}table td.settings_logs_action,table th.settings_logs_action{width:70px;text-align:center;white-space:nowrap}table td.settings_logs_action a,table th.settings_logs_action a{text-decoration:none;color:#000}table td.settings_logs_action a.disabled,table th.settings_logs_action a.disabled{color:#ccc;cursor:default} \ No newline at end of file From 3e24b68b4529c907b42cc9a34a82472955983d24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Mon, 19 Mar 2018 10:35:24 +0100 Subject: [PATCH 292/333] Attach additionalClasses to all custom controls Fixes #2489 --- src/octoprint/static/js/app/viewmodels/control.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/octoprint/static/js/app/viewmodels/control.js b/src/octoprint/static/js/app/viewmodels/control.js index 0722054146..3eaba58e3f 100644 --- a/src/octoprint/static/js/app/viewmodels/control.js +++ b/src/octoprint/static/js/app/viewmodels/control.js @@ -226,7 +226,7 @@ $(function() { } } - if (control.hasOwnProperty("command") && !control.hasOwnProperty("additionalClasses")) { + if (!control.hasOwnProperty("additionalClasses")) { control.additionalClasses = ""; } From 2ecbd9f8c8c1063e8167187a86eb2a4742e629ba Mon Sep 17 00:00:00 2001 From: Daniele Forsi Date: Sat, 17 Mar 2018 19:52:39 +0100 Subject: [PATCH 293/333] Fix typos in docs --- docs/api/general.rst | 4 ++-- docs/api/printer.rst | 4 ++-- docs/api/timelapse.rst | 4 ++-- docs/api/util.rst | 4 ++-- docs/bundledplugins/logging.rst | 2 +- docs/bundledplugins/softwareupdate.rst | 16 ++++++++-------- docs/configuration/config_yaml.rst | 2 +- docs/events/index.rst | 2 +- docs/features/accesscontrol.rst | 2 +- docs/features/plugins.rst | 2 +- docs/jsclientlib/index.rst | 4 ++-- docs/jsclientlib/job.rst | 2 +- docs/jsclientlib/languages.rst | 2 +- docs/jsclientlib/printer.rst | 2 +- docs/jsclientlib/system.rst | 4 ++-- docs/jsclientlib/timelapse.rst | 2 +- docs/jsclientlib/users.rst | 2 +- docs/jsclientlib/util.rst | 4 ++-- docs/jsclientlib/wizard.rst | 2 +- docs/plugins/concepts.rst | 6 +++--- docs/plugins/gettingstarted.rst | 4 ++-- docs/plugins/helpers.rst | 2 +- docs/plugins/hooks.rst | 8 ++++---- docs/plugins/injectedproperties.rst | 2 +- docs/plugins/mixins.rst | 2 +- docs/plugins/viewmodels.rst | 6 +++--- 26 files changed, 48 insertions(+), 48 deletions(-) diff --git a/docs/api/general.rst b/docs/api/general.rst index 91e6735714..4232a96c95 100644 --- a/docs/api/general.rst +++ b/docs/api/general.rst @@ -12,7 +12,7 @@ Authorization ============= OctoPrint's API expects an API key to be supplied with each request. This API key can be either the globally -configured one, a user specific one if "Access Control" is enabled or an ref:`App Session Key `. +configured one, a user specific one if "Access Control" is enabled or an :ref:`App Session Key `. Users are able to generate and revoke their custom API key via the "Change password" dialog. The API key must be supplied in the custom HTTP header ``X-Api-Key``, e.g. @@ -65,7 +65,7 @@ Please be advised that clients should use the header field variant if at all pos Content Type ============ -If not otherwise stated OctoPrint's API expects request bodies and issues response bodies as ``Content-Type: application/json``. +If not otherwise stated, OctoPrint's API expects request bodies and issues response bodies as ``Content-Type: application/json``. .. _sec-api-general-encoding: diff --git a/docs/api/printer.rst b/docs/api/printer.rst index 23d002afe5..1409bf3000 100644 --- a/docs/api/printer.rst +++ b/docs/api/printer.rst @@ -217,7 +217,7 @@ Issue a print head command * ``z``: Optional. Amount/coordinate to jog print head on z axis, must be a valid number corresponding to the distance to travel in mm. * ``absolute``: Optional. Boolean value specifying whether to move relative to current position (provided axes values are relative amounts) or to absolute position (provided axes values are coordinates) - * ``speed``: Optiona. Speed at which to move. If not provided, minimum speed for all selected axes from printer + * ``speed``: Optional. Speed at which to move. If not provided, minimum speed for all selected axes from printer profile will be used. If provided but ``false``, no speed parameter will be appended to the command. Otherwise interpreted as an integer signifying the speed in mm/s, to append to the command. @@ -227,7 +227,7 @@ Issue a print head command * ``axes``: A list of axes which to home, valid values are one or more of ``x``, ``y``, ``z``. feedrate - Changes the feedrate factor to apply to the movement's of the axes. + Changes the feedrate factor to apply to the movements of the axes. * ``factor``: The new factor, percentage as integer or float (percentage divided by 100) between 50 and 200%. diff --git a/docs/api/timelapse.rst b/docs/api/timelapse.rst index 914e43490b..54b3668ec9 100644 --- a/docs/api/timelapse.rst +++ b/docs/api/timelapse.rst @@ -128,7 +128,7 @@ Rendered timelapse * - ``date`` - 1 - string - - Formatted timestamp of the the timelapse creation date + - Formatted timestamp of the timelapse creation date * - ``url`` - 1 - string @@ -162,7 +162,7 @@ Unrendered timelapse * - ``date`` - 1 - string - - Formatted timestamp of the the timelapse job creation date + - Formatted timestamp of the timelapse job creation date * - ``recording`` - 1 - bool diff --git a/docs/api/util.rst b/docs/api/util.rst index e01664d201..ca1e46333a 100644 --- a/docs/api/util.rst +++ b/docs/api/util.rst @@ -38,10 +38,10 @@ Test paths or URLs .. _sec-api-util-test-url: url - Tests whether a provided url responds. Request method and expected status codes can + Tests whether a provided URL responds. Request method and expected status codes can optionally be specified as well. Supported parameters are: - * ``url``: The url to test. Mandatory. + * ``url``: The URL to test. Mandatory. * ``method``: The request method to use for the test. Optional, defaults to ``HEAD``. * ``timeout``: A timeout for the request, in seconds. If no reply from the tested URL has been received within this time frame, the check will be considered a failure. Optional, defaults to 3 seconds. diff --git a/docs/bundledplugins/logging.rst b/docs/bundledplugins/logging.rst index 67e0a923ed..d06d7915c6 100644 --- a/docs/bundledplugins/logging.rst +++ b/docs/bundledplugins/logging.rst @@ -198,7 +198,7 @@ JS Client Library .. note:: - All methods here require that the used API token or a the existing browser session + All methods here require that the used API token or the existing browser session has admin rights. .. js:function:: OctoPrintClient.plugins.logging.listLogs(opts) diff --git a/docs/bundledplugins/softwareupdate.rst b/docs/bundledplugins/softwareupdate.rst index b8909ee23e..de4896b6d5 100644 --- a/docs/bundledplugins/softwareupdate.rst +++ b/docs/bundledplugins/softwareupdate.rst @@ -46,7 +46,7 @@ There you can adjust the following settings: for the various components registered with the plugin, so that they don't have to be queried from the internet every time you load the page. Defaults to 24h, you usually shouldn't need to change that value. -More settings are available by :ref:`editing the correspondi.. _section in config.yaml `. +More settings are available by :ref:`editing the corresponding section in config.yaml `. That restart commands for OctoPrint and the whole server can be configured under Settings > Server. @@ -56,9 +56,9 @@ Command line usage ------------------ The functionality of the Software Update Plugin is also available on OctoPrint's command line interface under the -``plugins`` sub command. It's is possible to check for updates via ``octoprint plugins softwareupdate:check`` -and to apply available updates via ``octoprint plugins softwareupdate:update``. Please the corresponding -``--help`` pages on details: +``plugins`` sub command. It's possible to check for updates via ``octoprint plugins softwareupdate:check`` +and to apply available updates via ``octoprint plugins softwareupdate:update``. Please see the corresponding +``--help`` pages for details: .. code-block:: none @@ -126,7 +126,7 @@ Configuring the Plugin checks: # "octoprint" is reserved for OctoPrint octoprint: - # this defines an version check that will check against releases + # this defines a version check that will check against releases # published on OctoPrint's Github repository and pip as update method # against the release archives on Github - this is the default type: github_release @@ -135,7 +135,7 @@ Configuring the Plugin method: pip pip: 'https://github.com/foosel/OctoPrint/archive/{target_version}.zip' - # further checks may be define here + # further checks may be defined here # pip command, if another one than the automatically detected one should be # used - should normally NOT be necessary and hence set @@ -209,7 +209,7 @@ Version checks :ref:`hook `. Additional config parameters: - * ``python_checker``: (mandatory) A python callable which returns version + * ``python_checker``: (mandatory) A Python callable which returns version information and whether the current version is up-to-date or not, see below for details. @@ -241,7 +241,7 @@ Update methods ``update_folder`` setting or if the ``git_commit`` check is used its ``checkout_folder`` setting. * ``python_updater``: Can only be specified by plugins through the - :ref:`hook `. A python callable + :ref:`hook `. A Python callable which performs the update, see below for details. * ``sleep_a_bit``: Does nothing but block for a configurable ``duration`` and log a countdown in the meantime. Useful for debugging software update mechanisms diff --git a/docs/configuration/config_yaml.rst b/docs/configuration/config_yaml.rst index 7205ee1498..790702fd15 100644 --- a/docs/configuration/config_yaml.rst +++ b/docs/configuration/config_yaml.rst @@ -1107,7 +1107,7 @@ OctoPrint is running is allowed to do this without password entry: command: sudo shutdown -h now confirm: You are about to shutdown the system. -You can also add an divider by setting action to divider like this: +You can also add a divider by setting action to divider like this: .. code-block:: yaml diff --git a/docs/events/index.rst b/docs/events/index.rst index 7086ccecdc..1935742473 100644 --- a/docs/events/index.rst +++ b/docs/events/index.rst @@ -265,7 +265,7 @@ MetadataAnalysisFinished * ``name``: the file's name * ``path``: the file's path within its storage location * ``origin``: the file's origin storage location - * ``result``: the analysis result -- this is a python object currently only available for internal use + * ``result``: the analysis result -- this is a Python object currently only available for internal use .. deprecated:: 1.3.0 diff --git a/docs/features/accesscontrol.rst b/docs/features/accesscontrol.rst index dd59bd6b85..3207b1c42a 100644 --- a/docs/features/accesscontrol.rst +++ b/docs/features/accesscontrol.rst @@ -39,7 +39,7 @@ Rerunning the wizard -------------------- In case Access Control was disabled in the configuration wizard, it is -possibly to re-run it by editing ``config.yaml`` [#f1]_ and setting ``firstRun`` +possible to re-run it by editing ``config.yaml`` [#f1]_ and setting ``firstRun`` in the ``server`` section and ``enabled`` in the ``accessControl`` section to ``true``: diff --git a/docs/features/plugins.rst b/docs/features/plugins.rst index 2530f78171..4500747135 100644 --- a/docs/features/plugins.rst +++ b/docs/features/plugins.rst @@ -46,7 +46,7 @@ Manual Installation If you don't want or can't use the Plugin Manager, plugins may also be installed manually either by copying and unpacking them into one of the configured plugin folders (regularly those are ``/plugins`` and -``/plugins`` [#f1]_ or by installing them as regular python modules via ``pip`` [#f2]_. +``/plugins`` [#f1]_ or by installing them as regular Python modules via ``pip`` [#f2]_. For a plugin available on the Python Package Index (PyPi), the process is as simple as issuing a diff --git a/docs/jsclientlib/index.rst b/docs/jsclientlib/index.rst index 8fcc6ee181..cf4f203766 100644 --- a/docs/jsclientlib/index.rst +++ b/docs/jsclientlib/index.rst @@ -32,7 +32,7 @@ correct URL prefix: --> -Regardless of which way you use to include the library, you'll also need to make sure you included JQuery and lodash, +Regardless of which way you use to include the library, you'll also need to make sure you included jQuery and lodash, because the library depends on those to be available (as ``$`` and ``_``). You can embed those like this: .. code-block:: html+jinja @@ -45,7 +45,7 @@ number of components, make sure to at the very least include that one to be able When you import the client library as described above, a global variable ``OctoPrint`` will become available, which is a prepared instance of the ``OctoPrintClient`` class the library assembles from registered components. You can directly -used that singular ``OctoPrint`` instance if you only need to talk to one OctoPrint server: +use that singular ``OctoPrint`` instance if you only need to talk to one OctoPrint server: .. code-block:: javascript diff --git a/docs/jsclientlib/job.rst b/docs/jsclientlib/job.rst index eed5ac8081..834b67f70b 100644 --- a/docs/jsclientlib/job.rst +++ b/docs/jsclientlib/job.rst @@ -64,7 +64,7 @@ .. js:function:: OctoPrintClient.job.resume(opts) - Resumes the current job if it's currently pause, does nothing if it's running. + Resumes the current job if it's currently paused, does nothing if it's running. See :ref:`Issue a job command ` for details. diff --git a/docs/jsclientlib/languages.rst b/docs/jsclientlib/languages.rst index 5e187525fd..f13b6430be 100644 --- a/docs/jsclientlib/languages.rst +++ b/docs/jsclientlib/languages.rst @@ -5,7 +5,7 @@ .. note:: - All methods here require that the used API token or a the existing browser session + All methods here require that the used API token or the existing browser session has admin rights. .. js:function:: OctoPrintClient.languages.list(opts) diff --git a/docs/jsclientlib/printer.rst b/docs/jsclientlib/printer.rst index b4e107d45d..14df29834b 100644 --- a/docs/jsclientlib/printer.rst +++ b/docs/jsclientlib/printer.rst @@ -174,7 +174,7 @@ Sets the given temperature on the printer's heated bed (if available). - ``target`` is expected to be a the target temperature as a float value. + ``target`` is expected to be the target temperature as a float value. **Example:** diff --git a/docs/jsclientlib/system.rst b/docs/jsclientlib/system.rst index 7099771412..a82b89f294 100644 --- a/docs/jsclientlib/system.rst +++ b/docs/jsclientlib/system.rst @@ -5,7 +5,7 @@ .. note:: - All methods here require that the used API token or a the existing browser session + All methods here require that the used API token or the existing browser session has admin rights. .. js:function:: OctoPrintClient.system.getCommands(opts) @@ -35,4 +35,4 @@ .. seealso:: :ref:`System API ` - Documentation of the underlying system API + Documentation of the underlying system API. diff --git a/docs/jsclientlib/timelapse.rst b/docs/jsclientlib/timelapse.rst index a16b8ec9ad..351483ceb3 100644 --- a/docs/jsclientlib/timelapse.rst +++ b/docs/jsclientlib/timelapse.rst @@ -15,7 +15,7 @@ .. js:function:: OctoPrintClient.timelapse.list(opts) - Get the lists of rendered and unrendered timelapses. The returned promis + Get the lists of rendered and unrendered timelapses. The returned promise will be resolved with an object containing the properties ``rendered`` which will have the list of rendered timelapses, and ``unrendered`` which will have the list of unrendered timelapses. diff --git a/docs/jsclientlib/users.rst b/docs/jsclientlib/users.rst index a07ac7cf9c..dd11adb193 100644 --- a/docs/jsclientlib/users.rst +++ b/docs/jsclientlib/users.rst @@ -5,7 +5,7 @@ .. note:: - Most methods here require that the used API token or a the existing browser session + Most methods here require that the used API token or the existing browser session has admin rights *or* corresponds to the user to be modified. Some methods definitely require admin rights. diff --git a/docs/jsclientlib/util.rst b/docs/jsclientlib/util.rst index 4569d1d794..f88062ee7f 100644 --- a/docs/jsclientlib/util.rst +++ b/docs/jsclientlib/util.rst @@ -5,7 +5,7 @@ .. note:: - All methods here require that the used API token or a the existing browser session + All methods here require that the used API token or the existing browser session has admin rights. .. js:function:: OctoPrintClient.util.test(command, parameters, opts) @@ -206,4 +206,4 @@ .. seealso:: :ref:`Util API ` - Documentation of the underlying util API + Documentation of the underlying util API. diff --git a/docs/jsclientlib/wizard.rst b/docs/jsclientlib/wizard.rst index 12a0cad251..90de044d07 100644 --- a/docs/jsclientlib/wizard.rst +++ b/docs/jsclientlib/wizard.rst @@ -5,7 +5,7 @@ .. note:: - All methods here require that the used API token or a the existing browser session + All methods here require that the used API token or the existing browser session has admin rights. .. js:function:: OctoPrintClient.wizard.get(opts) diff --git a/docs/plugins/concepts.rst b/docs/plugins/concepts.rst index ceaf3acaa7..5e1c624cf2 100644 --- a/docs/plugins/concepts.rst +++ b/docs/plugins/concepts.rst @@ -23,9 +23,9 @@ Lifecycle There are three sources of installed plugins that OctoPrint will check during start up: - * it's own ``octoprint/plugins`` folder (this is where the bundled plugins reside), + * its own ``octoprint/plugins`` folder (this is where the bundled plugins reside), * the ``plugins`` folder in its configuration directory (e.g. ``~/.octoprint/plugins`` on Linux), - * any python packages registered for the entry point ``octoprint.plugin``. + * any Python packages registered for the entry point ``octoprint.plugin``. Each plugin that OctoPrint finds it will first load, then enable. On enabling a plugin, OctoPrint will register its declared :ref:`hook handlers ` and :ref:`helpers `, apply @@ -36,7 +36,7 @@ any :ref:`settings overlays .py ``~/.octoprint/plugins`` folder. You already know how that works. But let's say you have more than just a simple plugin that can be done in one file. Distributing multiple files and getting your users to install them in the right way so that OctoPrint will be able to actually find and load them is certainly not impossible, but we want to do it in the -best way possible, meaning we want to make our plugin a fully installable python module that your users will be able to +best way possible, meaning we want to make our plugin a fully installable Python module that your users will be able to install directly via `OctoPrint's built-in Plugin Manager `_ or alternatively manually utilizing Python's standard package manager ``pip`` directly. @@ -282,7 +282,7 @@ of information now defined twice: plugin_version = "1.0.0" plugin_description = "A quick \"Hello World\" example plugin for OctoPrint" -The nice thing about our plugin now being a proper python package is that OctoPrint can and will access the metadata defined +The nice thing about our plugin now being a proper Python package is that OctoPrint can and will access the metadata defined within ``setup.py``! So, we don't really need to define all this data twice. Remove ``__plugin_name__``, ``__plugin_version__`` and ``__plugin_description__`` from ``__init__.py``: diff --git a/docs/plugins/helpers.rst b/docs/plugins/helpers.rst index 17cd182897..c411d37764 100644 --- a/docs/plugins/helpers.rst +++ b/docs/plugins/helpers.rst @@ -8,7 +8,7 @@ system. They are registered with the OctoPrint plugin system through the use of An example for providing some helper functions to the system can be found in the `Discovery Plugin `_, -which provides it's SSDP browsing and Zeroconf browsing and publishing functions as helper methods. +which provides its SSDP browsing and Zeroconf browsing and publishing functions as helper methods. .. code-block:: python :linenos: diff --git a/docs/plugins/hooks.rst b/docs/plugins/hooks.rst index 6843e1d012..c20d482eaf 100644 --- a/docs/plugins/hooks.rst +++ b/docs/plugins/hooks.rst @@ -608,7 +608,7 @@ octoprint.comm.protocol.gcode.received .. py:function:: gcode_received_hook(comm_instance, line, *args, **kwargs) Get the returned lines sent by the printer. Handlers should return the received line or in any case, the modified - version of it. If the the handler returns None, processing will be aborted and the communication layer will get an + version of it. If the handler returns None, processing will be aborted and the communication layer will get an empty string as the received line. Note that Python functions will also automatically return ``None`` if an empty ``return`` statement is used or just nothing is returned explicitly from the handler. @@ -896,7 +896,7 @@ octoprint.printer.factory If the factory returns anything but ``None``, it will be assigned to the global ``printer`` instance. - If no of the registered factories return a printer instance, the default :class:`~octoprint.printer.standard.Printer` + If none of the registered factories return a printer instance, the default :class:`~octoprint.printer.standard.Printer` class will be instantiated. :param dict components: System components to use for printer instance initialization @@ -998,7 +998,7 @@ octoprint.server.http.routes that allows delivery of the requested resource as attachment and access validation through an optional callback. :class:`~octoprint.server.util.tornado.UrlForwardHandler` `tornado.web.RequestHandler `_ that proxies - requests to a preconfigured url and returns the response. + requests to a preconfigured URL and returns the response. :param list server_routes: read-only list of the currently configured server routes :return: a list of 3-tuples with additional routes as defined above @@ -1141,7 +1141,7 @@ octoprint.users.factory If the factory returns anything but ``None``, it will be assigned to the global ``userManager`` instance. - If no of the registered factories return a user manager instance, the class referenced by the ``config.yaml`` + If none of the registered factories return a user manager instance, the class referenced by the ``config.yaml`` entry ``accessControl.userManager`` will be initialized if possible, otherwise a stock :class:`~octoprint.users.FilebasedUserManager` will be instantiated, linked to the default user storage file ``~/.octoprint/users.yaml``. diff --git a/docs/plugins/injectedproperties.rst b/docs/plugins/injectedproperties.rst index 0e063773a4..7b4bf311aa 100644 --- a/docs/plugins/injectedproperties.rst +++ b/docs/plugins/injectedproperties.rst @@ -51,7 +51,7 @@ An overview of these properties follows. .. seealso:: :class:`~octoprint.plugin.core.Plugin` and :class:`~octoprint.plugin.types.OctoPrintPlugin` - Class documentation also containing the properties shared among all mixing implementations. + Class documentation also containing the properties shared among all mixin implementations. :ref:`Available Mixins ` Some mixin types trigger the injection of additional properties. diff --git a/docs/plugins/mixins.rst b/docs/plugins/mixins.rst index 95bf3e5cf9..fa73c1757f 100644 --- a/docs/plugins/mixins.rst +++ b/docs/plugins/mixins.rst @@ -153,7 +153,7 @@ An overview of these properties can be found in the section :ref:`Injected Prope .. seealso:: :class:`~octoprint.plugin.core.Plugin` and :class:`~octoprint.plugin.types.OctoPrintPlugin` - Class documentation also containing the properties shared among all mixing implementations. + Class documentation also containing the properties shared among all mixin implementations. .. _sec-plugins-mixins-available: diff --git a/docs/plugins/viewmodels.rst b/docs/plugins/viewmodels.rst index 584b745ba4..b21d07e1fa 100644 --- a/docs/plugins/viewmodels.rst +++ b/docs/plugins/viewmodels.rst @@ -125,7 +125,7 @@ gcodeFilesViewModel logViewModel View model for the logfile settings dialog. loginStateViewModel - View model for the current loginstate of the user, very interesting for plugins that need to + View model for the current login state of the user, very interesting for plugins that need to evaluate the current login state or information about the current user, e.g. associated roles. navigationViewModel View model for the navigation bar. @@ -168,7 +168,7 @@ OctoPrint's web application will call several callbacks on all registered view m Those are listed below: onStartup() - Called when the first initialization has been done: All view models are constructed and hence their dependencies + Called when the first initialization has been done. All view models are constructed and hence their dependencies resolved, no bindings have been done yet. onBeforeBinding() @@ -210,7 +210,7 @@ onDataUpdaterPluginMessage(plugin, message) Called when a plugin message is pushed from the server with the identifier of the calling plugin as first and the actual message as the second parameter. Note that the latter might be a full fledged object, depending on the plugin sending the message. You can use this method to asynchronously push data from your plugin's server - component to it's frontend component. + component to its frontend component. onUserLoggedIn(user) Called when a user gets logged into the web app, either passively (upon initial load of the page due to a valid From c04fade9a77f80cf134b3afe2b25587f8d67ce94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Mon, 19 Mar 2018 10:46:20 +0100 Subject: [PATCH 294/333] docs: Fix two issues pointed out by @dforsi Thanks! --- docs/api/system.rst | 2 +- docs/plugins/hooks.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/api/system.rst b/docs/api/system.rst index 9bf0907ff3..b39e0a1ad1 100644 --- a/docs/api/system.rst +++ b/docs/api/system.rst @@ -139,7 +139,7 @@ Execute a registered system command .. http:post:: /api/system/commands/(string:source)/(string:action) - Execute the system command ``action`` on defined in ``source``. + Execute the system command ``action`` defined in ``source``. **Example** diff --git a/docs/plugins/hooks.rst b/docs/plugins/hooks.rst index c20d482eaf..18d63f75f7 100644 --- a/docs/plugins/hooks.rst +++ b/docs/plugins/hooks.rst @@ -851,7 +851,7 @@ octoprint.filemanager.preprocessor ``file_object`` will be a subclass of :class:`~octoprint.filemanager.util.AbstractFileWrapper`. Handlers may access the raw data of the file via :func:`~octoprint.filemanager.util.AbstractFileWrapper.stream`, e.g. - to wrap it further. Handlers which do not wish to handle the `file_object` + to wrap it further. Handlers which do not wish to handle the `file_object` should just return it untouched. **Example** From 709f7a76f10198b94695d00aa382b62660907d9c Mon Sep 17 00:00:00 2001 From: Daniele Forsi Date: Sun, 18 Mar 2018 18:13:17 +0100 Subject: [PATCH 295/333] Remove unused or duplicated imports Fixes the following pylint's warnings: W: 9, 0: Unused import io (unused-import) W: 33, 1: Unused walk imported from os (unused-import) W:308, 4: Reimport 're' (imported line 16) (reimported) W: 17, 1: Reimport 'os' (imported line 8) (reimported) --- src/octoprint/_version.py | 1 - src/octoprint/daemon.py | 2 +- src/octoprint/timelapse.py | 4 ++-- src/octoprint_setuptools/__init__.py | 1 - 4 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/octoprint/_version.py b/src/octoprint/_version.py index ee94065844..9c9eeab62f 100644 --- a/src/octoprint/_version.py +++ b/src/octoprint/_version.py @@ -305,7 +305,6 @@ def git_parse_lookup_file(path): if not os.path.exists(path): return [] - import re lookup = [] with open(path, "r") as f: for line in f: diff --git a/src/octoprint/daemon.py b/src/octoprint/daemon.py index b6888271b0..6b42e1d885 100644 --- a/src/octoprint/daemon.py +++ b/src/octoprint/daemon.py @@ -6,7 +6,7 @@ """ from __future__ import absolute_import, division, print_function -import sys, os, time, signal, io +import sys, os, time, signal class Daemon: """ diff --git a/src/octoprint/timelapse.py b/src/octoprint/timelapse.py index d18fb2d9af..4019654e3e 100644 --- a/src/octoprint/timelapse.py +++ b/src/octoprint/timelapse.py @@ -29,9 +29,9 @@ import re try: - from os import scandir, walk + from os import scandir except ImportError: - from scandir import scandir, walk + from scandir import scandir # currently configured timelapse diff --git a/src/octoprint_setuptools/__init__.py b/src/octoprint_setuptools/__init__.py index 6913bc55c7..1fb8b99097 100644 --- a/src/octoprint_setuptools/__init__.py +++ b/src/octoprint_setuptools/__init__.py @@ -14,7 +14,6 @@ def package_data_dirs(source, sub_folders): - import os dirs = [] for d in sub_folders: From 8cdae335a2a8f21d7daad6d565c7a6c089bc377d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Mon, 19 Mar 2018 10:50:56 +0100 Subject: [PATCH 296/333] Fix typo in issue template --- .github/ISSUE_TEMPLATE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index e362979384..c65e40fb28 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -61,7 +61,7 @@ on how to enable safe mode in the Contribution Guidelines. If you can't reproduce in safe mode, this is a bug with one of your installed third party plugins. Don't open a ticket here! -If you can't text this in safe mode, state why. +If you can't test this in safe mode, state why. --> #### Version of OctoPrint From 8a3a87efac04e9d004b915dd3072524fcc390f26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Mon, 19 Mar 2018 13:21:09 +0100 Subject: [PATCH 297/333] Extract firmware safety check into bundled plugin --- docs/bundledplugins/index.rst | 1 + docs/bundledplugins/printer_safety_check.rst | 43 +++++++ src/octoprint/events.py | 1 + .../plugins/printer_safety_check/__init__.py | 110 ++++++++++++++++++ .../static/css/printer_safety_check.css | 1 + .../static/js/printer_safety_check.js | 78 +++++++++++++ .../static/less/printer_safety_check.less | 15 +++ .../printer_safety_check_sidebar.jinja2 | 7 ++ src/octoprint/printer/__init__.py | 8 ++ src/octoprint/printer/standard.py | 24 ++-- src/octoprint/settings.py | 2 +- .../static/js/app/viewmodels/printerstate.js | 11 -- src/octoprint/static/less/octoprint.less | 23 ---- src/octoprint/templates/sidebar/state.jinja2 | 7 -- src/octoprint/util/comm.py | 25 +--- 15 files changed, 275 insertions(+), 81 deletions(-) create mode 100644 docs/bundledplugins/printer_safety_check.rst create mode 100644 src/octoprint/plugins/printer_safety_check/__init__.py create mode 100644 src/octoprint/plugins/printer_safety_check/static/css/printer_safety_check.css create mode 100644 src/octoprint/plugins/printer_safety_check/static/js/printer_safety_check.js create mode 100644 src/octoprint/plugins/printer_safety_check/static/less/printer_safety_check.less create mode 100644 src/octoprint/plugins/printer_safety_check/templates/printer_safety_check_sidebar.jinja2 diff --git a/docs/bundledplugins/index.rst b/docs/bundledplugins/index.rst index 53516474c4..bd862b005a 100644 --- a/docs/bundledplugins/index.rst +++ b/docs/bundledplugins/index.rst @@ -11,4 +11,5 @@ Bundled Plugins discovery.rst logging.rst pluginmanager.rst + printer_safety_check.rst softwareupdate.rst diff --git a/docs/bundledplugins/printer_safety_check.rst b/docs/bundledplugins/printer_safety_check.rst new file mode 100644 index 0000000000..89974de481 --- /dev/null +++ b/docs/bundledplugins/printer_safety_check.rst @@ -0,0 +1,43 @@ +.. _sec-bundledplugins-printer_safety_check: + +Printer Safety Check +==================== + +The Printer Safety Check plugin comes bundled with OctoPrint starting with version 1.3.7. + +It tries to identify printers or rather printer firmwares with known safety issues, such as +disabled thermal runaway protection, and displays a warning box to logged in users on +such identification. + +.. _fig-bundledplugins-printer_safety_check-example: +.. figure:: ../images/bundledplugins-printer_safety_check-example.png + :align: center + :alt: Printer Safety Check warning example + + An example of a warning generated by the Printer Safety Check + +Currently the following firmwares produce this warning: + + * Anet A8 stock firmware + +If you know of further printers/printer firmwares that need to be added here, please +`get in touch on the forum and provide their response to an M115 `_. + +.. seealso:: + + `Entry on the "unsafe firmware" warning in OctoPrint's FAQ `_ + +.. note:: + + Feel free to disable the plugin in OctoPrint's Plugin Manager if you feel like it is unnecessary. Be advised though + that even if your printer might be running totally fine with a known unsafe configuration, that might change + unexpectedly and with `catastrophic `_ + `results `_. + +.. _sec-bundledplugins-printer_safety_check-source: + +Source Code +----------- + +The source of the Printer Safety Check plugin is bundled with OctoPrint and can be +found in its source repository under ``src/octoprint/plugins/printer_safety_check``. diff --git a/src/octoprint/events.py b/src/octoprint/events.py index 476e6d1825..4b2e2a12bd 100644 --- a/src/octoprint/events.py +++ b/src/octoprint/events.py @@ -88,6 +88,7 @@ class Events(object): EJECT = "Eject" E_STOP = "EStop" POSITION_UPDATE = "PositionUpdate" + FIRMWARE_DATA = "FirmwareData" TOOL_CHANGE = "ToolChange" REGISTERED_MESSAGE_RECEIVED = "RegisteredMessageReceived" diff --git a/src/octoprint/plugins/printer_safety_check/__init__.py b/src/octoprint/plugins/printer_safety_check/__init__.py new file mode 100644 index 0000000000..431892cf65 --- /dev/null +++ b/src/octoprint/plugins/printer_safety_check/__init__.py @@ -0,0 +1,110 @@ +# coding=utf-8 +from __future__ import absolute_import, division, print_function + +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' +__copyright__ = "Copyright (C) 2018 The OctoPrint Project - Released under terms of the AGPLv3 License" + +import octoprint.plugin + +from octoprint.events import Events +from octoprint.server import user_permission + +import flask +from flask_babel import gettext + +import textwrap + +TERMINAL_SAFETY_WARNING = """ +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +{message} + +Learn more at https://faq.octoprint.org/warning-{warning_type} +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +""" + +SAFETY_CHECKS = { + "firmware-unsafe": (lambda name, data: name and name.lower().startswith("anet_a8_"),) +} + +class PrinterSafetyCheckPlugin(octoprint.plugin.AssetPlugin, + octoprint.plugin.EventHandlerPlugin, + octoprint.plugin.SimpleApiPlugin, + octoprint.plugin.TemplatePlugin): + + # noinspection PyMissingConstructor + def __init__(self): + self._warnings = dict() + + ##~~ TemplatePlugin API + + def get_template_configs(self): + return [ + dict(type="sidebar", + name=gettext("Printer Safety Warning"), + data_bind="visible: printerState.isOperational() && loginState.isAdmin() && warnings().length > 0", + icon="exclamation-triangle", + styles_wrapper=["display: none"]) + ] + + ##~~ AssetPlugin API + + def get_assets(self): + return dict(js=("js/printer_safety_check.js",), + css=("css/printer_safety_check.css",), + less=("less/printer_safety_check.less",)) + + ##~~ EventHandlerPlugin API + + def on_event(self, event, payload): + if event == Events.FIRMWARE_DATA: + self._analyze_firmware_data(payload.get("name"), payload.get("data")) + elif event == Events.DISCONNECTED: + self._reset_warnings() + + ##~~ SimpleApiPlugin API + + def on_api_get(self, request): + if not user_permission.can(): + return flask.make_response("Insufficient rights", 403) + return flask.jsonify(self._warnings) + + ##~~ Helpers + + def _analyze_firmware_data(self, name, data): + changes = False + + for warning_type, checks in SAFETY_CHECKS.items(): + if any(x(name, data) for x in checks): + message = u"Your printer's firmware is known to lack mandatory safety features (e.g. " \ + u"thermal runaway protection). This is a fire risk." + self._log_to_terminal(TERMINAL_SAFETY_WARNING.format(message="\n".join(textwrap.wrap(message, 75)), + warning_type=warning_type)) + self._warnings[warning_type] = message + changes = True + + if changes: + self._ping_clients() + + def _reset_warnings(self): + self._warnings.clear() + self._ping_clients() + + def _log_to_terminal(self, message): + if self._printer: + lines = message.split("\n") + self._printer.log_lines(*lines) + + def _ping_clients(self): + self._plugin_manager.send_plugin_message(self._identifier, dict(type="update")) + +__plugin_name__ = "Printer Safety Check" +__plugin_author__ = "Gina Häußge" +__plugin_url__ = "http://docs.octoprint.org/en/master/bundledplugins/printer_safety_check.html" +__plugin_description__ = "Checks for unsafe printers/printer firmwares" +__plugin_disabling_discouraged__ = gettext("Without this plugin OctoPrint will no longer be able to " + "check if the printer it is connected to has a known safety" + "issue and inform you about that fact.") +__plugin_license__ = "AGPLv3" +__plugin_implementation__ = PrinterSafetyCheckPlugin() + diff --git a/src/octoprint/plugins/printer_safety_check/static/css/printer_safety_check.css b/src/octoprint/plugins/printer_safety_check/static/css/printer_safety_check.css new file mode 100644 index 0000000000..b806ecf6a7 --- /dev/null +++ b/src/octoprint/plugins/printer_safety_check/static/css/printer_safety_check.css @@ -0,0 +1 @@ +#sidebar_plugin_printer_safety_check_wrapper{border:1px solid #b94a48}#sidebar_plugin_printer_safety_check_wrapper .accordion-heading{background-color:#b94a48;font-weight:700;color:#fff}#sidebar_plugin_printer_safety_check_wrapper .accordion-heading .icon-black,#sidebar_plugin_printer_safety_check_wrapper .accordion-heading a,#sidebar_plugin_printer_safety_check_wrapper .accordion-heading a:active,#sidebar_plugin_printer_safety_check_wrapper .accordion-heading a:hover,#sidebar_plugin_printer_safety_check_wrapper .accordion-heading a:visited{color:#fff} \ No newline at end of file diff --git a/src/octoprint/plugins/printer_safety_check/static/js/printer_safety_check.js b/src/octoprint/plugins/printer_safety_check/static/js/printer_safety_check.js new file mode 100644 index 0000000000..c69bf8eb67 --- /dev/null +++ b/src/octoprint/plugins/printer_safety_check/static/js/printer_safety_check.js @@ -0,0 +1,78 @@ +(function (global, factory) { + if (typeof define === "function" && define.amd) { + define(["OctoPrintClient"], factory); + } else { + factory(global.OctoPrintClient); + } +})(this, function(OctoPrintClient) { + var OctoPrintPrinterSafetyCheckClient = function(base) { + this.base = base; + }; + + OctoPrintPrinterSafetyCheckClient.prototype.get = function(opts) { + return this.base.get(this.base.getSimpleApiUrl("printer_safety_check"), opts); + }; + + OctoPrintClient.registerPluginComponent("printer_safety_check", OctoPrintPrinterSafetyCheckClient); + return OctoPrintPrinterSafetyCheckClient; +}); + +$(function() { + function PrinterSafetyCheckViewModel(parameters) { + var self = this; + + self.loginState = parameters[0]; + self.printerState = parameters[1]; + + self.warnings = ko.observableArray([]); + + self.requestData = function() { + if (!self.loginState.isUser()) { + self.warnings([]); + return; + } + + OctoPrint.plugins.printer_safety_check.get() + .done(self.fromResponse) + .fail(function() { + self.warnings([]); + }); + }; + + self.fromResponse = function(data) { + var warnings = []; + _.each(data, function(message, warning_type) { + warnings.push({type: warning_type, message: message}); + }); + self.warnings(warnings); + }; + + self.onStartup = function() { + self.requestData(); + }; + + self.onUserLoggedIn = function() { + self.requestData(); + }; + + self.onUserLoggedOut = function() { + self.requestData(); + }; + + self.onDataUpdaterPluginMessage = function(plugin, data) { + if (plugin !== "printer_safety_check") return; + if (!data.hasOwnProperty("type")) return; + + if (data.type === "update") { + self.requestData(); + } + } + } + + OCTOPRINT_VIEWMODELS.push({ + construct: PrinterSafetyCheckViewModel, + dependencies: ["loginStateViewModel", "printerStateViewModel"], + elements: ["#sidebar_plugin_printer_safety_check_wrapper"] + }); +}); + diff --git a/src/octoprint/plugins/printer_safety_check/static/less/printer_safety_check.less b/src/octoprint/plugins/printer_safety_check/static/less/printer_safety_check.less new file mode 100644 index 0000000000..3dc124f1bf --- /dev/null +++ b/src/octoprint/plugins/printer_safety_check/static/less/printer_safety_check.less @@ -0,0 +1,15 @@ +#sidebar_plugin_printer_safety_check_wrapper { + border: 1px solid #b94a48; + + .accordion-heading { + background-color: #b94a48; + font-weight: bold; + color: #fff; + a, a:hover, a:visited, a:active { + color: #fff; + } + .icon-black { + color: #fff; + } + } +} diff --git a/src/octoprint/plugins/printer_safety_check/templates/printer_safety_check_sidebar.jinja2 b/src/octoprint/plugins/printer_safety_check/templates/printer_safety_check_sidebar.jinja2 new file mode 100644 index 0000000000..a09f768128 --- /dev/null +++ b/src/octoprint/plugins/printer_safety_check/templates/printer_safety_check_sidebar.jinja2 @@ -0,0 +1,7 @@ +

{{ _('Warning!') }}

+ +

+
+ {{ _('Learn more...') }} +

+ diff --git a/src/octoprint/printer/__init__.py b/src/octoprint/printer/__init__.py index 89dc75c718..7c52b13881 100644 --- a/src/octoprint/printer/__init__.py +++ b/src/octoprint/printer/__init__.py @@ -430,6 +430,14 @@ def cancel_print(self, tags=None, *args, **kwargs): """ raise NotImplementedError() + def log_lines(self, *lines): + """ + Logs the provided lines to the printer log and serial.log + Args: + *lines: the lines to log + """ + pass + def get_state_string(self, *args, **kwargs): """ Returns: diff --git a/src/octoprint/printer/standard.py b/src/octoprint/printer/standard.py index aa058687ca..c7f27492de 100644 --- a/src/octoprint/printer/standard.py +++ b/src/octoprint/printer/standard.py @@ -120,8 +120,7 @@ def __init__(self, fileManager, analysisQueue, printerProfileManager): printTimeLeft=None, printTimeOrigin=None), current_z=None, - offsets=self._dict(), - warnings=self._dict() + offsets=self._dict() ) eventManager().subscribe(Events.METADATA_ANALYSIS_FINISHED, self._on_event_MetadataAnalysisFinished) @@ -532,6 +531,12 @@ def cancel_print(self, *args, **kwargs): # for further processing self._comm.cancelPrint(tags=kwargs.get("tags", set()) | {"trigger:printer.cancel_print"}) + def log_lines(self, *lines): + serial_logger = logging.getLogger("SERIAL") + self.on_comm_log("\n".join(lines)) + for line in lines: + serial_logger.debug(line) + def get_state_string(self, state=None, *args, **kwargs): if self._comm is None: return "Offline" @@ -1067,7 +1072,6 @@ def log_print(): self._setCurrentZ(None) self._setJobData(None, None, None) self._setOffsets(None) - self._stateMonitor.set_warnings(None) self._addTemperatureData() self._printerProfileManager.deselect() eventManager().fire(Events.DISCONNECTED) @@ -1256,9 +1260,6 @@ def on_comm_record_fileposition(self, origin, name, pos): except: self._logger.exception("Error while trying to persist print recovery data") - def on_comm_set_warnings(self, warnings): - self._stateMonitor.set_warnings(self._dict(warnings)) - def _payload_for_print_job_event(self, location=None, print_job_file=None, print_job_size=None, position=None): if print_job_file is None: with self._selectedFileMutex: @@ -1315,7 +1316,6 @@ def __init__(self, interval=0.5, on_update=None, on_add_temperature=None, on_add self._current_z = None self._offsets = dict() self._progress = None - self._warnings = dict() self._progress_dirty = False @@ -1333,13 +1333,12 @@ def _get_current_progress(self): return self._on_get_progress() return self._progress - def reset(self, state=None, job_data=None, progress=None, current_z=None, offsets=None, warnings=None): + def reset(self, state=None, job_data=None, progress=None, current_z=None, offsets=None): self.set_state(state) self.set_job_data(job_data) self.set_progress(progress) self.set_current_z(current_z) self.set_temp_offsets(offsets) - self.set_warnings(warnings) def add_temperature(self, temperature): self._on_add_temperature(temperature) @@ -1353,10 +1352,6 @@ def add_message(self, message): self._on_add_message(message) self._change_event.set() - def set_warnings(self, warnings): - self._warnings = warnings - self._change_event.set() - def set_current_z(self, current_z): self._current_z = current_z self._change_event.set() @@ -1414,8 +1409,7 @@ def get_current_data(self): "job": self._job_data, "currentZ": self._current_z, "progress": self._progress, - "offsets": self._offsets, - "warnings": self._warnings + "offsets": self._offsets } diff --git a/src/octoprint/settings.py b/src/octoprint/settings.py index e3600772a1..28bae863d8 100644 --- a/src/octoprint/settings.py +++ b/src/octoprint/settings.py @@ -280,7 +280,7 @@ def settings(init=False, basedir=None, configfile=None): "components": { "order": { "navbar": ["settings", "systemmenu", "plugin_announcements", "login"], - "sidebar": ["connection", "state", "files"], + "sidebar": ["plugin_printer_safety_check", "connection", "state", "files"], "tab": ["temperature", "control", "gcodeviewer", "terminal", "timelapse"], "settings": [ "section_printer", "serial", "printerprofiles", "temperatures", "terminalfilters", "gcodescripts", diff --git a/src/octoprint/static/js/app/viewmodels/printerstate.js b/src/octoprint/static/js/app/viewmodels/printerstate.js index e88c635c43..9028291c9b 100644 --- a/src/octoprint/static/js/app/viewmodels/printerstate.js +++ b/src/octoprint/static/js/app/viewmodels/printerstate.js @@ -39,8 +39,6 @@ $(function() { self.sd = ko.observable(undefined); self.timelapse = ko.observable(undefined); - self.warnings = ko.observableArray([]); - self.busyFiles = ko.observableArray([]); self.filament = ko.observableArray([]); @@ -187,7 +185,6 @@ $(function() { self._processProgressData(data.progress); self._processZData(data.currentZ); self._processBusyFiles(data.busyFiles); - self._processWarnings(data.warnings); }; self._processStateData = function(data) { @@ -275,14 +272,6 @@ $(function() { self.busyFiles(busyFiles); }; - self._processWarnings = function(data) { - var warnings = []; - _.each(data, function(message, key) { - warnings.push({type: key, message: message}); - }); - self.warnings(warnings); - }; - self.print = function() { if (self.isPaused()) { showConfirmationDialog({ diff --git a/src/octoprint/static/less/octoprint.less b/src/octoprint/static/less/octoprint.less index 7b973313b4..afc82f0987 100644 --- a/src/octoprint/static/less/octoprint.less +++ b/src/octoprint/static/less/octoprint.less @@ -638,29 +638,6 @@ ul.dropdown-menu li a { hr { margin: 5px 0; } - - .alert { - margin-bottom: 5px; - - a { - text-decoration: underline; - } - } - - .alert a { - color: #c09853; - } - .alert-error a, - .alert-danger a { - color: #b94a48; - } - .alert-success a { - color: #468847; - } - .alert-info a { - color: #3a87ad; - } - } /** GCODE file manager */ diff --git a/src/octoprint/templates/sidebar/state.jinja2 b/src/octoprint/templates/sidebar/state.jinja2 index 0c2e43c076..c961ba07fe 100644 --- a/src/octoprint/templates/sidebar/state.jinja2 +++ b/src/octoprint/templates/sidebar/state.jinja2 @@ -1,10 +1,3 @@ -
-
- {{ _('Warning!') }} -
- {{ _('Learn more...') }} -
-
{{ _('State') }}:

{{ _('File') }}:  (SD)
diff --git a/src/octoprint/util/comm.py b/src/octoprint/util/comm.py index cc7834aed8..b24fc53109 100644 --- a/src/octoprint/util/comm.py +++ b/src/octoprint/util/comm.py @@ -19,7 +19,6 @@ from past.builtins import basestring import logging -import textwrap import serial @@ -199,16 +198,6 @@ def restart(self, duration): regex_resend_linenumber = re.compile("(N|N:)?(?P%s)" % regex_int_pattern) """Regex to use for request line numbers in resend requests""" -SAFETY_WARNING = """ -!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -{message} - -Learn more at https://faq.octoprint.org/warning-{key} -!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - -""" - - def serialList(): baselist=[] if os.name=="nt": @@ -444,8 +433,6 @@ def __init__(self, port = None, baudrate=None, callbackObject=None, printerProfi self._toolBeforeHeatup = None self._knownInvalidTools = set() - self._warnings = dict() - self._long_running_command = False self._heating = False self._dwelling_until = False @@ -1691,14 +1678,7 @@ def convert_line(line): if name and "malyan" in name.lower() and ver: firmware_name = name.strip() + " " + ver.strip() - # special handling for dangerous firmware without obligatory safety features - if firmware_name and firmware_name.lower().startswith("anet_a8"): - key = "firmware-unsafe" - message = "Your printer's firmware is known to lack mandatory safety features (e.g. " \ - "thermal runaway protection). This is a fire risk." - self._log(SAFETY_WARNING.format(message="\n".join(textwrap.wrap(message, 75)), key=key)) - self._warnings[key] = message - self._callback.on_comm_set_warnings(self._warnings) + eventManager().fire(Events.FIRMWARE_DATA, dict(name=firmware_name, data=data)) if not self._firmware_info_received and firmware_name: firmware_name = firmware_name.strip() @@ -3392,9 +3372,6 @@ def on_comm_force_disconnect(self): def on_comm_record_fileposition(self, origin, name, pos): pass - def on_comm_set_warnings(self, warnings): - pass - ### Printing file information classes ################################################################################## class PrintingFileInformation(object): From da2c9632ea54fd1b3c8ae34f8f26bab29dbaf3c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Mon, 19 Mar 2018 13:46:19 +0100 Subject: [PATCH 298/333] Preparing release of 1.3.7rc1 --- CHANGELOG.md | 75 + README.md | 2 + SUPPORTERS.md | 16 +- .../translations/de/LC_MESSAGES/messages.mo | Bin 125354 -> 125348 bytes .../translations/de/LC_MESSAGES/messages.po | 1645 +++++++++------- translations/de/LC_MESSAGES/messages.mo | Bin 125354 -> 125348 bytes translations/de/LC_MESSAGES/messages.po | 1663 ++++++++++------- translations/messages.pot | 1468 ++++++++------- 8 files changed, 2826 insertions(+), 2043 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a325b975ee..490503c4eb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,80 @@ # OctoPrint Changelog +## 1.3.7rc1 (2018-03-19) + +### Improvements + + * [#324](https://github.com/foosel/OctoPrint/issues/324) & [#2414](https://github.com/foosel/OctoPrint/issues/2414) - Native support for the following [@ commands](http://docs.octoprint.org/en/maintenance/features/atcommands.html): `@pause` (pauses the print), `@resume` (resumes the print), `@cancel` or `@abort` (cancels the print). More commands can be added through the plugin hooks [`octoprint.comm.protocol.atcommand.*`](http://docs.octoprint.org/en/maintenance/plugins/hooks.html#octoprint-comm-protocol-atcommand-phase). + * [#2208](https://github.com/foosel/OctoPrint/issues/2208) - New plugin hook [`octoprint.comm.protocol.gcode.error`](http://docs.octoprint.org/en/maintenance/plugins/hooks.html#octoprint-comm-protocol-gcode-error) to allow plugins to override OctoPrint's handling of `Error:`/`!!` messages reported by the firmware. Can be used to "downgrade" errors that aren't actually fatal errors that make the printer halt. + * [#2213](https://github.com/foosel/OctoPrint/issues/2213) - Made sure to prevent accidental configuration of a temperature cutoff value less than 1min for the temperature graph. + * [#2250](https://github.com/foosel/OctoPrint/pull/2250) - Added [`octoprint.util.ResettableTimer`](http://docs.octoprint.org/en/maintenance/modules/util.html#octoprint.util.ResettableTimer) helper class. + * [#2287](https://github.com/foosel/OctoPrint/issues/2287) - Added confirmation for attempting to disconnect during an ongoing print. See also [#2466](https://github.com/foosel/OctoPrint/pull/2466). + * [#2302](https://github.com/foosel/OctoPrint/issues/2302) - Detect invalid tools as reported by firmware and blacklist them for `T` commands. + * [#2317](https://github.com/foosel/OctoPrint/issues/2317) - Removed capturing of post roll images for time based timelapses, was causing too much confusion and surprise. + * [#2335](https://github.com/foosel/OctoPrint/issues/2335) - First throw at detecting if a print from the printer's SD was started outside of OctoPrint via the printer's control panel. Requires some specific requirements to be fulfilled by the printer's firmware to function properly: + * Firmware must send a "File opened: ..." message on start of the print + * Firmware must respond to an immediately sent `M27` with `SD printing byte /` + * Firmware must stay responsive during ongoing print to allow for regular M27 polls (or push those automatically) or M25 to pause/cancel the print through OctoPrint. + * [#2362](https://github.com/foosel/OctoPrint/issues/2362) - Added option to configure timelapse snapshot timeout. + * [#2367](https://github.com/foosel/OctoPrint/issues/2367) - Added support for `//action:cancel` action command. + * [#2378](https://github.com/foosel/OctoPrint/issues/2378) - Made GCODE viewer gracefully handle GCODE subcodes. + * [#2385](https://github.com/foosel/OctoPrint/pull/2385) - Made valid boolean trues check case insensitive. + * [#2304](https://github.com/foosel/OctoPrint/pull/2304) & [#2405](https://github.com/foosel/OctoPrint/pull/2405) - Added support to trust Basic Authentication headers for user login. Currently requires [manual configuration through `config.yaml`](http://docs.octoprint.org/en/maintenance/configuration/config_yaml.html#access-control), see the `accessControl.trustBasicAuthentication` and `accessControl.checkBasicAuthenticationPassword` settings. + * [#2338](https://github.com/foosel/OctoPrint/pull/2338) - Allowed plugins to define GCODE script variables using the `octoprint.comm.protocol.scripts` hook. + * [#2406](https://github.com/foosel/OctoPrint/pull/2406) - Extracted log management into its own bundled plugin and allow fine tuning of log levels. + * [#2409](https://github.com/foosel/OctoPrint/pull/2409) - Added `m4v` and `mkv` to the list of accepted timelapse extensions. + * [#2444](https://github.com/foosel/OctoPrint/pull/2444) - Support additional CSS classes on custom control buttons. + * [#2448](https://github.com/foosel/OctoPrint/issues/2448) - Also detect plugins in `~/.octoprint/plugins` that are provided as bytecode `pyc` instead of source `py` files. + * [#2455](https://github.com/foosel/OctoPrint/issues/2455) - Added option to configure SSL validation for snapshot URL. + * Support `M114` response format of RepRapFirmware (uses `X:... Y:... Z:... E0:... E1:...` instead of +`X:... Y:... Z:... E:...`) + * Added refresh button to connection panel for easy refresh of the available ports to connect to without having to reload the whole page. + * Increased upper bound of PySerial dependency from 2.7 to 3.4. See also [#2333](https://github.com/foosel/OctoPrint/issues/2333). + * Switch to lower communication timeouts if support of the `busy` protocol by the firmware is detected. + * Refactored serial settings dialog, now has sub tabs and more explanations. + * Allow to disable support for certain firmware capabilities, even if reported as supported by the firmware: `busy` protocol, temperature auto reporting, SD status auto reporting + * Attached tags to GCODE commands moving through the comm layer and allowed plugins to access and extend these tags through the GCODE hooks. Makes it possible for plugins to detect the origin of a command (streamed file vs. GCODE script vs. user input vs. plugin), the point where it entered the system and so on. Also added a new logger `octoprint.util.comm.command_phases` that if set to `DEBUG` will log the lifecycle of commands through the comm layer including tags to `octoprint.log`. See also [the docs here](http://docs.octoprint.org/en/maintenance/plugins/hooks.html#octoprint-comm-protocol-gcode-phase). + * Added new [`job_on_hold`](http://docs.octoprint.org/en/maintenance/modules/printer.html#octoprint.printer.PrinterInterface.job_on_hold) and [`set_job_on_hold`](http://docs.octoprint.org/en/maintenance/modules/printer.html#octoprint.printer.PrinterInterface.set_job_on_hold) methods on the printer instance, allowing plugins to quickly stall the streaming of a queue. This should be used sparingly - abuse will have significant negative effects on the print job. Read the docs if you plan to utilize this. + * Support extraction of plugin metadata even from disabled and blacklisted plugins through the AST of the module. + * Better logging of printer callback errors and utilization of `frozendict` for internal printer state propagation in an attempt to narrow down on [#1951](https://github.com/foosel/OctoPrint/issues/1951). Should `frozendict` cause issues it can be disabled through the settings in `config.yaml`, just set `devel.useFrozenDictForPrinterState` to `false` + * Log comm state changes to `octoprint.log`. + * Added a regular server heartbeat to the log (every 15min). + * Support SD status auto report by the firmware. + * Added `Not SD printing` to default "SD status" terminal filters. + * Added custom `readline` implementation for the serial port. Instead of relying on PySerial's `readline` that depending on the installed version might only read from the port one byte at the time, we now wait for one byte and then read everything available into a persistent buffer which then is used to fetch lines from. + * Ensure that `callViewModelIf` doesn't try to call null or uncallable `method`s. + * Added links to the new [Community Forum](https://discourse.octoprint.org) and the new location of the [FAQ](https://faq.octoprint.org). + * Improved pip utility logging, `LocalPipHelper` wasn't producing output, making it hard to debug such non writable install directories. + * Added a new bundled plugin "Printer Safety Check" that will try to identify printer/printer firmware with known safety issues such as missing thermal runaway protection. + * Plugin Manager: Reduce notification spam. See also [#2260](https://github.com/foosel/OctoPrint/issues/2260). + * Software Update Plugin: Better detection if an update is already running. + * Software Update Plugin: Refer to `plugin_softwareupdate_console.log` on update errors in log and notification. + +### Bugfixes + + * [#2294](https://github.com/foosel/OctoPrint/issues/2294) - Improved resilience against errors during gathering the file list (e.g. permission errors) + * [#2296](https://github.com/foosel/OctoPrint/issues/2296) - Fixed `X-Forwarded-For` handling in Flask Login through monkey patched backport. + * [#2297](https://github.com/foosel/OctoPrint/issues/2297) - Return `403` instead of `401` on missing/insufficient credentials. + * [#2311](https://github.com/foosel/OctoPrint/issues/2311) - Fixed server not auto connecting on startup if port is set to `AUTO` (see also [#2311](https://github.com/foosel/OctoPrint/pull/2337)). + * [#2316](https://github.com/foosel/OctoPrint/issues/2316) - Check for valid queue entry in file analysis queue before trying to dequeue, fix for a HTTP `500` on upload of a file not supported for analysis. + * [#2321](https://github.com/foosel/OctoPrint/issues/2321) & [#2449](https://github.com/foosel/OctoPrint/issues/2449) - Fixed wrong queuing order of cancel script & first line from printed file on quick start-cancel-start scenarios by introducing a two new states "Cancelling" and "Pausing". + * [#2324](https://github.com/foosel/OctoPrint/pull/2324) - Fixed 500 error when + * [#2333](https://github.com/foosel/OctoPrint/issues/2333) (Part 1/3) - Workaround for an update problem caused by interaction of `pip` with dependencies formerly installed as eggs through `python setup.py install`. + * [#2333](https://github.com/foosel/OctoPrint/issues/2333) (Part 2/3) - If supported by the underlying PySerial version, cancel all reads and writes on disconnect if possible for a faster connection release. Part 3 (forcing an upgrade of PySerial to 3.4 so this should work in more cases) will in OctoPrint 1.3.8. + * [#2364](https://github.com/foosel/OctoPrint/issues/2364) - Fixed firmware error reporting in case of cancelling a print due to a firmware error. + * [#2368](https://github.com/foosel/OctoPrint/issues/2368) - Fixed for incorrect handling of unicode filenames in cura slicer profiles + * [#2371](https://github.com/foosel/OctoPrint/issues/2371) - Removed "just now"/"gerade eben" label from temperature graph to work around a graph resize issue caused by that. + * [#2392](https://github.com/foosel/OctoPrint/issues/2392) - Fixed "Copy all" on terminal tab only working the first time. + * [#2406](https://github.com/foosel/OctoPrint/pull/2406) - Fixed `showTab` JS function of about dialog. + * [#2426](https://github.com/foosel/OctoPrint/issues/2426) - Made resend logging to `octoprint.log` unicode safe + * [#2442](https://github.com/foosel/OctoPrint/issues/2442) - Don't even import disabled plugins, use a dummy entry for them just like for backlisted plugins. More resilience against misbehaving plugins. + * [#2461](https://github.com/foosel/OctoPrint/issues/2461) - Fixed OctoPrint not properly disconnecting in case of a firmware error. + * Announcement Plugin: Fixed a missing line break in case of more than three announcements in a notification. + * Docs: Documentation for printer profile related bits and pieces + * Docs: Various fixes of typos and grammar. + +([Commits](https://github.com/foosel/OctoPrint/compare/1.3.6...1.3.7rc1)) + ## 1.3.6 (2017-12-12) ### Note for upgraders and plugin authors: Change in the bundling of JS assets can lead to issues in plugins diff --git a/README.md b/README.md index 05481b5cab..b44dfe19e1 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,8 @@ Its website can be found at [octoprint.org](https://octoprint.org/?utm_source=gi The community forum is available at [discourse.octoprint.org](https://discourse.octoprint.org/?utm_source=github&utm_medium=readme). +The FAQ can be accessed by following [faq.octoprint.org](https://faq.octoprint.org/?utm_source=github&utm_medium=readme). + The documentation is located at [docs.octoprint.org](http://docs.octoprint.org). The official plugin repository can be reached at [plugins.octoprint.org](https://plugins.octoprint.org/?utm_source=github&utm_medium=readme). diff --git a/SUPPORTERS.md b/SUPPORTERS.md index 29c55a3e39..2690df2f64 100644 --- a/SUPPORTERS.md +++ b/SUPPORTERS.md @@ -11,12 +11,10 @@ thanks to everyone who contributed! * Andrew Moorby * Arnljot Arntsen * BEEVERYCREATIVE - * Boris Hussein * Brad Jackson * Brian E. Tyler * Christian Petropolis * COLLE+McVOY - * CreativeTools * D Brian Kimmel * DeltaMaker 3D Printers * E3D BigBox @@ -24,19 +22,18 @@ thanks to everyone who contributed! * F. Kunsmann * Frank Sander * Gary Deen - * Gary N McKinney - * George Robles * günter weber * James Seigel - * Jason Lawrence * Jeff Moe + * Jefferson Hunt + * Jeremiah Avery * Josh Daniels * Kaile Riser * Kale Stedman * Kazuhiro Ogura + * Kodama Inc. * Makespace Madrid - * Marcus Ackermann - * Mark Walker + * Martin Beattie * Michael Aumock * Miles Flavel * mitchell hirsch @@ -44,8 +41,8 @@ thanks to everyone who contributed! * Noe Ruiz * Patrick McGinnis * Peter Schmehl - * PRINT3Dforum.com * Randy C. Will + * Robert Gusek * Roger Strolz * Roy Cortes * Samer Najia @@ -55,7 +52,6 @@ thanks to everyone who contributed! * Sven Mueller * Symbiotic Devices * Thomas Hatley - * Timeshell.ca * Trent Shumay -and 1062 more wonderful people pledging on the [Patreon campaign](https://patreon.com/foosel)! \ No newline at end of file +and 1188 more wonderful people pledging on the [Patreon campaign](https://patreon.com/foosel)! \ No newline at end of file diff --git a/src/octoprint/translations/de/LC_MESSAGES/messages.mo b/src/octoprint/translations/de/LC_MESSAGES/messages.mo index 700ea0115f3bcec4324de3b9f5211e40e233041b..f779144bdcd5d7286c930131bb90613094b86da1 100644 GIT binary patch delta 23384 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 delta 24295 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�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 z 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 diff --git a/src/octoprint/translations/de/LC_MESSAGES/messages.po b/src/octoprint/translations/de/LC_MESSAGES/messages.po index 8220bcd44b..3fb9207a20 100644 --- a/src/octoprint/translations/de/LC_MESSAGES/messages.po +++ b/src/octoprint/translations/de/LC_MESSAGES/messages.po @@ -7,8 +7,8 @@ msgid "" msgstr "" "Project-Id-Version: OctoPrint\n" "Report-Msgid-Bugs-To: i18n@octoprint.org\n" -"POT-Creation-Date: 2017-12-05 12:29+0100\n" -"PO-Revision-Date: 2017-12-05 12:31+0100\n" +"POT-Creation-Date: 2018-03-16 13:18+0100\n" +"PO-Revision-Date: 2018-03-16 14:07+0100\n" "Last-Translator: Gina Häußge \n" "Language: de\n" "Language-Team: German (http://www.transifex.com/projects/p/octoprint/language/de/)\n" @@ -46,12 +46,12 @@ msgid "You can edit your announcement subscriptions under Settings > Announcemen msgstr "Du kannst deine Benachrichtigungsabonnements unter Einstellungen > Benachrichtigungen konfigurieren." #: src/octoprint/plugins/announcements/static/js/announcements.js:271 -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:957 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:973 msgid "Later" msgstr "Später" #: src/octoprint/plugins/announcements/static/js/announcements.js:277 -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:964 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:980 msgid "Mark read" msgstr "Gelesen" @@ -81,15 +81,16 @@ msgstr "Konfigurierte Kanäle" #: src/octoprint/plugins/cura/templates/snippets/settings/cura/profileImporter.jinja2:27 #: src/octoprint/plugins/cura/templates/snippets/settings/cura/profiles.jinja2:3 #: src/octoprint/plugins/cura/templates/snippets/settings/cura/profiles.jinja2:9 +#: src/octoprint/plugins/logging/templates/logging_settings.jinja2:5 +#: src/octoprint/plugins/logging/templates/logging_settings.jinja2:11 +#: src/octoprint/plugins/logging/templates/logging_settings.jinja2:50 #: src/octoprint/plugins/pluginmanager/templates/pluginmanager_settings.jinja2:43 #: src/octoprint/templates/dialogs/settings/accesscontrol.jinja2:4 -#: src/octoprint/templates/dialogs/settings/logs.jinja2:6 -#: src/octoprint/templates/dialogs/settings/logs.jinja2:12 #: src/octoprint/templates/dialogs/settings/terminalfilters.jinja2:3 #: src/octoprint/templates/snippets/settings/printerprofiles/profileEditorGeneral.jinja2:3 #: src/octoprint/templates/snippets/settings/printerprofiles/profiles.jinja2:4 -#: src/octoprint/templates/tabs/timelapse.jinja2:130 -#: src/octoprint/templates/tabs/timelapse.jinja2:175 +#: src/octoprint/templates/tabs/timelapse.jinja2:121 +#: src/octoprint/templates/tabs/timelapse.jinja2:166 msgid "Name" msgstr "Name" @@ -362,6 +363,7 @@ msgstr "Bitte stelle sicher, dass die folgenden Einstellungen deinem Drucker ent #: src/octoprint/plugins/corewizard/templates/corewizard_printerprofile_wizard.jinja2:7 #: src/octoprint/plugins/cura/templates/cura_settings.jinja2:1 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:3 #: src/octoprint/templates/snippets/settings/printerprofiles/profileEditor.jinja2:9 #: src/octoprint/templates/tabs/control.jinja2:114 msgid "General" @@ -446,22 +448,22 @@ msgid "

\n" msgstr "

Um Snapshots zu Zeitrafferaufnahmen zu rendern muss OctoPrint auch den korrekten Pfad zu FFMPEG wissen.

" #: src/octoprint/plugins/cura/static/js/cura.js:234 -#: src/octoprint/static/js/app/viewmodels/settings.js:357 +#: src/octoprint/static/js/app/viewmodels/settings.js:387 msgid "The path doesn't exist" msgstr "Der Pfad existiert nicht" #: src/octoprint/plugins/cura/static/js/cura.js:236 -#: src/octoprint/static/js/app/viewmodels/settings.js:359 +#: src/octoprint/static/js/app/viewmodels/settings.js:389 msgid "The path is not a file" msgstr "Der Pfad ist keine Datei" #: src/octoprint/plugins/cura/static/js/cura.js:238 -#: src/octoprint/static/js/app/viewmodels/settings.js:361 +#: src/octoprint/static/js/app/viewmodels/settings.js:391 msgid "The path is not an executable" msgstr "Der Pfad ist nicht ausführbar" #: src/octoprint/plugins/cura/static/js/cura.js:241 -#: src/octoprint/static/js/app/viewmodels/settings.js:364 +#: src/octoprint/static/js/app/viewmodels/settings.js:394 msgid "The path is valid" msgstr "Der Pfad ist valide" @@ -597,15 +599,15 @@ msgid "Confirm" msgstr "Bestätigen" #: src/octoprint/plugins/cura/templates/snippets/settings/cura/profiles.jinja2:3 -#: src/octoprint/templates/dialogs/settings/logs.jinja2:6 +#: src/octoprint/plugins/logging/templates/logging_settings.jinja2:5 msgid "Sort by" msgstr "Sortieren" #: src/octoprint/plugins/cura/templates/snippets/settings/cura/profiles.jinja2:3 +#: src/octoprint/plugins/logging/templates/logging_settings.jinja2:5 #: src/octoprint/plugins/pluginmanager/templates/pluginmanager_settings.jinja2:142 -#: src/octoprint/templates/dialogs/settings/logs.jinja2:6 #: src/octoprint/templates/sidebar/files_header.jinja2:6 -#: src/octoprint/templates/tabs/timelapse.jinja2:108 +#: src/octoprint/templates/tabs/timelapse.jinja2:99 msgid "ascending" msgstr "aufsteigend" @@ -622,6 +624,64 @@ msgstr "Profil entfernen" msgid "Without this plugin your OctoPrint instance will no longer be discoverable on the network via Bonjour and uPnP." msgstr "Ohne dieses Plugin wird deine OctoPrint Instanz nicht mehr in deinem Netzwerk mittels Bonjour oder uPnP automatisch auffindbar sein." +#: src/octoprint/plugins/logging/templates/logging_settings.jinja2:1 +msgid "Logs" +msgstr "Logs" + +#: src/octoprint/plugins/logging/templates/logging_settings.jinja2:5 +msgid "Modification date" +msgstr "Änderungsdatum" + +#: src/octoprint/plugins/logging/templates/logging_settings.jinja2:5 +#: src/octoprint/plugins/pluginmanager/templates/pluginmanager_settings.jinja2:143 +#: src/octoprint/templates/sidebar/files_header.jinja2:7 +#: src/octoprint/templates/sidebar/files_header.jinja2:8 +#: src/octoprint/templates/tabs/timelapse.jinja2:100 +#: src/octoprint/templates/tabs/timelapse.jinja2:101 +msgid "descending" +msgstr "absteigend" + +#: src/octoprint/plugins/logging/templates/logging_settings.jinja2:5 +#: src/octoprint/plugins/logging/templates/logging_settings.jinja2:12 +#: src/octoprint/templates/sidebar/files.jinja2:18 +#: src/octoprint/templates/sidebar/files.jinja2:33 +#: src/octoprint/templates/sidebar/files.jinja2:44 +#: src/octoprint/templates/tabs/timelapse.jinja2:122 +#: src/octoprint/templates/tabs/timelapse.jinja2:168 +msgid "Size" +msgstr "Größe" + +#: src/octoprint/plugins/logging/templates/logging_settings.jinja2:13 +msgid "Date" +msgstr "Datum" + +#: src/octoprint/plugins/logging/templates/logging_settings.jinja2:14 +#: src/octoprint/templates/dialogs/settings/accesscontrol.jinja2:7 +#: src/octoprint/templates/snippets/settings/printerprofiles/profiles.jinja2:6 +#: src/octoprint/templates/tabs/timelapse.jinja2:123 +#: src/octoprint/templates/tabs/timelapse.jinja2:169 +msgid "Action" +msgstr "Aktion" + +#: src/octoprint/plugins/logging/templates/logging_settings.jinja2:46 +msgid "Logging Levels" +msgstr "Logginglevel" + +#: src/octoprint/plugins/logging/templates/logging_settings.jinja2:51 +msgid "Level" +msgstr "Level" + +#: src/octoprint/plugins/logging/templates/logging_settings.jinja2:68 +#: src/octoprint/templates/sidebar/files.jinja2:23 +#: src/octoprint/templates/sidebar/files.jinja2:36 +#: src/octoprint/templates/sidebar/files.jinja2:46 +msgid "Remove" +msgstr "Entfernen" + +#: src/octoprint/plugins/logging/templates/logging_settings.jinja2:87 +msgid "Add" +msgstr "Hinzufügen" + #: src/octoprint/plugins/octopi_support/__init__.py:164 msgid "Without this plugin OctoPrint will no longer be able to provide additional information about your OctoPi instance,which will make it more tricky to help you if you need support." msgstr "Ohne dieses Plugin kann OctoPrint nicht mehr zusätzliche Informationen über deine OctoPi Instanz zur Verfügung stellen, was es ggf. schwieriger macht, dir zu helfen, falls du Support brauchst." @@ -642,385 +702,246 @@ msgstr "Über OctoPi" msgid "Plugin Manager" msgstr "Pluginmanager" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:211 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:221 msgid "There are no plugin notices. Great!" msgstr "Keine Nachrichten zu deinen Plugins. Super!" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:213 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:223 msgid "There is a plugin notice for one of your installed plugins." msgstr "Es gibt eine Nachricht zu einem deiner installierten Plugins." -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:215 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:225 #, python-format msgid "There are %(count)d plugin notices for one or more of your installed plugins." msgstr "Es gibt %(count)d Nachrichten zu einem oder mehreren deiner installierten Plugins." -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:310 -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:533 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:320 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:543 msgid "Installing plugin..." msgstr "Installiere Plugin..." -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:310 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:320 msgid "Installing plugin from uploaded archive..." msgstr "Installiere Plugin von hochgeladenem Archiv..." -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:331 -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:457 -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:557 -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:595 -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:758 -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1163 -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1214 -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1232 -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1250 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:341 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:467 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:567 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:605 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:795 msgid "Something went wrong" msgstr "Etwas ist schief gegangen" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:332 -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:458 -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:558 -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:596 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:342 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:468 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:568 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:606 msgid "Please consult octoprint.log for details" msgstr "Bitte konsultiere octoprint.log für Details" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:477 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:487 #, python-format msgid "You are about to disable \"%(name)s\"." msgstr "Du bist im Begriff \"%(name)s\" zu deaktivieren." -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:480 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:490 msgid "This is not recommended" msgstr "Das ist nicht empfohlen" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:482 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:492 msgid "Do you still want to disable it?" msgstr "Möchtest du es immer noch deaktivieren?" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:483 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:493 msgid "Keep enabled" msgstr "Aktiviert lassen" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:484 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:494 msgid "Disable anyway" msgstr "Trotzdem deaktivieren" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:535 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:545 #, python-format msgid "Installing plugin \"%(name)s\" from %(url)s..." msgstr "Installiere Plugin \"%(name)s\" von %(url)s..." -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:537 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:547 #, python-format msgid "Installing plugin from %(url)s..." msgstr "Installiere Plugin von %(url)s..." -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:540 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:550 msgid "Reinstalling plugin..." msgstr "Reinstalliere Plugin..." -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:541 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:551 #, python-format msgid "Reinstalling plugin \"%(name)s\" from %(url)s..." msgstr "Reinstalliere Plugin \"%(name)s\" von %(url)s..." -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:587 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:597 msgid "Uninstalling plugin..." msgstr "Deinstalliere Plugin..." -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:587 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:597 #, python-format msgid "Uninstalling plugin \"%(name)s\"" msgstr "Deinstalliere Plugin \"%(name)s\"" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:701 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:711 msgid "Reinstall" msgstr "Reinstallieren" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:701 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:711 #: src/octoprint/plugins/pluginmanager/templates/pluginmanager_settings.jinja2:198 #: src/octoprint/plugins/pluginmanager/templates/pluginmanager_settings.jinja2:214 msgid "Install" msgstr "Installieren" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:701 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:711 msgid "Disabled" msgstr "Deaktiviert" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:701 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:711 msgid "Incompatible" msgstr "Inkompatibel" +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:720 +msgid "Plugin management log" +msgstr "Pluginmanagementlog" + +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:728 +#, python-format +msgid "%(count)d earlier actions..." +msgstr "%(count)d frühere Aktionen..." + +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:730 +#, python-format +msgid "%(count)d earlier action" +msgstr "%(count)d earlier Aktion" + #: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:741 +#, python-format +msgid "Install %(plugin)s: %(result)s" +msgstr "%(plugin)s installiert: %(result)s" + +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:745 +#, python-format +msgid "Uninstall %(plugin)s: %(result)s" +msgstr "%(plugin)s deinstalliert: %(result)s" + +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:749 +#, python-format +msgid "Enable %(plugin)s: %(result)s" +msgstr "%(plugin)s aktiviert: %(result)s" + +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:753 +#, python-format +msgid "Disable %(plugin)s: %(result)s" +msgstr "%(plugin)s deaktiviert: %(result)s" + +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:770 +msgid "A restart is needed for the changes to take effect." +msgstr "Ein Neustart von OctoPrint ist notwendig, damit die Änderungen wirksam werden." + +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:778 msgid "Restart now" msgstr "Jetzt neu starten" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:746 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:783 msgid "This will restart your OctoPrint server.

This action may disrupt any ongoing print jobs (depending on your printer's controller and general setup that might also apply to prints run directly from your printer's internal storage)." msgstr "Dies wird deinen OctoPrint server neu starten

Diese Aktion wird laufende Druckaufträge unterbrechen (abhängig von deinem Druckerkontroller und generellem Setup gilt das auch für Druckaufträge, die du direkt von der SD deines Druckers laufen lässt)." -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:752 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:789 msgid "Restart in progress" msgstr "Neustart findet statt" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:753 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:790 msgid "The server is now being restarted in the background" msgstr "Der Server wird nun im Hintergrund neu gestartet" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:759 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:796 msgid "Trying to restart the server produced an error, please check octoprint.log for details. You'll have to restart manually." msgstr "Beim Versuch, den Server neu zu starten, ist ein Fehler aufgetreten. Bitte konsultiere octoprint.log für Details. Du musst manuell neu starten." -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:781 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:809 +msgid "A refresh is needed for the changes to take effect." +msgstr "Ein Neuladen von OctoPrint ist notwendig, damit die Änderungen wirksam werden." + +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:816 #: src/octoprint/templates/overlays/reloadui.jinja2:14 msgid "Reload now" msgstr "Jetzt neu laden" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:847 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:825 +msgid "A reconnect to the printer is needed for the changes to take effect." +msgstr "Ein Reconnect zum Drucker ist notwendig, damit die Änderungen wirksam werden." + +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:862 msgid "Error!" msgstr "Fehler!" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:850 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:865 msgid "Done!" msgstr "Fertig!" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:875 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:891 msgid "Blacklisted" msgstr "Geblacklistet" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:877 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:893 msgid "Disabled due to active safe mode" msgstr "Deaktiviert wegen aktiviertem Safe Mode" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:879 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:895 msgid "Enable Plugin" msgstr "Plugin enablen" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:882 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:898 msgid "Disable Plugin" msgstr "Plugin disablen" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:902 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:918 #, python-format msgid "There are %(count)d notices (%(important)d marked as important) available regarding this plugin - click to show!" msgstr "Es gibt %(count)d Nachrichten (%(important)d als wichtig markiert) zu diesem Plugin - hier klicken um sie anzuzeigen!" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:904 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:920 #, python-format msgid "There are %(count)d notices available regarding this plugin - click to show!" msgstr "Es gibt %(count)d Nachrichten zu diesem Plugin - hier klicken um sie anzuzeigen!" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:908 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:924 msgid "There is an important notice available regarding this plugin - click to show!" msgstr "Es gibt eine wichtige Nachricht zu diesem Plugin - hier klicken um sie anzuzeigen!" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:910 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:926 msgid "There is a notice available regarding this plugin - click to show!" msgstr "Es gibt eine Nachricht zu diesem Plugin - hier klicken um sie anzuzeigen!" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:924 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:940 #, python-format msgid "Important notice regarding plugin \"%(name)s\"" msgstr "Wichtige Nachricht zu Plugin \"%(name)s\"" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:926 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:942 #, python-format msgid "Notice regarding plugin \"%(name)s\"" msgstr "Nachricht zu Plugin \"%(name)s\"" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:933 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:949 #, python-format msgid "Affected versions: %(versions)s" msgstr "Betroffene Versionen: %(versions)s" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:935 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:951 msgid "Affected versions: all" msgstr "Betroffene Versionen: alle" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:940 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:956 msgid "Read more..." msgstr "Mehr..." -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1133 -msgid "Plugin installed" -msgstr "Plugin installiert" - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1134 -msgid "A plugin was installed successfully, however it was impossible to detect which one. Please Restart OctoPrint to make sure everything will be registered properly" -msgstr "Ein Plugin wurde erfolgreich installiert, es war aber unmöglich zu detektieren, welches. Bitte starte OctoPrint neu um sicherzustellen, dass alles ordnungsgemäß registriert wird." - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1140 -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1150 -#, python-format -msgid "Plugin \"%(name)s\" reinstalled" -msgstr "Plugin \"%(name)s\" reinstalliert" - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1141 -msgid "The plugin was reinstalled successfully, however it is blacklisted and therefore won't be loaded." -msgstr "Das Plugin wurde erfolgreich reinstalliert, es ist aber auf der Pluginblackliste und wird daher nicht geladen." - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1143 -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1156 -#, python-format -msgid "Plugin \"%(name)s\" installed" -msgstr "Plugin \"%(name)s\" installiert" - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1144 -msgid "The plugin was installed successfully, however it is blacklisted and therefore won't be loaded." -msgstr "Das Plugin wurde erfolgreich installiert, es ist jedoch auf der Pluginblackliste und wird daher nicht geladen." - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1151 -msgid "The plugin was reinstalled successfully" -msgstr "Das Plugin wurde erfolgreich reinstalliert" - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1152 -msgid "The plugin was reinstalled successfully, however a restart of OctoPrint is needed for that to take effect." -msgstr "Das Plugin wurde erfolgreich reinstalliert, es ist aber ein Neustart von OctoPrint notwendig bevor es genutzt werden kann." - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1153 -msgid "The plugin was reinstalled successfully, however a reload of the page is needed for that to take effect." -msgstr "Das Plugin wurde erfolgreich reinstalliert, es ist aber ein Neuladen der Seite notwendig bevor es genutzt werden kann." - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1154 -msgid "The plugin was reinstalled successfully, however a reconnect to the printer is needed for that to take effect." -msgstr "Das Plugin wurde erfolgreich reinstalliert, es ist aber eine Neuverbindung zum Drucker notwendig bevor es genutzt werden kann." - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1157 -msgid "The plugin was installed successfully" -msgstr "Das Plugin wurde erfolgreich installiert" - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1158 -msgid "The plugin was installed successfully, however a restart of OctoPrint is needed for that to take effect." -msgstr "Das Plugin wurde erfolgreich installiert, es ist jedoch ein Neustart von OctoPrint notwendig bevor es genutzt werden kann." - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1159 -msgid "The plugin was installed successfully, however a reload of the page is needed for that to take effect." -msgstr "Das Plugin wurde erfolgreich installiert, es ist jedoch ein Neuladen der Seite notwendig bevor es genutzt werden kann." - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1160 -msgid "The plugin was installed successfully, however a reconnect to the printer is needed for that to take effect." -msgstr "Das Plugin wurde erfolgreich installiert, es ist jedoch eine Neuverbindung zum Drucker notwendig bevor es genutzt werden kann." - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1176 -#, python-format -msgid "Reinstalling the plugin from file failed: %(reason)s" -msgstr "Reinstallation des Plugins aus Datei fehlgeschlagen: %(reason)s" - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1178 -#, python-format -msgid "Reinstalling the plugin from \"%(source)s\" failed: %(reason)s" -msgstr "Reinstallation des Plugins von \"%(source)s\" fehlgeschlagen: %(reason)s" - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1182 -#, python-format -msgid "Installing the plugin from file failed: %(reason)s" -msgstr "Installation des Plugins aus Datei fehlgeschlagen: %(reason)s" - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1184 -#, python-format -msgid "Installing the plugin from \"%(source)s\" failed: %(reason)s" -msgstr "Installation des Plugins von \"%(source)s\" fehlgeschlagen: %(reason)s" - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1190 -msgid "Reinstalling the plugin from file failed, please see the log for details." -msgstr "Reinstallation des Plugins aus Datei fehlgeschlagen, bitte konsultiere das Log für Details." - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1192 -#, python-format -msgid "Reinstalling the plugin from \"%(source)s\" failed, please see the log for details." -msgstr "Reinstallation des Plugins von \"%(source)s\" fehlgeschlagen, bitte konsultiere das Log für Details." - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1196 -msgid "Installing the plugin from file failed, please see the log for details." -msgstr "Installation des Plugins aus Datei fehlgeschlagen, bitte konsultiere das Log für Details." - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1198 -#, python-format -msgid "Installing the plugin from \"%(source)s\" failed, please see the log for details." -msgstr "Installation des Plugins von \"%(source)s\" fehlgeschlagen, bitte konsultiere das Log für Details." - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1208 -#, python-format -msgid "Plugin \"%(name)s\" uninstalled" -msgstr "Plugin \"%(name)s\" deinstalliert" - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1209 -msgid "The plugin was uninstalled successfully" -msgstr "Das Plugin wurde erfolgreich deinstalliert" - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1210 -msgid "The plugin was uninstalled successfully, however a restart of OctoPrint is needed for that to take effect." -msgstr "Das Plugin wurde erfolgreich deinstalliert, es ist jedoch ein Neustart von OctoPrint notwendig." - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1211 -msgid "The plugin was uninstalled successfully, however a reload of the page is needed for that to take effect." -msgstr "Das Plugin wurde erfolgreich deinstalliert, es ist jedoch ein Neuladen der Seite notwendig." - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1212 -msgid "The plugin was uninstalled successfully, however a reconnect to the printer is needed for that to take effect." -msgstr "Das Plugin wurde erfolgreich deinstalliert, es ist jedoch eine Neuverbindung zum Drucker notwendig." - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1216 -#, python-format -msgid "Uninstalling the plugin failed: %(reason)s" -msgstr "Deinstallation des Plugins fehlgeschlagen: %(reason)s" - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1218 -msgid "Uninstalling the plugin failed, please see the log for details." -msgstr "Deinstallation des Plugins fehlgeschlagen, bitte konsultiere das Log für Details." - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1226 -#, python-format -msgid "Plugin \"%(name)s\" enabled" -msgstr "Plugin \"%(name)s\" aktiviert" - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1227 -msgid "The plugin was enabled successfully." -msgstr "Das Plugin wurde erfolgreich aktiviert." - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1228 -msgid "The plugin was enabled successfully, however a restart of OctoPrint is needed for that to take effect." -msgstr "Das Plugin wurde erfolgreich aktiviert, es ist jedoch ein Neustart von OctoPrint notwendig." - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1229 -msgid "The plugin was enabled successfully, however a reload of the page is needed for that to take effect." -msgstr "Das Plugin wurde erfolgreich aktiviert, es ist jedoch ein Neuladen der Seite notwendig." - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1230 -msgid "The plugin was enabled successfully, however a reconnect to the printer is needed for that to take effect." -msgstr "Das Plugin wurde erfolgreich aktiviert, es ist jedoch eine Neuverbindung zum Drucker notwendig." - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1234 -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1252 -#, python-format -msgid "Toggling the plugin failed: %(reason)s" -msgstr "Togglen des Plugins fehlgeschalgen: %(reason)s" - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1236 -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1254 -msgid "Toggling the plugin failed, please see the log for details." -msgstr "Togglen des Plugins fehlgeschlagen, bitte konsultiere das Log für Details." - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1244 -#, python-format -msgid "Plugin \"%(name)s\" disabled" -msgstr "Plugin \"%(name)s\" deaktiviert" - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1245 -msgid "The plugin was disabled successfully." -msgstr "Das Plugin wurde erfolgreich deaktiviert." - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1246 -msgid "The plugin was disabled successfully, however a restart of OctoPrint is needed for that to take effect." -msgstr "Das Plugin wurde erfolgreich deaktiviert, es ist jedoch ein Neustart von OctoPrint notwendig." - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1247 -msgid "The plugin was disabled successfully, however a reload of the page is needed for that to take effect." -msgstr "Das Plugin wurde erfolgreich deaktiviert, es ist jedoch ein Neuladen der Seite notwendig." - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1248 -msgid "The plugin was disabled successfully, however a reconnect to the printer is needed for that to take effect." -msgstr "Das Plugin wurde erfolgreich deaktiviert, es ist jedoch eine Neuverbindung zum Drucker notwendig." - #: src/octoprint/plugins/pluginmanager/templates/pluginmanager_settings.jinja2:3 msgid "Take note that all plugin management functionality is disabled while your printer is printing." msgstr "Bitte beachte dass jegliche Pluginmanagementfunktionen während des Druckens deaktiviert sind." @@ -1127,15 +1048,6 @@ msgstr "Nach Titel sortieren" msgid "Sort by publication date" msgstr "Nach Veröffentlichungsdatum sortieren" -#: src/octoprint/plugins/pluginmanager/templates/pluginmanager_settings.jinja2:143 -#: src/octoprint/templates/dialogs/settings/logs.jinja2:6 -#: src/octoprint/templates/sidebar/files_header.jinja2:7 -#: src/octoprint/templates/sidebar/files_header.jinja2:8 -#: src/octoprint/templates/tabs/timelapse.jinja2:109 -#: src/octoprint/templates/tabs/timelapse.jinja2:110 -msgid "descending" -msgstr "absteigend" - #: src/octoprint/plugins/pluginmanager/templates/pluginmanager_settings.jinja2:145 msgid "Only show uninstalled plugins" msgstr "Nur uninstallierte Plugins anzeigen" @@ -1192,7 +1104,9 @@ msgstr "Das sieht nicht aus wie ein valides Pluginarchiv. Valide Pluginarchive s #: src/octoprint/plugins/pluginmanager/templates/pluginmanager_settings.jinja2:220 #: src/octoprint/plugins/softwareupdate/templates/softwareupdate_settings.jinja2:64 -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:121 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:147 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:247 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:307 #: src/octoprint/templates/dialogs/settings/webcam.jinja2:9 #: src/octoprint/templates/dialogs/settings/webcam.jinja2:23 #: src/octoprint/templates/snippets/settings/server/serverPluginBlacklist.jinja2:5 @@ -1282,12 +1196,12 @@ msgstr "Abbruch" msgid "Save" msgstr "Speichern" -#: src/octoprint/plugins/softwareupdate/__init__.py:589 +#: src/octoprint/plugins/softwareupdate/__init__.py:570 #: src/octoprint/plugins/softwareupdate/templates/softwareupdate_wizard.jinja2:1 msgid "Software Update" msgstr "Software Update" -#: src/octoprint/plugins/softwareupdate/__init__.py:999 +#: src/octoprint/plugins/softwareupdate/__init__.py:982 #: src/octoprint/server/views.py:571 #: src/octoprint/static/js/app/viewmodels/appearance.js:13 #: src/octoprint/static/js/app/viewmodels/appearance.js:18 @@ -1298,7 +1212,7 @@ msgstr "Software Update" msgid "OctoPrint" msgstr "OctoPrint" -#: src/octoprint/plugins/softwareupdate/__init__.py:1166 +#: src/octoprint/plugins/softwareupdate/__init__.py:1142 msgid "Without this plugin OctoPrint will no longer be able to update itself or any of your installed plugins which might put your system at risk." msgstr "Ohne dieses Plugin wird OctoPrint nicht länger in der Lage sein, sich selbst oder deine installierten Plugins zu aktualisierten. Das könnte dein System gefährden." @@ -1319,156 +1233,159 @@ msgstr "%(name)s: %(version)s" msgid "unknown" msgstr "unbekannt" -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:309 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:307 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:511 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:525 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:682 +msgid "Updating..." +msgstr "Aktualisiere..." + +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:307 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:525 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:682 +msgid "Updating, please wait." +msgstr "Aktualisiere gerade, bitte warten." + +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:314 msgid "There are updates available for the following components:" msgstr "Es gibt Aktualisierungen für die folgenden Komponenten:" -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:317 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:322 #: src/octoprint/plugins/softwareupdate/templates/softwareupdate.jinja2:14 msgid "Release Notes" msgstr "Release Notes" -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:323 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:328 msgid "Those components marked with can be updated directly." msgstr "Die mit markierten Komponenten können direkt aktualisiert werden." -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:326 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:331 msgid "To have updates applied, get in touch with an administrator of this OctoPrint instance." msgstr "Um Updates durchzuführen wende dich bitte an einen Administrator dieser OctoPrint Instanz." -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:332 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:337 msgid "Update Available" msgstr "Aktualisierung verfügbar" -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:344 -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:370 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:349 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:375 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:347 msgid "Ignore" msgstr "Ignorieren" -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:348 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:353 msgid "You can make this message display again via \"Settings\" > \"Software Update\" > \"Check for update now\"" msgstr "Du kannst diese Nachricht erneut anzeigen lassen mittels \"Einstellungen\" > \"Software Update\" > \"Jetzt nach Aktualisierungen suchen\"" -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:352 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:357 msgid "Update now" msgstr "Jetzt aktualisieren" -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:390 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:395 msgid "Everything is up-to-date" msgstr "Alles ist auf dem neusten Stand" -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:416 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:421 msgid "No internet connection" msgstr "Keine Onlineverbindung" -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:420 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:425 msgid "Update not possible" msgstr "Update nicht möglich" -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:431 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:436 msgid "Unknown update check, configuration ok?" msgstr "Unbekannter Updatechecktyp, Konfiguration in Ordnung?" -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:434 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:439 msgid "Cannot check for update, need online connection" msgstr "Updatecheck ist nicht mögich, benötige Onlineverbindung" -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:437 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:442 msgid "Network error while checking for update" msgstr "Netzwerkfehler bei Updatecheck" -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:440 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:445 msgid "Unknown error while checking for update, please check the logs" msgstr "Unbekannter Fehler beim Updatecheck, bitte prüfe die Logs" -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:506 -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:520 -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:681 -msgid "Updating..." -msgstr "Aktualisiere..." - -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:507 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:512 msgid "Now updating, please wait." msgstr "Aktualisiere gerade, bitte warten." -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:520 -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:681 -msgid "Updating, please wait." -msgstr "Aktualisiere gerade, bitte warten." - -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:525 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:530 msgid "Update not started!" msgstr "Aktualisierung nicht gestartet!" -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:526 -msgid "The update could not be started. Is it already active? Please consult the log for details." -msgstr "Die Aktualisierung konnte nicht gestartet werden. Läuft bereits eine? Bitte konsultiere das Log für Details." +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:531 +msgid "The update could not be started. Is it already active? Please consult octoprint.log for details." +msgstr "Die Aktualisierung konnte nicht gestartet werden. Läuft bereits eine? Bitte konsultiere octoprint.log für Details." -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:548 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:553 msgid "Can't update while printing" msgstr "Aktualisierung nicht möglich während gedruckt wird" -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:549 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:554 msgid "A print job is currently in progress. Updating will be prevented until it is done." msgstr "Ein Druckjob ist zur Zeit aktiv. Aktualisierungen werden unterbunden bis er fertig ist." -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:684 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:699 #, python-format msgid "Now updating %(name)s to %(version)s" msgstr "Aktualisiere %(name)s auf %(version)s" -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:703 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:718 msgid "Update successful, restarting!" msgstr "Aktualisierung erfolgreich, starte neu!" -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:704 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:719 msgid "The update finished successfully and the server will now be restarted." msgstr "Die Aktualisierung wurde erfolgreich durchgeführt und der Server wird jetzt neu gestartet." -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:721 -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:770 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:736 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:785 msgid "Restart failed" msgstr "Neustart fehlgeschlagen" -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:722 -msgid "The server apparently did not restart by itself, you'll have to do it manually. Please consult the log file on what went wrong." -msgstr "Der Server hat anscheinend nicht von selbst neu gstartet, Du wirst das manuell tun müssen. Bitte konsultiere das Logfile." +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:737 +msgid "The server apparently did not restart by itself, you'll have to do it manually. Please consult octoprint.log on what went wrong." +msgstr "Der Server hat anscheinend nicht von selbst neu gstartet, Du wirst das manuell tun müssen. Bitte konsultiere octoprint.log." -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:744 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:759 msgid "The update finished successfully, please restart OctoPrint now." msgstr "Die Aktualisierung wurde erfolgreich abgeschlossen, bitte starte OctoPrint jetzt neu." -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:746 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:761 msgid "The update finished successfully, please reboot the server now." msgstr "Die Aktualisierung wurde erfolgreich abgeschlossen, bitte reboote den Server jetzt." -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:749 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:764 msgid "Update successful, restart required!" msgstr "Aktualisierung erfolgreich, Neustart notwendig!" -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:765 -msgid "Restarting OctoPrint failed, please restart it manually. You might also want to consult the log file on what went wrong here." -msgstr "Der Neustart von OctoPrint ist fehlgeschlagen, bitte starte es manuell neu. Du solltest das Logfile konsultieren, um herauszufinden, was hier schief gelaufen ist." +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:780 +msgid "Restarting OctoPrint failed, please restart it manually. You might also want to consult octoprint.log on what went wrong here." +msgstr "Der Neustart von OctoPrint ist fehlgeschlagen, bitte starte es manuell neu. Du solltest auch octoprint.log konsultieren, um herauszufinden, was hier schief gelaufen ist." -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:767 -msgid "Rebooting the server failed, please reboot it manually. You might also want to consult the log file on what went wrong here." -msgstr "Reboot des Servers fehlgeschlagen, bitte reboote ihn manuell. Du solltest auch das Logfile konsultieren, um herauszufinden, was hier gerade schief gelaufen ist." +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:782 +msgid "Rebooting the server failed, please reboot it manually. You might also want to consult octoprint.log on what went wrong here." +msgstr "Reboot des Servers fehlgeschlagen, bitte reboote ihn manuell. Du solltest auch octoprint.log konsultieren, um herauszufinden, was hier gerade schief gelaufen ist." -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:786 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:801 msgid "Update successful!" msgstr "Aktualisierung erfolgreich!" -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:787 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:802 msgid "The update finished successfully." msgstr "Die Aktualisierung wurde erfolgreich abgeschlossen." -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:802 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:817 msgid "Update failed!" msgstr "Aktualisierung fehlgeschlagen!" -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:803 -msgid "The update did not finish successfully. Please consult the log for details." -msgstr "Die Aktualisierung wurde nicht erfolgreich abgeschlossen. Bitte konsultiere das Log für Details." +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:818 +msgid "The update did not finish successfully. Please consult octoprint.log and plugin_softwareupdate_console.log for details." +msgstr "Die Aktualisierung wurde nicht erfolgreich abgeschlossen. Bitte konsultiere octoprint.log und plugin_softwareupdate_console.log für Details." #: src/octoprint/plugins/softwareupdate/templates/softwareupdate.jinja2:4 msgid "Are you sure you want to update now?" @@ -1611,6 +1528,7 @@ msgid "Plugins" msgstr "Plugins" #: src/octoprint/server/views.py:538 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:12 msgid "Connection" msgstr "Verbindung" @@ -1692,27 +1610,22 @@ msgid "Appearance" msgstr "Aussehen" #: src/octoprint/server/views.py:576 -#: src/octoprint/templates/dialogs/settings/logs.jinja2:2 -msgid "Logs" -msgstr "Logs" - -#: src/octoprint/server/views.py:577 msgid "Server" msgstr "Server" -#: src/octoprint/server/views.py:583 +#: src/octoprint/server/views.py:582 msgid "Access" msgstr "Zugriff" -#: src/octoprint/server/views.py:584 +#: src/octoprint/server/views.py:583 msgid "Interface" msgstr "Interface" -#: src/octoprint/server/views.py:600 +#: src/octoprint/server/views.py:599 msgid "Start" msgstr "Start" -#: src/octoprint/server/views.py:601 +#: src/octoprint/server/views.py:600 #: src/octoprint/templates/dialogs/wizard.jinja2:57 msgid "Finish" msgstr "Beenden" @@ -1781,28 +1694,64 @@ msgstr "Der Server ist aktuell im Sicherheitsmodus. Third-Party-Plugins sind dea msgid "Slicing ... (%(percentage)d%%)" msgstr "Slice ... (%(percentage)d%%)" -#: src/octoprint/static/js/app/dataupdater.js:231 -#: src/octoprint/static/js/app/dataupdater.js:240 -msgid "Unhandled communication error" -msgstr "Unbehandelter Kommunikationsfehler" +#: src/octoprint/static/js/app/dataupdater.js:230 +#: src/octoprint/static/js/app/dataupdater.js:241 +msgid "Error reported by printer" +msgstr "Fehler vom Drucker gemeldet" -#: src/octoprint/static/js/app/dataupdater.js:232 +#: src/octoprint/static/js/app/dataupdater.js:231 #, python-format -msgid "There was an unhandled error while talking to the printer. Due to that the ongoing print job was cancelled. Error: %(firmwareError)s" -msgstr "Es gab einen unbehandelten Fehler bei der Kommunikation mit dem Drucker. Daher wurder der laufende Druckauftrag abgebrochen. Fehler: %(firmwareError)s" +msgid "Your printer's firmware reported an error. Due to that the ongoing print job will be cancelled. Reported error: %(firmwareError)s" +msgstr "Es gab einen unbehandelten Fehler bei der Kommunikation mit dem Drucker. Daher wird der laufende Druckauftrag abgebrochen. Fehler: %(firmwareError)s" -#: src/octoprint/static/js/app/dataupdater.js:241 +#: src/octoprint/static/js/app/dataupdater.js:242 #, python-format -msgid "There was an unhandled error while talking to the printer. Due to that OctoPrint disconnected. Error: %(error)s" -msgstr "Es gab einen unbehandelten Fehler bei der Kommunikation mit dem Drucker. Daher hat OctoPrint die Verbindung getrennt. Fehler: %(error)s" +msgid "Your printer's firmware reported an error. Due to that OctoPrint will disconnect. Reported error: %(error)s" +msgstr "Die Firmware Deines Druckers hat einen Fehler gemeldet. Darum hat OctoPrint die Verbindung geschlossen. Gemeldeter Fehler: %(error)s" + +#: src/octoprint/static/js/app/dataupdater.js:247 +msgid "Communication error" +msgstr "Kommunikationsfehler" #: src/octoprint/static/js/app/dataupdater.js:248 +#, python-format +msgid "There was a communication error while talking to your printer. Please consult the terminal output and octoprint.log for details. Error: %(error)s" +msgstr "Es gab einen Fehler bei der Kommunikation mit dem Drucker. Bitte konsultiere den Terminaloutput und octoprint.log für Details. Fehler: %(error)s" + +#: src/octoprint/static/js/app/dataupdater.js:252 +msgid "Error connecting to printer" +msgstr "Fehler bei der Verbindung zum Drucker" + +#: src/octoprint/static/js/app/dataupdater.js:253 +#, python-format +msgid "There was an error while trying to connect to your printer. Error: %(error)s" +msgstr "Es gab einen Fehler bei der Verbindung zum Drucker. Fehler: %(error)s" + +#: src/octoprint/static/js/app/dataupdater.js:257 +msgid "Error starting a print" +msgstr "Fehler beim Start eines Drucks" + +#: src/octoprint/static/js/app/dataupdater.js:258 +#, python-format +msgid "There was an error while trying to start a print job. Error: %(error)s" +msgstr "Es gab einen Fehler beim Start eines Drucks. Fehler: %(error)s" + +#: src/octoprint/static/js/app/dataupdater.js:267 +msgid "Unknown error" +msgstr "Unbekannter Fehler" + +#: src/octoprint/static/js/app/dataupdater.js:268 +#, python-format +msgid "There was an unknown error while talking to your printer. Please consult the terminal output and octoprint.log for details. Error: %(error)s" +msgstr "Es gab einen unbekannten Fehler bei der Kommunikation mit dem Drucker. Bitte konsultiere den Terminaloutput und octoprint.log für Details. Fehler: %(error)s" + +#: src/octoprint/static/js/app/dataupdater.js:283 msgid "Printer reset detected" msgstr "Druckerreset erkannt" -#: src/octoprint/static/js/app/dataupdater.js:249 -msgid "It looks like the printer was reset while a connection was active. If this was intentional you may safely ignore this message. Otherwise you should investigate why your printer reset itself, since this will interrupt prints and also file transfers to your printer's SD." -msgstr "Es sieht so als hätte sich der Drucker resettet während eine Verbindung offen war. Falls das Absicht war kannst du diese Nachricht ignorieren. Andernfalls solltest du prüfen, warum sich dein Drucker resettet hat, denn das wird Druckaufträge und Filetransfers zu der SD Karte deines Druckers unterbrechen." +#: src/octoprint/static/js/app/dataupdater.js:284 +msgid "It looks like your printer reset while a connection was active. If this was intentional you may safely ignore this message. Otherwise you should investigate why your printer reset itself, since this will interrupt prints and also file transfers to your printer's SD." +msgstr "Es sieht so als hätte sich der Drucker resettet während eine Verbindung offen war. Falls das Absicht war kannst du diese Nachricht ignorieren. Andernfalls solltest du prüfen, warum sich dein Drucker resettet hat, da dies Druckaufträge und Filetransfers zu der SD Karte deines Druckers unterbrechen wird." #: src/octoprint/static/js/app/helpers.js:378 #, python-format @@ -1891,6 +1840,7 @@ msgid "off" msgstr "Aus" #: src/octoprint/static/js/app/helpers.js:654 +#: src/octoprint/static/js/app/viewmodels/connection.js:133 msgid "Are you sure?" msgstr "Bist Du sicher?" @@ -1952,13 +1902,26 @@ msgid "Connect" msgstr "Verbinden" #: src/octoprint/static/js/app/viewmodels/connection.js:44 +#: src/octoprint/static/js/app/viewmodels/connection.js:142 msgid "Disconnect" msgstr "Trennen" +#: src/octoprint/static/js/app/viewmodels/connection.js:134 +msgid "

You are about to disconnect from the printer while a print is in progress.

Disconnecting while a print is in progress will prevent OctoPrint from completing the print. If you're printing from an SD card attached directly to the printer, any attempt to restart OctoPrint or reconnect to the printer could interrupt the print.

" +msgstr "

Du bist im Begriff, die Verbindung zu deinem Drucker zu trennen während ein Druck läuft.

Das Trennen der Verbindung während eines Drucks wird OctoPrint daran hindern, diesen zu vollenden. Falls du von der SD deines Druckers druckst, könnte jeglicher Versuch, OctoPrint neuzustarten oder erneut mit dem Drucker zu verbinden den Druck unterbrechen.

" + +#: src/octoprint/static/js/app/viewmodels/connection.js:140 +msgid "Are you sure you want to disconnect from the printer?" +msgstr "Bist Du sicher, dass du die Verbindung zum Drucker trennen willst?" + +#: src/octoprint/static/js/app/viewmodels/connection.js:141 +msgid "Stay Connected" +msgstr "Verbunden bleiben" + #: src/octoprint/static/js/app/viewmodels/control.js:72 #: src/octoprint/static/js/app/viewmodels/files.js:606 #: src/octoprint/static/js/app/viewmodels/gcode.js:567 -#: src/octoprint/static/js/app/viewmodels/printerstate.js:237 +#: src/octoprint/static/js/app/viewmodels/printerstate.js:241 #: src/octoprint/static/js/app/viewmodels/temperature.js:115 #: src/octoprint/static/js/app/viewmodels/temperature.js:126 msgid "Tool" @@ -2117,7 +2080,7 @@ msgid "Estimated layer height" msgstr "Geschätzte Schichthöhe" #: src/octoprint/static/js/app/viewmodels/gcode.js:528 -#: src/octoprint/templates/tabs/timelapse.jinja2:77 +#: src/octoprint/templates/tabs/timelapse.jinja2:68 msgid "mm" msgstr "mm" @@ -2291,89 +2254,89 @@ msgstr "Druckerprofil hinzufügen" msgid "Edit Printer Profile \"%(name)s\"" msgstr "Druckerprofile \"%(name)s\" bearbeiten" -#: src/octoprint/static/js/app/viewmodels/printerstate.js:48 +#: src/octoprint/static/js/app/viewmodels/printerstate.js:50 msgid "Restarts the print job from the beginning" msgstr "Started den Druckjob von vorne" -#: src/octoprint/static/js/app/viewmodels/printerstate.js:49 +#: src/octoprint/static/js/app/viewmodels/printerstate.js:51 msgid "Starts the print job" msgstr "Startet den Druckjob" -#: src/octoprint/static/js/app/viewmodels/printerstate.js:50 +#: src/octoprint/static/js/app/viewmodels/printerstate.js:52 msgid "Resumes the print job" msgstr "Setzt den Druckjob fort" -#: src/octoprint/static/js/app/viewmodels/printerstate.js:51 +#: src/octoprint/static/js/app/viewmodels/printerstate.js:53 msgid "Pauses the print job" msgstr "Pausiert den Druckjob" -#: src/octoprint/static/js/app/viewmodels/printerstate.js:84 +#: src/octoprint/static/js/app/viewmodels/printerstate.js:86 msgid "Still stabilizing..." msgstr "Noch zu ungenau..." -#: src/octoprint/static/js/app/viewmodels/printerstate.js:94 +#: src/octoprint/static/js/app/viewmodels/printerstate.js:96 msgid "Based on a linear approximation (very low accuracy, especially at the beginning of the print)" msgstr "Basiert auf einer linearen Approximation (sehr geringe Genauigkeit, insbesondere zu Beginn eines Drucks)" -#: src/octoprint/static/js/app/viewmodels/printerstate.js:97 +#: src/octoprint/static/js/app/viewmodels/printerstate.js:99 msgid "Based on the estimate from analysis of file (medium accuracy)" msgstr "Basiert auf der Schätzung der Analyse der Datei (mittlere Genauigkeit)" -#: src/octoprint/static/js/app/viewmodels/printerstate.js:100 +#: src/octoprint/static/js/app/viewmodels/printerstate.js:102 msgid "Based on a mix of estimate from analysis and calculation (medium accuracy)" msgstr "Basiert auf einem Mix der Schätzung aus der Analyse und der Berechnung (mittlere Genauigkeit)" -#: src/octoprint/static/js/app/viewmodels/printerstate.js:103 +#: src/octoprint/static/js/app/viewmodels/printerstate.js:105 msgid "Based on the average total of past prints of this model with the same printer profile (usually good accuracy)" msgstr "Basiert auf der durchschnittlichen Dauer vergangener Druckjobs dieses Modells mit dem selben Druckerprofil (normalerweise gute Genauigkeit)" -#: src/octoprint/static/js/app/viewmodels/printerstate.js:106 +#: src/octoprint/static/js/app/viewmodels/printerstate.js:108 msgid "Based on a mix of average total from past prints and calculation (usually good accuracy)" msgstr "Basiert auf einem Mix der durschnittlichen Dauer vergangener Druckjobs und der Berechnung (normalerweise gute Genauigkeit)" -#: src/octoprint/static/js/app/viewmodels/printerstate.js:109 +#: src/octoprint/static/js/app/viewmodels/printerstate.js:111 msgid "Based on the calculated estimate (best accuracy)" msgstr "Basiert auf der berechneten Schätzung (beste Genauigkeit)" -#: src/octoprint/static/js/app/viewmodels/printerstate.js:147 +#: src/octoprint/static/js/app/viewmodels/printerstate.js:149 msgid "Continue" msgstr "Fortsetzen" -#: src/octoprint/static/js/app/viewmodels/printerstate.js:149 +#: src/octoprint/static/js/app/viewmodels/printerstate.js:151 #: src/octoprint/templates/sidebar/state.jinja2:24 msgid "Pause" msgstr "Pause" -#: src/octoprint/static/js/app/viewmodels/printerstate.js:160 +#: src/octoprint/static/js/app/viewmodels/printerstate.js:162 #: src/octoprint/templates/tabs/timelapse.jinja2:15 msgid "On Z Change" msgstr "Bei Ebenenwechsel" -#: src/octoprint/static/js/app/viewmodels/printerstate.js:162 +#: src/octoprint/static/js/app/viewmodels/printerstate.js:164 #: src/octoprint/templates/tabs/timelapse.jinja2:14 msgid "Timed" msgstr "Nach Zeit" -#: src/octoprint/static/js/app/viewmodels/printerstate.js:162 +#: src/octoprint/static/js/app/viewmodels/printerstate.js:164 #: src/octoprint/templates/tabs/timelapse.jinja2:26 #: src/octoprint/templates/tabs/timelapse.jinja2:37 #: src/octoprint/templates/tabs/timelapse.jinja2:57 msgid "sec" msgstr "Sek" -#: src/octoprint/static/js/app/viewmodels/printerstate.js:274 +#: src/octoprint/static/js/app/viewmodels/printerstate.js:278 msgid "This will restart the print job from the beginning." msgstr "Der Druckjob wird zurückgesetzt und von vorne begonnen." -#: src/octoprint/static/js/app/viewmodels/printerstate.js:301 +#: src/octoprint/static/js/app/viewmodels/printerstate.js:305 msgid "This will cancel your print." msgstr "Das wird deinen Druck abbrechen." -#: src/octoprint/static/js/app/viewmodels/printerstate.js:302 +#: src/octoprint/static/js/app/viewmodels/printerstate.js:306 msgid "No" msgstr "Nein" -#: src/octoprint/static/js/app/viewmodels/printerstate.js:303 +#: src/octoprint/static/js/app/viewmodels/printerstate.js:307 msgid "Yes" msgstr "Ja" @@ -2391,39 +2354,39 @@ msgstr "weiß" msgid "Autodetect from browser" msgstr "Automatisch vom Browser erkennen" -#: src/octoprint/static/js/app/viewmodels/settings.js:274 +#: src/octoprint/static/js/app/viewmodels/settings.js:299 msgid "If you see your webcam stream below, the entered stream URL is ok." msgstr "Falls du unten den Webcamstream sehen kannst, ist die Stream-URL ok." -#: src/octoprint/static/js/app/viewmodels/settings.js:282 +#: src/octoprint/static/js/app/viewmodels/settings.js:307 msgid "Stream test" msgstr "Stream Test" -#: src/octoprint/static/js/app/viewmodels/settings.js:300 +#: src/octoprint/static/js/app/viewmodels/settings.js:325 msgid "Could not retrieve snapshot URL, please double check the URL" msgstr "Konnte die Snapshot-URL nicht abgreifen, bitte prüfen" -#: src/octoprint/static/js/app/viewmodels/settings.js:301 +#: src/octoprint/static/js/app/viewmodels/settings.js:326 msgid "Snapshot test failed" msgstr "Snapshot Test fehlgeschlagen" -#: src/octoprint/static/js/app/viewmodels/settings.js:322 +#: src/octoprint/static/js/app/viewmodels/settings.js:352 msgid "If you see your webcam snapshot picture below, the entered snapshot URL is ok." msgstr "Falls du unten dein Snapshotbild sehen kannst, ist die Snapshot-URL ok." -#: src/octoprint/static/js/app/viewmodels/settings.js:324 +#: src/octoprint/static/js/app/viewmodels/settings.js:354 msgid "Snapshot test" msgstr "Snapshot Test" -#: src/octoprint/static/js/app/viewmodels/settings.js:384 +#: src/octoprint/static/js/app/viewmodels/settings.js:414 msgid "The server is not reachable" msgstr "Der Server ist nicht erreichbar" -#: src/octoprint/static/js/app/viewmodels/settings.js:386 +#: src/octoprint/static/js/app/viewmodels/settings.js:416 msgid "The server is reachable" msgstr "Der Server ist errechbar" -#: src/octoprint/static/js/app/viewmodels/settings.js:506 +#: src/octoprint/static/js/app/viewmodels/settings.js:536 #: src/octoprint/static/js/app/viewmodels/usersettings.js:87 msgid "This will generate a new API Key. The old API Key will cease to function immediately." msgstr "Dies wird einen neuen API-Key generieren. Der alte wird sofort aufhören zu funktionieren." @@ -2492,20 +2455,16 @@ msgstr "Definiere unten ein neues Offset für alle Temperaturkommandos für \"%( msgid "Bed" msgstr "Bett" -#: src/octoprint/static/js/app/viewmodels/temperature.js:350 -msgid "just now" -msgstr "gerade eben" - -#: src/octoprint/static/js/app/viewmodels/temperature.js:355 +#: src/octoprint/static/js/app/viewmodels/temperature.js:361 msgid "min" msgstr "Min" -#: src/octoprint/static/js/app/viewmodels/temperature.js:401 +#: src/octoprint/static/js/app/viewmodels/temperature.js:407 #: src/octoprint/templates/tabs/temperature.jinja2:11 msgid "Actual" msgstr "Ist" -#: src/octoprint/static/js/app/viewmodels/temperature.js:406 +#: src/octoprint/static/js/app/viewmodels/temperature.js:412 #: src/octoprint/templates/tabs/temperature.jinja2:12 msgid "Target" msgstr "Soll" @@ -2530,144 +2489,144 @@ msgstr "zeige %(displayed)d Zeilen (Buffer voll)" msgid "showing %(displayed)d lines" msgstr "zeige %(displayed)d Zeilen" -#: src/octoprint/static/js/app/viewmodels/timelapse.js:251 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:240 #, python-format msgid "Failed to remove timelapse %(name)s.

Please consult octoprint.log for details.

" msgstr "Konnte Zeitrafferaufnahme %(name)s nicht entfernen.

Bitte konsultiere octoprint.log für Details.

" -#: src/octoprint/static/js/app/viewmodels/timelapse.js:254 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:243 msgid "Could not remove timelapse" msgstr "Konnte Zeitrafferaufnahme nicht entfernen" -#: src/octoprint/static/js/app/viewmodels/timelapse.js:262 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:251 #, python-format msgid "You are about to delete timelapse file \"%(name)s\"." msgstr "Du bist im Begriff die Zeitrafferaufnahme \"%(name)s\" zu löschen." -#: src/octoprint/static/js/app/viewmodels/timelapse.js:274 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:263 #, python-format msgid "You are about to delete %(count)d timelapse files." msgstr "Du bist im Begriff %(count)d Zeitrafferaufnahmen zu löschen." -#: src/octoprint/static/js/app/viewmodels/timelapse.js:299 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:288 #, python-format msgid "You are about to delete unrendered timelapse \"%(name)s\"." msgstr "Du bist im Begriff die ungerenderte Zeitrafferaufnahme \"%(name)s\" zu löschen." -#: src/octoprint/static/js/app/viewmodels/timelapse.js:311 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:300 #, python-format msgid "You are about to delete %(count)d unrendered timelapses." msgstr "Du bist im Begriff %(count)d ungerenderte Zeitrafferaufnahmen zu löschen." -#: src/octoprint/static/js/app/viewmodels/timelapse.js:319 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:308 msgid "Deleting timelapse files" msgstr "Lösche Zeitrafferaufnahmen" -#: src/octoprint/static/js/app/viewmodels/timelapse.js:320 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:309 #, python-format msgid "Deleting %(count)d timelapse files..." msgstr "Lösche %(count)d Zeitrafferaufnahmen" -#: src/octoprint/static/js/app/viewmodels/timelapse.js:324 -#: src/octoprint/static/js/app/viewmodels/timelapse.js:338 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:313 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:327 #, python-format msgid "Deleted %(filename)s..." msgstr "%(filename)s gelöscht..." -#: src/octoprint/static/js/app/viewmodels/timelapse.js:327 -#: src/octoprint/static/js/app/viewmodels/timelapse.js:341 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:316 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:330 #, python-format msgid "Deletion of %(filename)s failed, continuing..." msgstr "Löschen von %(filename)s fehlgeschlagen, fahre fort..." -#: src/octoprint/static/js/app/viewmodels/timelapse.js:328 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:317 #, python-format msgid "Deletion of %(filename)s failed: %(error)s" msgstr "Löschen von %(filename)s fehlgeschlagen: %(error)s" -#: src/octoprint/static/js/app/viewmodels/timelapse.js:333 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:322 msgid "Deleting unrendered timelapses" msgstr "Lösche ungerenderte Zeitrafferaufnahmen" -#: src/octoprint/static/js/app/viewmodels/timelapse.js:334 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:323 #, python-format msgid "Deleting %(count)d unrendered timelapses..." msgstr "Lösche %(count)d ungerenderte Zeitrafferaufnahmen" -#: src/octoprint/static/js/app/viewmodels/timelapse.js:429 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:417 msgid "Capturing timelapse postroll" msgstr "Zeichne Timelapse-Postroll auf" -#: src/octoprint/static/js/app/viewmodels/timelapse.js:433 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:421 msgid "Now capturing timelapse post roll, this will take only a moment..." msgstr "Zeichne jetzt Zeitraffernachlauf auf, dies wird nur einen Moment dauern..." -#: src/octoprint/static/js/app/viewmodels/timelapse.js:440 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:428 #, python-format msgid "%(minutes)d min" msgstr "%(minutes)d Min" -#: src/octoprint/static/js/app/viewmodels/timelapse.js:441 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:429 #, python-format msgid "Now capturing timelapse post roll, this will take approximately %(duration)s (so until %(time)s)..." msgstr "Zeichne jetzt Zeitraffernachlauf auf, dies wird voraussichtlich %(duration)s dauern (also etwa bis %(time)s)..." -#: src/octoprint/static/js/app/viewmodels/timelapse.js:443 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:431 #, python-format msgid "%(seconds)d sec" msgstr "%(seconds)d Sek" -#: src/octoprint/static/js/app/viewmodels/timelapse.js:444 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:432 #, python-format msgid "Now capturing timelapse post roll, this will take approximately %(duration)s..." msgstr "Zeichne jetzt Zeitraffernachlauf auf, dies wird voraussichtlich %(duration)s dauern..." -#: src/octoprint/static/js/app/viewmodels/timelapse.js:474 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:462 msgid "Failed repeatedly to capture timelapse frame from webcam - is the snapshot URL configured correctly and the camera on?" msgstr "Konnte wiederholt kein Zeitrafferbild von der Webcam beziehen - ist die Snapshot-URL korrekt konfiguriert und die Kamera an?" -#: src/octoprint/static/js/app/viewmodels/timelapse.js:477 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:465 msgid "Could not capture snapshots" msgstr "Konnte keine Snapshots aufnehmen" -#: src/octoprint/static/js/app/viewmodels/timelapse.js:486 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:474 msgid "Rendering timelapse" msgstr "Zeitrafferaufnahme wird gerendert" -#: src/octoprint/static/js/app/viewmodels/timelapse.js:487 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:475 #, python-format msgid "Now rendering timelapse %(movie_prefix)s. Due to performance reasons it is not recommended to start a print job while a movie is still rendering." msgstr "Rendere jetzt die Zeitrafferaufnahme %(movie_prefix)s. Aus Gründen der Performance ist es nicht empfehlenswert, einen Druckauftrag zu starten, so lange die Aufnahme noch gerendert wird." -#: src/octoprint/static/js/app/viewmodels/timelapse.js:496 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:484 msgid "Cannot render timelapse" msgstr "Kann Zeitrafferaufnahme nicht rendern" -#: src/octoprint/static/js/app/viewmodels/timelapse.js:497 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:485 #, python-format msgid "Rendering of timelapse %(movie_prefix)s is not possible since no frames were captured. Is the snapshot URL configured correctly?" msgstr "Rendering des Zeitraffers %(movie_prefix)s ist nicht möglich, da keine Frames gespeichert wurden. Ist die Snapshot-URL korrekt konfiguriert?" -#: src/octoprint/static/js/app/viewmodels/timelapse.js:499 -#: src/octoprint/static/js/app/viewmodels/timelapse.js:503 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:487 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:491 msgid "Rendering timelapse failed" msgstr "Rendern von Zeitrafferaufnahme fehlgeschlagen" -#: src/octoprint/static/js/app/viewmodels/timelapse.js:500 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:488 #, python-format msgid "Rendering of timelapse %(movie_prefix)s failed with return code %(returncode)s" msgstr "Rendering der Zeitrafferaufnahme %(movie_prefix)s fehlgeschlagen mit Returncode %(returncode)s" -#: src/octoprint/static/js/app/viewmodels/timelapse.js:504 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:492 #, python-format msgid "Rendering of timelapse %(movie_prefix)s failed due to an unknown error, please consult the log file" msgstr "Rendering der Zeitrafferaufnahme %(movie_prefix)s fehlgeschlagen aufgrund eines unbekannten Fehlers, bitte konsultiere die Logdatei" -#: src/octoprint/static/js/app/viewmodels/timelapse.js:517 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:505 msgid "Timelapse ready" msgstr "Zeitrafferaufnahme fertig" -#: src/octoprint/static/js/app/viewmodels/timelapse.js:518 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:506 #, python-format msgid "New timelapse %(movie_prefix)s is done rendering." msgstr "Neue Zeitrafferaufnahme %(movie_prefix)s wurde fertig gerendert" @@ -2821,14 +2780,6 @@ msgstr "Aktiv" msgid "Admin" msgstr "Admin" -#: src/octoprint/templates/dialogs/settings/accesscontrol.jinja2:7 -#: src/octoprint/templates/dialogs/settings/logs.jinja2:15 -#: src/octoprint/templates/snippets/settings/printerprofiles/profiles.jinja2:6 -#: src/octoprint/templates/tabs/timelapse.jinja2:132 -#: src/octoprint/templates/tabs/timelapse.jinja2:178 -msgid "Action" -msgstr "Aktion" - #: src/octoprint/templates/dialogs/settings/accesscontrol.jinja2:12 #: src/octoprint/templates/dialogs/settings/api.jinja2:17 #: src/octoprint/templates/dialogs/usersettings/access.jinja2:22 @@ -3038,81 +2989,28 @@ msgid "Enable Keyboard Control" msgstr "Tastatursteuerung aktivieren" #: src/octoprint/templates/dialogs/settings/features.jinja2:54 -msgid "Wait for start on connect" -msgstr "Bei der Verbindung auf start warten" - -#: src/octoprint/templates/dialogs/settings/features.jinja2:61 msgid "G90/G91 overrides relative extruder mode" msgstr "G90/G91 überschreibt relativen Modus des Extruders" -#: src/octoprint/templates/dialogs/settings/features.jinja2:61 +#: src/octoprint/templates/dialogs/settings/features.jinja2:54 msgid "Smoothieware" msgstr "Smoothieware" -#: src/octoprint/templates/dialogs/settings/features.jinja2:68 -msgid "Enable automatic firmware detection" -msgstr "Automatische Firmwareerkennung einschalten" - -#: src/octoprint/templates/dialogs/settings/features.jinja2:69 -msgid "\n" -" If enabled, OctoPrint will try to figure out your printer's firmware automatically and adjust some communication parameters based on that.\n" -" If that doesn't work out, or you want more granular control, uncheck this and the parameters in question will become visible for you to adjust.\n" -" " -msgstr "Falls diese Option eingeschaltet ist, versucht OctoPrint die Firmware des Druckers automatisch zu erkennen und darauf basierend diverse Kommunikationsparameter zu konfigurieren. Falls das für dich nicht korrekt funktioniert oder du mehr Kontrolle haben möchtest, schalte diese Option aus und die entsprechenden Parameter werden zur manuellen Konfiguration eingeblendet." - -#: src/octoprint/templates/dialogs/settings/features.jinja2:80 -msgid "Select SD files by relative path" -msgstr "SD Dateien per relativem Pfad addressieren" - -#: src/octoprint/templates/dialogs/settings/features.jinja2:80 -msgid "RepRap Firmware" -msgstr "RepRap Firmware" - -#: src/octoprint/templates/dialogs/settings/features.jinja2:87 -msgid "Always assume SD card is present" -msgstr "Immer davon ausgehen, dass eine SD-Karte vorhanden ist" - -#: src/octoprint/templates/dialogs/settings/features.jinja2:87 -#: src/octoprint/templates/dialogs/settings/features.jinja2:94 -#: src/octoprint/templates/dialogs/settings/features.jinja2:101 -#: src/octoprint/templates/dialogs/settings/features.jinja2:108 -#: src/octoprint/templates/dialogs/settings/features.jinja2:115 -#: src/octoprint/templates/dialogs/settings/features.jinja2:126 -msgid "Repetier" -msgstr "Repetier" - -#: src/octoprint/templates/dialogs/settings/features.jinja2:94 -msgid "Ignore consecutive resend requests for the same line" -msgstr "Aufeinanderfolgende Resend Requests für die selbe Zeilennummer ignorieren" - -#: src/octoprint/templates/dialogs/settings/features.jinja2:101 -#, python-format -msgid "Support TargetExtr%%n/TargetBed target temperature format" -msgstr "TargetExtr%%n/TargetBed Zieltemperaturformat unterstützen" - -#: src/octoprint/templates/dialogs/settings/features.jinja2:108 -msgid "Disable detection of external heatups" -msgstr "Detektierung externer Aufheizvorgänge deaktivieren" - -#: src/octoprint/templates/dialogs/settings/features.jinja2:115 -msgid "Actively pause communication during G4 dwell command" -msgstr "Pausiere Kommunikation mit dem Drucker aktiv während eines G4 dwell Befehls." - -#: src/octoprint/templates/dialogs/settings/features.jinja2:120 -msgid "Send a checksum with the command" -msgstr "Eine Prüfsumme mit dem Befehl senden" +#: src/octoprint/templates/dialogs/settings/features.jinja2:55 +msgid "This is used by the GCODE analysis in the backend and the viewer in the frontend to interpret your sliced files correctly." +msgstr "Dies wird von der GCODE Analyse im Backend und dem GCODE Viewer im Frontend verwendet, um die gesliceten Dateien korrekt zu interpretieren." -#: src/octoprint/templates/dialogs/settings/features.jinja2:123 -msgid "When printing" -msgstr "Beim Drucken" +#: src/octoprint/templates/dialogs/settings/features.jinja2:59 +msgid "Commands to not completely auto uppercase in the terminal tab" +msgstr "Befehle, die im Terminaltab nicht vollständig in Großbuchstaben gewandelt werden sollen" -#: src/octoprint/templates/dialogs/settings/features.jinja2:126 -msgid "Always" -msgstr "Immer" +#: src/octoprint/templates/dialogs/settings/features.jinja2:60 +msgid "Terminal Auto Uppercase Blacklist" +msgstr "Terminal Auto Uppercase Blackliste" -#: src/octoprint/templates/dialogs/settings/features.jinja2:129 -msgid "Never" -msgstr "Nie" +#: src/octoprint/templates/dialogs/settings/features.jinja2:63 +msgid "Use this to specify the commands that should not have their parameters automatically uppercased in the terminal tab. Just the G or M code, comma separated." +msgstr "Nutze diese Option um Befehle zu spezifizieren, deren Parameter nicht automatisch im Terminaltab in Großbuchstaben überführt werden sollen. Nur den G oder M code, kommasepariert." #: src/octoprint/templates/dialogs/settings/folders.jinja2:5 msgid "Upload Folder" @@ -3147,10 +3045,10 @@ msgid "If the free disk space falls below these thresholds, OctoPrint will warn msgstr "Falls der freie Plattenplatz unter diese Schwellwerte fallen sollte wird OctoPrint den Nutzer warnen." #: src/octoprint/templates/dialogs/settings/folders.jinja2:47 -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:87 -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:115 -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:163 -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:169 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:52 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:347 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:357 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:363 #: src/octoprint/templates/tabs/gcodeviewer.jinja2:111 msgid "Warning" msgstr "Warnung" @@ -3224,249 +3122,383 @@ msgstr "auf dem Desktop" msgid "on mobile" msgstr "auf Mobile" -#: src/octoprint/templates/dialogs/settings/logs.jinja2:6 -msgid "Modification date" -msgstr "Änderungsdatum" +#: src/octoprint/templates/dialogs/settings/printerprofiles.jinja2:5 +msgid "Add Profile..." +msgstr "Profil hinzufügen..." -#: src/octoprint/templates/dialogs/settings/logs.jinja2:6 -#: src/octoprint/templates/dialogs/settings/logs.jinja2:13 -#: src/octoprint/templates/sidebar/files.jinja2:18 -#: src/octoprint/templates/sidebar/files.jinja2:33 -#: src/octoprint/templates/sidebar/files.jinja2:44 -#: src/octoprint/templates/tabs/timelapse.jinja2:131 -#: src/octoprint/templates/tabs/timelapse.jinja2:177 -msgid "Size" -msgstr "Größe" +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:4 +msgid "Intervals & timeouts" +msgstr "Intervalle & Timeouts" -#: src/octoprint/templates/dialogs/settings/logs.jinja2:14 -msgid "Date" -msgstr "Datum" +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:5 +msgid "Firmware & protocol" +msgstr "Firmware & Protokoll" -#: src/octoprint/templates/dialogs/settings/printerprofiles.jinja2:5 -msgid "Add Profile..." -msgstr "Profil hinzufügen..." +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:6 +msgid "Behaviour" +msgstr "Verhalten" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:2 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:13 msgid "Serial port to connect to, setting this to AUTO will make OctoPrint try to automatically find the right setting" msgstr "Serieller Port, mit der sich verbunden werden soll. Falls AUTO konfiguriert ist wird OctoPrint versuchen, automatisch den richtigen Port zu finden." -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:3 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:14 #: src/octoprint/templates/sidebar/connection.jinja2:1 msgid "Serial Port" msgstr "Serialport" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:8 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:19 msgid "Serial baud rate to connect with, setting this to AUTO will make OctoPrint try to automatically find the right setting" msgstr "Baudrate mit der sich verbunden werden soll. Falls AUTO konfiguriert ist wird OctoPrint versuchen, automatisch die richtige Baudrate zu finden." -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:9 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:20 #: src/octoprint/templates/sidebar/connection.jinja2:3 msgid "Baudrate" msgstr "Baudrate" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:14 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:25 msgid "Makes OctoPrint try to connect to the printer automatically during start up" msgstr "OctoPrint wird versuchen, sich beim Startup automatisch mit dem Drucker zu verbinden" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:17 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:28 msgid "Auto-connect to printer on server start" msgstr "Automatisch bei Serverstart verbinden" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:21 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:33 +msgid "Additional serial ports" +msgstr "Zusätzliche serielle Ports" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:36 +#, python-format +msgid "Use this to define additional glob patterns matching serial ports to list for connecting against, e.g. /dev/ttyAMA*. One entry per line." +msgstr "Nutze diese Einstellung um zusätzliche glob patterns zu konfigurieren, die auf serielle Ports deines Druckers matchen, z.B. /dev/ttyAMA*. Ein Eintrag pro Zeile." + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:40 +msgid "Additional baud rates" +msgstr "Weitere Baudraten" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:43 +msgid "Use this to define additional serial port baud rates to list for connecting with, e.g. 123456. Comma separated." +msgstr "Nutze diese Einstellung um zusätzliche Baudraten zur Verbindung zu konfigurieren, 123456. Komma-separiert." + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:48 +msgid "Serial logging" +msgstr "Logging der seriellen Kommunikation" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:52 +msgid "Log communication to serial.log" +msgstr "Kommunikation nach serial.log loggen" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:53 +msgid "While this can negatively impact performance, a serial.log can be incredibly useful for debugging any issues observed in the communication between OctoPrint and your printer." +msgstr "Dies kann zwar die Performance negativ beeinflussen, ein serial.log ist jedoch unglaublich nützlich beim Debugging jeglicher Probleme, die in der Kommunikation zwischen OctoPrint und deinem Drucker auftreten." + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:61 +msgid "Query intervals" +msgstr "Abfrageintervalle" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:62 msgid "Interval in which to poll for the temperature information from the printer" msgstr "Intervall in welchem die Temperaturdaten vom Drucker abgerufen werden sollen" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:22 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:63 msgid "Temperature interval (polling)" msgstr "Temperaturintervall (polling)" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:28 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:69 msgid "When printing or idle" msgstr "Beim Drucken oder Idle" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:35 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:76 msgid "When a target temperature is set" msgstr "Wenn eine Zieltemperatur eingestellt ist" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:38 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:79 msgid "Temperature report interval to request from autoreport capable firmwares. A value of 0 disables autoreporting by the firmware and forces polling." msgstr "Von der autoreportfähigen Firmware anzuforderndes Temperaturupdateintervall. Ein Wert von 0 deaktiviert Autoreporting der Firmware und forciert Polling." -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:39 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:80 msgid "Temperature interval (autoreport)" msgstr "Temperaturintervall (autoreport)" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:45 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:86 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:105 msgid "Autoreport interval to request from firmware" msgstr "Anzuforderndes Temperaturupdateintervall" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:48 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:89 msgid "Interval in which to poll for the SD printing status information from the printer while printing" msgstr "Intervall in welchem die SD-Statusdaten vom Drucker während des Druckens abgerufen werden sollen" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:49 -msgid "SD status interval" -msgstr "SD-Status-Intervall" +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:90 +msgid "SD status interval (polling)" +msgstr "SD-Statusintervall (polling)" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:98 +msgid "SD status interval to request from autoreport capable firmwares. A value of 0 disables autoreporting by the firmware and forces polling." +msgstr "Von der autoreportfähigen Firmware anzuforderndes SD-Statusintervall. Ein Wert von 0 deaktiviert Autoreporting der Firmware und forciert Polling." + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:99 +msgid "SD status interval (autoreport)" +msgstr "SD-Statusintervall (autoreport)" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:57 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:110 +msgid "Timeouts" +msgstr "Timeouts" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:111 msgid "Time after which the communication with your printer will be considered timed out if nothing was sent by your printer (and an attempt to get it talking again will be done). Increase this if your printer takes longer than this for some moves." msgstr "Zeit nach der OctoPrint davon ausgehen wird, dass die Kommunikation mit deinem Drucker unterbrochen wurde falls Dein Drucker keine Daten sendet. OctoPrint wird dann einen Versuch unternehmen, die Kommunikation wieder zu reetablieren. Erhöhe diesen Wert falls Dein Drucker für manche Bewegungen länger braucht." -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:58 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:112 msgid "Communication timeout" msgstr "Kommunikationstimeout" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:66 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:118 +msgid "busy protocol support not detected" +msgstr "busy Protokoll nicht unterstützt" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:125 +msgid "busy protocol support detected" +msgstr "busy Protokoll unterstützt" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:128 msgid "Time after which a connection attempt to the printer will be considered as having failed" msgstr "Zeit nach der ein unbeantworteter Verbindungsversuch als gescheitert angenommen wird" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:67 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:129 msgid "Connection timeout" msgstr "Verbindungstimeout" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:75 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:137 msgid "Time after which to consider an auto detection attempt to have failed if no successful connection is detected" msgstr "Zeit nach der ein unbeantworteter Autodetektierungsversuch als gescheitert angenommen wird" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:76 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:138 msgid "Autodetection timeout" msgstr "Autodetectiontimeout" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:87 -msgid "Log communication to serial.log (might negatively impact performance)" -msgstr "Logge die Kommunikation in das serial.log (kann die Performance negativ beeinflussen)" +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:149 +msgid "Maximum consecutive communication timeouts while idle. More than this and the printer will be considered to be gone. Set to 0 to disable." +msgstr "Maximale Anzahl aufeinanderfolgender Communication Timeouts im Idlezustand. Mehr als das und es wird angenommen, dass der Drucker offline ist. Auf 0 setzen um das zu verhindern." -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:92 -msgid "Additional serial ports" -msgstr "Zusätzliche serielle Ports" +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:150 +msgid "Max. consecutive timeouts while idle" +msgstr "Max. aufeinanderfolgende Timeouts wenn idle" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:153 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:160 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:167 +msgid "Set to 0 to disable consecutive timeout detection and handling." +msgstr "Auf 0 setzen um aufeinanderfolgende Timeouts zu ignorieren" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:156 +msgid "Maximum consecutive communication timeouts while printing. More than this and the printer will be considered to be gone. Set to 0 to disable." +msgstr "Maximale Anzahl aufeinanderfolgender Communication Timeouts beim Drucken. Mehr als das und es wird angenommen, dass der Drucker offline ist. Auf 0 setzen um das zu verhindern." + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:157 +msgid "Max. consecutive timeouts while printing" +msgstr "Max. aufeinanderfolgende Timeouts beim Drucken" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:163 +msgid "Maximum consecutive communication timeouts while a long running command is active. More than this and the printer will be considered to be gone. Set to 0 to disable." +msgstr "Maximale Anzahl aufeinanderfolgender Communication Timeouts wenn ein lang laufender Befehl aktiv ist. Mehr als das und es wird angenommen, dass der Drucker offline ist. Auf 0 setzen um das zu verhindern." + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:164 +msgid "Max. consecutive timeouts during long running commands" +msgstr "Max. aufeinanderfolgende Timeouts während lang laufender Befehle" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:176 +msgid "Firmware specific settings" +msgstr "Firmwarespezifische Einstellungen" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:180 +msgid "Enable automatic firmware detection" +msgstr "Automatische Firmwareerkennung einschalten" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:181 +msgid "\n" +" If enabled, OctoPrint will try to figure out your printer's firmware automatically and adjust some communication parameters based on that.\n" +" If that doesn't work out, or you want more granular control, uncheck this and the parameters in question will become visible for you to adjust.\n" +" " +msgstr "Falls diese Option eingeschaltet ist, versucht OctoPrint die Firmware des Druckers automatisch zu erkennen und darauf basierend diverse Kommunikationsparameter zu konfigurieren. Falls das für dich nicht korrekt funktioniert oder du mehr Kontrolle haben möchtest, schalte diese Option aus und die entsprechenden Parameter werden zur manuellen Konfiguration eingeblendet." -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:95 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:192 +msgid "Select SD files by relative path" +msgstr "SD Dateien per relativem Pfad addressieren" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:192 +msgid "RepRap Firmware" +msgstr "RepRap Firmware" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:199 +msgid "Always assume SD card is present" +msgstr "Immer davon ausgehen, dass eine SD-Karte vorhanden ist" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:199 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:206 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:213 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:220 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:227 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:238 +msgid "Repetier" +msgstr "Repetier" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:206 +msgid "Ignore consecutive resend requests for the same line" +msgstr "Aufeinanderfolgende Resend Requests für die selbe Zeilennummer ignorieren" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:213 #, python-format -msgid "Use this to define additional glob patterns matching serial ports to list for connecting against, e.g. /dev/ttyAMA*. One entry per line." -msgstr "Nutze diese Einstellung um zusätzliche glob patterns zu konfigurieren, die auf serielle Ports deines Druckers matchen, z.B. /dev/ttyAMA*. Ein Eintrag pro Zeile." +msgid "Support TargetExtr%%n/TargetBed target temperature format" +msgstr "TargetExtr%%n/TargetBed Zieltemperaturformat unterstützen" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:99 -msgid "Additional baud rates" -msgstr "Weitere Baudraten" +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:220 +msgid "Disable detection of external heatups" +msgstr "Detektierung externer Aufheizvorgänge deaktivieren" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:102 -msgid "Use this to define additional serial port baud rates to list for connecting with, e.g. 123456. Comma separated." -msgstr "Nutze diese Einstellung um zusätzliche Baudraten zur Verbindung zu konfigurieren, 123456. Komma-separiert." +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:227 +msgid "Actively pause communication during G4 dwell command" +msgstr "Pausiere Kommunikation mit dem Drucker aktiv während eines G4 dwell Befehls." + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:232 +msgid "Send a checksum with the command" +msgstr "Eine Prüfsumme mit dem Befehl senden" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:235 +msgid "When printing" +msgstr "Beim Drucken" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:238 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:285 +msgid "Always" +msgstr "Immer" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:241 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:288 +msgid "Never" +msgstr "Nie" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:252 +msgid "Support temperature autoreporting by firmware, if detected" +msgstr "Temperaturautoreporting durch die Firmware unterstützen, falls detektiert" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:259 +msgid "Support sd status autoreporting by firmware, if detected" +msgstr "SD-Status-Autoreporting durch die Firmware unterstützen, falls detektiert" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:266 +msgid "Support busy protocol, if detected" +msgstr "busy Protokoll unterstützen, falls detektiert" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:273 +msgid "Simulate ok for M29" +msgstr "Zusätzliches ok für M29 generieren" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:274 +msgid "Most Marlin forks that were derived from Marlin versions < v1.1.0 do not send an acknowledging ok for a M29. Check this if you run into communication stalls following streaming of a file to your printer's SD." +msgstr "Die meisten Marlinforks, die von Marlin Versionen < v1.1.0 abgeleitet wurden, senden kein bestätigendes ok für ein M29. Aktiviere diese Option, falls du Kommunikationsprobleme nach dem Streaming einer Datei zu der SD des Druckers beobachtest." + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:279 +msgid "Simulate ok for resend requests" +msgstr "Zusätzliches ok für Resendrequests simulieren" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:282 +msgid "If detected as necessary" +msgstr "Falls als notwendig erkannt" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:108 -msgid "Not only cancel ongoing prints but also disconnect on unhandled errors from the firmware." -msgstr "Bei unbehandelten Firmwarefehlern nicht nur den Druckauftrag abbrechen, sondern auch die Verbindung zum Drucker trennen." +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:290 +msgid "Some Marlin forks lack an acknowledging ok with their resend requests. Set this to \"always\" or \"if detected as necessary\" if you run into communication stalls on resend requests." +msgstr "Einige Marlinforks senden bei einem Resendrequest kein bestätigendes ok. Setze diese Option auf \"immer\" oder \"falls als notwendig erkannt\" falls du Kommunikationsprobleme nach Resendrequests beobachtest." + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:297 +msgid "Protocol fine tuning" +msgstr "Protokollfinetuning" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:301 +msgid "Wait for start on connect" +msgstr "Bei der Verbindung auf start warten" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:115 -msgid "Ignore any unhandled errors from the firmware. Only use this if your firmware sends stuff prefixed with \"Error\" that is not an actual error. Might mask printer issues, be careful!" -msgstr "Alle unbehandelten Firmwarefehler ignorieren. Nur nutzen wenn Deine Firmware Dinge mit \"Error\" sendet die nicht wirklich Fehler sind. Könnte Druckerprobleme maskieren, vorsicht!" +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:302 +msgid "Try checking this if you run into connection timeouts due to your printer taking too long to respond to OctoPrint' handshake attempts on connect." +msgstr "Versuche es mit dieser Option, falls du Kommunikationsprobleme beobachtest, weil dein Drucker zu lange braucht, um auf OctoPrints Handshake Versuche zu antworten." -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:123 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:309 msgid "Command to send to the firmware on first handshake attempt." msgstr "Kommando, das als erster Handshakeversuch an die Firmware gesendet werden soll" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:124 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:310 msgid "\"Hello\" command" msgstr "\"Hallo\"-Befehl" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:127 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:313 msgid "Use this to specify a different command than the default M110 to send to the printer on initial connection to trigger a communication handshake." msgstr "Nutze diese Einstellung um einen anderen Befehl als M110 beim initialen Verbindungsaufbau zum drucker zu senden." -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:130 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:316 msgid "Commands that are know to run long and hence should suppress communication timeouts from being triggered." msgstr "Befehle, von denen bekannt ist, dass sie lang zur Ausführung benötigen und daher das Auslösen von Kommunikationstimeouts unterdrücken sollten." -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:131 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:317 msgid "Long running commands" msgstr "Lang laufende Befehle" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:134 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:320 msgid "Use this to specify the commands known to take a long time to complete without output from your printer and hence might cause timeout issues. Just the G or M code, comma separated." msgstr "Nutze diese Option, um solche Befehle zu definieren, von denen Du weißt, dass sie eine längere Zeit lang laufen, währenddessen keinen Output produzieren und daher Timeoutprobleme verursachen könnten. Nur den G- oder M-Code, kommasepariert." -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:137 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:323 msgid "Commands that always require a line number and checksum to be sent with them." msgstr "Befehle, die immer mit einer Prüfsumme und Zeilennummer gesendet werden müssen." -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:138 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:324 msgid "Commands that always require a checksum" msgstr "Befehle, die immer eine Prüfsumme benötigen" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:141 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:327 msgid "Use this to specify which commands always need to be sent with a checksum. Comma separated list." msgstr "Nutze diese Einstellung um Befehle zu spezifizieren, die immer mit Prüfsumme gesendet werden müssen. Komma-separierte Liste." -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:144 -msgid "Commands to not completely auto uppercase in the terminal tab" -msgstr "Befehle, die im Terminaltab nicht vollständig in Großbuchstaben gewandelt werden sollen" +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:336 +msgid "Error handling" +msgstr "Fehlerbehandlung" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:145 -msgid "Terminal Auto Uppercase Blacklist" -msgstr "Terminal Auto Uppercase Blackliste" +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:338 +msgid "What to do on a firmware error (Error: or !!)" +msgstr "Was im Falle eines Firmwarefehlers (Error: oder !!) gemacht werden soll" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:148 -msgid "Use this to specify the commands that should not have their parameters automatically uppercased in the terminal tab. Just the G or M code, comma separated." -msgstr "Nutze diese Option um Befehle zu spezifizieren, deren Parameter nicht automatisch im Terminaltab in Großbuchstaben überführt werden sollen. Nur den G oder M code, kommasepariert." +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:341 +msgid "Disconnect from the printer" +msgstr "Verbindung zum Drucker trennen" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:153 -msgid "Generate additional ok for M29" -msgstr "Zusätzliches ok für M29 generieren" +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:344 +msgid "Cancel any ongoing prints but stay connected to the printer" +msgstr "Laufende Druckaufträge abbrechen, aber mit dem Drucker verbunden bleiben" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:153 -msgid "Most Marlin < v1.1.0" -msgstr "Die meisten Varianten von Marlin < v1.1.0" +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:348 +msgid "Only choose this if your firmware sends error messages that are not actual errors. Might mask printer issues, be careful!" +msgstr "Wähle das nur aus, wenn deine Firmware Fehlermeldungen für Dinge sendet, die gar keine wirklich Fehler sind. Könnte Druckerprobleme verbergen, Vorsicht!" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:158 -msgid "Simulate an additional ok for resend requests" -msgstr "Zusätzliches ok für Resendrequests simulieren" +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:354 +msgid "Event based position logging" +msgstr "Eventbasiertes Positionslogging" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:163 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:357 msgid "Log position on pause" msgstr "Position bei Pause loggen" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:164 -msgid "If you disabled this, the pause_position placeholders in your pause/resume GCODE scripts will stay unpopulated! However, pausing speed might improve slightly." +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:358 +msgid "If you disable this, the pause_position placeholders in your pause/resume GCODE scripts will stay unpopulated! However, pausing speed might improve slightly." msgstr "Falls du das deaktivierst, werden die pause_position Platzhalter in deinen Pausieren/Fortsetzen GCODE Scripts nicht befüllt! Allerdings könnte sich die Pausierungsgeschwindkeit einen Hauch verbessern." -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:169 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:363 msgid "Log position on cancel" msgstr "Position bei Abbruch loggen" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:170 -msgid "If you disabled this, the cancel_position placeholders in your cancel GCODE script and the corresponding data in the print recovery data will stay unpopulated! However, cancelling speed might improve slightly." +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:364 +msgid "If you disable this, the cancel_position placeholders in your cancel GCODE script and the corresponding data in the print recovery data will stay unpopulated! However, cancelling speed might improve slightly." msgstr "Falls du das deaktivierst, werden die cancel_position Platzhalter in deinem Abbruch GCODE Script nicht befüllt! Allerdings könnte sich die Abbruchgeschwindkeit einen Hauch verbessern." -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:173 -msgid "Maximum consecutive communication timeouts while idle. More than this and the printer will be considered to be gone. Set to 0 to disable." -msgstr "Maximale Anzahl aufeinanderfolgender Communication Timeouts im Idlezustand. Mehr als das und es wird angenommen, dass der Drucker offline ist. Auf 0 setzen um das zu verhindern." - -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:174 -msgid "Max. consecutive timeouts while idle" -msgstr "Max. aufeinanderfolgende Timeouts wenn idle" - -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:177 -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:184 -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:191 -msgid "Set to 0 to disable consecutive timeout detection and handling." -msgstr "Auf 0 setzen um aufeinanderfolgende Timeouts zu ignorieren" - -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:180 -msgid "Maximum consecutive communication timeouts while printing. More than this and the printer will be considered to be gone. Set to 0 to disable." -msgstr "Maximale Anzahl aufeinanderfolgender Communication Timeouts beim Drucken. Mehr als das und es wird angenommen, dass der Drucker offline ist. Auf 0 setzen um das zu verhindern." - -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:181 -msgid "Max. consecutive timeouts while printing" -msgstr "Max. aufeinanderfolgende Timeouts beim Drucken" - -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:187 -msgid "Maximum consecutive communication timeouts while a long running command is active. More than this and the printer will be considered to be gone. Set to 0 to disable." -msgstr "Maximale Anzahl aufeinanderfolgender Communication Timeouts wenn ein lang laufender Befehl aktiv ist. Mehr als das und es wird angenommen, dass der Drucker offline ist. Auf 0 setzen um das zu verhindern." - -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:188 -msgid "Max. consecutive timeouts during long running commands" -msgstr "Max. aufeinanderfolgende Timeouts während lang laufender Befehle" - #: src/octoprint/templates/dialogs/settings/server.jinja2:2 msgid "Commands" msgstr "Befehle" @@ -3654,6 +3686,10 @@ msgstr "Verbindungseinstellungen speichern" msgid "Auto-connect on server startup" msgstr "Automatisch bei Serverstart verbinden" +#: src/octoprint/templates/sidebar/connection_header.jinja2:2 +msgid "Refresh connection options" +msgstr "Verbindungsoptionen aktualisieren" + #: src/octoprint/templates/sidebar/files.jinja2:6 msgid "Back" msgstr "Zurück" @@ -3682,12 +3718,6 @@ msgstr "Weitere Daten" msgid "Download" msgstr "Download" -#: src/octoprint/templates/sidebar/files.jinja2:23 -#: src/octoprint/templates/sidebar/files.jinja2:36 -#: src/octoprint/templates/sidebar/files.jinja2:46 -msgid "Remove" -msgstr "Entfernen" - #: src/octoprint/templates/sidebar/files.jinja2:24 msgid "Load" msgstr "Laden" @@ -3717,7 +3747,7 @@ msgid "File list settings" msgstr "Einstellungen der Dateiliste" #: src/octoprint/templates/sidebar/files_header.jinja2:6 -#: src/octoprint/templates/tabs/timelapse.jinja2:108 +#: src/octoprint/templates/tabs/timelapse.jinja2:99 msgid "Sort by name" msgstr "Nach Name sortieren" @@ -3726,7 +3756,7 @@ msgid "Sort by upload date" msgstr "Nach Uploaddatum sortieren" #: src/octoprint/templates/sidebar/files_header.jinja2:8 -#: src/octoprint/templates/tabs/timelapse.jinja2:110 +#: src/octoprint/templates/tabs/timelapse.jinja2:101 msgid "Sort by file size" msgstr "Nach Größe sortieren" @@ -4019,7 +4049,7 @@ msgid "\n" " If the connectivity check is enabled, OctoPrint will regularly check if it's connected to the internet.\n" " This is useful to prevent resource intensive operations (such as checking for updates) if it's already\n" " clear that they won't succeed anyhow.\n" -msgstr "Wenn die Onlineprüfung aktiviert ist, wird OctoPrint in regelmäßigen Abständen prüfen, ob es mit dem Internet verbunden ist. Das ist nützlich um resourcenintensive Operationen zu verhindern (wie z.b. der Prüfung, ob Updates vorliegen), wenn es bereits klar ist, dass diese Operationen mangels Verbindung nicht durchgeführt werden können." +msgstr "Wenn die Onlineprüfung aktiviert ist, wird OctoPrint in regelmäßigen Abständen prüfen, ob es mit dem Internet verbunden ist. Das ist nützlich um resourcenintensive Operationen zu verhindern (wie z.b. der Prüfung, ob Updates vorliegen), wenn es bereits klar ist, dass diese Operationen mangels Verbindung nicht durchgeführt werden können.\n" #: src/octoprint/templates/snippets/settings/server/serverOnlineCheckEnabled.jinja2:4 msgid "Enable regular connectivity check" @@ -4124,6 +4154,18 @@ msgstr "Webcam vertikal flippen" msgid "Rotate webcam 90 degrees counter clockwise" msgstr "Webcam um 90° gegen den Uhrzeigersinn rotieren" +#: src/octoprint/templates/snippets/settings/webcam/webcamSnapshotSslValidation.jinja2:4 +msgid "Validate SSL on snapshot URL (if applicable)" +msgstr "SSL für Snapshot URL validieren (falls relevant)" + +#: src/octoprint/templates/snippets/settings/webcam/webcamSnapshotTimeout.jinja2:1 +msgid "Timeout for taking a snapshot" +msgstr "Timeout für die Aufnahme eines Snapshots" + +#: src/octoprint/templates/snippets/settings/webcam/webcamSnapshotTimeout.jinja2:2 +msgid "Snapshot timeout" +msgstr "Snapshot Timeout" + #: src/octoprint/templates/snippets/settings/webcam/webcamSnapshotUrl.jinja2:1 msgid "URL to use for retrieving webcam snapshot images for timelapse creation" msgstr "URL, die genutzt werden soll, um Einzelaufnahmen für die Zeitraffererstellung abzurufen" @@ -4226,7 +4268,7 @@ msgstr "Schrittgröße" #: src/octoprint/templates/tabs/control.jinja2:38 msgid "Hint: If you move your mouse over the picture, you enter keyboard control mode." -msgstr "Hinweis: Bewegen der Maus über das Bild aktiviert die Tastatursteuerung" +msgstr "Hinweis: Bewegen der Maus über das Bild aktiviert die Tastatursteuerung." #: src/octoprint/templates/tabs/control.jinja2:84 msgid "Feed rate:" @@ -4380,7 +4422,7 @@ msgstr "Ja, bitte visualisiere %(name)s unabhängig seiner Größe" #: src/octoprint/templates/tabs/temperature.jinja2:11 msgid "Current actual temperature as reported by your printer" -msgstr "Aktuelle vom Drucker gemeldete Temperatur " +msgstr "Aktuelle vom Drucker gemeldete Temperatur" #: src/octoprint/templates/tabs/temperature.jinja2:12 msgid "Current target temperature as reported by your printer" @@ -4496,86 +4538,78 @@ msgid "Timelapse post roll" msgstr "Timelapse-Postroll" #: src/octoprint/templates/tabs/timelapse.jinja2:59 -msgid "OctoPrint will take additional pictures to add this many seconds to the end of your rendered timelapse." -msgstr "OctoPrint wird zusätzliche Aufnahmen machen um so viele zusätzliche Sekunden zur gerenderten Zeitrafferaufnahme hinzuzufügen." - -#: src/octoprint/templates/tabs/timelapse.jinja2:66 -msgid "Capture post roll images" -msgstr "Bilder für Zeitraffernachlauf aufzeichnen" +msgid "OctoPrint will use the final picture to add this many seconds to the end of your rendered timelapse." +msgstr "OctoPrint wird die letzte Aufnahme verwenden um so viele zusätzliche Sekunden zur gerenderten Zeitrafferaufnahme hinzuzufügen." -#: src/octoprint/templates/tabs/timelapse.jinja2:67 -msgid "If this is unchecked, OctoPrint will simply repeat the last frame for the post roll instead of continuing to capture new frames." -msgstr "Wenn diese Option nicht angehakt ist wird OctoPrint einfach den letzten Frame der regulären Aufnahme für den Nachlauf verwenden, anstatt neue Frames aufzuzeichnen." - -#: src/octoprint/templates/tabs/timelapse.jinja2:73 +#: src/octoprint/templates/tabs/timelapse.jinja2:64 msgid "Retraction Z-Hop" msgstr "Retraction Z-Hop" -#: src/octoprint/templates/tabs/timelapse.jinja2:79 +#: src/octoprint/templates/tabs/timelapse.jinja2:70 msgid "Enter the retraction z-hop used in the firmware or the gcode file to trigger snapshots for the timelapse only if a real layer change happens. For this to work properly your retraction z-hop has to be different from your layerheight!" msgstr "Gib den Retraction Z-Hop Wert aus deiner Firmware oder den GCODE Dateien hier ein, um Snapshots für Zeitraffer nur bei echten Schichtwechseln auszulösen. Damit das korrekt funktionieren kann muss dein Retraction Z-Hop Wert anders als deine Schichthöhe sein!" -#: src/octoprint/templates/tabs/timelapse.jinja2:86 +#: src/octoprint/templates/tabs/timelapse.jinja2:77 msgid "Save as default" msgstr "Als Standard speichern" -#: src/octoprint/templates/tabs/timelapse.jinja2:87 +#: src/octoprint/templates/tabs/timelapse.jinja2:78 msgid "Check this to make your selected timelapse mode and options persist across restarts." msgstr "Auswählen um deinen gewählten Zeitraffermodus und -parameter über Neustarts hinweg zu persistieren." -#: src/octoprint/templates/tabs/timelapse.jinja2:94 +#: src/octoprint/templates/tabs/timelapse.jinja2:85 msgid "You have unsaved changes. Don't forget to save them." msgstr "Du hast ungespeicherte Änderungen. Vergiss nicht sie zu speichern." -#: src/octoprint/templates/tabs/timelapse.jinja2:95 +#: src/octoprint/templates/tabs/timelapse.jinja2:86 msgid "Save changes" msgstr "Änderungen speichern" -#: src/octoprint/templates/tabs/timelapse.jinja2:96 +#: src/octoprint/templates/tabs/timelapse.jinja2:87 msgid "Reset to active configuration" msgstr "Auf aktive Konfiguration zurücksetzen" -#: src/octoprint/templates/tabs/timelapse.jinja2:102 +#: src/octoprint/templates/tabs/timelapse.jinja2:93 msgid "Finished Timelapses" msgstr "Abgeschlossene Zeitraffer" -#: src/octoprint/templates/tabs/timelapse.jinja2:109 +#: src/octoprint/templates/tabs/timelapse.jinja2:100 msgid "Sort by date" msgstr "Nach Datum sortieren" -#: src/octoprint/templates/tabs/timelapse.jinja2:118 -#: src/octoprint/templates/tabs/timelapse.jinja2:163 +#: src/octoprint/templates/tabs/timelapse.jinja2:109 +#: src/octoprint/templates/tabs/timelapse.jinja2:154 msgid "Select all on this page" msgstr "Alles auf dieser Seite auswählen" -#: src/octoprint/templates/tabs/timelapse.jinja2:119 -#: src/octoprint/templates/tabs/timelapse.jinja2:164 +#: src/octoprint/templates/tabs/timelapse.jinja2:110 +#: src/octoprint/templates/tabs/timelapse.jinja2:155 msgid "Select all" msgstr "Alles auswählen" -#: src/octoprint/templates/tabs/timelapse.jinja2:121 -#: src/octoprint/templates/tabs/timelapse.jinja2:166 +#: src/octoprint/templates/tabs/timelapse.jinja2:112 +#: src/octoprint/templates/tabs/timelapse.jinja2:157 msgid "Clear selection" msgstr "Auswahl aufheben" -#: src/octoprint/templates/tabs/timelapse.jinja2:124 -#: src/octoprint/templates/tabs/timelapse.jinja2:169 +#: src/octoprint/templates/tabs/timelapse.jinja2:115 +#: src/octoprint/templates/tabs/timelapse.jinja2:160 msgid "Delete selected" msgstr "Ausgewählte Elemente löschen" -#: src/octoprint/templates/tabs/timelapse.jinja2:157 +#: src/octoprint/templates/tabs/timelapse.jinja2:148 msgid "Unrendered Timelapses" msgstr "Ungerenderte Zeitrafferaufnahmen" -#: src/octoprint/templates/tabs/timelapse.jinja2:176 +#: src/octoprint/templates/tabs/timelapse.jinja2:167 msgid "Frames" msgstr "Frames" -#: src/octoprint/templates/tabs/timelapse.jinja2:189 +#: src/octoprint/templates/tabs/timelapse.jinja2:180 msgid "Delete unrendered timelapse" msgstr "Ungerenderte Zeitrafferaufnahme löschen" -#: src/octoprint/templates/tabs/timelapse.jinja2:189 +#: src/octoprint/templates/tabs/timelapse.jinja2:180 msgid "Render timelapse" msgstr "Zeitrafferaufnahme rendern" @@ -4766,3 +4800,298 @@ msgstr "Zeitrafferaufnahme rendern" #~ msgid "The ready-to-go Raspberry Pi image with OctoPrint" #~ msgstr "Das fertige RaspberryPi Image mit OctoPrint" + +#~ msgid "Plugin installed" +#~ msgstr "Plugin installiert" + +#~ msgid "" +#~ "A plugin was installed successfully, " +#~ "however it was impossible to detect " +#~ "which one. Please Restart OctoPrint to" +#~ " make sure everything will be " +#~ "registered properly" +#~ msgstr "" +#~ "Ein Plugin wurde erfolgreich installiert, " +#~ "es war aber unmöglich zu detektieren," +#~ " welches. Bitte starte OctoPrint neu " +#~ "um sicherzustellen, dass alles ordnungsgemäß" +#~ " registriert wird." + +#~ msgid "Plugin \"%(name)s\" reinstalled" +#~ msgstr "Plugin \"%(name)s\" reinstalliert" + +#~ msgid "" +#~ "The plugin was reinstalled successfully, " +#~ "however it is blacklisted and therefore" +#~ " won't be loaded." +#~ msgstr "" +#~ "Das Plugin wurde erfolgreich reinstalliert," +#~ " es ist aber auf der Pluginblackliste" +#~ " und wird daher nicht geladen." + +#~ msgid "Plugin \"%(name)s\" installed" +#~ msgstr "Plugin \"%(name)s\" installiert" + +#~ msgid "" +#~ "The plugin was installed successfully, " +#~ "however it is blacklisted and therefore" +#~ " won't be loaded." +#~ msgstr "" +#~ "Das Plugin wurde erfolgreich installiert, " +#~ "es ist jedoch auf der Pluginblackliste" +#~ " und wird daher nicht geladen." + +#~ msgid "The plugin was reinstalled successfully" +#~ msgstr "Das Plugin wurde erfolgreich reinstalliert" + +#~ msgid "" +#~ "The plugin was reinstalled successfully, " +#~ "however a restart of OctoPrint is " +#~ "needed for that to take effect." +#~ msgstr "" +#~ "Das Plugin wurde erfolgreich reinstalliert," +#~ " es ist aber ein Neustart von " +#~ "OctoPrint notwendig bevor es genutzt " +#~ "werden kann." + +#~ msgid "" +#~ "The plugin was reinstalled successfully, " +#~ "however a reload of the page is" +#~ " needed for that to take effect." +#~ msgstr "" +#~ "Das Plugin wurde erfolgreich reinstalliert," +#~ " es ist aber ein Neuladen der " +#~ "Seite notwendig bevor es genutzt werden" +#~ " kann." + +#~ msgid "" +#~ "The plugin was reinstalled successfully, " +#~ "however a reconnect to the printer " +#~ "is needed for that to take effect." +#~ msgstr "" +#~ "Das Plugin wurde erfolgreich reinstalliert," +#~ " es ist aber eine Neuverbindung zum" +#~ " Drucker notwendig bevor es genutzt " +#~ "werden kann." + +#~ msgid "The plugin was installed successfully" +#~ msgstr "Das Plugin wurde erfolgreich installiert" + +#~ msgid "" +#~ "The plugin was installed successfully, " +#~ "however a restart of OctoPrint is " +#~ "needed for that to take effect." +#~ msgstr "" +#~ "Das Plugin wurde erfolgreich installiert, " +#~ "es ist jedoch ein Neustart von " +#~ "OctoPrint notwendig bevor es genutzt " +#~ "werden kann." + +#~ msgid "" +#~ "The plugin was installed successfully, " +#~ "however a reload of the page is" +#~ " needed for that to take effect." +#~ msgstr "" +#~ "Das Plugin wurde erfolgreich installiert, " +#~ "es ist jedoch ein Neuladen der " +#~ "Seite notwendig bevor es genutzt werden" +#~ " kann." + +#~ msgid "" +#~ "The plugin was installed successfully, " +#~ "however a reconnect to the printer " +#~ "is needed for that to take effect." +#~ msgstr "" +#~ "Das Plugin wurde erfolgreich installiert, " +#~ "es ist jedoch eine Neuverbindung zum " +#~ "Drucker notwendig bevor es genutzt " +#~ "werden kann." + +#~ msgid "Reinstalling the plugin from file failed: %(reason)s" +#~ msgstr "Reinstallation des Plugins aus Datei fehlgeschlagen: %(reason)s" + +#~ msgid "Reinstalling the plugin from \"%(source)s\" failed: %(reason)s" +#~ msgstr "" +#~ "Reinstallation des Plugins von \"%(source)s\"" +#~ " fehlgeschlagen: %(reason)s" + +#~ msgid "Installing the plugin from file failed: %(reason)s" +#~ msgstr "Installation des Plugins aus Datei fehlgeschlagen: %(reason)s" + +#~ msgid "Installing the plugin from \"%(source)s\" failed: %(reason)s" +#~ msgstr "Installation des Plugins von \"%(source)s\" fehlgeschlagen: %(reason)s" + +#~ msgid "" +#~ "Reinstalling the plugin from file " +#~ "failed, please see the log for " +#~ "details." +#~ msgstr "" +#~ "Reinstallation des Plugins aus Datei " +#~ "fehlgeschlagen, bitte konsultiere das Log " +#~ "für Details." + +#~ msgid "" +#~ "Reinstalling the plugin from \"%(source)s\"" +#~ " failed, please see the log for " +#~ "details." +#~ msgstr "" +#~ "Reinstallation des Plugins von \"%(source)s\"" +#~ " fehlgeschlagen, bitte konsultiere das Log" +#~ " für Details." + +#~ msgid "Installing the plugin from file failed, please see the log for details." +#~ msgstr "" +#~ "Installation des Plugins aus Datei " +#~ "fehlgeschlagen, bitte konsultiere das Log " +#~ "für Details." + +#~ msgid "" +#~ "Installing the plugin from \"%(source)s\" " +#~ "failed, please see the log for " +#~ "details." +#~ msgstr "" +#~ "Installation des Plugins von \"%(source)s\"" +#~ " fehlgeschlagen, bitte konsultiere das Log" +#~ " für Details." + +#~ msgid "Plugin \"%(name)s\" uninstalled" +#~ msgstr "Plugin \"%(name)s\" deinstalliert" + +#~ msgid "The plugin was uninstalled successfully" +#~ msgstr "Das Plugin wurde erfolgreich deinstalliert" + +#~ msgid "" +#~ "The plugin was uninstalled successfully, " +#~ "however a restart of OctoPrint is " +#~ "needed for that to take effect." +#~ msgstr "" +#~ "Das Plugin wurde erfolgreich deinstalliert," +#~ " es ist jedoch ein Neustart von " +#~ "OctoPrint notwendig." + +#~ msgid "" +#~ "The plugin was uninstalled successfully, " +#~ "however a reload of the page is" +#~ " needed for that to take effect." +#~ msgstr "" +#~ "Das Plugin wurde erfolgreich deinstalliert," +#~ " es ist jedoch ein Neuladen der " +#~ "Seite notwendig." + +#~ msgid "" +#~ "The plugin was uninstalled successfully, " +#~ "however a reconnect to the printer " +#~ "is needed for that to take effect." +#~ msgstr "" +#~ "Das Plugin wurde erfolgreich deinstalliert," +#~ " es ist jedoch eine Neuverbindung zum" +#~ " Drucker notwendig." + +#~ msgid "Uninstalling the plugin failed, please see the log for details." +#~ msgstr "" +#~ "Deinstallation des Plugins fehlgeschlagen, " +#~ "bitte konsultiere das Log für Details." + +#~ msgid "Plugin \"%(name)s\" enabled" +#~ msgstr "Plugin \"%(name)s\" aktiviert" + +#~ msgid "The plugin was enabled successfully." +#~ msgstr "Das Plugin wurde erfolgreich aktiviert." + +#~ msgid "" +#~ "The plugin was enabled successfully, " +#~ "however a restart of OctoPrint is " +#~ "needed for that to take effect." +#~ msgstr "" +#~ "Das Plugin wurde erfolgreich aktiviert, " +#~ "es ist jedoch ein Neustart von " +#~ "OctoPrint notwendig." + +#~ msgid "" +#~ "The plugin was enabled successfully, " +#~ "however a reload of the page is" +#~ " needed for that to take effect." +#~ msgstr "" +#~ "Das Plugin wurde erfolgreich aktiviert, " +#~ "es ist jedoch ein Neuladen der " +#~ "Seite notwendig." + +#~ msgid "" +#~ "The plugin was enabled successfully, " +#~ "however a reconnect to the printer " +#~ "is needed for that to take effect." +#~ msgstr "" +#~ "Das Plugin wurde erfolgreich aktiviert, " +#~ "es ist jedoch eine Neuverbindung zum " +#~ "Drucker notwendig." + +#~ msgid "Toggling the plugin failed: %(reason)s" +#~ msgstr "Togglen des Plugins fehlgeschalgen: %(reason)s" + +#~ msgid "Toggling the plugin failed, please see the log for details." +#~ msgstr "" +#~ "Togglen des Plugins fehlgeschlagen, bitte " +#~ "konsultiere das Log für Details." + +#~ msgid "Plugin \"%(name)s\" disabled" +#~ msgstr "Plugin \"%(name)s\" deaktiviert" + +#~ msgid "The plugin was disabled successfully." +#~ msgstr "Das Plugin wurde erfolgreich deaktiviert." + +#~ msgid "" +#~ "The plugin was disabled successfully, " +#~ "however a restart of OctoPrint is " +#~ "needed for that to take effect." +#~ msgstr "" +#~ "Das Plugin wurde erfolgreich deaktiviert, " +#~ "es ist jedoch ein Neustart von " +#~ "OctoPrint notwendig." + +#~ msgid "" +#~ "The plugin was disabled successfully, " +#~ "however a reload of the page is" +#~ " needed for that to take effect." +#~ msgstr "" +#~ "Das Plugin wurde erfolgreich deaktiviert, " +#~ "es ist jedoch ein Neuladen der " +#~ "Seite notwendig." + +#~ msgid "" +#~ "The plugin was disabled successfully, " +#~ "however a reconnect to the printer " +#~ "is needed for that to take effect." +#~ msgstr "" +#~ "Das Plugin wurde erfolgreich deaktiviert, " +#~ "es ist jedoch eine Neuverbindung zum " +#~ "Drucker notwendig." + +#~ msgid "just now" +#~ msgstr "gerade eben" + +#~ msgid "SD status interval" +#~ msgstr "SD-Status-Intervall" + +#~ msgid "Log communication to serial.log (might negatively impact performance)" +#~ msgstr "" +#~ "Logge die Kommunikation in das " +#~ "serial.log (kann die Performance negativ " +#~ "beeinflussen)" + +#~ msgid "Most Marlin < v1.1.0" +#~ msgstr "Die meisten Varianten von Marlin < v1.1.0" + +#~ msgid "Capture post roll images" +#~ msgstr "Bilder für Zeitraffernachlauf aufzeichnen" + +#~ msgid "" +#~ "If this is unchecked, OctoPrint will " +#~ "simply repeat the last frame for " +#~ "the post roll instead of continuing " +#~ "to capture new frames." +#~ msgstr "" +#~ "Wenn diese Option nicht angehakt ist " +#~ "wird OctoPrint einfach den letzten Frame" +#~ " der regulären Aufnahme für den " +#~ "Nachlauf verwenden, anstatt neue Frames " +#~ "aufzuzeichnen." diff --git a/translations/de/LC_MESSAGES/messages.mo b/translations/de/LC_MESSAGES/messages.mo index 700ea0115f3bcec4324de3b9f5211e40e233041b..f779144bdcd5d7286c930131bb90613094b86da1 100644 GIT binary patch delta 23384 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 delta 24295 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�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 z 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 diff --git a/translations/de/LC_MESSAGES/messages.po b/translations/de/LC_MESSAGES/messages.po index ab19296359..4a3c05f82e 100644 --- a/translations/de/LC_MESSAGES/messages.po +++ b/translations/de/LC_MESSAGES/messages.po @@ -7,8 +7,8 @@ msgid "" msgstr "" "Project-Id-Version: OctoPrint\n" "Report-Msgid-Bugs-To: i18n@octoprint.org\n" -"POT-Creation-Date: 2017-12-05 12:29+0100\n" -"PO-Revision-Date: 2017-12-05 12:31+0100\n" +"POT-Creation-Date: 2018-03-19 13:41+0100\n" +"PO-Revision-Date: 2018-03-19 13:42+0100\n" "Last-Translator: Gina Häußge \n" "Language: de\n" "Language-Team: German (http://www.transifex.com/projects/p/octoprint/language/de/)\n" @@ -46,12 +46,12 @@ msgid "You can edit your announcement subscriptions under Settings > Announcemen msgstr "Du kannst deine Benachrichtigungsabonnements unter Einstellungen > Benachrichtigungen konfigurieren." #: src/octoprint/plugins/announcements/static/js/announcements.js:271 -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:957 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:973 msgid "Later" msgstr "Später" #: src/octoprint/plugins/announcements/static/js/announcements.js:277 -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:964 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:980 msgid "Mark read" msgstr "Gelesen" @@ -81,15 +81,16 @@ msgstr "Konfigurierte Kanäle" #: src/octoprint/plugins/cura/templates/snippets/settings/cura/profileImporter.jinja2:27 #: src/octoprint/plugins/cura/templates/snippets/settings/cura/profiles.jinja2:3 #: src/octoprint/plugins/cura/templates/snippets/settings/cura/profiles.jinja2:9 +#: src/octoprint/plugins/logging/templates/logging_settings.jinja2:5 +#: src/octoprint/plugins/logging/templates/logging_settings.jinja2:11 +#: src/octoprint/plugins/logging/templates/logging_settings.jinja2:50 #: src/octoprint/plugins/pluginmanager/templates/pluginmanager_settings.jinja2:43 #: src/octoprint/templates/dialogs/settings/accesscontrol.jinja2:4 -#: src/octoprint/templates/dialogs/settings/logs.jinja2:6 -#: src/octoprint/templates/dialogs/settings/logs.jinja2:12 #: src/octoprint/templates/dialogs/settings/terminalfilters.jinja2:3 #: src/octoprint/templates/snippets/settings/printerprofiles/profileEditorGeneral.jinja2:3 #: src/octoprint/templates/snippets/settings/printerprofiles/profiles.jinja2:4 -#: src/octoprint/templates/tabs/timelapse.jinja2:130 -#: src/octoprint/templates/tabs/timelapse.jinja2:175 +#: src/octoprint/templates/tabs/timelapse.jinja2:121 +#: src/octoprint/templates/tabs/timelapse.jinja2:166 msgid "Name" msgstr "Name" @@ -362,6 +363,7 @@ msgstr "Bitte stelle sicher, dass die folgenden Einstellungen deinem Drucker ent #: src/octoprint/plugins/corewizard/templates/corewizard_printerprofile_wizard.jinja2:7 #: src/octoprint/plugins/cura/templates/cura_settings.jinja2:1 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:3 #: src/octoprint/templates/snippets/settings/printerprofiles/profileEditor.jinja2:9 #: src/octoprint/templates/tabs/control.jinja2:114 msgid "General" @@ -446,22 +448,22 @@ msgid "

\n" msgstr "

Um Snapshots zu Zeitrafferaufnahmen zu rendern muss OctoPrint auch den korrekten Pfad zu FFMPEG wissen.

" #: src/octoprint/plugins/cura/static/js/cura.js:234 -#: src/octoprint/static/js/app/viewmodels/settings.js:357 +#: src/octoprint/static/js/app/viewmodels/settings.js:387 msgid "The path doesn't exist" msgstr "Der Pfad existiert nicht" #: src/octoprint/plugins/cura/static/js/cura.js:236 -#: src/octoprint/static/js/app/viewmodels/settings.js:359 +#: src/octoprint/static/js/app/viewmodels/settings.js:389 msgid "The path is not a file" msgstr "Der Pfad ist keine Datei" #: src/octoprint/plugins/cura/static/js/cura.js:238 -#: src/octoprint/static/js/app/viewmodels/settings.js:361 +#: src/octoprint/static/js/app/viewmodels/settings.js:391 msgid "The path is not an executable" msgstr "Der Pfad ist nicht ausführbar" #: src/octoprint/plugins/cura/static/js/cura.js:241 -#: src/octoprint/static/js/app/viewmodels/settings.js:364 +#: src/octoprint/static/js/app/viewmodels/settings.js:394 msgid "The path is valid" msgstr "Der Pfad ist valide" @@ -597,15 +599,15 @@ msgid "Confirm" msgstr "Bestätigen" #: src/octoprint/plugins/cura/templates/snippets/settings/cura/profiles.jinja2:3 -#: src/octoprint/templates/dialogs/settings/logs.jinja2:6 +#: src/octoprint/plugins/logging/templates/logging_settings.jinja2:5 msgid "Sort by" msgstr "Sortieren" #: src/octoprint/plugins/cura/templates/snippets/settings/cura/profiles.jinja2:3 +#: src/octoprint/plugins/logging/templates/logging_settings.jinja2:5 #: src/octoprint/plugins/pluginmanager/templates/pluginmanager_settings.jinja2:142 -#: src/octoprint/templates/dialogs/settings/logs.jinja2:6 #: src/octoprint/templates/sidebar/files_header.jinja2:6 -#: src/octoprint/templates/tabs/timelapse.jinja2:108 +#: src/octoprint/templates/tabs/timelapse.jinja2:99 msgid "ascending" msgstr "aufsteigend" @@ -622,6 +624,64 @@ msgstr "Profil entfernen" msgid "Without this plugin your OctoPrint instance will no longer be discoverable on the network via Bonjour and uPnP." msgstr "Ohne dieses Plugin wird deine OctoPrint Instanz nicht mehr in deinem Netzwerk mittels Bonjour oder uPnP automatisch auffindbar sein." +#: src/octoprint/plugins/logging/templates/logging_settings.jinja2:1 +msgid "Logs" +msgstr "Logs" + +#: src/octoprint/plugins/logging/templates/logging_settings.jinja2:5 +msgid "Modification date" +msgstr "Änderungsdatum" + +#: src/octoprint/plugins/logging/templates/logging_settings.jinja2:5 +#: src/octoprint/plugins/pluginmanager/templates/pluginmanager_settings.jinja2:143 +#: src/octoprint/templates/sidebar/files_header.jinja2:7 +#: src/octoprint/templates/sidebar/files_header.jinja2:8 +#: src/octoprint/templates/tabs/timelapse.jinja2:100 +#: src/octoprint/templates/tabs/timelapse.jinja2:101 +msgid "descending" +msgstr "absteigend" + +#: src/octoprint/plugins/logging/templates/logging_settings.jinja2:5 +#: src/octoprint/plugins/logging/templates/logging_settings.jinja2:12 +#: src/octoprint/templates/sidebar/files.jinja2:18 +#: src/octoprint/templates/sidebar/files.jinja2:33 +#: src/octoprint/templates/sidebar/files.jinja2:44 +#: src/octoprint/templates/tabs/timelapse.jinja2:122 +#: src/octoprint/templates/tabs/timelapse.jinja2:168 +msgid "Size" +msgstr "Größe" + +#: src/octoprint/plugins/logging/templates/logging_settings.jinja2:13 +msgid "Date" +msgstr "Datum" + +#: src/octoprint/plugins/logging/templates/logging_settings.jinja2:14 +#: src/octoprint/templates/dialogs/settings/accesscontrol.jinja2:7 +#: src/octoprint/templates/snippets/settings/printerprofiles/profiles.jinja2:6 +#: src/octoprint/templates/tabs/timelapse.jinja2:123 +#: src/octoprint/templates/tabs/timelapse.jinja2:169 +msgid "Action" +msgstr "Aktion" + +#: src/octoprint/plugins/logging/templates/logging_settings.jinja2:46 +msgid "Logging Levels" +msgstr "Logginglevel" + +#: src/octoprint/plugins/logging/templates/logging_settings.jinja2:51 +msgid "Level" +msgstr "Level" + +#: src/octoprint/plugins/logging/templates/logging_settings.jinja2:68 +#: src/octoprint/templates/sidebar/files.jinja2:23 +#: src/octoprint/templates/sidebar/files.jinja2:36 +#: src/octoprint/templates/sidebar/files.jinja2:46 +msgid "Remove" +msgstr "Entfernen" + +#: src/octoprint/plugins/logging/templates/logging_settings.jinja2:87 +msgid "Add" +msgstr "Hinzufügen" + #: src/octoprint/plugins/octopi_support/__init__.py:164 msgid "Without this plugin OctoPrint will no longer be able to provide additional information about your OctoPi instance,which will make it more tricky to help you if you need support." msgstr "Ohne dieses Plugin kann OctoPrint nicht mehr zusätzliche Informationen über deine OctoPi Instanz zur Verfügung stellen, was es ggf. schwieriger macht, dir zu helfen, falls du Support brauchst." @@ -642,385 +702,246 @@ msgstr "Über OctoPi" msgid "Plugin Manager" msgstr "Pluginmanager" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:211 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:221 msgid "There are no plugin notices. Great!" msgstr "Keine Nachrichten zu deinen Plugins. Super!" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:213 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:223 msgid "There is a plugin notice for one of your installed plugins." msgstr "Es gibt eine Nachricht zu einem deiner installierten Plugins." -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:215 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:225 #, python-format msgid "There are %(count)d plugin notices for one or more of your installed plugins." msgstr "Es gibt %(count)d Nachrichten zu einem oder mehreren deiner installierten Plugins." -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:310 -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:533 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:320 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:543 msgid "Installing plugin..." msgstr "Installiere Plugin..." -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:310 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:320 msgid "Installing plugin from uploaded archive..." msgstr "Installiere Plugin von hochgeladenem Archiv..." -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:331 -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:457 -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:557 -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:595 -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:758 -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1163 -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1214 -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1232 -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1250 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:341 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:467 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:567 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:605 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:795 msgid "Something went wrong" msgstr "Etwas ist schief gegangen" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:332 -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:458 -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:558 -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:596 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:342 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:468 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:568 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:606 msgid "Please consult octoprint.log for details" msgstr "Bitte konsultiere octoprint.log für Details" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:477 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:487 #, python-format msgid "You are about to disable \"%(name)s\"." msgstr "Du bist im Begriff \"%(name)s\" zu deaktivieren." -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:480 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:490 msgid "This is not recommended" msgstr "Das ist nicht empfohlen" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:482 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:492 msgid "Do you still want to disable it?" msgstr "Möchtest du es immer noch deaktivieren?" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:483 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:493 msgid "Keep enabled" msgstr "Aktiviert lassen" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:484 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:494 msgid "Disable anyway" msgstr "Trotzdem deaktivieren" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:535 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:545 #, python-format msgid "Installing plugin \"%(name)s\" from %(url)s..." msgstr "Installiere Plugin \"%(name)s\" von %(url)s..." -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:537 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:547 #, python-format msgid "Installing plugin from %(url)s..." msgstr "Installiere Plugin von %(url)s..." -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:540 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:550 msgid "Reinstalling plugin..." msgstr "Reinstalliere Plugin..." -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:541 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:551 #, python-format msgid "Reinstalling plugin \"%(name)s\" from %(url)s..." msgstr "Reinstalliere Plugin \"%(name)s\" von %(url)s..." -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:587 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:597 msgid "Uninstalling plugin..." msgstr "Deinstalliere Plugin..." -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:587 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:597 #, python-format msgid "Uninstalling plugin \"%(name)s\"" msgstr "Deinstalliere Plugin \"%(name)s\"" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:701 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:711 msgid "Reinstall" msgstr "Reinstallieren" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:701 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:711 #: src/octoprint/plugins/pluginmanager/templates/pluginmanager_settings.jinja2:198 #: src/octoprint/plugins/pluginmanager/templates/pluginmanager_settings.jinja2:214 msgid "Install" msgstr "Installieren" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:701 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:711 msgid "Disabled" msgstr "Deaktiviert" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:701 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:711 msgid "Incompatible" msgstr "Inkompatibel" +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:720 +msgid "Plugin management log" +msgstr "Pluginmanagementlog" + +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:728 +#, python-format +msgid "%(count)d earlier actions..." +msgstr "%(count)d frühere Aktionen..." + +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:730 +#, python-format +msgid "%(count)d earlier action" +msgstr "%(count)d earlier Aktion" + #: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:741 +#, python-format +msgid "Install %(plugin)s: %(result)s" +msgstr "%(plugin)s installiert: %(result)s" + +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:745 +#, python-format +msgid "Uninstall %(plugin)s: %(result)s" +msgstr "%(plugin)s deinstalliert: %(result)s" + +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:749 +#, python-format +msgid "Enable %(plugin)s: %(result)s" +msgstr "%(plugin)s aktiviert: %(result)s" + +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:753 +#, python-format +msgid "Disable %(plugin)s: %(result)s" +msgstr "%(plugin)s deaktiviert: %(result)s" + +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:770 +msgid "A restart is needed for the changes to take effect." +msgstr "Ein Neustart von OctoPrint ist notwendig, damit die Änderungen wirksam werden." + +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:778 msgid "Restart now" msgstr "Jetzt neu starten" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:746 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:783 msgid "This will restart your OctoPrint server.

This action may disrupt any ongoing print jobs (depending on your printer's controller and general setup that might also apply to prints run directly from your printer's internal storage)." msgstr "Dies wird deinen OctoPrint server neu starten

Diese Aktion wird laufende Druckaufträge unterbrechen (abhängig von deinem Druckerkontroller und generellem Setup gilt das auch für Druckaufträge, die du direkt von der SD deines Druckers laufen lässt)." -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:752 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:789 msgid "Restart in progress" msgstr "Neustart findet statt" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:753 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:790 msgid "The server is now being restarted in the background" msgstr "Der Server wird nun im Hintergrund neu gestartet" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:759 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:796 msgid "Trying to restart the server produced an error, please check octoprint.log for details. You'll have to restart manually." msgstr "Beim Versuch, den Server neu zu starten, ist ein Fehler aufgetreten. Bitte konsultiere octoprint.log für Details. Du musst manuell neu starten." -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:781 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:809 +msgid "A refresh is needed for the changes to take effect." +msgstr "Ein Neuladen von OctoPrint ist notwendig, damit die Änderungen wirksam werden." + +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:816 #: src/octoprint/templates/overlays/reloadui.jinja2:14 msgid "Reload now" msgstr "Jetzt neu laden" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:847 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:825 +msgid "A reconnect to the printer is needed for the changes to take effect." +msgstr "Ein Reconnect zum Drucker ist notwendig, damit die Änderungen wirksam werden." + +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:862 msgid "Error!" msgstr "Fehler!" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:850 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:865 msgid "Done!" msgstr "Fertig!" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:875 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:891 msgid "Blacklisted" msgstr "Geblacklistet" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:877 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:893 msgid "Disabled due to active safe mode" msgstr "Deaktiviert wegen aktiviertem Safe Mode" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:879 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:895 msgid "Enable Plugin" msgstr "Plugin enablen" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:882 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:898 msgid "Disable Plugin" msgstr "Plugin disablen" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:902 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:918 #, python-format msgid "There are %(count)d notices (%(important)d marked as important) available regarding this plugin - click to show!" msgstr "Es gibt %(count)d Nachrichten (%(important)d als wichtig markiert) zu diesem Plugin - hier klicken um sie anzuzeigen!" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:904 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:920 #, python-format msgid "There are %(count)d notices available regarding this plugin - click to show!" msgstr "Es gibt %(count)d Nachrichten zu diesem Plugin - hier klicken um sie anzuzeigen!" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:908 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:924 msgid "There is an important notice available regarding this plugin - click to show!" msgstr "Es gibt eine wichtige Nachricht zu diesem Plugin - hier klicken um sie anzuzeigen!" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:910 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:926 msgid "There is a notice available regarding this plugin - click to show!" msgstr "Es gibt eine Nachricht zu diesem Plugin - hier klicken um sie anzuzeigen!" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:924 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:940 #, python-format msgid "Important notice regarding plugin \"%(name)s\"" msgstr "Wichtige Nachricht zu Plugin \"%(name)s\"" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:926 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:942 #, python-format msgid "Notice regarding plugin \"%(name)s\"" msgstr "Nachricht zu Plugin \"%(name)s\"" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:933 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:949 #, python-format msgid "Affected versions: %(versions)s" msgstr "Betroffene Versionen: %(versions)s" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:935 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:951 msgid "Affected versions: all" msgstr "Betroffene Versionen: alle" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:940 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:956 msgid "Read more..." msgstr "Mehr..." -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1133 -msgid "Plugin installed" -msgstr "Plugin installiert" - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1134 -msgid "A plugin was installed successfully, however it was impossible to detect which one. Please Restart OctoPrint to make sure everything will be registered properly" -msgstr "Ein Plugin wurde erfolgreich installiert, es war aber unmöglich zu detektieren, welches. Bitte starte OctoPrint neu um sicherzustellen, dass alles ordnungsgemäß registriert wird." - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1140 -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1150 -#, python-format -msgid "Plugin \"%(name)s\" reinstalled" -msgstr "Plugin \"%(name)s\" reinstalliert" - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1141 -msgid "The plugin was reinstalled successfully, however it is blacklisted and therefore won't be loaded." -msgstr "Das Plugin wurde erfolgreich reinstalliert, es ist aber auf der Pluginblackliste und wird daher nicht geladen." - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1143 -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1156 -#, python-format -msgid "Plugin \"%(name)s\" installed" -msgstr "Plugin \"%(name)s\" installiert" - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1144 -msgid "The plugin was installed successfully, however it is blacklisted and therefore won't be loaded." -msgstr "Das Plugin wurde erfolgreich installiert, es ist jedoch auf der Pluginblackliste und wird daher nicht geladen." - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1151 -msgid "The plugin was reinstalled successfully" -msgstr "Das Plugin wurde erfolgreich reinstalliert" - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1152 -msgid "The plugin was reinstalled successfully, however a restart of OctoPrint is needed for that to take effect." -msgstr "Das Plugin wurde erfolgreich reinstalliert, es ist aber ein Neustart von OctoPrint notwendig bevor es genutzt werden kann." - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1153 -msgid "The plugin was reinstalled successfully, however a reload of the page is needed for that to take effect." -msgstr "Das Plugin wurde erfolgreich reinstalliert, es ist aber ein Neuladen der Seite notwendig bevor es genutzt werden kann." - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1154 -msgid "The plugin was reinstalled successfully, however a reconnect to the printer is needed for that to take effect." -msgstr "Das Plugin wurde erfolgreich reinstalliert, es ist aber eine Neuverbindung zum Drucker notwendig bevor es genutzt werden kann." - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1157 -msgid "The plugin was installed successfully" -msgstr "Das Plugin wurde erfolgreich installiert" - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1158 -msgid "The plugin was installed successfully, however a restart of OctoPrint is needed for that to take effect." -msgstr "Das Plugin wurde erfolgreich installiert, es ist jedoch ein Neustart von OctoPrint notwendig bevor es genutzt werden kann." - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1159 -msgid "The plugin was installed successfully, however a reload of the page is needed for that to take effect." -msgstr "Das Plugin wurde erfolgreich installiert, es ist jedoch ein Neuladen der Seite notwendig bevor es genutzt werden kann." - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1160 -msgid "The plugin was installed successfully, however a reconnect to the printer is needed for that to take effect." -msgstr "Das Plugin wurde erfolgreich installiert, es ist jedoch eine Neuverbindung zum Drucker notwendig bevor es genutzt werden kann." - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1176 -#, python-format -msgid "Reinstalling the plugin from file failed: %(reason)s" -msgstr "Reinstallation des Plugins aus Datei fehlgeschlagen: %(reason)s" - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1178 -#, python-format -msgid "Reinstalling the plugin from \"%(source)s\" failed: %(reason)s" -msgstr "Reinstallation des Plugins von \"%(source)s\" fehlgeschlagen: %(reason)s" - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1182 -#, python-format -msgid "Installing the plugin from file failed: %(reason)s" -msgstr "Installation des Plugins aus Datei fehlgeschlagen: %(reason)s" - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1184 -#, python-format -msgid "Installing the plugin from \"%(source)s\" failed: %(reason)s" -msgstr "Installation des Plugins von \"%(source)s\" fehlgeschlagen: %(reason)s" - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1190 -msgid "Reinstalling the plugin from file failed, please see the log for details." -msgstr "Reinstallation des Plugins aus Datei fehlgeschlagen, bitte konsultiere das Log für Details." - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1192 -#, python-format -msgid "Reinstalling the plugin from \"%(source)s\" failed, please see the log for details." -msgstr "Reinstallation des Plugins von \"%(source)s\" fehlgeschlagen, bitte konsultiere das Log für Details." - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1196 -msgid "Installing the plugin from file failed, please see the log for details." -msgstr "Installation des Plugins aus Datei fehlgeschlagen, bitte konsultiere das Log für Details." - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1198 -#, python-format -msgid "Installing the plugin from \"%(source)s\" failed, please see the log for details." -msgstr "Installation des Plugins von \"%(source)s\" fehlgeschlagen, bitte konsultiere das Log für Details." - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1208 -#, python-format -msgid "Plugin \"%(name)s\" uninstalled" -msgstr "Plugin \"%(name)s\" deinstalliert" - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1209 -msgid "The plugin was uninstalled successfully" -msgstr "Das Plugin wurde erfolgreich deinstalliert" - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1210 -msgid "The plugin was uninstalled successfully, however a restart of OctoPrint is needed for that to take effect." -msgstr "Das Plugin wurde erfolgreich deinstalliert, es ist jedoch ein Neustart von OctoPrint notwendig." - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1211 -msgid "The plugin was uninstalled successfully, however a reload of the page is needed for that to take effect." -msgstr "Das Plugin wurde erfolgreich deinstalliert, es ist jedoch ein Neuladen der Seite notwendig." - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1212 -msgid "The plugin was uninstalled successfully, however a reconnect to the printer is needed for that to take effect." -msgstr "Das Plugin wurde erfolgreich deinstalliert, es ist jedoch eine Neuverbindung zum Drucker notwendig." - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1216 -#, python-format -msgid "Uninstalling the plugin failed: %(reason)s" -msgstr "Deinstallation des Plugins fehlgeschlagen: %(reason)s" - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1218 -msgid "Uninstalling the plugin failed, please see the log for details." -msgstr "Deinstallation des Plugins fehlgeschlagen, bitte konsultiere das Log für Details." - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1226 -#, python-format -msgid "Plugin \"%(name)s\" enabled" -msgstr "Plugin \"%(name)s\" aktiviert" - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1227 -msgid "The plugin was enabled successfully." -msgstr "Das Plugin wurde erfolgreich aktiviert." - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1228 -msgid "The plugin was enabled successfully, however a restart of OctoPrint is needed for that to take effect." -msgstr "Das Plugin wurde erfolgreich aktiviert, es ist jedoch ein Neustart von OctoPrint notwendig." - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1229 -msgid "The plugin was enabled successfully, however a reload of the page is needed for that to take effect." -msgstr "Das Plugin wurde erfolgreich aktiviert, es ist jedoch ein Neuladen der Seite notwendig." - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1230 -msgid "The plugin was enabled successfully, however a reconnect to the printer is needed for that to take effect." -msgstr "Das Plugin wurde erfolgreich aktiviert, es ist jedoch eine Neuverbindung zum Drucker notwendig." - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1234 -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1252 -#, python-format -msgid "Toggling the plugin failed: %(reason)s" -msgstr "Togglen des Plugins fehlgeschalgen: %(reason)s" - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1236 -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1254 -msgid "Toggling the plugin failed, please see the log for details." -msgstr "Togglen des Plugins fehlgeschlagen, bitte konsultiere das Log für Details." - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1244 -#, python-format -msgid "Plugin \"%(name)s\" disabled" -msgstr "Plugin \"%(name)s\" deaktiviert" - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1245 -msgid "The plugin was disabled successfully." -msgstr "Das Plugin wurde erfolgreich deaktiviert." - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1246 -msgid "The plugin was disabled successfully, however a restart of OctoPrint is needed for that to take effect." -msgstr "Das Plugin wurde erfolgreich deaktiviert, es ist jedoch ein Neustart von OctoPrint notwendig." - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1247 -msgid "The plugin was disabled successfully, however a reload of the page is needed for that to take effect." -msgstr "Das Plugin wurde erfolgreich deaktiviert, es ist jedoch ein Neuladen der Seite notwendig." - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1248 -msgid "The plugin was disabled successfully, however a reconnect to the printer is needed for that to take effect." -msgstr "Das Plugin wurde erfolgreich deaktiviert, es ist jedoch eine Neuverbindung zum Drucker notwendig." - #: src/octoprint/plugins/pluginmanager/templates/pluginmanager_settings.jinja2:3 msgid "Take note that all plugin management functionality is disabled while your printer is printing." msgstr "Bitte beachte dass jegliche Pluginmanagementfunktionen während des Druckens deaktiviert sind." @@ -1127,15 +1048,6 @@ msgstr "Nach Titel sortieren" msgid "Sort by publication date" msgstr "Nach Veröffentlichungsdatum sortieren" -#: src/octoprint/plugins/pluginmanager/templates/pluginmanager_settings.jinja2:143 -#: src/octoprint/templates/dialogs/settings/logs.jinja2:6 -#: src/octoprint/templates/sidebar/files_header.jinja2:7 -#: src/octoprint/templates/sidebar/files_header.jinja2:8 -#: src/octoprint/templates/tabs/timelapse.jinja2:109 -#: src/octoprint/templates/tabs/timelapse.jinja2:110 -msgid "descending" -msgstr "absteigend" - #: src/octoprint/plugins/pluginmanager/templates/pluginmanager_settings.jinja2:145 msgid "Only show uninstalled plugins" msgstr "Nur uninstallierte Plugins anzeigen" @@ -1192,7 +1104,9 @@ msgstr "Das sieht nicht aus wie ein valides Pluginarchiv. Valide Pluginarchive s #: src/octoprint/plugins/pluginmanager/templates/pluginmanager_settings.jinja2:220 #: src/octoprint/plugins/softwareupdate/templates/softwareupdate_settings.jinja2:64 -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:121 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:147 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:247 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:307 #: src/octoprint/templates/dialogs/settings/webcam.jinja2:9 #: src/octoprint/templates/dialogs/settings/webcam.jinja2:23 #: src/octoprint/templates/snippets/settings/server/serverPluginBlacklist.jinja2:5 @@ -1282,12 +1196,28 @@ msgstr "Abbruch" msgid "Save" msgstr "Speichern" -#: src/octoprint/plugins/softwareupdate/__init__.py:589 +#: src/octoprint/plugins/printer_safety_check/__init__.py:44 +msgid "Printer Safety Warning" +msgstr "Druckersicherheitswarnung" + +#: src/octoprint/plugins/printer_safety_check/__init__.py:105 +msgid "Without this plugin OctoPrint will no longer be able to check if the printer it is connected to has a known safetyissue and inform you about that fact." +msgstr "Ohne dieses Plugin wird OctoPrint nicht länger in der Lage sein, dich über bekannte Sicherheitsprobleme mit verbundenen Druckern zu informieren." + +#: src/octoprint/plugins/printer_safety_check/templates/printer_safety_check_sidebar.jinja2:1 +msgid "Warning!" +msgstr "Warnung!" + +#: src/octoprint/plugins/printer_safety_check/templates/printer_safety_check_sidebar.jinja2:5 +msgid "Learn more..." +msgstr "Mehr erfahren..." + +#: src/octoprint/plugins/softwareupdate/__init__.py:570 #: src/octoprint/plugins/softwareupdate/templates/softwareupdate_wizard.jinja2:1 msgid "Software Update" msgstr "Software Update" -#: src/octoprint/plugins/softwareupdate/__init__.py:999 +#: src/octoprint/plugins/softwareupdate/__init__.py:982 #: src/octoprint/server/views.py:571 #: src/octoprint/static/js/app/viewmodels/appearance.js:13 #: src/octoprint/static/js/app/viewmodels/appearance.js:18 @@ -1298,7 +1228,7 @@ msgstr "Software Update" msgid "OctoPrint" msgstr "OctoPrint" -#: src/octoprint/plugins/softwareupdate/__init__.py:1166 +#: src/octoprint/plugins/softwareupdate/__init__.py:1142 msgid "Without this plugin OctoPrint will no longer be able to update itself or any of your installed plugins which might put your system at risk." msgstr "Ohne dieses Plugin wird OctoPrint nicht länger in der Lage sein, sich selbst oder deine installierten Plugins zu aktualisierten. Das könnte dein System gefährden." @@ -1319,156 +1249,159 @@ msgstr "%(name)s: %(version)s" msgid "unknown" msgstr "unbekannt" -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:309 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:307 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:511 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:525 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:682 +msgid "Updating..." +msgstr "Aktualisiere..." + +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:307 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:525 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:682 +msgid "Updating, please wait." +msgstr "Aktualisiere gerade, bitte warten." + +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:314 msgid "There are updates available for the following components:" msgstr "Es gibt Aktualisierungen für die folgenden Komponenten:" -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:317 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:322 #: src/octoprint/plugins/softwareupdate/templates/softwareupdate.jinja2:14 msgid "Release Notes" msgstr "Release Notes" -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:323 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:328 msgid "Those components marked with can be updated directly." msgstr "Die mit markierten Komponenten können direkt aktualisiert werden." -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:326 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:331 msgid "To have updates applied, get in touch with an administrator of this OctoPrint instance." msgstr "Um Updates durchzuführen wende dich bitte an einen Administrator dieser OctoPrint Instanz." -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:332 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:337 msgid "Update Available" msgstr "Aktualisierung verfügbar" -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:344 -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:370 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:349 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:375 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:347 msgid "Ignore" msgstr "Ignorieren" -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:348 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:353 msgid "You can make this message display again via \"Settings\" > \"Software Update\" > \"Check for update now\"" msgstr "Du kannst diese Nachricht erneut anzeigen lassen mittels \"Einstellungen\" > \"Software Update\" > \"Jetzt nach Aktualisierungen suchen\"" -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:352 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:357 msgid "Update now" msgstr "Jetzt aktualisieren" -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:390 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:395 msgid "Everything is up-to-date" msgstr "Alles ist auf dem neusten Stand" -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:416 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:421 msgid "No internet connection" msgstr "Keine Onlineverbindung" -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:420 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:425 msgid "Update not possible" msgstr "Update nicht möglich" -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:431 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:436 msgid "Unknown update check, configuration ok?" msgstr "Unbekannter Updatechecktyp, Konfiguration in Ordnung?" -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:434 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:439 msgid "Cannot check for update, need online connection" msgstr "Updatecheck ist nicht mögich, benötige Onlineverbindung" -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:437 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:442 msgid "Network error while checking for update" msgstr "Netzwerkfehler bei Updatecheck" -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:440 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:445 msgid "Unknown error while checking for update, please check the logs" msgstr "Unbekannter Fehler beim Updatecheck, bitte prüfe die Logs" -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:506 -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:520 -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:681 -msgid "Updating..." -msgstr "Aktualisiere..." - -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:507 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:512 msgid "Now updating, please wait." msgstr "Aktualisiere gerade, bitte warten." -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:520 -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:681 -msgid "Updating, please wait." -msgstr "Aktualisiere gerade, bitte warten." - -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:525 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:530 msgid "Update not started!" msgstr "Aktualisierung nicht gestartet!" -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:526 -msgid "The update could not be started. Is it already active? Please consult the log for details." -msgstr "Die Aktualisierung konnte nicht gestartet werden. Läuft bereits eine? Bitte konsultiere das Log für Details." +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:531 +msgid "The update could not be started. Is it already active? Please consult octoprint.log for details." +msgstr "Die Aktualisierung konnte nicht gestartet werden. Läuft bereits eine? Bitte konsultiere octoprint.log für Details." -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:548 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:553 msgid "Can't update while printing" msgstr "Aktualisierung nicht möglich während gedruckt wird" -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:549 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:554 msgid "A print job is currently in progress. Updating will be prevented until it is done." msgstr "Ein Druckjob ist zur Zeit aktiv. Aktualisierungen werden unterbunden bis er fertig ist." -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:684 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:699 #, python-format msgid "Now updating %(name)s to %(version)s" msgstr "Aktualisiere %(name)s auf %(version)s" -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:703 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:718 msgid "Update successful, restarting!" msgstr "Aktualisierung erfolgreich, starte neu!" -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:704 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:719 msgid "The update finished successfully and the server will now be restarted." msgstr "Die Aktualisierung wurde erfolgreich durchgeführt und der Server wird jetzt neu gestartet." -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:721 -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:770 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:736 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:785 msgid "Restart failed" msgstr "Neustart fehlgeschlagen" -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:722 -msgid "The server apparently did not restart by itself, you'll have to do it manually. Please consult the log file on what went wrong." -msgstr "Der Server hat anscheinend nicht von selbst neu gstartet, Du wirst das manuell tun müssen. Bitte konsultiere das Logfile." +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:737 +msgid "The server apparently did not restart by itself, you'll have to do it manually. Please consult octoprint.log on what went wrong." +msgstr "Der Server hat anscheinend nicht von selbst neu gstartet, Du wirst das manuell tun müssen. Bitte konsultiere octoprint.log." -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:744 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:759 msgid "The update finished successfully, please restart OctoPrint now." msgstr "Die Aktualisierung wurde erfolgreich abgeschlossen, bitte starte OctoPrint jetzt neu." -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:746 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:761 msgid "The update finished successfully, please reboot the server now." msgstr "Die Aktualisierung wurde erfolgreich abgeschlossen, bitte reboote den Server jetzt." -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:749 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:764 msgid "Update successful, restart required!" msgstr "Aktualisierung erfolgreich, Neustart notwendig!" -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:765 -msgid "Restarting OctoPrint failed, please restart it manually. You might also want to consult the log file on what went wrong here." -msgstr "Der Neustart von OctoPrint ist fehlgeschlagen, bitte starte es manuell neu. Du solltest das Logfile konsultieren, um herauszufinden, was hier schief gelaufen ist." +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:780 +msgid "Restarting OctoPrint failed, please restart it manually. You might also want to consult octoprint.log on what went wrong here." +msgstr "Der Neustart von OctoPrint ist fehlgeschlagen, bitte starte es manuell neu. Du solltest auch octoprint.log konsultieren, um herauszufinden, was hier schief gelaufen ist." -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:767 -msgid "Rebooting the server failed, please reboot it manually. You might also want to consult the log file on what went wrong here." -msgstr "Reboot des Servers fehlgeschlagen, bitte reboote ihn manuell. Du solltest auch das Logfile konsultieren, um herauszufinden, was hier gerade schief gelaufen ist." +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:782 +msgid "Rebooting the server failed, please reboot it manually. You might also want to consult octoprint.log on what went wrong here." +msgstr "Reboot des Servers fehlgeschlagen, bitte reboote ihn manuell. Du solltest auch octoprint.log konsultieren, um herauszufinden, was hier gerade schief gelaufen ist." -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:786 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:801 msgid "Update successful!" msgstr "Aktualisierung erfolgreich!" -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:787 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:802 msgid "The update finished successfully." msgstr "Die Aktualisierung wurde erfolgreich abgeschlossen." -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:802 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:817 msgid "Update failed!" msgstr "Aktualisierung fehlgeschlagen!" -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:803 -msgid "The update did not finish successfully. Please consult the log for details." -msgstr "Die Aktualisierung wurde nicht erfolgreich abgeschlossen. Bitte konsultiere das Log für Details." +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:818 +msgid "The update did not finish successfully. Please consult octoprint.log and plugin_softwareupdate_console.log for details." +msgstr "Die Aktualisierung wurde nicht erfolgreich abgeschlossen. Bitte konsultiere octoprint.log und plugin_softwareupdate_console.log für Details." #: src/octoprint/plugins/softwareupdate/templates/softwareupdate.jinja2:4 msgid "Are you sure you want to update now?" @@ -1611,6 +1544,7 @@ msgid "Plugins" msgstr "Plugins" #: src/octoprint/server/views.py:538 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:12 msgid "Connection" msgstr "Verbindung" @@ -1692,27 +1626,22 @@ msgid "Appearance" msgstr "Aussehen" #: src/octoprint/server/views.py:576 -#: src/octoprint/templates/dialogs/settings/logs.jinja2:2 -msgid "Logs" -msgstr "Logs" - -#: src/octoprint/server/views.py:577 msgid "Server" msgstr "Server" -#: src/octoprint/server/views.py:583 +#: src/octoprint/server/views.py:582 msgid "Access" msgstr "Zugriff" -#: src/octoprint/server/views.py:584 +#: src/octoprint/server/views.py:583 msgid "Interface" msgstr "Interface" -#: src/octoprint/server/views.py:600 +#: src/octoprint/server/views.py:599 msgid "Start" msgstr "Start" -#: src/octoprint/server/views.py:601 +#: src/octoprint/server/views.py:600 #: src/octoprint/templates/dialogs/wizard.jinja2:57 msgid "Finish" msgstr "Beenden" @@ -1781,28 +1710,64 @@ msgstr "Der Server ist aktuell im Sicherheitsmodus. Third-Party-Plugins sind dea msgid "Slicing ... (%(percentage)d%%)" msgstr "Slice ... (%(percentage)d%%)" -#: src/octoprint/static/js/app/dataupdater.js:231 -#: src/octoprint/static/js/app/dataupdater.js:240 -msgid "Unhandled communication error" -msgstr "Unbehandelter Kommunikationsfehler" +#: src/octoprint/static/js/app/dataupdater.js:230 +#: src/octoprint/static/js/app/dataupdater.js:241 +msgid "Error reported by printer" +msgstr "Fehler vom Drucker gemeldet" -#: src/octoprint/static/js/app/dataupdater.js:232 +#: src/octoprint/static/js/app/dataupdater.js:231 #, python-format -msgid "There was an unhandled error while talking to the printer. Due to that the ongoing print job was cancelled. Error: %(firmwareError)s" -msgstr "Es gab einen unbehandelten Fehler bei der Kommunikation mit dem Drucker. Daher wurder der laufende Druckauftrag abgebrochen. Fehler: %(firmwareError)s" +msgid "Your printer's firmware reported an error. Due to that the ongoing print job will be cancelled. Reported error: %(firmwareError)s" +msgstr "Es gab einen unbehandelten Fehler bei der Kommunikation mit dem Drucker. Daher wird der laufende Druckauftrag abgebrochen. Fehler: %(firmwareError)s" -#: src/octoprint/static/js/app/dataupdater.js:241 +#: src/octoprint/static/js/app/dataupdater.js:242 #, python-format -msgid "There was an unhandled error while talking to the printer. Due to that OctoPrint disconnected. Error: %(error)s" -msgstr "Es gab einen unbehandelten Fehler bei der Kommunikation mit dem Drucker. Daher hat OctoPrint die Verbindung getrennt. Fehler: %(error)s" +msgid "Your printer's firmware reported an error. Due to that OctoPrint will disconnect. Reported error: %(error)s" +msgstr "Die Firmware Deines Druckers hat einen Fehler gemeldet. Darum hat OctoPrint die Verbindung geschlossen. Gemeldeter Fehler: %(error)s" + +#: src/octoprint/static/js/app/dataupdater.js:247 +msgid "Communication error" +msgstr "Kommunikationsfehler" #: src/octoprint/static/js/app/dataupdater.js:248 +#, python-format +msgid "There was a communication error while talking to your printer. Please consult the terminal output and octoprint.log for details. Error: %(error)s" +msgstr "Es gab einen Fehler bei der Kommunikation mit dem Drucker. Bitte konsultiere den Terminaloutput und octoprint.log für Details. Fehler: %(error)s" + +#: src/octoprint/static/js/app/dataupdater.js:252 +msgid "Error connecting to printer" +msgstr "Fehler bei der Verbindung zum Drucker" + +#: src/octoprint/static/js/app/dataupdater.js:253 +#, python-format +msgid "There was an error while trying to connect to your printer. Error: %(error)s" +msgstr "Es gab einen Fehler bei der Verbindung zum Drucker. Fehler: %(error)s" + +#: src/octoprint/static/js/app/dataupdater.js:257 +msgid "Error starting a print" +msgstr "Fehler beim Start eines Drucks" + +#: src/octoprint/static/js/app/dataupdater.js:258 +#, python-format +msgid "There was an error while trying to start a print job. Error: %(error)s" +msgstr "Es gab einen Fehler beim Start eines Drucks. Fehler: %(error)s" + +#: src/octoprint/static/js/app/dataupdater.js:267 +msgid "Unknown error" +msgstr "Unbekannter Fehler" + +#: src/octoprint/static/js/app/dataupdater.js:268 +#, python-format +msgid "There was an unknown error while talking to your printer. Please consult the terminal output and octoprint.log for details. Error: %(error)s" +msgstr "Es gab einen unbekannten Fehler bei der Kommunikation mit dem Drucker. Bitte konsultiere den Terminaloutput und octoprint.log für Details. Fehler: %(error)s" + +#: src/octoprint/static/js/app/dataupdater.js:283 msgid "Printer reset detected" msgstr "Druckerreset erkannt" -#: src/octoprint/static/js/app/dataupdater.js:249 -msgid "It looks like the printer was reset while a connection was active. If this was intentional you may safely ignore this message. Otherwise you should investigate why your printer reset itself, since this will interrupt prints and also file transfers to your printer's SD." -msgstr "Es sieht so als hätte sich der Drucker resettet während eine Verbindung offen war. Falls das Absicht war kannst du diese Nachricht ignorieren. Andernfalls solltest du prüfen, warum sich dein Drucker resettet hat, denn das wird Druckaufträge und Filetransfers zu der SD Karte deines Druckers unterbrechen." +#: src/octoprint/static/js/app/dataupdater.js:284 +msgid "It looks like your printer reset while a connection was active. If this was intentional you may safely ignore this message. Otherwise you should investigate why your printer reset itself, since this will interrupt prints and also file transfers to your printer's SD." +msgstr "Es sieht so als hätte sich der Drucker resettet während eine Verbindung offen war. Falls das Absicht war kannst du diese Nachricht ignorieren. Andernfalls solltest du prüfen, warum sich dein Drucker resettet hat, da dies Druckaufträge und Filetransfers zu der SD Karte deines Druckers unterbrechen wird." #: src/octoprint/static/js/app/helpers.js:378 #, python-format @@ -1891,6 +1856,7 @@ msgid "off" msgstr "Aus" #: src/octoprint/static/js/app/helpers.js:654 +#: src/octoprint/static/js/app/viewmodels/connection.js:135 msgid "Are you sure?" msgstr "Bist Du sicher?" @@ -1952,13 +1918,26 @@ msgid "Connect" msgstr "Verbinden" #: src/octoprint/static/js/app/viewmodels/connection.js:44 +#: src/octoprint/static/js/app/viewmodels/connection.js:144 msgid "Disconnect" msgstr "Trennen" +#: src/octoprint/static/js/app/viewmodels/connection.js:136 +msgid "

You are about to disconnect from the printer while a print is in progress.

Disconnecting while a print is in progress will prevent OctoPrint from completing the print. If you're printing from an SD card attached directly to the printer, any attempt to restart OctoPrint or reconnect to the printer could interrupt the print.

" +msgstr "

Du bist im Begriff, die Verbindung zu deinem Drucker zu trennen während ein Druck läuft.

Das Trennen der Verbindung während eines Drucks wird OctoPrint daran hindern, diesen zu vollenden. Falls du von der SD deines Druckers druckst, könnte jeglicher Versuch, OctoPrint neuzustarten oder erneut mit dem Drucker zu verbinden den Druck unterbrechen.

" + +#: src/octoprint/static/js/app/viewmodels/connection.js:142 +msgid "Are you sure you want to disconnect from the printer?" +msgstr "Bist Du sicher, dass du die Verbindung zum Drucker trennen willst?" + +#: src/octoprint/static/js/app/viewmodels/connection.js:143 +msgid "Stay Connected" +msgstr "Verbunden bleiben" + #: src/octoprint/static/js/app/viewmodels/control.js:72 #: src/octoprint/static/js/app/viewmodels/files.js:606 #: src/octoprint/static/js/app/viewmodels/gcode.js:567 -#: src/octoprint/static/js/app/viewmodels/printerstate.js:237 +#: src/octoprint/static/js/app/viewmodels/printerstate.js:241 #: src/octoprint/static/js/app/viewmodels/temperature.js:115 #: src/octoprint/static/js/app/viewmodels/temperature.js:126 msgid "Tool" @@ -2117,7 +2096,7 @@ msgid "Estimated layer height" msgstr "Geschätzte Schichthöhe" #: src/octoprint/static/js/app/viewmodels/gcode.js:528 -#: src/octoprint/templates/tabs/timelapse.jinja2:77 +#: src/octoprint/templates/tabs/timelapse.jinja2:68 msgid "mm" msgstr "mm" @@ -2291,89 +2270,89 @@ msgstr "Druckerprofil hinzufügen" msgid "Edit Printer Profile \"%(name)s\"" msgstr "Druckerprofile \"%(name)s\" bearbeiten" -#: src/octoprint/static/js/app/viewmodels/printerstate.js:48 +#: src/octoprint/static/js/app/viewmodels/printerstate.js:50 msgid "Restarts the print job from the beginning" msgstr "Started den Druckjob von vorne" -#: src/octoprint/static/js/app/viewmodels/printerstate.js:49 +#: src/octoprint/static/js/app/viewmodels/printerstate.js:51 msgid "Starts the print job" msgstr "Startet den Druckjob" -#: src/octoprint/static/js/app/viewmodels/printerstate.js:50 +#: src/octoprint/static/js/app/viewmodels/printerstate.js:52 msgid "Resumes the print job" msgstr "Setzt den Druckjob fort" -#: src/octoprint/static/js/app/viewmodels/printerstate.js:51 +#: src/octoprint/static/js/app/viewmodels/printerstate.js:53 msgid "Pauses the print job" msgstr "Pausiert den Druckjob" -#: src/octoprint/static/js/app/viewmodels/printerstate.js:84 +#: src/octoprint/static/js/app/viewmodels/printerstate.js:86 msgid "Still stabilizing..." msgstr "Noch zu ungenau..." -#: src/octoprint/static/js/app/viewmodels/printerstate.js:94 +#: src/octoprint/static/js/app/viewmodels/printerstate.js:96 msgid "Based on a linear approximation (very low accuracy, especially at the beginning of the print)" msgstr "Basiert auf einer linearen Approximation (sehr geringe Genauigkeit, insbesondere zu Beginn eines Drucks)" -#: src/octoprint/static/js/app/viewmodels/printerstate.js:97 +#: src/octoprint/static/js/app/viewmodels/printerstate.js:99 msgid "Based on the estimate from analysis of file (medium accuracy)" msgstr "Basiert auf der Schätzung der Analyse der Datei (mittlere Genauigkeit)" -#: src/octoprint/static/js/app/viewmodels/printerstate.js:100 +#: src/octoprint/static/js/app/viewmodels/printerstate.js:102 msgid "Based on a mix of estimate from analysis and calculation (medium accuracy)" msgstr "Basiert auf einem Mix der Schätzung aus der Analyse und der Berechnung (mittlere Genauigkeit)" -#: src/octoprint/static/js/app/viewmodels/printerstate.js:103 +#: src/octoprint/static/js/app/viewmodels/printerstate.js:105 msgid "Based on the average total of past prints of this model with the same printer profile (usually good accuracy)" msgstr "Basiert auf der durchschnittlichen Dauer vergangener Druckjobs dieses Modells mit dem selben Druckerprofil (normalerweise gute Genauigkeit)" -#: src/octoprint/static/js/app/viewmodels/printerstate.js:106 +#: src/octoprint/static/js/app/viewmodels/printerstate.js:108 msgid "Based on a mix of average total from past prints and calculation (usually good accuracy)" msgstr "Basiert auf einem Mix der durschnittlichen Dauer vergangener Druckjobs und der Berechnung (normalerweise gute Genauigkeit)" -#: src/octoprint/static/js/app/viewmodels/printerstate.js:109 +#: src/octoprint/static/js/app/viewmodels/printerstate.js:111 msgid "Based on the calculated estimate (best accuracy)" msgstr "Basiert auf der berechneten Schätzung (beste Genauigkeit)" -#: src/octoprint/static/js/app/viewmodels/printerstate.js:147 +#: src/octoprint/static/js/app/viewmodels/printerstate.js:149 msgid "Continue" msgstr "Fortsetzen" -#: src/octoprint/static/js/app/viewmodels/printerstate.js:149 +#: src/octoprint/static/js/app/viewmodels/printerstate.js:151 #: src/octoprint/templates/sidebar/state.jinja2:24 msgid "Pause" msgstr "Pause" -#: src/octoprint/static/js/app/viewmodels/printerstate.js:160 +#: src/octoprint/static/js/app/viewmodels/printerstate.js:162 #: src/octoprint/templates/tabs/timelapse.jinja2:15 msgid "On Z Change" msgstr "Bei Ebenenwechsel" -#: src/octoprint/static/js/app/viewmodels/printerstate.js:162 +#: src/octoprint/static/js/app/viewmodels/printerstate.js:164 #: src/octoprint/templates/tabs/timelapse.jinja2:14 msgid "Timed" msgstr "Nach Zeit" -#: src/octoprint/static/js/app/viewmodels/printerstate.js:162 +#: src/octoprint/static/js/app/viewmodels/printerstate.js:164 #: src/octoprint/templates/tabs/timelapse.jinja2:26 #: src/octoprint/templates/tabs/timelapse.jinja2:37 #: src/octoprint/templates/tabs/timelapse.jinja2:57 msgid "sec" msgstr "Sek" -#: src/octoprint/static/js/app/viewmodels/printerstate.js:274 +#: src/octoprint/static/js/app/viewmodels/printerstate.js:278 msgid "This will restart the print job from the beginning." msgstr "Der Druckjob wird zurückgesetzt und von vorne begonnen." -#: src/octoprint/static/js/app/viewmodels/printerstate.js:301 +#: src/octoprint/static/js/app/viewmodels/printerstate.js:305 msgid "This will cancel your print." msgstr "Das wird deinen Druck abbrechen." -#: src/octoprint/static/js/app/viewmodels/printerstate.js:302 +#: src/octoprint/static/js/app/viewmodels/printerstate.js:306 msgid "No" msgstr "Nein" -#: src/octoprint/static/js/app/viewmodels/printerstate.js:303 +#: src/octoprint/static/js/app/viewmodels/printerstate.js:307 msgid "Yes" msgstr "Ja" @@ -2391,39 +2370,39 @@ msgstr "weiß" msgid "Autodetect from browser" msgstr "Automatisch vom Browser erkennen" -#: src/octoprint/static/js/app/viewmodels/settings.js:274 +#: src/octoprint/static/js/app/viewmodels/settings.js:299 msgid "If you see your webcam stream below, the entered stream URL is ok." msgstr "Falls du unten den Webcamstream sehen kannst, ist die Stream-URL ok." -#: src/octoprint/static/js/app/viewmodels/settings.js:282 +#: src/octoprint/static/js/app/viewmodels/settings.js:307 msgid "Stream test" msgstr "Stream Test" -#: src/octoprint/static/js/app/viewmodels/settings.js:300 +#: src/octoprint/static/js/app/viewmodels/settings.js:325 msgid "Could not retrieve snapshot URL, please double check the URL" msgstr "Konnte die Snapshot-URL nicht abgreifen, bitte prüfen" -#: src/octoprint/static/js/app/viewmodels/settings.js:301 +#: src/octoprint/static/js/app/viewmodels/settings.js:326 msgid "Snapshot test failed" msgstr "Snapshot Test fehlgeschlagen" -#: src/octoprint/static/js/app/viewmodels/settings.js:322 +#: src/octoprint/static/js/app/viewmodels/settings.js:352 msgid "If you see your webcam snapshot picture below, the entered snapshot URL is ok." msgstr "Falls du unten dein Snapshotbild sehen kannst, ist die Snapshot-URL ok." -#: src/octoprint/static/js/app/viewmodels/settings.js:324 +#: src/octoprint/static/js/app/viewmodels/settings.js:354 msgid "Snapshot test" msgstr "Snapshot Test" -#: src/octoprint/static/js/app/viewmodels/settings.js:384 +#: src/octoprint/static/js/app/viewmodels/settings.js:414 msgid "The server is not reachable" msgstr "Der Server ist nicht erreichbar" -#: src/octoprint/static/js/app/viewmodels/settings.js:386 +#: src/octoprint/static/js/app/viewmodels/settings.js:416 msgid "The server is reachable" msgstr "Der Server ist errechbar" -#: src/octoprint/static/js/app/viewmodels/settings.js:506 +#: src/octoprint/static/js/app/viewmodels/settings.js:536 #: src/octoprint/static/js/app/viewmodels/usersettings.js:87 msgid "This will generate a new API Key. The old API Key will cease to function immediately." msgstr "Dies wird einen neuen API-Key generieren. Der alte wird sofort aufhören zu funktionieren." @@ -2492,20 +2471,16 @@ msgstr "Definiere unten ein neues Offset für alle Temperaturkommandos für \"%( msgid "Bed" msgstr "Bett" -#: src/octoprint/static/js/app/viewmodels/temperature.js:350 -msgid "just now" -msgstr "gerade eben" - -#: src/octoprint/static/js/app/viewmodels/temperature.js:355 +#: src/octoprint/static/js/app/viewmodels/temperature.js:361 msgid "min" msgstr "Min" -#: src/octoprint/static/js/app/viewmodels/temperature.js:401 +#: src/octoprint/static/js/app/viewmodels/temperature.js:407 #: src/octoprint/templates/tabs/temperature.jinja2:11 msgid "Actual" msgstr "Ist" -#: src/octoprint/static/js/app/viewmodels/temperature.js:406 +#: src/octoprint/static/js/app/viewmodels/temperature.js:412 #: src/octoprint/templates/tabs/temperature.jinja2:12 msgid "Target" msgstr "Soll" @@ -2530,144 +2505,144 @@ msgstr "zeige %(displayed)d Zeilen (Buffer voll)" msgid "showing %(displayed)d lines" msgstr "zeige %(displayed)d Zeilen" -#: src/octoprint/static/js/app/viewmodels/timelapse.js:251 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:240 #, python-format msgid "Failed to remove timelapse %(name)s.

Please consult octoprint.log for details.

" msgstr "Konnte Zeitrafferaufnahme %(name)s nicht entfernen.

Bitte konsultiere octoprint.log für Details.

" -#: src/octoprint/static/js/app/viewmodels/timelapse.js:254 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:243 msgid "Could not remove timelapse" msgstr "Konnte Zeitrafferaufnahme nicht entfernen" -#: src/octoprint/static/js/app/viewmodels/timelapse.js:262 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:251 #, python-format msgid "You are about to delete timelapse file \"%(name)s\"." msgstr "Du bist im Begriff die Zeitrafferaufnahme \"%(name)s\" zu löschen." -#: src/octoprint/static/js/app/viewmodels/timelapse.js:274 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:263 #, python-format msgid "You are about to delete %(count)d timelapse files." msgstr "Du bist im Begriff %(count)d Zeitrafferaufnahmen zu löschen." -#: src/octoprint/static/js/app/viewmodels/timelapse.js:299 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:288 #, python-format msgid "You are about to delete unrendered timelapse \"%(name)s\"." msgstr "Du bist im Begriff die ungerenderte Zeitrafferaufnahme \"%(name)s\" zu löschen." -#: src/octoprint/static/js/app/viewmodels/timelapse.js:311 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:300 #, python-format msgid "You are about to delete %(count)d unrendered timelapses." msgstr "Du bist im Begriff %(count)d ungerenderte Zeitrafferaufnahmen zu löschen." -#: src/octoprint/static/js/app/viewmodels/timelapse.js:319 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:308 msgid "Deleting timelapse files" msgstr "Lösche Zeitrafferaufnahmen" -#: src/octoprint/static/js/app/viewmodels/timelapse.js:320 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:309 #, python-format msgid "Deleting %(count)d timelapse files..." msgstr "Lösche %(count)d Zeitrafferaufnahmen" -#: src/octoprint/static/js/app/viewmodels/timelapse.js:324 -#: src/octoprint/static/js/app/viewmodels/timelapse.js:338 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:313 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:327 #, python-format msgid "Deleted %(filename)s..." msgstr "%(filename)s gelöscht..." -#: src/octoprint/static/js/app/viewmodels/timelapse.js:327 -#: src/octoprint/static/js/app/viewmodels/timelapse.js:341 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:316 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:330 #, python-format msgid "Deletion of %(filename)s failed, continuing..." msgstr "Löschen von %(filename)s fehlgeschlagen, fahre fort..." -#: src/octoprint/static/js/app/viewmodels/timelapse.js:328 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:317 #, python-format msgid "Deletion of %(filename)s failed: %(error)s" msgstr "Löschen von %(filename)s fehlgeschlagen: %(error)s" -#: src/octoprint/static/js/app/viewmodels/timelapse.js:333 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:322 msgid "Deleting unrendered timelapses" msgstr "Lösche ungerenderte Zeitrafferaufnahmen" -#: src/octoprint/static/js/app/viewmodels/timelapse.js:334 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:323 #, python-format msgid "Deleting %(count)d unrendered timelapses..." msgstr "Lösche %(count)d ungerenderte Zeitrafferaufnahmen" -#: src/octoprint/static/js/app/viewmodels/timelapse.js:429 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:417 msgid "Capturing timelapse postroll" msgstr "Zeichne Timelapse-Postroll auf" -#: src/octoprint/static/js/app/viewmodels/timelapse.js:433 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:421 msgid "Now capturing timelapse post roll, this will take only a moment..." msgstr "Zeichne jetzt Zeitraffernachlauf auf, dies wird nur einen Moment dauern..." -#: src/octoprint/static/js/app/viewmodels/timelapse.js:440 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:428 #, python-format msgid "%(minutes)d min" msgstr "%(minutes)d Min" -#: src/octoprint/static/js/app/viewmodels/timelapse.js:441 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:429 #, python-format msgid "Now capturing timelapse post roll, this will take approximately %(duration)s (so until %(time)s)..." msgstr "Zeichne jetzt Zeitraffernachlauf auf, dies wird voraussichtlich %(duration)s dauern (also etwa bis %(time)s)..." -#: src/octoprint/static/js/app/viewmodels/timelapse.js:443 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:431 #, python-format msgid "%(seconds)d sec" msgstr "%(seconds)d Sek" -#: src/octoprint/static/js/app/viewmodels/timelapse.js:444 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:432 #, python-format msgid "Now capturing timelapse post roll, this will take approximately %(duration)s..." msgstr "Zeichne jetzt Zeitraffernachlauf auf, dies wird voraussichtlich %(duration)s dauern..." -#: src/octoprint/static/js/app/viewmodels/timelapse.js:474 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:462 msgid "Failed repeatedly to capture timelapse frame from webcam - is the snapshot URL configured correctly and the camera on?" msgstr "Konnte wiederholt kein Zeitrafferbild von der Webcam beziehen - ist die Snapshot-URL korrekt konfiguriert und die Kamera an?" -#: src/octoprint/static/js/app/viewmodels/timelapse.js:477 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:465 msgid "Could not capture snapshots" msgstr "Konnte keine Snapshots aufnehmen" -#: src/octoprint/static/js/app/viewmodels/timelapse.js:486 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:474 msgid "Rendering timelapse" msgstr "Zeitrafferaufnahme wird gerendert" -#: src/octoprint/static/js/app/viewmodels/timelapse.js:487 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:475 #, python-format msgid "Now rendering timelapse %(movie_prefix)s. Due to performance reasons it is not recommended to start a print job while a movie is still rendering." msgstr "Rendere jetzt die Zeitrafferaufnahme %(movie_prefix)s. Aus Gründen der Performance ist es nicht empfehlenswert, einen Druckauftrag zu starten, so lange die Aufnahme noch gerendert wird." -#: src/octoprint/static/js/app/viewmodels/timelapse.js:496 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:484 msgid "Cannot render timelapse" msgstr "Kann Zeitrafferaufnahme nicht rendern" -#: src/octoprint/static/js/app/viewmodels/timelapse.js:497 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:485 #, python-format msgid "Rendering of timelapse %(movie_prefix)s is not possible since no frames were captured. Is the snapshot URL configured correctly?" msgstr "Rendering des Zeitraffers %(movie_prefix)s ist nicht möglich, da keine Frames gespeichert wurden. Ist die Snapshot-URL korrekt konfiguriert?" -#: src/octoprint/static/js/app/viewmodels/timelapse.js:499 -#: src/octoprint/static/js/app/viewmodels/timelapse.js:503 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:487 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:491 msgid "Rendering timelapse failed" msgstr "Rendern von Zeitrafferaufnahme fehlgeschlagen" -#: src/octoprint/static/js/app/viewmodels/timelapse.js:500 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:488 #, python-format msgid "Rendering of timelapse %(movie_prefix)s failed with return code %(returncode)s" msgstr "Rendering der Zeitrafferaufnahme %(movie_prefix)s fehlgeschlagen mit Returncode %(returncode)s" -#: src/octoprint/static/js/app/viewmodels/timelapse.js:504 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:492 #, python-format msgid "Rendering of timelapse %(movie_prefix)s failed due to an unknown error, please consult the log file" msgstr "Rendering der Zeitrafferaufnahme %(movie_prefix)s fehlgeschlagen aufgrund eines unbekannten Fehlers, bitte konsultiere die Logdatei" -#: src/octoprint/static/js/app/viewmodels/timelapse.js:517 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:505 msgid "Timelapse ready" msgstr "Zeitrafferaufnahme fertig" -#: src/octoprint/static/js/app/viewmodels/timelapse.js:518 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:506 #, python-format msgid "New timelapse %(movie_prefix)s is done rendering." msgstr "Neue Zeitrafferaufnahme %(movie_prefix)s wurde fertig gerendert" @@ -2821,14 +2796,6 @@ msgstr "Aktiv" msgid "Admin" msgstr "Admin" -#: src/octoprint/templates/dialogs/settings/accesscontrol.jinja2:7 -#: src/octoprint/templates/dialogs/settings/logs.jinja2:15 -#: src/octoprint/templates/snippets/settings/printerprofiles/profiles.jinja2:6 -#: src/octoprint/templates/tabs/timelapse.jinja2:132 -#: src/octoprint/templates/tabs/timelapse.jinja2:178 -msgid "Action" -msgstr "Aktion" - #: src/octoprint/templates/dialogs/settings/accesscontrol.jinja2:12 #: src/octoprint/templates/dialogs/settings/api.jinja2:17 #: src/octoprint/templates/dialogs/usersettings/access.jinja2:22 @@ -3038,81 +3005,28 @@ msgid "Enable Keyboard Control" msgstr "Tastatursteuerung aktivieren" #: src/octoprint/templates/dialogs/settings/features.jinja2:54 -msgid "Wait for start on connect" -msgstr "Bei der Verbindung auf start warten" - -#: src/octoprint/templates/dialogs/settings/features.jinja2:61 msgid "G90/G91 overrides relative extruder mode" msgstr "G90/G91 überschreibt relativen Modus des Extruders" -#: src/octoprint/templates/dialogs/settings/features.jinja2:61 +#: src/octoprint/templates/dialogs/settings/features.jinja2:54 msgid "Smoothieware" msgstr "Smoothieware" -#: src/octoprint/templates/dialogs/settings/features.jinja2:68 -msgid "Enable automatic firmware detection" -msgstr "Automatische Firmwareerkennung einschalten" - -#: src/octoprint/templates/dialogs/settings/features.jinja2:69 -msgid "\n" -" If enabled, OctoPrint will try to figure out your printer's firmware automatically and adjust some communication parameters based on that.\n" -" If that doesn't work out, or you want more granular control, uncheck this and the parameters in question will become visible for you to adjust.\n" -" " -msgstr "Falls diese Option eingeschaltet ist, versucht OctoPrint die Firmware des Druckers automatisch zu erkennen und darauf basierend diverse Kommunikationsparameter zu konfigurieren. Falls das für dich nicht korrekt funktioniert oder du mehr Kontrolle haben möchtest, schalte diese Option aus und die entsprechenden Parameter werden zur manuellen Konfiguration eingeblendet." - -#: src/octoprint/templates/dialogs/settings/features.jinja2:80 -msgid "Select SD files by relative path" -msgstr "SD Dateien per relativem Pfad addressieren" - -#: src/octoprint/templates/dialogs/settings/features.jinja2:80 -msgid "RepRap Firmware" -msgstr "RepRap Firmware" - -#: src/octoprint/templates/dialogs/settings/features.jinja2:87 -msgid "Always assume SD card is present" -msgstr "Immer davon ausgehen, dass eine SD-Karte vorhanden ist" - -#: src/octoprint/templates/dialogs/settings/features.jinja2:87 -#: src/octoprint/templates/dialogs/settings/features.jinja2:94 -#: src/octoprint/templates/dialogs/settings/features.jinja2:101 -#: src/octoprint/templates/dialogs/settings/features.jinja2:108 -#: src/octoprint/templates/dialogs/settings/features.jinja2:115 -#: src/octoprint/templates/dialogs/settings/features.jinja2:126 -msgid "Repetier" -msgstr "Repetier" - -#: src/octoprint/templates/dialogs/settings/features.jinja2:94 -msgid "Ignore consecutive resend requests for the same line" -msgstr "Aufeinanderfolgende Resend Requests für die selbe Zeilennummer ignorieren" - -#: src/octoprint/templates/dialogs/settings/features.jinja2:101 -#, python-format -msgid "Support TargetExtr%%n/TargetBed target temperature format" -msgstr "TargetExtr%%n/TargetBed Zieltemperaturformat unterstützen" - -#: src/octoprint/templates/dialogs/settings/features.jinja2:108 -msgid "Disable detection of external heatups" -msgstr "Detektierung externer Aufheizvorgänge deaktivieren" - -#: src/octoprint/templates/dialogs/settings/features.jinja2:115 -msgid "Actively pause communication during G4 dwell command" -msgstr "Pausiere Kommunikation mit dem Drucker aktiv während eines G4 dwell Befehls." - -#: src/octoprint/templates/dialogs/settings/features.jinja2:120 -msgid "Send a checksum with the command" -msgstr "Eine Prüfsumme mit dem Befehl senden" +#: src/octoprint/templates/dialogs/settings/features.jinja2:55 +msgid "This is used by the GCODE analysis in the backend and the viewer in the frontend to interpret your sliced files correctly." +msgstr "Dies wird von der GCODE Analyse im Backend und dem GCODE Viewer im Frontend verwendet, um die gesliceten Dateien korrekt zu interpretieren." -#: src/octoprint/templates/dialogs/settings/features.jinja2:123 -msgid "When printing" -msgstr "Beim Drucken" +#: src/octoprint/templates/dialogs/settings/features.jinja2:59 +msgid "Commands to not completely auto uppercase in the terminal tab" +msgstr "Befehle, die im Terminaltab nicht vollständig in Großbuchstaben gewandelt werden sollen" -#: src/octoprint/templates/dialogs/settings/features.jinja2:126 -msgid "Always" -msgstr "Immer" +#: src/octoprint/templates/dialogs/settings/features.jinja2:60 +msgid "Terminal Auto Uppercase Blacklist" +msgstr "Terminal Auto Uppercase Blackliste" -#: src/octoprint/templates/dialogs/settings/features.jinja2:129 -msgid "Never" -msgstr "Nie" +#: src/octoprint/templates/dialogs/settings/features.jinja2:63 +msgid "Use this to specify the commands that should not have their parameters automatically uppercased in the terminal tab. Just the G or M code, comma separated." +msgstr "Nutze diese Option um Befehle zu spezifizieren, deren Parameter nicht automatisch im Terminaltab in Großbuchstaben überführt werden sollen. Nur den G oder M code, kommasepariert." #: src/octoprint/templates/dialogs/settings/folders.jinja2:5 msgid "Upload Folder" @@ -3147,10 +3061,10 @@ msgid "If the free disk space falls below these thresholds, OctoPrint will warn msgstr "Falls der freie Plattenplatz unter diese Schwellwerte fallen sollte wird OctoPrint den Nutzer warnen." #: src/octoprint/templates/dialogs/settings/folders.jinja2:47 -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:87 -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:115 -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:163 -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:169 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:52 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:347 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:357 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:363 #: src/octoprint/templates/tabs/gcodeviewer.jinja2:111 msgid "Warning" msgstr "Warnung" @@ -3224,249 +3138,383 @@ msgstr "auf dem Desktop" msgid "on mobile" msgstr "auf Mobile" -#: src/octoprint/templates/dialogs/settings/logs.jinja2:6 -msgid "Modification date" -msgstr "Änderungsdatum" +#: src/octoprint/templates/dialogs/settings/printerprofiles.jinja2:5 +msgid "Add Profile..." +msgstr "Profil hinzufügen..." -#: src/octoprint/templates/dialogs/settings/logs.jinja2:6 -#: src/octoprint/templates/dialogs/settings/logs.jinja2:13 -#: src/octoprint/templates/sidebar/files.jinja2:18 -#: src/octoprint/templates/sidebar/files.jinja2:33 -#: src/octoprint/templates/sidebar/files.jinja2:44 -#: src/octoprint/templates/tabs/timelapse.jinja2:131 -#: src/octoprint/templates/tabs/timelapse.jinja2:177 -msgid "Size" -msgstr "Größe" +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:4 +msgid "Intervals & timeouts" +msgstr "Intervalle & Timeouts" -#: src/octoprint/templates/dialogs/settings/logs.jinja2:14 -msgid "Date" -msgstr "Datum" +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:5 +msgid "Firmware & protocol" +msgstr "Firmware & Protokoll" -#: src/octoprint/templates/dialogs/settings/printerprofiles.jinja2:5 -msgid "Add Profile..." -msgstr "Profil hinzufügen..." +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:6 +msgid "Behaviour" +msgstr "Verhalten" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:2 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:13 msgid "Serial port to connect to, setting this to AUTO will make OctoPrint try to automatically find the right setting" msgstr "Serieller Port, mit der sich verbunden werden soll. Falls AUTO konfiguriert ist wird OctoPrint versuchen, automatisch den richtigen Port zu finden." -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:3 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:14 #: src/octoprint/templates/sidebar/connection.jinja2:1 msgid "Serial Port" msgstr "Serialport" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:8 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:19 msgid "Serial baud rate to connect with, setting this to AUTO will make OctoPrint try to automatically find the right setting" msgstr "Baudrate mit der sich verbunden werden soll. Falls AUTO konfiguriert ist wird OctoPrint versuchen, automatisch die richtige Baudrate zu finden." -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:9 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:20 #: src/octoprint/templates/sidebar/connection.jinja2:3 msgid "Baudrate" msgstr "Baudrate" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:14 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:25 msgid "Makes OctoPrint try to connect to the printer automatically during start up" msgstr "OctoPrint wird versuchen, sich beim Startup automatisch mit dem Drucker zu verbinden" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:17 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:28 msgid "Auto-connect to printer on server start" msgstr "Automatisch bei Serverstart verbinden" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:21 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:33 +msgid "Additional serial ports" +msgstr "Zusätzliche serielle Ports" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:36 +#, python-format +msgid "Use this to define additional glob patterns matching serial ports to list for connecting against, e.g. /dev/ttyAMA*. One entry per line." +msgstr "Nutze diese Einstellung um zusätzliche glob patterns zu konfigurieren, die auf serielle Ports deines Druckers matchen, z.B. /dev/ttyAMA*. Ein Eintrag pro Zeile." + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:40 +msgid "Additional baud rates" +msgstr "Weitere Baudraten" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:43 +msgid "Use this to define additional serial port baud rates to list for connecting with, e.g. 123456. Comma separated." +msgstr "Nutze diese Einstellung um zusätzliche Baudraten zur Verbindung zu konfigurieren, 123456. Komma-separiert." + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:48 +msgid "Serial logging" +msgstr "Logging der seriellen Kommunikation" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:52 +msgid "Log communication to serial.log" +msgstr "Kommunikation nach serial.log loggen" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:53 +msgid "While this can negatively impact performance, a serial.log can be incredibly useful for debugging any issues observed in the communication between OctoPrint and your printer." +msgstr "Dies kann zwar die Performance negativ beeinflussen, ein serial.log ist jedoch unglaublich nützlich beim Debugging jeglicher Probleme, die in der Kommunikation zwischen OctoPrint und deinem Drucker auftreten." + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:61 +msgid "Query intervals" +msgstr "Abfrageintervalle" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:62 msgid "Interval in which to poll for the temperature information from the printer" msgstr "Intervall in welchem die Temperaturdaten vom Drucker abgerufen werden sollen" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:22 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:63 msgid "Temperature interval (polling)" msgstr "Temperaturintervall (polling)" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:28 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:69 msgid "When printing or idle" msgstr "Beim Drucken oder Idle" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:35 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:76 msgid "When a target temperature is set" msgstr "Wenn eine Zieltemperatur eingestellt ist" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:38 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:79 msgid "Temperature report interval to request from autoreport capable firmwares. A value of 0 disables autoreporting by the firmware and forces polling." msgstr "Von der autoreportfähigen Firmware anzuforderndes Temperaturupdateintervall. Ein Wert von 0 deaktiviert Autoreporting der Firmware und forciert Polling." -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:39 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:80 msgid "Temperature interval (autoreport)" msgstr "Temperaturintervall (autoreport)" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:45 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:86 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:105 msgid "Autoreport interval to request from firmware" msgstr "Anzuforderndes Temperaturupdateintervall" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:48 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:89 msgid "Interval in which to poll for the SD printing status information from the printer while printing" msgstr "Intervall in welchem die SD-Statusdaten vom Drucker während des Druckens abgerufen werden sollen" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:49 -msgid "SD status interval" -msgstr "SD-Status-Intervall" +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:90 +msgid "SD status interval (polling)" +msgstr "SD-Statusintervall (polling)" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:98 +msgid "SD status interval to request from autoreport capable firmwares. A value of 0 disables autoreporting by the firmware and forces polling." +msgstr "Von der autoreportfähigen Firmware anzuforderndes SD-Statusintervall. Ein Wert von 0 deaktiviert Autoreporting der Firmware und forciert Polling." + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:99 +msgid "SD status interval (autoreport)" +msgstr "SD-Statusintervall (autoreport)" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:110 +msgid "Timeouts" +msgstr "Timeouts" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:57 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:111 msgid "Time after which the communication with your printer will be considered timed out if nothing was sent by your printer (and an attempt to get it talking again will be done). Increase this if your printer takes longer than this for some moves." msgstr "Zeit nach der OctoPrint davon ausgehen wird, dass die Kommunikation mit deinem Drucker unterbrochen wurde falls Dein Drucker keine Daten sendet. OctoPrint wird dann einen Versuch unternehmen, die Kommunikation wieder zu reetablieren. Erhöhe diesen Wert falls Dein Drucker für manche Bewegungen länger braucht." -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:58 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:112 msgid "Communication timeout" msgstr "Kommunikationstimeout" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:66 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:118 +msgid "busy protocol support not detected" +msgstr "busy Protokoll nicht unterstützt" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:125 +msgid "busy protocol support detected" +msgstr "busy Protokoll unterstützt" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:128 msgid "Time after which a connection attempt to the printer will be considered as having failed" msgstr "Zeit nach der ein unbeantworteter Verbindungsversuch als gescheitert angenommen wird" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:67 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:129 msgid "Connection timeout" msgstr "Verbindungstimeout" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:75 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:137 msgid "Time after which to consider an auto detection attempt to have failed if no successful connection is detected" msgstr "Zeit nach der ein unbeantworteter Autodetektierungsversuch als gescheitert angenommen wird" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:76 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:138 msgid "Autodetection timeout" msgstr "Autodetectiontimeout" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:87 -msgid "Log communication to serial.log (might negatively impact performance)" -msgstr "Logge die Kommunikation in das serial.log (kann die Performance negativ beeinflussen)" +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:149 +msgid "Maximum consecutive communication timeouts while idle. More than this and the printer will be considered to be gone. Set to 0 to disable." +msgstr "Maximale Anzahl aufeinanderfolgender Communication Timeouts im Idlezustand. Mehr als das und es wird angenommen, dass der Drucker offline ist. Auf 0 setzen um das zu verhindern." -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:92 -msgid "Additional serial ports" -msgstr "Zusätzliche serielle Ports" +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:150 +msgid "Max. consecutive timeouts while idle" +msgstr "Max. aufeinanderfolgende Timeouts wenn idle" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:153 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:160 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:167 +msgid "Set to 0 to disable consecutive timeout detection and handling." +msgstr "Auf 0 setzen um aufeinanderfolgende Timeouts zu ignorieren" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:156 +msgid "Maximum consecutive communication timeouts while printing. More than this and the printer will be considered to be gone. Set to 0 to disable." +msgstr "Maximale Anzahl aufeinanderfolgender Communication Timeouts beim Drucken. Mehr als das und es wird angenommen, dass der Drucker offline ist. Auf 0 setzen um das zu verhindern." + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:157 +msgid "Max. consecutive timeouts while printing" +msgstr "Max. aufeinanderfolgende Timeouts beim Drucken" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:163 +msgid "Maximum consecutive communication timeouts while a long running command is active. More than this and the printer will be considered to be gone. Set to 0 to disable." +msgstr "Maximale Anzahl aufeinanderfolgender Communication Timeouts wenn ein lang laufender Befehl aktiv ist. Mehr als das und es wird angenommen, dass der Drucker offline ist. Auf 0 setzen um das zu verhindern." + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:164 +msgid "Max. consecutive timeouts during long running commands" +msgstr "Max. aufeinanderfolgende Timeouts während lang laufender Befehle" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:95 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:176 +msgid "Firmware specific settings" +msgstr "Firmwarespezifische Einstellungen" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:180 +msgid "Enable automatic firmware detection" +msgstr "Automatische Firmwareerkennung einschalten" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:181 +msgid "\n" +" If enabled, OctoPrint will try to figure out your printer's firmware automatically and adjust some communication parameters based on that.\n" +" If that doesn't work out, or you want more granular control, uncheck this and the parameters in question will become visible for you to adjust.\n" +" " +msgstr "Falls diese Option eingeschaltet ist, versucht OctoPrint die Firmware des Druckers automatisch zu erkennen und darauf basierend diverse Kommunikationsparameter zu konfigurieren. Falls das für dich nicht korrekt funktioniert oder du mehr Kontrolle haben möchtest, schalte diese Option aus und die entsprechenden Parameter werden zur manuellen Konfiguration eingeblendet." + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:192 +msgid "Select SD files by relative path" +msgstr "SD Dateien per relativem Pfad addressieren" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:192 +msgid "RepRap Firmware" +msgstr "RepRap Firmware" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:199 +msgid "Always assume SD card is present" +msgstr "Immer davon ausgehen, dass eine SD-Karte vorhanden ist" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:199 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:206 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:213 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:220 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:227 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:238 +msgid "Repetier" +msgstr "Repetier" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:206 +msgid "Ignore consecutive resend requests for the same line" +msgstr "Aufeinanderfolgende Resend Requests für die selbe Zeilennummer ignorieren" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:213 #, python-format -msgid "Use this to define additional glob patterns matching serial ports to list for connecting against, e.g. /dev/ttyAMA*. One entry per line." -msgstr "Nutze diese Einstellung um zusätzliche glob patterns zu konfigurieren, die auf serielle Ports deines Druckers matchen, z.B. /dev/ttyAMA*. Ein Eintrag pro Zeile." +msgid "Support TargetExtr%%n/TargetBed target temperature format" +msgstr "TargetExtr%%n/TargetBed Zieltemperaturformat unterstützen" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:99 -msgid "Additional baud rates" -msgstr "Weitere Baudraten" +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:220 +msgid "Disable detection of external heatups" +msgstr "Detektierung externer Aufheizvorgänge deaktivieren" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:102 -msgid "Use this to define additional serial port baud rates to list for connecting with, e.g. 123456. Comma separated." -msgstr "Nutze diese Einstellung um zusätzliche Baudraten zur Verbindung zu konfigurieren, 123456. Komma-separiert." +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:227 +msgid "Actively pause communication during G4 dwell command" +msgstr "Pausiere Kommunikation mit dem Drucker aktiv während eines G4 dwell Befehls." -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:108 -msgid "Not only cancel ongoing prints but also disconnect on unhandled errors from the firmware." -msgstr "Bei unbehandelten Firmwarefehlern nicht nur den Druckauftrag abbrechen, sondern auch die Verbindung zum Drucker trennen." +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:232 +msgid "Send a checksum with the command" +msgstr "Eine Prüfsumme mit dem Befehl senden" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:115 -msgid "Ignore any unhandled errors from the firmware. Only use this if your firmware sends stuff prefixed with \"Error\" that is not an actual error. Might mask printer issues, be careful!" -msgstr "Alle unbehandelten Firmwarefehler ignorieren. Nur nutzen wenn Deine Firmware Dinge mit \"Error\" sendet die nicht wirklich Fehler sind. Könnte Druckerprobleme maskieren, vorsicht!" +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:235 +msgid "When printing" +msgstr "Beim Drucken" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:123 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:238 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:285 +msgid "Always" +msgstr "Immer" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:241 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:288 +msgid "Never" +msgstr "Nie" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:252 +msgid "Support temperature autoreporting by firmware, if detected" +msgstr "Temperaturautoreporting durch die Firmware unterstützen, falls detektiert" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:259 +msgid "Support sd status autoreporting by firmware, if detected" +msgstr "SD-Status-Autoreporting durch die Firmware unterstützen, falls detektiert" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:266 +msgid "Support busy protocol, if detected" +msgstr "busy Protokoll unterstützen, falls detektiert" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:273 +msgid "Simulate ok for M29" +msgstr "Zusätzliches ok für M29 generieren" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:274 +msgid "Most Marlin forks that were derived from Marlin versions < v1.1.0 do not send an acknowledging ok for a M29. Check this if you run into communication stalls following streaming of a file to your printer's SD." +msgstr "Die meisten Marlinforks, die von Marlin Versionen < v1.1.0 abgeleitet wurden, senden kein bestätigendes ok für ein M29. Aktiviere diese Option, falls du Kommunikationsprobleme nach dem Streaming einer Datei zu der SD des Druckers beobachtest." + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:279 +msgid "Simulate ok for resend requests" +msgstr "Zusätzliches ok für Resendrequests simulieren" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:282 +msgid "If detected as necessary" +msgstr "Falls als notwendig erkannt" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:290 +msgid "Some Marlin forks lack an acknowledging ok with their resend requests. Set this to \"always\" or \"if detected as necessary\" if you run into communication stalls on resend requests." +msgstr "Einige Marlinforks senden bei einem Resendrequest kein bestätigendes ok. Setze diese Option auf \"immer\" oder \"falls als notwendig erkannt\" falls du Kommunikationsprobleme nach Resendrequests beobachtest." + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:297 +msgid "Protocol fine tuning" +msgstr "Protokollfinetuning" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:301 +msgid "Wait for start on connect" +msgstr "Bei der Verbindung auf start warten" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:302 +msgid "Try checking this if you run into connection timeouts due to your printer taking too long to respond to OctoPrint' handshake attempts on connect." +msgstr "Versuche es mit dieser Option, falls du Kommunikationsprobleme beobachtest, weil dein Drucker zu lange braucht, um auf OctoPrints Handshake Versuche zu antworten." + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:309 msgid "Command to send to the firmware on first handshake attempt." msgstr "Kommando, das als erster Handshakeversuch an die Firmware gesendet werden soll" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:124 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:310 msgid "\"Hello\" command" msgstr "\"Hallo\"-Befehl" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:127 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:313 msgid "Use this to specify a different command than the default M110 to send to the printer on initial connection to trigger a communication handshake." msgstr "Nutze diese Einstellung um einen anderen Befehl als M110 beim initialen Verbindungsaufbau zum drucker zu senden." -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:130 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:316 msgid "Commands that are know to run long and hence should suppress communication timeouts from being triggered." msgstr "Befehle, von denen bekannt ist, dass sie lang zur Ausführung benötigen und daher das Auslösen von Kommunikationstimeouts unterdrücken sollten." -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:131 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:317 msgid "Long running commands" msgstr "Lang laufende Befehle" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:134 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:320 msgid "Use this to specify the commands known to take a long time to complete without output from your printer and hence might cause timeout issues. Just the G or M code, comma separated." msgstr "Nutze diese Option, um solche Befehle zu definieren, von denen Du weißt, dass sie eine längere Zeit lang laufen, währenddessen keinen Output produzieren und daher Timeoutprobleme verursachen könnten. Nur den G- oder M-Code, kommasepariert." -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:137 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:323 msgid "Commands that always require a line number and checksum to be sent with them." msgstr "Befehle, die immer mit einer Prüfsumme und Zeilennummer gesendet werden müssen." -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:138 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:324 msgid "Commands that always require a checksum" msgstr "Befehle, die immer eine Prüfsumme benötigen" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:141 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:327 msgid "Use this to specify which commands always need to be sent with a checksum. Comma separated list." msgstr "Nutze diese Einstellung um Befehle zu spezifizieren, die immer mit Prüfsumme gesendet werden müssen. Komma-separierte Liste." -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:144 -msgid "Commands to not completely auto uppercase in the terminal tab" -msgstr "Befehle, die im Terminaltab nicht vollständig in Großbuchstaben gewandelt werden sollen" +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:336 +msgid "Error handling" +msgstr "Fehlerbehandlung" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:145 -msgid "Terminal Auto Uppercase Blacklist" -msgstr "Terminal Auto Uppercase Blackliste" +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:338 +msgid "What to do on a firmware error (Error: or !!)" +msgstr "Was im Falle eines Firmwarefehlers (Error: oder !!) gemacht werden soll" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:148 -msgid "Use this to specify the commands that should not have their parameters automatically uppercased in the terminal tab. Just the G or M code, comma separated." -msgstr "Nutze diese Option um Befehle zu spezifizieren, deren Parameter nicht automatisch im Terminaltab in Großbuchstaben überführt werden sollen. Nur den G oder M code, kommasepariert." +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:341 +msgid "Disconnect from the printer" +msgstr "Verbindung zum Drucker trennen" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:153 -msgid "Generate additional ok for M29" -msgstr "Zusätzliches ok für M29 generieren" +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:344 +msgid "Cancel any ongoing prints but stay connected to the printer" +msgstr "Laufende Druckaufträge abbrechen, aber mit dem Drucker verbunden bleiben" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:153 -msgid "Most Marlin < v1.1.0" -msgstr "Die meisten Varianten von Marlin < v1.1.0" +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:348 +msgid "Only choose this if your firmware sends error messages that are not actual errors. Might mask printer issues, be careful!" +msgstr "Wähle das nur aus, wenn deine Firmware Fehlermeldungen für Dinge sendet, die gar keine wirklich Fehler sind. Könnte Druckerprobleme verbergen, Vorsicht!" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:158 -msgid "Simulate an additional ok for resend requests" -msgstr "Zusätzliches ok für Resendrequests simulieren" +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:354 +msgid "Event based position logging" +msgstr "Eventbasiertes Positionslogging" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:163 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:357 msgid "Log position on pause" msgstr "Position bei Pause loggen" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:164 -msgid "If you disabled this, the pause_position placeholders in your pause/resume GCODE scripts will stay unpopulated! However, pausing speed might improve slightly." +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:358 +msgid "If you disable this, the pause_position placeholders in your pause/resume GCODE scripts will stay unpopulated! However, pausing speed might improve slightly." msgstr "Falls du das deaktivierst, werden die pause_position Platzhalter in deinen Pausieren/Fortsetzen GCODE Scripts nicht befüllt! Allerdings könnte sich die Pausierungsgeschwindkeit einen Hauch verbessern." -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:169 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:363 msgid "Log position on cancel" msgstr "Position bei Abbruch loggen" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:170 -msgid "If you disabled this, the cancel_position placeholders in your cancel GCODE script and the corresponding data in the print recovery data will stay unpopulated! However, cancelling speed might improve slightly." +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:364 +msgid "If you disable this, the cancel_position placeholders in your cancel GCODE script and the corresponding data in the print recovery data will stay unpopulated! However, cancelling speed might improve slightly." msgstr "Falls du das deaktivierst, werden die cancel_position Platzhalter in deinem Abbruch GCODE Script nicht befüllt! Allerdings könnte sich die Abbruchgeschwindkeit einen Hauch verbessern." -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:173 -msgid "Maximum consecutive communication timeouts while idle. More than this and the printer will be considered to be gone. Set to 0 to disable." -msgstr "Maximale Anzahl aufeinanderfolgender Communication Timeouts im Idlezustand. Mehr als das und es wird angenommen, dass der Drucker offline ist. Auf 0 setzen um das zu verhindern." - -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:174 -msgid "Max. consecutive timeouts while idle" -msgstr "Max. aufeinanderfolgende Timeouts wenn idle" - -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:177 -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:184 -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:191 -msgid "Set to 0 to disable consecutive timeout detection and handling." -msgstr "Auf 0 setzen um aufeinanderfolgende Timeouts zu ignorieren" - -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:180 -msgid "Maximum consecutive communication timeouts while printing. More than this and the printer will be considered to be gone. Set to 0 to disable." -msgstr "Maximale Anzahl aufeinanderfolgender Communication Timeouts beim Drucken. Mehr als das und es wird angenommen, dass der Drucker offline ist. Auf 0 setzen um das zu verhindern." - -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:181 -msgid "Max. consecutive timeouts while printing" -msgstr "Max. aufeinanderfolgende Timeouts beim Drucken" - -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:187 -msgid "Maximum consecutive communication timeouts while a long running command is active. More than this and the printer will be considered to be gone. Set to 0 to disable." -msgstr "Maximale Anzahl aufeinanderfolgender Communication Timeouts wenn ein lang laufender Befehl aktiv ist. Mehr als das und es wird angenommen, dass der Drucker offline ist. Auf 0 setzen um das zu verhindern." - -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:188 -msgid "Max. consecutive timeouts during long running commands" -msgstr "Max. aufeinanderfolgende Timeouts während lang laufender Befehle" - #: src/octoprint/templates/dialogs/settings/server.jinja2:2 msgid "Commands" msgstr "Befehle" @@ -3588,7 +3636,7 @@ msgstr "Danke, dass du OctoPrint installiert hast!\n" #: src/octoprint/templates/dialogs/wizard/firstrun_start.jinja2:6 msgid "\n" -" This wizard will guide you through the final steps to get your OctoPrint instance all setup and ready to go. We'll\n" +" This wizard will guide you through the final steps to get your OctoPrint instance all set up and ready to go. We'll\n" " get you printing in no time!\n" msgstr "Dieser Wizard wird dich nun durch die letzten Schritte begleiten, um deine OctoPrint Instanz fertig einzurichten.\n" @@ -3654,6 +3702,10 @@ msgstr "Verbindungseinstellungen speichern" msgid "Auto-connect on server startup" msgstr "Automatisch bei Serverstart verbinden" +#: src/octoprint/templates/sidebar/connection_header.jinja2:2 +msgid "Refresh connection options" +msgstr "Verbindungsoptionen aktualisieren" + #: src/octoprint/templates/sidebar/files.jinja2:6 msgid "Back" msgstr "Zurück" @@ -3682,12 +3734,6 @@ msgstr "Weitere Daten" msgid "Download" msgstr "Download" -#: src/octoprint/templates/sidebar/files.jinja2:23 -#: src/octoprint/templates/sidebar/files.jinja2:36 -#: src/octoprint/templates/sidebar/files.jinja2:46 -msgid "Remove" -msgstr "Entfernen" - #: src/octoprint/templates/sidebar/files.jinja2:24 msgid "Load" msgstr "Laden" @@ -3717,7 +3763,7 @@ msgid "File list settings" msgstr "Einstellungen der Dateiliste" #: src/octoprint/templates/sidebar/files_header.jinja2:6 -#: src/octoprint/templates/tabs/timelapse.jinja2:108 +#: src/octoprint/templates/tabs/timelapse.jinja2:99 msgid "Sort by name" msgstr "Nach Name sortieren" @@ -3726,7 +3772,7 @@ msgid "Sort by upload date" msgstr "Nach Uploaddatum sortieren" #: src/octoprint/templates/sidebar/files_header.jinja2:8 -#: src/octoprint/templates/tabs/timelapse.jinja2:110 +#: src/octoprint/templates/tabs/timelapse.jinja2:101 msgid "Sort by file size" msgstr "Nach Größe sortieren" @@ -4019,7 +4065,7 @@ msgid "\n" " If the connectivity check is enabled, OctoPrint will regularly check if it's connected to the internet.\n" " This is useful to prevent resource intensive operations (such as checking for updates) if it's already\n" " clear that they won't succeed anyhow.\n" -msgstr "Wenn die Onlineprüfung aktiviert ist, wird OctoPrint in regelmäßigen Abständen prüfen, ob es mit dem Internet verbunden ist. Das ist nützlich um resourcenintensive Operationen zu verhindern (wie z.b. der Prüfung, ob Updates vorliegen), wenn es bereits klar ist, dass diese Operationen mangels Verbindung nicht durchgeführt werden können." +msgstr "Wenn die Onlineprüfung aktiviert ist, wird OctoPrint in regelmäßigen Abständen prüfen, ob es mit dem Internet verbunden ist. Das ist nützlich um resourcenintensive Operationen zu verhindern (wie z.b. der Prüfung, ob Updates vorliegen), wenn es bereits klar ist, dass diese Operationen mangels Verbindung nicht durchgeführt werden können.\n" #: src/octoprint/templates/snippets/settings/server/serverOnlineCheckEnabled.jinja2:4 msgid "Enable regular connectivity check" @@ -4124,6 +4170,18 @@ msgstr "Webcam vertikal flippen" msgid "Rotate webcam 90 degrees counter clockwise" msgstr "Webcam um 90° gegen den Uhrzeigersinn rotieren" +#: src/octoprint/templates/snippets/settings/webcam/webcamSnapshotSslValidation.jinja2:4 +msgid "Validate SSL on snapshot URL (if applicable)" +msgstr "SSL für Snapshot URL validieren (falls relevant)" + +#: src/octoprint/templates/snippets/settings/webcam/webcamSnapshotTimeout.jinja2:1 +msgid "Timeout for taking a snapshot" +msgstr "Timeout für die Aufnahme eines Snapshots" + +#: src/octoprint/templates/snippets/settings/webcam/webcamSnapshotTimeout.jinja2:2 +msgid "Snapshot timeout" +msgstr "Snapshot Timeout" + #: src/octoprint/templates/snippets/settings/webcam/webcamSnapshotUrl.jinja2:1 msgid "URL to use for retrieving webcam snapshot images for timelapse creation" msgstr "URL, die genutzt werden soll, um Einzelaufnahmen für die Zeitraffererstellung abzurufen" @@ -4226,7 +4284,7 @@ msgstr "Schrittgröße" #: src/octoprint/templates/tabs/control.jinja2:38 msgid "Hint: If you move your mouse over the picture, you enter keyboard control mode." -msgstr "Hinweis: Bewegen der Maus über das Bild aktiviert die Tastatursteuerung" +msgstr "Hinweis: Bewegen der Maus über das Bild aktiviert die Tastatursteuerung." #: src/octoprint/templates/tabs/control.jinja2:84 msgid "Feed rate:" @@ -4380,7 +4438,7 @@ msgstr "Ja, bitte visualisiere %(name)s unabhängig seiner Größe" #: src/octoprint/templates/tabs/temperature.jinja2:11 msgid "Current actual temperature as reported by your printer" -msgstr "Aktuelle vom Drucker gemeldete Temperatur " +msgstr "Aktuelle vom Drucker gemeldete Temperatur" #: src/octoprint/templates/tabs/temperature.jinja2:12 msgid "Current target temperature as reported by your printer" @@ -4496,86 +4554,78 @@ msgid "Timelapse post roll" msgstr "Timelapse-Postroll" #: src/octoprint/templates/tabs/timelapse.jinja2:59 -msgid "OctoPrint will take additional pictures to add this many seconds to the end of your rendered timelapse." -msgstr "OctoPrint wird zusätzliche Aufnahmen machen um so viele zusätzliche Sekunden zur gerenderten Zeitrafferaufnahme hinzuzufügen." - -#: src/octoprint/templates/tabs/timelapse.jinja2:66 -msgid "Capture post roll images" -msgstr "Bilder für Zeitraffernachlauf aufzeichnen" +msgid "OctoPrint will use the final picture to add this many seconds to the end of your rendered timelapse." +msgstr "OctoPrint wird die letzte Aufnahme verwenden um so viele zusätzliche Sekunden zur gerenderten Zeitrafferaufnahme hinzuzufügen." -#: src/octoprint/templates/tabs/timelapse.jinja2:67 -msgid "If this is unchecked, OctoPrint will simply repeat the last frame for the post roll instead of continuing to capture new frames." -msgstr "Wenn diese Option nicht angehakt ist wird OctoPrint einfach den letzten Frame der regulären Aufnahme für den Nachlauf verwenden, anstatt neue Frames aufzuzeichnen." - -#: src/octoprint/templates/tabs/timelapse.jinja2:73 +#: src/octoprint/templates/tabs/timelapse.jinja2:64 msgid "Retraction Z-Hop" msgstr "Retraction Z-Hop" -#: src/octoprint/templates/tabs/timelapse.jinja2:79 +#: src/octoprint/templates/tabs/timelapse.jinja2:70 msgid "Enter the retraction z-hop used in the firmware or the gcode file to trigger snapshots for the timelapse only if a real layer change happens. For this to work properly your retraction z-hop has to be different from your layerheight!" msgstr "Gib den Retraction Z-Hop Wert aus deiner Firmware oder den GCODE Dateien hier ein, um Snapshots für Zeitraffer nur bei echten Schichtwechseln auszulösen. Damit das korrekt funktionieren kann muss dein Retraction Z-Hop Wert anders als deine Schichthöhe sein!" -#: src/octoprint/templates/tabs/timelapse.jinja2:86 +#: src/octoprint/templates/tabs/timelapse.jinja2:77 msgid "Save as default" msgstr "Als Standard speichern" -#: src/octoprint/templates/tabs/timelapse.jinja2:87 +#: src/octoprint/templates/tabs/timelapse.jinja2:78 msgid "Check this to make your selected timelapse mode and options persist across restarts." msgstr "Auswählen um deinen gewählten Zeitraffermodus und -parameter über Neustarts hinweg zu persistieren." -#: src/octoprint/templates/tabs/timelapse.jinja2:94 +#: src/octoprint/templates/tabs/timelapse.jinja2:85 msgid "You have unsaved changes. Don't forget to save them." msgstr "Du hast ungespeicherte Änderungen. Vergiss nicht sie zu speichern." -#: src/octoprint/templates/tabs/timelapse.jinja2:95 +#: src/octoprint/templates/tabs/timelapse.jinja2:86 msgid "Save changes" msgstr "Änderungen speichern" -#: src/octoprint/templates/tabs/timelapse.jinja2:96 +#: src/octoprint/templates/tabs/timelapse.jinja2:87 msgid "Reset to active configuration" msgstr "Auf aktive Konfiguration zurücksetzen" -#: src/octoprint/templates/tabs/timelapse.jinja2:102 +#: src/octoprint/templates/tabs/timelapse.jinja2:93 msgid "Finished Timelapses" msgstr "Abgeschlossene Zeitraffer" -#: src/octoprint/templates/tabs/timelapse.jinja2:109 +#: src/octoprint/templates/tabs/timelapse.jinja2:100 msgid "Sort by date" msgstr "Nach Datum sortieren" -#: src/octoprint/templates/tabs/timelapse.jinja2:118 -#: src/octoprint/templates/tabs/timelapse.jinja2:163 +#: src/octoprint/templates/tabs/timelapse.jinja2:109 +#: src/octoprint/templates/tabs/timelapse.jinja2:154 msgid "Select all on this page" msgstr "Alles auf dieser Seite auswählen" -#: src/octoprint/templates/tabs/timelapse.jinja2:119 -#: src/octoprint/templates/tabs/timelapse.jinja2:164 +#: src/octoprint/templates/tabs/timelapse.jinja2:110 +#: src/octoprint/templates/tabs/timelapse.jinja2:155 msgid "Select all" msgstr "Alles auswählen" -#: src/octoprint/templates/tabs/timelapse.jinja2:121 -#: src/octoprint/templates/tabs/timelapse.jinja2:166 +#: src/octoprint/templates/tabs/timelapse.jinja2:112 +#: src/octoprint/templates/tabs/timelapse.jinja2:157 msgid "Clear selection" msgstr "Auswahl aufheben" -#: src/octoprint/templates/tabs/timelapse.jinja2:124 -#: src/octoprint/templates/tabs/timelapse.jinja2:169 +#: src/octoprint/templates/tabs/timelapse.jinja2:115 +#: src/octoprint/templates/tabs/timelapse.jinja2:160 msgid "Delete selected" msgstr "Ausgewählte Elemente löschen" -#: src/octoprint/templates/tabs/timelapse.jinja2:157 +#: src/octoprint/templates/tabs/timelapse.jinja2:148 msgid "Unrendered Timelapses" msgstr "Ungerenderte Zeitrafferaufnahmen" -#: src/octoprint/templates/tabs/timelapse.jinja2:176 +#: src/octoprint/templates/tabs/timelapse.jinja2:167 msgid "Frames" msgstr "Frames" -#: src/octoprint/templates/tabs/timelapse.jinja2:189 +#: src/octoprint/templates/tabs/timelapse.jinja2:180 msgid "Delete unrendered timelapse" msgstr "Ungerenderte Zeitrafferaufnahme löschen" -#: src/octoprint/templates/tabs/timelapse.jinja2:189 +#: src/octoprint/templates/tabs/timelapse.jinja2:180 msgid "Render timelapse" msgstr "Zeitrafferaufnahme rendern" @@ -4766,3 +4816,298 @@ msgstr "Zeitrafferaufnahme rendern" #~ msgid "The ready-to-go Raspberry Pi image with OctoPrint" #~ msgstr "Das fertige RaspberryPi Image mit OctoPrint" + +#~ msgid "Plugin installed" +#~ msgstr "Plugin installiert" + +#~ msgid "" +#~ "A plugin was installed successfully, " +#~ "however it was impossible to detect " +#~ "which one. Please Restart OctoPrint to" +#~ " make sure everything will be " +#~ "registered properly" +#~ msgstr "" +#~ "Ein Plugin wurde erfolgreich installiert, " +#~ "es war aber unmöglich zu detektieren," +#~ " welches. Bitte starte OctoPrint neu " +#~ "um sicherzustellen, dass alles ordnungsgemäß" +#~ " registriert wird." + +#~ msgid "Plugin \"%(name)s\" reinstalled" +#~ msgstr "Plugin \"%(name)s\" reinstalliert" + +#~ msgid "" +#~ "The plugin was reinstalled successfully, " +#~ "however it is blacklisted and therefore" +#~ " won't be loaded." +#~ msgstr "" +#~ "Das Plugin wurde erfolgreich reinstalliert," +#~ " es ist aber auf der Pluginblackliste" +#~ " und wird daher nicht geladen." + +#~ msgid "Plugin \"%(name)s\" installed" +#~ msgstr "Plugin \"%(name)s\" installiert" + +#~ msgid "" +#~ "The plugin was installed successfully, " +#~ "however it is blacklisted and therefore" +#~ " won't be loaded." +#~ msgstr "" +#~ "Das Plugin wurde erfolgreich installiert, " +#~ "es ist jedoch auf der Pluginblackliste" +#~ " und wird daher nicht geladen." + +#~ msgid "The plugin was reinstalled successfully" +#~ msgstr "Das Plugin wurde erfolgreich reinstalliert" + +#~ msgid "" +#~ "The plugin was reinstalled successfully, " +#~ "however a restart of OctoPrint is " +#~ "needed for that to take effect." +#~ msgstr "" +#~ "Das Plugin wurde erfolgreich reinstalliert," +#~ " es ist aber ein Neustart von " +#~ "OctoPrint notwendig bevor es genutzt " +#~ "werden kann." + +#~ msgid "" +#~ "The plugin was reinstalled successfully, " +#~ "however a reload of the page is" +#~ " needed for that to take effect." +#~ msgstr "" +#~ "Das Plugin wurde erfolgreich reinstalliert," +#~ " es ist aber ein Neuladen der " +#~ "Seite notwendig bevor es genutzt werden" +#~ " kann." + +#~ msgid "" +#~ "The plugin was reinstalled successfully, " +#~ "however a reconnect to the printer " +#~ "is needed for that to take effect." +#~ msgstr "" +#~ "Das Plugin wurde erfolgreich reinstalliert," +#~ " es ist aber eine Neuverbindung zum" +#~ " Drucker notwendig bevor es genutzt " +#~ "werden kann." + +#~ msgid "The plugin was installed successfully" +#~ msgstr "Das Plugin wurde erfolgreich installiert" + +#~ msgid "" +#~ "The plugin was installed successfully, " +#~ "however a restart of OctoPrint is " +#~ "needed for that to take effect." +#~ msgstr "" +#~ "Das Plugin wurde erfolgreich installiert, " +#~ "es ist jedoch ein Neustart von " +#~ "OctoPrint notwendig bevor es genutzt " +#~ "werden kann." + +#~ msgid "" +#~ "The plugin was installed successfully, " +#~ "however a reload of the page is" +#~ " needed for that to take effect." +#~ msgstr "" +#~ "Das Plugin wurde erfolgreich installiert, " +#~ "es ist jedoch ein Neuladen der " +#~ "Seite notwendig bevor es genutzt werden" +#~ " kann." + +#~ msgid "" +#~ "The plugin was installed successfully, " +#~ "however a reconnect to the printer " +#~ "is needed for that to take effect." +#~ msgstr "" +#~ "Das Plugin wurde erfolgreich installiert, " +#~ "es ist jedoch eine Neuverbindung zum " +#~ "Drucker notwendig bevor es genutzt " +#~ "werden kann." + +#~ msgid "Reinstalling the plugin from file failed: %(reason)s" +#~ msgstr "Reinstallation des Plugins aus Datei fehlgeschlagen: %(reason)s" + +#~ msgid "Reinstalling the plugin from \"%(source)s\" failed: %(reason)s" +#~ msgstr "" +#~ "Reinstallation des Plugins von \"%(source)s\"" +#~ " fehlgeschlagen: %(reason)s" + +#~ msgid "Installing the plugin from file failed: %(reason)s" +#~ msgstr "Installation des Plugins aus Datei fehlgeschlagen: %(reason)s" + +#~ msgid "Installing the plugin from \"%(source)s\" failed: %(reason)s" +#~ msgstr "Installation des Plugins von \"%(source)s\" fehlgeschlagen: %(reason)s" + +#~ msgid "" +#~ "Reinstalling the plugin from file " +#~ "failed, please see the log for " +#~ "details." +#~ msgstr "" +#~ "Reinstallation des Plugins aus Datei " +#~ "fehlgeschlagen, bitte konsultiere das Log " +#~ "für Details." + +#~ msgid "" +#~ "Reinstalling the plugin from \"%(source)s\"" +#~ " failed, please see the log for " +#~ "details." +#~ msgstr "" +#~ "Reinstallation des Plugins von \"%(source)s\"" +#~ " fehlgeschlagen, bitte konsultiere das Log" +#~ " für Details." + +#~ msgid "Installing the plugin from file failed, please see the log for details." +#~ msgstr "" +#~ "Installation des Plugins aus Datei " +#~ "fehlgeschlagen, bitte konsultiere das Log " +#~ "für Details." + +#~ msgid "" +#~ "Installing the plugin from \"%(source)s\" " +#~ "failed, please see the log for " +#~ "details." +#~ msgstr "" +#~ "Installation des Plugins von \"%(source)s\"" +#~ " fehlgeschlagen, bitte konsultiere das Log" +#~ " für Details." + +#~ msgid "Plugin \"%(name)s\" uninstalled" +#~ msgstr "Plugin \"%(name)s\" deinstalliert" + +#~ msgid "The plugin was uninstalled successfully" +#~ msgstr "Das Plugin wurde erfolgreich deinstalliert" + +#~ msgid "" +#~ "The plugin was uninstalled successfully, " +#~ "however a restart of OctoPrint is " +#~ "needed for that to take effect." +#~ msgstr "" +#~ "Das Plugin wurde erfolgreich deinstalliert," +#~ " es ist jedoch ein Neustart von " +#~ "OctoPrint notwendig." + +#~ msgid "" +#~ "The plugin was uninstalled successfully, " +#~ "however a reload of the page is" +#~ " needed for that to take effect." +#~ msgstr "" +#~ "Das Plugin wurde erfolgreich deinstalliert," +#~ " es ist jedoch ein Neuladen der " +#~ "Seite notwendig." + +#~ msgid "" +#~ "The plugin was uninstalled successfully, " +#~ "however a reconnect to the printer " +#~ "is needed for that to take effect." +#~ msgstr "" +#~ "Das Plugin wurde erfolgreich deinstalliert," +#~ " es ist jedoch eine Neuverbindung zum" +#~ " Drucker notwendig." + +#~ msgid "Uninstalling the plugin failed, please see the log for details." +#~ msgstr "" +#~ "Deinstallation des Plugins fehlgeschlagen, " +#~ "bitte konsultiere das Log für Details." + +#~ msgid "Plugin \"%(name)s\" enabled" +#~ msgstr "Plugin \"%(name)s\" aktiviert" + +#~ msgid "The plugin was enabled successfully." +#~ msgstr "Das Plugin wurde erfolgreich aktiviert." + +#~ msgid "" +#~ "The plugin was enabled successfully, " +#~ "however a restart of OctoPrint is " +#~ "needed for that to take effect." +#~ msgstr "" +#~ "Das Plugin wurde erfolgreich aktiviert, " +#~ "es ist jedoch ein Neustart von " +#~ "OctoPrint notwendig." + +#~ msgid "" +#~ "The plugin was enabled successfully, " +#~ "however a reload of the page is" +#~ " needed for that to take effect." +#~ msgstr "" +#~ "Das Plugin wurde erfolgreich aktiviert, " +#~ "es ist jedoch ein Neuladen der " +#~ "Seite notwendig." + +#~ msgid "" +#~ "The plugin was enabled successfully, " +#~ "however a reconnect to the printer " +#~ "is needed for that to take effect." +#~ msgstr "" +#~ "Das Plugin wurde erfolgreich aktiviert, " +#~ "es ist jedoch eine Neuverbindung zum " +#~ "Drucker notwendig." + +#~ msgid "Toggling the plugin failed: %(reason)s" +#~ msgstr "Togglen des Plugins fehlgeschalgen: %(reason)s" + +#~ msgid "Toggling the plugin failed, please see the log for details." +#~ msgstr "" +#~ "Togglen des Plugins fehlgeschlagen, bitte " +#~ "konsultiere das Log für Details." + +#~ msgid "Plugin \"%(name)s\" disabled" +#~ msgstr "Plugin \"%(name)s\" deaktiviert" + +#~ msgid "The plugin was disabled successfully." +#~ msgstr "Das Plugin wurde erfolgreich deaktiviert." + +#~ msgid "" +#~ "The plugin was disabled successfully, " +#~ "however a restart of OctoPrint is " +#~ "needed for that to take effect." +#~ msgstr "" +#~ "Das Plugin wurde erfolgreich deaktiviert, " +#~ "es ist jedoch ein Neustart von " +#~ "OctoPrint notwendig." + +#~ msgid "" +#~ "The plugin was disabled successfully, " +#~ "however a reload of the page is" +#~ " needed for that to take effect." +#~ msgstr "" +#~ "Das Plugin wurde erfolgreich deaktiviert, " +#~ "es ist jedoch ein Neuladen der " +#~ "Seite notwendig." + +#~ msgid "" +#~ "The plugin was disabled successfully, " +#~ "however a reconnect to the printer " +#~ "is needed for that to take effect." +#~ msgstr "" +#~ "Das Plugin wurde erfolgreich deaktiviert, " +#~ "es ist jedoch eine Neuverbindung zum " +#~ "Drucker notwendig." + +#~ msgid "just now" +#~ msgstr "gerade eben" + +#~ msgid "SD status interval" +#~ msgstr "SD-Status-Intervall" + +#~ msgid "Log communication to serial.log (might negatively impact performance)" +#~ msgstr "" +#~ "Logge die Kommunikation in das " +#~ "serial.log (kann die Performance negativ " +#~ "beeinflussen)" + +#~ msgid "Most Marlin < v1.1.0" +#~ msgstr "Die meisten Varianten von Marlin < v1.1.0" + +#~ msgid "Capture post roll images" +#~ msgstr "Bilder für Zeitraffernachlauf aufzeichnen" + +#~ msgid "" +#~ "If this is unchecked, OctoPrint will " +#~ "simply repeat the last frame for " +#~ "the post roll instead of continuing " +#~ "to capture new frames." +#~ msgstr "" +#~ "Wenn diese Option nicht angehakt ist " +#~ "wird OctoPrint einfach den letzten Frame" +#~ " der regulären Aufnahme für den " +#~ "Nachlauf verwenden, anstatt neue Frames " +#~ "aufzuzeichnen." diff --git a/translations/messages.pot b/translations/messages.pot index 4a3cef5ce9..10fd9c6dcb 100644 --- a/translations/messages.pot +++ b/translations/messages.pot @@ -1,14 +1,14 @@ # Translations template for OctoPrint. -# Copyright (C) 2017 The OctoPrint Project +# Copyright (C) 2018 The OctoPrint Project # This file is distributed under the same license as the OctoPrint project. -# FIRST AUTHOR , 2017. +# FIRST AUTHOR , 2018. # #, fuzzy msgid "" msgstr "" -"Project-Id-Version: OctoPrint 1.3.6rc2.dev10+g41eb9481\n" +"Project-Id-Version: OctoPrint 1.3.7.dev166+g8a3a87efa.dirty\n" "Report-Msgid-Bugs-To: i18n@octoprint.org\n" -"POT-Creation-Date: 2017-12-05 12:29+0100\n" +"POT-Creation-Date: 2018-03-19 13:41+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -48,12 +48,12 @@ msgid "" msgstr "" #: src/octoprint/plugins/announcements/static/js/announcements.js:271 -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:957 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:973 msgid "Later" msgstr "" #: src/octoprint/plugins/announcements/static/js/announcements.js:277 -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:964 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:980 msgid "Mark read" msgstr "" @@ -83,15 +83,16 @@ msgstr "" #: src/octoprint/plugins/cura/templates/snippets/settings/cura/profileImporter.jinja2:27 #: src/octoprint/plugins/cura/templates/snippets/settings/cura/profiles.jinja2:3 #: src/octoprint/plugins/cura/templates/snippets/settings/cura/profiles.jinja2:9 +#: src/octoprint/plugins/logging/templates/logging_settings.jinja2:5 +#: src/octoprint/plugins/logging/templates/logging_settings.jinja2:11 +#: src/octoprint/plugins/logging/templates/logging_settings.jinja2:50 #: src/octoprint/plugins/pluginmanager/templates/pluginmanager_settings.jinja2:43 #: src/octoprint/templates/dialogs/settings/accesscontrol.jinja2:4 -#: src/octoprint/templates/dialogs/settings/logs.jinja2:6 -#: src/octoprint/templates/dialogs/settings/logs.jinja2:12 #: src/octoprint/templates/dialogs/settings/terminalfilters.jinja2:3 #: src/octoprint/templates/snippets/settings/printerprofiles/profileEditorGeneral.jinja2:3 #: src/octoprint/templates/snippets/settings/printerprofiles/profiles.jinja2:4 -#: src/octoprint/templates/tabs/timelapse.jinja2:130 -#: src/octoprint/templates/tabs/timelapse.jinja2:175 +#: src/octoprint/templates/tabs/timelapse.jinja2:121 +#: src/octoprint/templates/tabs/timelapse.jinja2:166 msgid "Name" msgstr "" @@ -406,6 +407,7 @@ msgstr "" #: src/octoprint/plugins/corewizard/templates/corewizard_printerprofile_wizard.jinja2:7 #: src/octoprint/plugins/cura/templates/cura_settings.jinja2:1 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:3 #: src/octoprint/templates/snippets/settings/printerprofiles/profileEditor.jinja2:9 #: src/octoprint/templates/tabs/control.jinja2:114 msgid "General" @@ -504,22 +506,22 @@ msgid "" msgstr "" #: src/octoprint/plugins/cura/static/js/cura.js:234 -#: src/octoprint/static/js/app/viewmodels/settings.js:357 +#: src/octoprint/static/js/app/viewmodels/settings.js:387 msgid "The path doesn't exist" msgstr "" #: src/octoprint/plugins/cura/static/js/cura.js:236 -#: src/octoprint/static/js/app/viewmodels/settings.js:359 +#: src/octoprint/static/js/app/viewmodels/settings.js:389 msgid "The path is not a file" msgstr "" #: src/octoprint/plugins/cura/static/js/cura.js:238 -#: src/octoprint/static/js/app/viewmodels/settings.js:361 +#: src/octoprint/static/js/app/viewmodels/settings.js:391 msgid "The path is not an executable" msgstr "" #: src/octoprint/plugins/cura/static/js/cura.js:241 -#: src/octoprint/static/js/app/viewmodels/settings.js:364 +#: src/octoprint/static/js/app/viewmodels/settings.js:394 msgid "The path is valid" msgstr "" @@ -670,15 +672,15 @@ msgid "Confirm" msgstr "" #: src/octoprint/plugins/cura/templates/snippets/settings/cura/profiles.jinja2:3 -#: src/octoprint/templates/dialogs/settings/logs.jinja2:6 +#: src/octoprint/plugins/logging/templates/logging_settings.jinja2:5 msgid "Sort by" msgstr "" #: src/octoprint/plugins/cura/templates/snippets/settings/cura/profiles.jinja2:3 +#: src/octoprint/plugins/logging/templates/logging_settings.jinja2:5 #: src/octoprint/plugins/pluginmanager/templates/pluginmanager_settings.jinja2:142 -#: src/octoprint/templates/dialogs/settings/logs.jinja2:6 #: src/octoprint/templates/sidebar/files_header.jinja2:6 -#: src/octoprint/templates/tabs/timelapse.jinja2:108 +#: src/octoprint/templates/tabs/timelapse.jinja2:99 msgid "ascending" msgstr "" @@ -697,6 +699,64 @@ msgid "" "discoverable on the network via Bonjour and uPnP." msgstr "" +#: src/octoprint/plugins/logging/templates/logging_settings.jinja2:1 +msgid "Logs" +msgstr "" + +#: src/octoprint/plugins/logging/templates/logging_settings.jinja2:5 +msgid "Modification date" +msgstr "" + +#: src/octoprint/plugins/logging/templates/logging_settings.jinja2:5 +#: src/octoprint/plugins/pluginmanager/templates/pluginmanager_settings.jinja2:143 +#: src/octoprint/templates/sidebar/files_header.jinja2:7 +#: src/octoprint/templates/sidebar/files_header.jinja2:8 +#: src/octoprint/templates/tabs/timelapse.jinja2:100 +#: src/octoprint/templates/tabs/timelapse.jinja2:101 +msgid "descending" +msgstr "" + +#: src/octoprint/plugins/logging/templates/logging_settings.jinja2:5 +#: src/octoprint/plugins/logging/templates/logging_settings.jinja2:12 +#: src/octoprint/templates/sidebar/files.jinja2:18 +#: src/octoprint/templates/sidebar/files.jinja2:33 +#: src/octoprint/templates/sidebar/files.jinja2:44 +#: src/octoprint/templates/tabs/timelapse.jinja2:122 +#: src/octoprint/templates/tabs/timelapse.jinja2:168 +msgid "Size" +msgstr "" + +#: src/octoprint/plugins/logging/templates/logging_settings.jinja2:13 +msgid "Date" +msgstr "" + +#: src/octoprint/plugins/logging/templates/logging_settings.jinja2:14 +#: src/octoprint/templates/dialogs/settings/accesscontrol.jinja2:7 +#: src/octoprint/templates/snippets/settings/printerprofiles/profiles.jinja2:6 +#: src/octoprint/templates/tabs/timelapse.jinja2:123 +#: src/octoprint/templates/tabs/timelapse.jinja2:169 +msgid "Action" +msgstr "" + +#: src/octoprint/plugins/logging/templates/logging_settings.jinja2:46 +msgid "Logging Levels" +msgstr "" + +#: src/octoprint/plugins/logging/templates/logging_settings.jinja2:51 +msgid "Level" +msgstr "" + +#: src/octoprint/plugins/logging/templates/logging_settings.jinja2:68 +#: src/octoprint/templates/sidebar/files.jinja2:23 +#: src/octoprint/templates/sidebar/files.jinja2:36 +#: src/octoprint/templates/sidebar/files.jinja2:46 +msgid "Remove" +msgstr "" + +#: src/octoprint/plugins/logging/templates/logging_settings.jinja2:87 +msgid "Add" +msgstr "" + #: src/octoprint/plugins/octopi_support/__init__.py:164 msgid "" "Without this plugin OctoPrint will no longer be able to provide " @@ -720,121 +780,155 @@ msgstr "" msgid "Plugin Manager" msgstr "" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:211 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:221 msgid "There are no plugin notices. Great!" msgstr "" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:213 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:223 msgid "There is a plugin notice for one of your installed plugins." msgstr "" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:215 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:225 #, python-format msgid "" "There are %(count)d plugin notices for one or more of your installed " "plugins." msgstr "" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:310 -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:533 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:320 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:543 msgid "Installing plugin..." msgstr "" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:310 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:320 msgid "Installing plugin from uploaded archive..." msgstr "" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:331 -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:457 -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:557 -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:595 -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:758 -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1163 -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1214 -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1232 -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1250 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:341 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:467 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:567 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:605 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:795 msgid "Something went wrong" msgstr "" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:332 -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:458 -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:558 -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:596 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:342 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:468 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:568 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:606 msgid "Please consult octoprint.log for details" msgstr "" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:477 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:487 #, python-format msgid "You are about to disable \"%(name)s\"." msgstr "" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:480 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:490 msgid "This is not recommended" msgstr "" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:482 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:492 msgid "Do you still want to disable it?" msgstr "" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:483 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:493 msgid "Keep enabled" msgstr "" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:484 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:494 msgid "Disable anyway" msgstr "" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:535 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:545 #, python-format msgid "Installing plugin \"%(name)s\" from %(url)s..." msgstr "" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:537 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:547 #, python-format msgid "Installing plugin from %(url)s..." msgstr "" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:540 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:550 msgid "Reinstalling plugin..." msgstr "" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:541 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:551 #, python-format msgid "Reinstalling plugin \"%(name)s\" from %(url)s..." msgstr "" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:587 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:597 msgid "Uninstalling plugin..." msgstr "" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:587 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:597 #, python-format msgid "Uninstalling plugin \"%(name)s\"" msgstr "" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:701 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:711 msgid "Reinstall" msgstr "" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:701 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:711 #: src/octoprint/plugins/pluginmanager/templates/pluginmanager_settings.jinja2:198 #: src/octoprint/plugins/pluginmanager/templates/pluginmanager_settings.jinja2:214 msgid "Install" msgstr "" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:701 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:711 msgid "Disabled" msgstr "" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:701 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:711 msgid "Incompatible" msgstr "" +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:720 +msgid "Plugin management log" +msgstr "" + +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:728 +#, python-format +msgid "%(count)d earlier actions..." +msgstr "" + +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:730 +#, python-format +msgid "%(count)d earlier action" +msgstr "" + #: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:741 +#, python-format +msgid "Install %(plugin)s: %(result)s" +msgstr "" + +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:745 +#, python-format +msgid "Uninstall %(plugin)s: %(result)s" +msgstr "" + +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:749 +#, python-format +msgid "Enable %(plugin)s: %(result)s" +msgstr "" + +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:753 +#, python-format +msgid "Disable %(plugin)s: %(result)s" +msgstr "" + +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:770 +msgid "A restart is needed for the changes to take effect." +msgstr "" + +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:778 msgid "Restart now" msgstr "" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:746 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:783 msgid "" "This will restart your OctoPrint server.

This " "action may disrupt any ongoing print jobs (depending on your printer's " @@ -842,318 +936,104 @@ msgid "" " from your printer's internal storage)." msgstr "" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:752 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:789 msgid "Restart in progress" msgstr "" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:753 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:790 msgid "The server is now being restarted in the background" msgstr "" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:759 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:796 msgid "" "Trying to restart the server produced an error, please check " "octoprint.log for details. You'll have to restart manually." msgstr "" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:781 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:809 +msgid "A refresh is needed for the changes to take effect." +msgstr "" + +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:816 #: src/octoprint/templates/overlays/reloadui.jinja2:14 msgid "Reload now" msgstr "" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:847 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:825 +msgid "A reconnect to the printer is needed for the changes to take effect." +msgstr "" + +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:862 msgid "Error!" msgstr "" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:850 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:865 msgid "Done!" msgstr "" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:875 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:891 msgid "Blacklisted" msgstr "" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:877 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:893 msgid "Disabled due to active safe mode" msgstr "" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:879 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:895 msgid "Enable Plugin" msgstr "" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:882 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:898 msgid "Disable Plugin" msgstr "" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:902 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:918 #, python-format msgid "" "There are %(count)d notices (%(important)d marked as important) available" " regarding this plugin - click to show!" msgstr "" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:904 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:920 #, python-format msgid "" "There are %(count)d notices available regarding this plugin - click to " "show!" msgstr "" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:908 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:924 msgid "" "There is an important notice available regarding this plugin - click to " "show!" msgstr "" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:910 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:926 msgid "There is a notice available regarding this plugin - click to show!" msgstr "" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:924 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:940 #, python-format msgid "Important notice regarding plugin \"%(name)s\"" msgstr "" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:926 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:942 #, python-format msgid "Notice regarding plugin \"%(name)s\"" msgstr "" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:933 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:949 #, python-format msgid "Affected versions: %(versions)s" msgstr "" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:935 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:951 msgid "Affected versions: all" msgstr "" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:940 +#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:956 msgid "Read more..." msgstr "" -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1133 -msgid "Plugin installed" -msgstr "" - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1134 -msgid "" -"A plugin was installed successfully, however it was impossible to detect " -"which one. Please Restart OctoPrint to make sure everything will be " -"registered properly" -msgstr "" - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1140 -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1150 -#, python-format -msgid "Plugin \"%(name)s\" reinstalled" -msgstr "" - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1141 -msgid "" -"The plugin was reinstalled successfully, however it is blacklisted and " -"therefore won't be loaded." -msgstr "" - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1143 -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1156 -#, python-format -msgid "Plugin \"%(name)s\" installed" -msgstr "" - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1144 -msgid "" -"The plugin was installed successfully, however it is blacklisted and " -"therefore won't be loaded." -msgstr "" - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1151 -msgid "The plugin was reinstalled successfully" -msgstr "" - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1152 -msgid "" -"The plugin was reinstalled successfully, however a restart of OctoPrint " -"is needed for that to take effect." -msgstr "" - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1153 -msgid "" -"The plugin was reinstalled successfully, however a reload of the page is " -"needed for that to take effect." -msgstr "" - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1154 -msgid "" -"The plugin was reinstalled successfully, however a reconnect to the " -"printer is needed for that to take effect." -msgstr "" - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1157 -msgid "The plugin was installed successfully" -msgstr "" - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1158 -msgid "" -"The plugin was installed successfully, however a restart of OctoPrint is " -"needed for that to take effect." -msgstr "" - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1159 -msgid "" -"The plugin was installed successfully, however a reload of the page is " -"needed for that to take effect." -msgstr "" - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1160 -msgid "" -"The plugin was installed successfully, however a reconnect to the printer" -" is needed for that to take effect." -msgstr "" - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1176 -#, python-format -msgid "Reinstalling the plugin from file failed: %(reason)s" -msgstr "" - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1178 -#, python-format -msgid "Reinstalling the plugin from \"%(source)s\" failed: %(reason)s" -msgstr "" - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1182 -#, python-format -msgid "Installing the plugin from file failed: %(reason)s" -msgstr "" - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1184 -#, python-format -msgid "Installing the plugin from \"%(source)s\" failed: %(reason)s" -msgstr "" - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1190 -msgid "Reinstalling the plugin from file failed, please see the log for details." -msgstr "" - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1192 -#, python-format -msgid "" -"Reinstalling the plugin from \"%(source)s\" failed, please see the log " -"for details." -msgstr "" - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1196 -msgid "Installing the plugin from file failed, please see the log for details." -msgstr "" - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1198 -#, python-format -msgid "" -"Installing the plugin from \"%(source)s\" failed, please see the log for " -"details." -msgstr "" - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1208 -#, python-format -msgid "Plugin \"%(name)s\" uninstalled" -msgstr "" - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1209 -msgid "The plugin was uninstalled successfully" -msgstr "" - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1210 -msgid "" -"The plugin was uninstalled successfully, however a restart of OctoPrint " -"is needed for that to take effect." -msgstr "" - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1211 -msgid "" -"The plugin was uninstalled successfully, however a reload of the page is " -"needed for that to take effect." -msgstr "" - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1212 -msgid "" -"The plugin was uninstalled successfully, however a reconnect to the " -"printer is needed for that to take effect." -msgstr "" - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1216 -#, python-format -msgid "Uninstalling the plugin failed: %(reason)s" -msgstr "" - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1218 -msgid "Uninstalling the plugin failed, please see the log for details." -msgstr "" - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1226 -#, python-format -msgid "Plugin \"%(name)s\" enabled" -msgstr "" - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1227 -msgid "The plugin was enabled successfully." -msgstr "" - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1228 -msgid "" -"The plugin was enabled successfully, however a restart of OctoPrint is " -"needed for that to take effect." -msgstr "" - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1229 -msgid "" -"The plugin was enabled successfully, however a reload of the page is " -"needed for that to take effect." -msgstr "" - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1230 -msgid "" -"The plugin was enabled successfully, however a reconnect to the printer " -"is needed for that to take effect." -msgstr "" - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1234 -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1252 -#, python-format -msgid "Toggling the plugin failed: %(reason)s" -msgstr "" - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1236 -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1254 -msgid "Toggling the plugin failed, please see the log for details." -msgstr "" - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1244 -#, python-format -msgid "Plugin \"%(name)s\" disabled" -msgstr "" - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1245 -msgid "The plugin was disabled successfully." -msgstr "" - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1246 -msgid "" -"The plugin was disabled successfully, however a restart of OctoPrint is " -"needed for that to take effect." -msgstr "" - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1247 -msgid "" -"The plugin was disabled successfully, however a reload of the page is " -"needed for that to take effect." -msgstr "" - -#: src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js:1248 -msgid "" -"The plugin was disabled successfully, however a reconnect to the printer " -"is needed for that to take effect." -msgstr "" - #: src/octoprint/plugins/pluginmanager/templates/pluginmanager_settings.jinja2:3 msgid "" "Take note that all plugin management functionality is disabled while your" @@ -1269,15 +1149,6 @@ msgstr "" msgid "Sort by publication date" msgstr "" -#: src/octoprint/plugins/pluginmanager/templates/pluginmanager_settings.jinja2:143 -#: src/octoprint/templates/dialogs/settings/logs.jinja2:6 -#: src/octoprint/templates/sidebar/files_header.jinja2:7 -#: src/octoprint/templates/sidebar/files_header.jinja2:8 -#: src/octoprint/templates/tabs/timelapse.jinja2:109 -#: src/octoprint/templates/tabs/timelapse.jinja2:110 -msgid "descending" -msgstr "" - #: src/octoprint/plugins/pluginmanager/templates/pluginmanager_settings.jinja2:145 msgid "Only show uninstalled plugins" msgstr "" @@ -1339,7 +1210,9 @@ msgstr "" #: src/octoprint/plugins/pluginmanager/templates/pluginmanager_settings.jinja2:220 #: src/octoprint/plugins/softwareupdate/templates/softwareupdate_settings.jinja2:64 -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:121 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:147 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:247 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:307 #: src/octoprint/templates/dialogs/settings/webcam.jinja2:9 #: src/octoprint/templates/dialogs/settings/webcam.jinja2:23 #: src/octoprint/templates/snippets/settings/server/serverPluginBlacklist.jinja2:5 @@ -1444,12 +1317,31 @@ msgstr "" msgid "Save" msgstr "" -#: src/octoprint/plugins/softwareupdate/__init__.py:589 +#: src/octoprint/plugins/printer_safety_check/__init__.py:44 +msgid "Printer Safety Warning" +msgstr "" + +#: src/octoprint/plugins/printer_safety_check/__init__.py:105 +msgid "" +"Without this plugin OctoPrint will no longer be able to check if the " +"printer it is connected to has a known safetyissue and inform you about " +"that fact." +msgstr "" + +#: src/octoprint/plugins/printer_safety_check/templates/printer_safety_check_sidebar.jinja2:1 +msgid "Warning!" +msgstr "" + +#: src/octoprint/plugins/printer_safety_check/templates/printer_safety_check_sidebar.jinja2:5 +msgid "Learn more..." +msgstr "" + +#: src/octoprint/plugins/softwareupdate/__init__.py:570 #: src/octoprint/plugins/softwareupdate/templates/softwareupdate_wizard.jinja2:1 msgid "Software Update" msgstr "" -#: src/octoprint/plugins/softwareupdate/__init__.py:999 +#: src/octoprint/plugins/softwareupdate/__init__.py:982 #: src/octoprint/server/views.py:571 #: src/octoprint/static/js/app/viewmodels/appearance.js:13 #: src/octoprint/static/js/app/viewmodels/appearance.js:18 @@ -1460,7 +1352,7 @@ msgstr "" msgid "OctoPrint" msgstr "" -#: src/octoprint/plugins/softwareupdate/__init__.py:1166 +#: src/octoprint/plugins/softwareupdate/__init__.py:1142 msgid "" "Without this plugin OctoPrint will no longer be able to update itself or " "any of your installed plugins which might put your system at risk." @@ -1483,173 +1375,177 @@ msgstr "" msgid "unknown" msgstr "" -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:309 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:307 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:511 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:525 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:682 +msgid "Updating..." +msgstr "" + +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:307 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:525 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:682 +msgid "Updating, please wait." +msgstr "" + +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:314 msgid "There are updates available for the following components:" msgstr "" -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:317 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:322 #: src/octoprint/plugins/softwareupdate/templates/softwareupdate.jinja2:14 msgid "Release Notes" msgstr "" -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:323 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:328 msgid "" "Those components marked with can be updated" " directly." msgstr "" -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:326 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:331 msgid "" "To have updates applied, get in touch with an administrator of this " "OctoPrint instance." msgstr "" -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:332 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:337 msgid "Update Available" msgstr "" -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:344 -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:370 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:349 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:375 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:347 msgid "Ignore" msgstr "" -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:348 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:353 msgid "" "You can make this message display again via \"Settings\" > \"Software " "Update\" > \"Check for update now\"" msgstr "" -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:352 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:357 msgid "Update now" msgstr "" -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:390 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:395 msgid "Everything is up-to-date" msgstr "" -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:416 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:421 msgid "No internet connection" msgstr "" -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:420 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:425 msgid "Update not possible" msgstr "" -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:431 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:436 msgid "Unknown update check, configuration ok?" msgstr "" -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:434 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:439 msgid "Cannot check for update, need online connection" msgstr "" -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:437 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:442 msgid "Network error while checking for update" msgstr "" -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:440 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:445 msgid "Unknown error while checking for update, please check the logs" msgstr "" -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:506 -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:520 -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:681 -msgid "Updating..." -msgstr "" - -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:507 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:512 msgid "Now updating, please wait." msgstr "" -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:520 -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:681 -msgid "Updating, please wait." -msgstr "" - -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:525 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:530 msgid "Update not started!" msgstr "" -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:526 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:531 msgid "" -"The update could not be started. Is it already active? Please consult the" -" log for details." +"The update could not be started. Is it already active? Please consult " +"octoprint.log for details." msgstr "" -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:548 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:553 msgid "Can't update while printing" msgstr "" -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:549 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:554 msgid "" "A print job is currently in progress. Updating will be prevented until it" " is done." msgstr "" -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:684 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:699 #, python-format msgid "Now updating %(name)s to %(version)s" msgstr "" -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:703 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:718 msgid "Update successful, restarting!" msgstr "" -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:704 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:719 msgid "The update finished successfully and the server will now be restarted." msgstr "" -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:721 -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:770 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:736 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:785 msgid "Restart failed" msgstr "" -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:722 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:737 msgid "" "The server apparently did not restart by itself, you'll have to do it " -"manually. Please consult the log file on what went wrong." +"manually. Please consult octoprint.log on what went wrong." msgstr "" -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:744 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:759 msgid "The update finished successfully, please restart OctoPrint now." msgstr "" -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:746 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:761 msgid "The update finished successfully, please reboot the server now." msgstr "" -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:749 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:764 msgid "Update successful, restart required!" msgstr "" -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:765 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:780 msgid "" "Restarting OctoPrint failed, please restart it manually. You might also " -"want to consult the log file on what went wrong here." +"want to consult octoprint.log on what went wrong here." msgstr "" -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:767 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:782 msgid "" "Rebooting the server failed, please reboot it manually. You might also " -"want to consult the log file on what went wrong here." +"want to consult octoprint.log on what went wrong here." msgstr "" -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:786 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:801 msgid "Update successful!" msgstr "" -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:787 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:802 msgid "The update finished successfully." msgstr "" -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:802 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:817 msgid "Update failed!" msgstr "" -#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:803 +#: src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js:818 msgid "" -"The update did not finish successfully. Please consult the log for " -"details." +"The update did not finish successfully. Please consult " +"octoprint.log and " +"plugin_softwareupdate_console.log for details." msgstr "" #: src/octoprint/plugins/softwareupdate/templates/softwareupdate.jinja2:4 @@ -1820,6 +1716,7 @@ msgid "Plugins" msgstr "" #: src/octoprint/server/views.py:538 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:12 msgid "Connection" msgstr "" @@ -1901,27 +1798,22 @@ msgid "Appearance" msgstr "" #: src/octoprint/server/views.py:576 -#: src/octoprint/templates/dialogs/settings/logs.jinja2:2 -msgid "Logs" -msgstr "" - -#: src/octoprint/server/views.py:577 msgid "Server" msgstr "" -#: src/octoprint/server/views.py:583 +#: src/octoprint/server/views.py:582 msgid "Access" msgstr "" -#: src/octoprint/server/views.py:584 +#: src/octoprint/server/views.py:583 msgid "Interface" msgstr "" -#: src/octoprint/server/views.py:600 +#: src/octoprint/server/views.py:599 msgid "Start" msgstr "" -#: src/octoprint/server/views.py:601 +#: src/octoprint/server/views.py:600 #: src/octoprint/templates/dialogs/wizard.jinja2:57 msgid "Finish" msgstr "" @@ -2015,62 +1907,105 @@ msgstr "" msgid "Slicing ... (%(percentage)d%%)" msgstr "" -#: src/octoprint/static/js/app/dataupdater.js:231 -#: src/octoprint/static/js/app/dataupdater.js:240 -msgid "Unhandled communication error" +#: src/octoprint/static/js/app/dataupdater.js:230 +#: src/octoprint/static/js/app/dataupdater.js:241 +msgid "Error reported by printer" msgstr "" -#: src/octoprint/static/js/app/dataupdater.js:232 +#: src/octoprint/static/js/app/dataupdater.js:231 #, python-format msgid "" -"There was an unhandled error while talking to the printer. Due to that " -"the ongoing print job was cancelled. Error: %(firmwareError)s" +"Your printer's firmware reported an error. Due to that the ongoing print " +"job will be cancelled. Reported error: %(firmwareError)s" msgstr "" -#: src/octoprint/static/js/app/dataupdater.js:241 +#: src/octoprint/static/js/app/dataupdater.js:242 #, python-format msgid "" -"There was an unhandled error while talking to the printer. Due to that " -"OctoPrint disconnected. Error: %(error)s" +"Your printer's firmware reported an error. Due to that OctoPrint will " +"disconnect. Reported error: %(error)s" msgstr "" -#: src/octoprint/static/js/app/dataupdater.js:248 -msgid "Printer reset detected" +#: src/octoprint/static/js/app/dataupdater.js:247 +msgid "Communication error" msgstr "" -#: src/octoprint/static/js/app/dataupdater.js:249 +#: src/octoprint/static/js/app/dataupdater.js:248 +#, python-format msgid "" -"It looks like the printer was reset while a connection was active. If " -"this was intentional you may safely ignore this message. Otherwise you " -"should investigate why your printer reset itself, since this will " -"interrupt prints and also file transfers to your printer's SD." +"There was a communication error while talking to your printer. Please " +"consult the terminal output and octoprint.log for details. Error: " +"%(error)s" msgstr "" -#: src/octoprint/static/js/app/helpers.js:378 -#, python-format -msgid "%(hour)02d:%(minute)02d:%(second)02d" +#: src/octoprint/static/js/app/dataupdater.js:252 +msgid "Error connecting to printer" msgstr "" -#: src/octoprint/static/js/app/helpers.js:437 -#: src/octoprint/static/js/app/helpers.js:445 +#: src/octoprint/static/js/app/dataupdater.js:253 #, python-format -msgid "%(days)d day" +msgid "" +"There was an error while trying to connect to your printer. Error: " +"%(error)s" msgstr "" -#: src/octoprint/static/js/app/helpers.js:439 -#: src/octoprint/static/js/app/helpers.js:447 -#, python-format -msgid "%(days)d days" +#: src/octoprint/static/js/app/dataupdater.js:257 +msgid "Error starting a print" msgstr "" -#: src/octoprint/static/js/app/helpers.js:442 +#: src/octoprint/static/js/app/dataupdater.js:258 #, python-format -msgid "%(days)d.5 days" +msgid "There was an error while trying to start a print job. Error: %(error)s" msgstr "" -#: src/octoprint/static/js/app/helpers.js:456 -#: src/octoprint/static/js/app/helpers.js:468 -#, python-format +#: src/octoprint/static/js/app/dataupdater.js:267 +msgid "Unknown error" +msgstr "" + +#: src/octoprint/static/js/app/dataupdater.js:268 +#, python-format +msgid "" +"There was an unknown error while talking to your printer. Please consult " +"the terminal output and octoprint.log for details. Error: %(error)s" +msgstr "" + +#: src/octoprint/static/js/app/dataupdater.js:283 +msgid "Printer reset detected" +msgstr "" + +#: src/octoprint/static/js/app/dataupdater.js:284 +msgid "" +"It looks like your printer reset while a connection was active. If this " +"was intentional you may safely ignore this message. Otherwise you should " +"investigate why your printer reset itself, since this will interrupt " +"prints and also file transfers to your printer's SD." +msgstr "" + +#: src/octoprint/static/js/app/helpers.js:378 +#, python-format +msgid "%(hour)02d:%(minute)02d:%(second)02d" +msgstr "" + +#: src/octoprint/static/js/app/helpers.js:437 +#: src/octoprint/static/js/app/helpers.js:445 +#, python-format +msgid "%(days)d day" +msgstr "" + +#: src/octoprint/static/js/app/helpers.js:439 +#: src/octoprint/static/js/app/helpers.js:447 +#, python-format +msgid "%(days)d days" +msgstr "" + +#: src/octoprint/static/js/app/helpers.js:442 +#, python-format +msgid "%(days)d.5 days" +msgstr "" + +#: src/octoprint/static/js/app/helpers.js:456 +#: src/octoprint/static/js/app/helpers.js:468 +#, python-format msgid "%(hours)d hour" msgstr "" @@ -2133,6 +2068,7 @@ msgid "off" msgstr "" #: src/octoprint/static/js/app/helpers.js:654 +#: src/octoprint/static/js/app/viewmodels/connection.js:135 msgid "Are you sure?" msgstr "" @@ -2194,13 +2130,31 @@ msgid "Connect" msgstr "" #: src/octoprint/static/js/app/viewmodels/connection.js:44 +#: src/octoprint/static/js/app/viewmodels/connection.js:144 msgid "Disconnect" msgstr "" +#: src/octoprint/static/js/app/viewmodels/connection.js:136 +msgid "" +"

You are about to disconnect from the printer while a print is " +"in progress.

Disconnecting while a print is in progress " +"will prevent OctoPrint from completing the print. If you're printing from" +" an SD card attached directly to the printer, any attempt to restart " +"OctoPrint or reconnect to the printer could interrupt the print.

" +msgstr "" + +#: src/octoprint/static/js/app/viewmodels/connection.js:142 +msgid "Are you sure you want to disconnect from the printer?" +msgstr "" + +#: src/octoprint/static/js/app/viewmodels/connection.js:143 +msgid "Stay Connected" +msgstr "" + #: src/octoprint/static/js/app/viewmodels/control.js:72 #: src/octoprint/static/js/app/viewmodels/files.js:606 #: src/octoprint/static/js/app/viewmodels/gcode.js:567 -#: src/octoprint/static/js/app/viewmodels/printerstate.js:237 +#: src/octoprint/static/js/app/viewmodels/printerstate.js:241 #: src/octoprint/static/js/app/viewmodels/temperature.js:115 #: src/octoprint/static/js/app/viewmodels/temperature.js:126 msgid "Tool" @@ -2371,7 +2325,7 @@ msgid "Estimated layer height" msgstr "" #: src/octoprint/static/js/app/viewmodels/gcode.js:528 -#: src/octoprint/templates/tabs/timelapse.jinja2:77 +#: src/octoprint/templates/tabs/timelapse.jinja2:68 msgid "mm" msgstr "" @@ -2551,95 +2505,95 @@ msgstr "" msgid "Edit Printer Profile \"%(name)s\"" msgstr "" -#: src/octoprint/static/js/app/viewmodels/printerstate.js:48 +#: src/octoprint/static/js/app/viewmodels/printerstate.js:50 msgid "Restarts the print job from the beginning" msgstr "" -#: src/octoprint/static/js/app/viewmodels/printerstate.js:49 +#: src/octoprint/static/js/app/viewmodels/printerstate.js:51 msgid "Starts the print job" msgstr "" -#: src/octoprint/static/js/app/viewmodels/printerstate.js:50 +#: src/octoprint/static/js/app/viewmodels/printerstate.js:52 msgid "Resumes the print job" msgstr "" -#: src/octoprint/static/js/app/viewmodels/printerstate.js:51 +#: src/octoprint/static/js/app/viewmodels/printerstate.js:53 msgid "Pauses the print job" msgstr "" -#: src/octoprint/static/js/app/viewmodels/printerstate.js:84 +#: src/octoprint/static/js/app/viewmodels/printerstate.js:86 msgid "Still stabilizing..." msgstr "" -#: src/octoprint/static/js/app/viewmodels/printerstate.js:94 +#: src/octoprint/static/js/app/viewmodels/printerstate.js:96 msgid "" "Based on a linear approximation (very low accuracy, especially at the " "beginning of the print)" msgstr "" -#: src/octoprint/static/js/app/viewmodels/printerstate.js:97 +#: src/octoprint/static/js/app/viewmodels/printerstate.js:99 msgid "Based on the estimate from analysis of file (medium accuracy)" msgstr "" -#: src/octoprint/static/js/app/viewmodels/printerstate.js:100 +#: src/octoprint/static/js/app/viewmodels/printerstate.js:102 msgid "Based on a mix of estimate from analysis and calculation (medium accuracy)" msgstr "" -#: src/octoprint/static/js/app/viewmodels/printerstate.js:103 +#: src/octoprint/static/js/app/viewmodels/printerstate.js:105 msgid "" "Based on the average total of past prints of this model with the same " "printer profile (usually good accuracy)" msgstr "" -#: src/octoprint/static/js/app/viewmodels/printerstate.js:106 +#: src/octoprint/static/js/app/viewmodels/printerstate.js:108 msgid "" "Based on a mix of average total from past prints and calculation (usually" " good accuracy)" msgstr "" -#: src/octoprint/static/js/app/viewmodels/printerstate.js:109 +#: src/octoprint/static/js/app/viewmodels/printerstate.js:111 msgid "Based on the calculated estimate (best accuracy)" msgstr "" -#: src/octoprint/static/js/app/viewmodels/printerstate.js:147 +#: src/octoprint/static/js/app/viewmodels/printerstate.js:149 msgid "Continue" msgstr "" -#: src/octoprint/static/js/app/viewmodels/printerstate.js:149 +#: src/octoprint/static/js/app/viewmodels/printerstate.js:151 #: src/octoprint/templates/sidebar/state.jinja2:24 msgid "Pause" msgstr "" -#: src/octoprint/static/js/app/viewmodels/printerstate.js:160 +#: src/octoprint/static/js/app/viewmodels/printerstate.js:162 #: src/octoprint/templates/tabs/timelapse.jinja2:15 msgid "On Z Change" msgstr "" -#: src/octoprint/static/js/app/viewmodels/printerstate.js:162 +#: src/octoprint/static/js/app/viewmodels/printerstate.js:164 #: src/octoprint/templates/tabs/timelapse.jinja2:14 msgid "Timed" msgstr "" -#: src/octoprint/static/js/app/viewmodels/printerstate.js:162 +#: src/octoprint/static/js/app/viewmodels/printerstate.js:164 #: src/octoprint/templates/tabs/timelapse.jinja2:26 #: src/octoprint/templates/tabs/timelapse.jinja2:37 #: src/octoprint/templates/tabs/timelapse.jinja2:57 msgid "sec" msgstr "" -#: src/octoprint/static/js/app/viewmodels/printerstate.js:274 +#: src/octoprint/static/js/app/viewmodels/printerstate.js:278 msgid "This will restart the print job from the beginning." msgstr "" -#: src/octoprint/static/js/app/viewmodels/printerstate.js:301 +#: src/octoprint/static/js/app/viewmodels/printerstate.js:305 msgid "This will cancel your print." msgstr "" -#: src/octoprint/static/js/app/viewmodels/printerstate.js:302 +#: src/octoprint/static/js/app/viewmodels/printerstate.js:306 msgid "No" msgstr "" -#: src/octoprint/static/js/app/viewmodels/printerstate.js:303 +#: src/octoprint/static/js/app/viewmodels/printerstate.js:307 msgid "Yes" msgstr "" @@ -2657,41 +2611,41 @@ msgstr "" msgid "Autodetect from browser" msgstr "" -#: src/octoprint/static/js/app/viewmodels/settings.js:274 +#: src/octoprint/static/js/app/viewmodels/settings.js:299 msgid "If you see your webcam stream below, the entered stream URL is ok." msgstr "" -#: src/octoprint/static/js/app/viewmodels/settings.js:282 +#: src/octoprint/static/js/app/viewmodels/settings.js:307 msgid "Stream test" msgstr "" -#: src/octoprint/static/js/app/viewmodels/settings.js:300 +#: src/octoprint/static/js/app/viewmodels/settings.js:325 msgid "Could not retrieve snapshot URL, please double check the URL" msgstr "" -#: src/octoprint/static/js/app/viewmodels/settings.js:301 +#: src/octoprint/static/js/app/viewmodels/settings.js:326 msgid "Snapshot test failed" msgstr "" -#: src/octoprint/static/js/app/viewmodels/settings.js:322 +#: src/octoprint/static/js/app/viewmodels/settings.js:352 msgid "" "If you see your webcam snapshot picture below, the entered snapshot URL " "is ok." msgstr "" -#: src/octoprint/static/js/app/viewmodels/settings.js:324 +#: src/octoprint/static/js/app/viewmodels/settings.js:354 msgid "Snapshot test" msgstr "" -#: src/octoprint/static/js/app/viewmodels/settings.js:384 +#: src/octoprint/static/js/app/viewmodels/settings.js:414 msgid "The server is not reachable" msgstr "" -#: src/octoprint/static/js/app/viewmodels/settings.js:386 +#: src/octoprint/static/js/app/viewmodels/settings.js:416 msgid "The server is reachable" msgstr "" -#: src/octoprint/static/js/app/viewmodels/settings.js:506 +#: src/octoprint/static/js/app/viewmodels/settings.js:536 #: src/octoprint/static/js/app/viewmodels/usersettings.js:87 msgid "" "This will generate a new API Key. The old API Key will cease to function " @@ -2764,20 +2718,16 @@ msgstr "" msgid "Bed" msgstr "" -#: src/octoprint/static/js/app/viewmodels/temperature.js:350 -msgid "just now" -msgstr "" - -#: src/octoprint/static/js/app/viewmodels/temperature.js:355 +#: src/octoprint/static/js/app/viewmodels/temperature.js:361 msgid "min" msgstr "" -#: src/octoprint/static/js/app/viewmodels/temperature.js:401 +#: src/octoprint/static/js/app/viewmodels/temperature.js:407 #: src/octoprint/templates/tabs/temperature.jinja2:11 msgid "Actual" msgstr "" -#: src/octoprint/static/js/app/viewmodels/temperature.js:406 +#: src/octoprint/static/js/app/viewmodels/temperature.js:412 #: src/octoprint/templates/tabs/temperature.jinja2:12 msgid "Target" msgstr "" @@ -2806,160 +2756,160 @@ msgstr "" msgid "showing %(displayed)d lines" msgstr "" -#: src/octoprint/static/js/app/viewmodels/timelapse.js:251 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:240 #, python-format msgid "" "Failed to remove timelapse %(name)s.

Please consult octoprint.log " "for details.

" msgstr "" -#: src/octoprint/static/js/app/viewmodels/timelapse.js:254 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:243 msgid "Could not remove timelapse" msgstr "" -#: src/octoprint/static/js/app/viewmodels/timelapse.js:262 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:251 #, python-format msgid "You are about to delete timelapse file \"%(name)s\"." msgstr "" -#: src/octoprint/static/js/app/viewmodels/timelapse.js:274 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:263 #, python-format msgid "You are about to delete %(count)d timelapse files." msgstr "" -#: src/octoprint/static/js/app/viewmodels/timelapse.js:299 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:288 #, python-format msgid "You are about to delete unrendered timelapse \"%(name)s\"." msgstr "" -#: src/octoprint/static/js/app/viewmodels/timelapse.js:311 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:300 #, python-format msgid "You are about to delete %(count)d unrendered timelapses." msgstr "" -#: src/octoprint/static/js/app/viewmodels/timelapse.js:319 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:308 msgid "Deleting timelapse files" msgstr "" -#: src/octoprint/static/js/app/viewmodels/timelapse.js:320 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:309 #, python-format msgid "Deleting %(count)d timelapse files..." msgstr "" -#: src/octoprint/static/js/app/viewmodels/timelapse.js:324 -#: src/octoprint/static/js/app/viewmodels/timelapse.js:338 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:313 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:327 #, python-format msgid "Deleted %(filename)s..." msgstr "" -#: src/octoprint/static/js/app/viewmodels/timelapse.js:327 -#: src/octoprint/static/js/app/viewmodels/timelapse.js:341 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:316 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:330 #, python-format msgid "Deletion of %(filename)s failed, continuing..." msgstr "" -#: src/octoprint/static/js/app/viewmodels/timelapse.js:328 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:317 #, python-format msgid "Deletion of %(filename)s failed: %(error)s" msgstr "" -#: src/octoprint/static/js/app/viewmodels/timelapse.js:333 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:322 msgid "Deleting unrendered timelapses" msgstr "" -#: src/octoprint/static/js/app/viewmodels/timelapse.js:334 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:323 #, python-format msgid "Deleting %(count)d unrendered timelapses..." msgstr "" -#: src/octoprint/static/js/app/viewmodels/timelapse.js:429 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:417 msgid "Capturing timelapse postroll" msgstr "" -#: src/octoprint/static/js/app/viewmodels/timelapse.js:433 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:421 msgid "Now capturing timelapse post roll, this will take only a moment..." msgstr "" -#: src/octoprint/static/js/app/viewmodels/timelapse.js:440 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:428 #, python-format msgid "%(minutes)d min" msgstr "" -#: src/octoprint/static/js/app/viewmodels/timelapse.js:441 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:429 #, python-format msgid "" "Now capturing timelapse post roll, this will take approximately " "%(duration)s (so until %(time)s)..." msgstr "" -#: src/octoprint/static/js/app/viewmodels/timelapse.js:443 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:431 #, python-format msgid "%(seconds)d sec" msgstr "" -#: src/octoprint/static/js/app/viewmodels/timelapse.js:444 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:432 #, python-format msgid "" "Now capturing timelapse post roll, this will take approximately " "%(duration)s..." msgstr "" -#: src/octoprint/static/js/app/viewmodels/timelapse.js:474 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:462 msgid "" "Failed repeatedly to capture timelapse frame from webcam - is the " "snapshot URL configured correctly and the camera on?" msgstr "" -#: src/octoprint/static/js/app/viewmodels/timelapse.js:477 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:465 msgid "Could not capture snapshots" msgstr "" -#: src/octoprint/static/js/app/viewmodels/timelapse.js:486 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:474 msgid "Rendering timelapse" msgstr "" -#: src/octoprint/static/js/app/viewmodels/timelapse.js:487 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:475 #, python-format msgid "" "Now rendering timelapse %(movie_prefix)s. Due to performance reasons it " "is not recommended to start a print job while a movie is still rendering." msgstr "" -#: src/octoprint/static/js/app/viewmodels/timelapse.js:496 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:484 msgid "Cannot render timelapse" msgstr "" -#: src/octoprint/static/js/app/viewmodels/timelapse.js:497 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:485 #, python-format msgid "" "Rendering of timelapse %(movie_prefix)s is not possible since no frames " "were captured. Is the snapshot URL configured correctly?" msgstr "" -#: src/octoprint/static/js/app/viewmodels/timelapse.js:499 -#: src/octoprint/static/js/app/viewmodels/timelapse.js:503 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:487 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:491 msgid "Rendering timelapse failed" msgstr "" -#: src/octoprint/static/js/app/viewmodels/timelapse.js:500 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:488 #, python-format msgid "" "Rendering of timelapse %(movie_prefix)s failed with return code " "%(returncode)s" msgstr "" -#: src/octoprint/static/js/app/viewmodels/timelapse.js:504 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:492 #, python-format msgid "" "Rendering of timelapse %(movie_prefix)s failed due to an unknown error, " "please consult the log file" msgstr "" -#: src/octoprint/static/js/app/viewmodels/timelapse.js:517 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:505 msgid "Timelapse ready" msgstr "" -#: src/octoprint/static/js/app/viewmodels/timelapse.js:518 +#: src/octoprint/static/js/app/viewmodels/timelapse.js:506 #, python-format msgid "New timelapse %(movie_prefix)s is done rendering." msgstr "" @@ -3124,14 +3074,6 @@ msgstr "" msgid "Admin" msgstr "" -#: src/octoprint/templates/dialogs/settings/accesscontrol.jinja2:7 -#: src/octoprint/templates/dialogs/settings/logs.jinja2:15 -#: src/octoprint/templates/snippets/settings/printerprofiles/profiles.jinja2:6 -#: src/octoprint/templates/tabs/timelapse.jinja2:132 -#: src/octoprint/templates/tabs/timelapse.jinja2:178 -msgid "Action" -msgstr "" - #: src/octoprint/templates/dialogs/settings/accesscontrol.jinja2:12 #: src/octoprint/templates/dialogs/settings/api.jinja2:17 #: src/octoprint/templates/dialogs/usersettings/access.jinja2:22 @@ -3363,87 +3305,32 @@ msgid "Enable Keyboard Control" msgstr "" #: src/octoprint/templates/dialogs/settings/features.jinja2:54 -msgid "Wait for start on connect" -msgstr "" - -#: src/octoprint/templates/dialogs/settings/features.jinja2:61 msgid "G90/G91 overrides relative extruder mode" msgstr "" -#: src/octoprint/templates/dialogs/settings/features.jinja2:61 +#: src/octoprint/templates/dialogs/settings/features.jinja2:54 msgid "Smoothieware" msgstr "" -#: src/octoprint/templates/dialogs/settings/features.jinja2:68 -msgid "Enable automatic firmware detection" -msgstr "" - -#: src/octoprint/templates/dialogs/settings/features.jinja2:69 +#: src/octoprint/templates/dialogs/settings/features.jinja2:55 msgid "" -"\n" -" If enabled, OctoPrint will try to figure out your " -"printer's firmware automatically and adjust some communication parameters" -" based on that.\n" -" If that doesn't work out, or you want more granular " -"control, uncheck this and the parameters in question will become visible " -"for you to adjust.\n" -" " -msgstr "" - -#: src/octoprint/templates/dialogs/settings/features.jinja2:80 -msgid "Select SD files by relative path" +"This is used by the GCODE analysis in the backend and the viewer in the " +"frontend to interpret your sliced files correctly." msgstr "" -#: src/octoprint/templates/dialogs/settings/features.jinja2:80 -msgid "RepRap Firmware" -msgstr "" - -#: src/octoprint/templates/dialogs/settings/features.jinja2:87 -msgid "Always assume SD card is present" -msgstr "" - -#: src/octoprint/templates/dialogs/settings/features.jinja2:87 -#: src/octoprint/templates/dialogs/settings/features.jinja2:94 -#: src/octoprint/templates/dialogs/settings/features.jinja2:101 -#: src/octoprint/templates/dialogs/settings/features.jinja2:108 -#: src/octoprint/templates/dialogs/settings/features.jinja2:115 -#: src/octoprint/templates/dialogs/settings/features.jinja2:126 -msgid "Repetier" +#: src/octoprint/templates/dialogs/settings/features.jinja2:59 +msgid "Commands to not completely auto uppercase in the terminal tab" msgstr "" -#: src/octoprint/templates/dialogs/settings/features.jinja2:94 -msgid "Ignore consecutive resend requests for the same line" +#: src/octoprint/templates/dialogs/settings/features.jinja2:60 +msgid "Terminal Auto Uppercase Blacklist" msgstr "" -#: src/octoprint/templates/dialogs/settings/features.jinja2:101 -#, python-format +#: src/octoprint/templates/dialogs/settings/features.jinja2:63 msgid "" -"Support TargetExtr%%n/TargetBed target " -"temperature format" -msgstr "" - -#: src/octoprint/templates/dialogs/settings/features.jinja2:108 -msgid "Disable detection of external heatups" -msgstr "" - -#: src/octoprint/templates/dialogs/settings/features.jinja2:115 -msgid "Actively pause communication during G4 dwell command" -msgstr "" - -#: src/octoprint/templates/dialogs/settings/features.jinja2:120 -msgid "Send a checksum with the command" -msgstr "" - -#: src/octoprint/templates/dialogs/settings/features.jinja2:123 -msgid "When printing" -msgstr "" - -#: src/octoprint/templates/dialogs/settings/features.jinja2:126 -msgid "Always" -msgstr "" - -#: src/octoprint/templates/dialogs/settings/features.jinja2:129 -msgid "Never" +"Use this to specify the commands that should not have their parameters " +"automatically uppercased in the terminal tab. Just the G or M code, comma" +" separated." msgstr "" #: src/octoprint/templates/dialogs/settings/folders.jinja2:5 @@ -3483,10 +3370,10 @@ msgid "" msgstr "" #: src/octoprint/templates/dialogs/settings/folders.jinja2:47 -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:87 -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:115 -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:163 -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:169 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:52 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:347 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:357 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:363 #: src/octoprint/templates/tabs/gcodeviewer.jinja2:111 msgid "Warning" msgstr "" @@ -3565,101 +3452,151 @@ msgstr "" msgid "on mobile" msgstr "" -#: src/octoprint/templates/dialogs/settings/logs.jinja2:6 -msgid "Modification date" +#: src/octoprint/templates/dialogs/settings/printerprofiles.jinja2:5 +msgid "Add Profile..." msgstr "" -#: src/octoprint/templates/dialogs/settings/logs.jinja2:6 -#: src/octoprint/templates/dialogs/settings/logs.jinja2:13 -#: src/octoprint/templates/sidebar/files.jinja2:18 -#: src/octoprint/templates/sidebar/files.jinja2:33 -#: src/octoprint/templates/sidebar/files.jinja2:44 -#: src/octoprint/templates/tabs/timelapse.jinja2:131 -#: src/octoprint/templates/tabs/timelapse.jinja2:177 -msgid "Size" +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:4 +msgid "Intervals & timeouts" msgstr "" -#: src/octoprint/templates/dialogs/settings/logs.jinja2:14 -msgid "Date" +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:5 +msgid "Firmware & protocol" msgstr "" -#: src/octoprint/templates/dialogs/settings/printerprofiles.jinja2:5 -msgid "Add Profile..." +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:6 +msgid "Behaviour" msgstr "" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:2 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:13 msgid "" "Serial port to connect to, setting this to AUTO will make OctoPrint try " "to automatically find the right setting" msgstr "" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:3 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:14 #: src/octoprint/templates/sidebar/connection.jinja2:1 msgid "Serial Port" msgstr "" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:8 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:19 msgid "" "Serial baud rate to connect with, setting this to AUTO will make " "OctoPrint try to automatically find the right setting" msgstr "" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:9 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:20 #: src/octoprint/templates/sidebar/connection.jinja2:3 msgid "Baudrate" msgstr "" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:14 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:25 msgid "" "Makes OctoPrint try to connect to the printer automatically during start " "up" msgstr "" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:17 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:28 msgid "Auto-connect to printer on server start" msgstr "" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:21 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:33 +msgid "Additional serial ports" +msgstr "" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:36 +#, python-format +msgid "" +"Use this to define additional glob patterns" +" matching serial ports to list for connecting against, e.g. " +"/dev/ttyAMA*. One entry per line." +msgstr "" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:40 +msgid "Additional baud rates" +msgstr "" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:43 +msgid "" +"Use this to define additional serial port baud rates to list for " +"connecting with, e.g. 123456. Comma separated." +msgstr "" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:48 +msgid "Serial logging" +msgstr "" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:52 +msgid "Log communication to serial.log" +msgstr "" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:53 +msgid "" +"While this can negatively impact performance, a serial.log " +"can be incredibly useful for debugging any issues observed in the " +"communication between OctoPrint and your printer." +msgstr "" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:61 +msgid "Query intervals" +msgstr "" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:62 msgid "Interval in which to poll for the temperature information from the printer" msgstr "" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:22 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:63 msgid "Temperature interval (polling)" msgstr "" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:28 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:69 msgid "When printing or idle" msgstr "" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:35 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:76 msgid "When a target temperature is set" msgstr "" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:38 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:79 msgid "" "Temperature report interval to request from autoreport capable firmwares." " A value of 0 disables autoreporting by the firmware and forces polling." msgstr "" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:39 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:80 msgid "Temperature interval (autoreport)" msgstr "" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:45 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:86 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:105 msgid "Autoreport interval to request from firmware" msgstr "" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:48 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:89 msgid "" "Interval in which to poll for the SD printing status information from the" " printer while printing" msgstr "" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:49 -msgid "SD status interval" +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:90 +msgid "SD status interval (polling)" msgstr "" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:57 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:98 +msgid "" +"SD status interval to request from autoreport capable firmwares. A value " +"of 0 disables autoreporting by the firmware and forces polling." +msgstr "" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:99 +msgid "SD status interval (autoreport)" +msgstr "" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:110 +msgid "Timeouts" +msgstr "" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:111 msgid "" "Time after which the communication with your printer will be considered " "timed out if nothing was sent by your printer (and an attempt to get it " @@ -3667,204 +3604,303 @@ msgid "" "than this for some moves." msgstr "" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:58 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:112 msgid "Communication timeout" msgstr "" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:66 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:118 +msgid "busy protocol support not detected" +msgstr "" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:125 +msgid "busy protocol support detected" +msgstr "" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:128 msgid "" "Time after which a connection attempt to the printer will be considered " "as having failed" msgstr "" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:67 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:129 msgid "Connection timeout" msgstr "" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:75 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:137 msgid "" "Time after which to consider an auto detection attempt to have failed if " "no successful connection is detected" msgstr "" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:76 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:138 msgid "Autodetection timeout" msgstr "" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:87 -msgid "Log communication to serial.log (might negatively impact performance)" +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:149 +msgid "" +"Maximum consecutive communication timeouts while idle. More than this and" +" the printer will be considered to be gone. Set to 0 to disable." msgstr "" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:92 -msgid "Additional serial ports" +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:150 +msgid "Max. consecutive timeouts while idle" +msgstr "" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:153 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:160 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:167 +msgid "Set to 0 to disable consecutive timeout detection and handling." +msgstr "" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:156 +msgid "" +"Maximum consecutive communication timeouts while printing. More than this" +" and the printer will be considered to be gone. Set to 0 to disable." +msgstr "" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:157 +msgid "Max. consecutive timeouts while printing" +msgstr "" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:163 +msgid "" +"Maximum consecutive communication timeouts while a long running command " +"is active. More than this and the printer will be considered to be gone. " +"Set to 0 to disable." +msgstr "" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:164 +msgid "Max. consecutive timeouts during long running commands" +msgstr "" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:176 +msgid "Firmware specific settings" +msgstr "" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:180 +msgid "Enable automatic firmware detection" +msgstr "" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:181 +msgid "" +"\n" +" If enabled, OctoPrint will try to figure " +"out your printer's firmware automatically and adjust some communication " +"parameters based on that.\n" +" If that doesn't work out, or you want " +"more granular control, uncheck this and the parameters in question will " +"become visible for you to adjust.\n" +" " +msgstr "" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:192 +msgid "Select SD files by relative path" +msgstr "" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:192 +msgid "RepRap Firmware" +msgstr "" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:199 +msgid "Always assume SD card is present" +msgstr "" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:199 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:206 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:213 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:220 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:227 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:238 +msgid "Repetier" +msgstr "" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:206 +msgid "Ignore consecutive resend requests for the same line" msgstr "" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:95 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:213 #, python-format msgid "" -"Use this to define additional glob patterns" -" matching serial ports to list for connecting against, e.g. " -"/dev/ttyAMA*. One entry per line." +"Support TargetExtr%%n/TargetBed target " +"temperature format" msgstr "" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:99 -msgid "Additional baud rates" +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:220 +msgid "Disable detection of external heatups" +msgstr "" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:227 +msgid "Actively pause communication during G4 dwell command" +msgstr "" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:232 +msgid "Send a checksum with the command" +msgstr "" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:235 +msgid "When printing" +msgstr "" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:238 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:285 +msgid "Always" +msgstr "" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:241 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:288 +msgid "Never" +msgstr "" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:252 +msgid "Support temperature autoreporting by firmware, if detected" +msgstr "" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:259 +msgid "Support sd status autoreporting by firmware, if detected" +msgstr "" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:266 +msgid "Support busy protocol, if detected" +msgstr "" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:273 +msgid "Simulate ok for M29" msgstr "" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:102 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:274 msgid "" -"Use this to define additional serial port baud rates to list for " -"connecting with, e.g. 123456. Comma separated." +"Most Marlin forks that were derived from Marlin versions < v1.1.0 do not " +"send an acknowledging ok for a M29. Check this " +"if you run into communication stalls following streaming of a file to " +"your printer's SD." +msgstr "" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:279 +msgid "Simulate ok for resend requests" +msgstr "" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:282 +msgid "If detected as necessary" msgstr "" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:108 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:290 msgid "" -"Not only cancel ongoing prints but also disconnect on unhandled errors " -"from the firmware." +"Some Marlin forks lack an acknowledging ok with their resend" +" requests. Set this to \"always\" or \"if detected as necessary\" if you " +"run into communication stalls on resend requests." +msgstr "" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:297 +msgid "Protocol fine tuning" +msgstr "" + +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:301 +msgid "Wait for start on connect" msgstr "" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:115 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:302 msgid "" -"Ignore any unhandled errors from the firmware. Only use this if your " -"firmware sends stuff prefixed with \"Error\" that is not an actual error." -" Might mask printer issues, be careful!" +"Try checking this if you run into connection timeouts due to your printer" +" taking too long to respond to OctoPrint' handshake attempts on connect." msgstr "" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:123 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:309 msgid "Command to send to the firmware on first handshake attempt." msgstr "" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:124 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:310 msgid "\"Hello\" command" msgstr "" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:127 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:313 msgid "" "Use this to specify a different command than the default " "M110 to send to the printer on initial connection to trigger" " a communication handshake." msgstr "" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:130 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:316 msgid "" "Commands that are know to run long and hence should suppress " "communication timeouts from being triggered." msgstr "" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:131 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:317 msgid "Long running commands" msgstr "" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:134 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:320 msgid "" "Use this to specify the commands known to take a long time to complete " "without output from your printer and hence might cause timeout issues. " "Just the G or M code, comma separated." msgstr "" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:137 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:323 msgid "" "Commands that always require a line number and checksum to be sent with " "them." msgstr "" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:138 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:324 msgid "Commands that always require a checksum" msgstr "" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:141 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:327 msgid "" "Use this to specify which commands always need to be " "sent with a checksum. Comma separated list." msgstr "" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:144 -msgid "Commands to not completely auto uppercase in the terminal tab" +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:336 +msgid "Error handling" msgstr "" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:145 -msgid "Terminal Auto Uppercase Blacklist" +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:338 +msgid "What to do on a firmware error (Error: or !!)" msgstr "" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:148 -msgid "" -"Use this to specify the commands that should not have their parameters " -"automatically uppercased in the terminal tab. Just the G or M code, comma" -" separated." +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:341 +msgid "Disconnect from the printer" msgstr "" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:153 -msgid "Generate additional ok for M29" +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:344 +msgid "Cancel any ongoing prints but stay connected to the printer" msgstr "" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:153 -msgid "Most Marlin < v1.1.0" +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:348 +msgid "" +"Only choose this if your firmware sends error messages that are not " +"actual errors. Might mask printer issues, be careful!" msgstr "" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:158 -msgid "Simulate an additional ok for resend requests" +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:354 +msgid "Event based position logging" msgstr "" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:163 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:357 msgid "Log position on pause" msgstr "" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:164 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:358 msgid "" -"If you disabled this, the pause_position placeholders in " -"your pause/resume GCODE scripts will stay unpopulated! However, pausing " -"speed might improve slightly." +"If you disable this, the pause_position placeholders in your" +" pause/resume GCODE scripts will stay unpopulated! However, pausing speed" +" might improve slightly." msgstr "" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:169 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:363 msgid "Log position on cancel" msgstr "" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:170 +#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:364 msgid "" -"If you disabled this, the cancel_position placeholders in " +"If you disable this, the cancel_position placeholders in " "your cancel GCODE script and the corresponding data in the print recovery" " data will stay unpopulated! However, cancelling speed might improve " "slightly." msgstr "" -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:173 -msgid "" -"Maximum consecutive communication timeouts while idle. More than this and" -" the printer will be considered to be gone. Set to 0 to disable." -msgstr "" - -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:174 -msgid "Max. consecutive timeouts while idle" -msgstr "" - -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:177 -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:184 -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:191 -msgid "Set to 0 to disable consecutive timeout detection and handling." -msgstr "" - -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:180 -msgid "" -"Maximum consecutive communication timeouts while printing. More than this" -" and the printer will be considered to be gone. Set to 0 to disable." -msgstr "" - -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:181 -msgid "Max. consecutive timeouts while printing" -msgstr "" - -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:187 -msgid "" -"Maximum consecutive communication timeouts while a long running command " -"is active. More than this and the printer will be considered to be gone. " -"Set to 0 to disable." -msgstr "" - -#: src/octoprint/templates/dialogs/settings/serialconnection.jinja2:188 -msgid "Max. consecutive timeouts during long running commands" -msgstr "" - #: src/octoprint/templates/dialogs/settings/server.jinja2:2 msgid "Commands" msgstr "" @@ -4078,6 +4114,10 @@ msgstr "" msgid "Auto-connect on server startup" msgstr "" +#: src/octoprint/templates/sidebar/connection_header.jinja2:2 +msgid "Refresh connection options" +msgstr "" + #: src/octoprint/templates/sidebar/files.jinja2:6 msgid "Back" msgstr "" @@ -4106,12 +4146,6 @@ msgstr "" msgid "Download" msgstr "" -#: src/octoprint/templates/sidebar/files.jinja2:23 -#: src/octoprint/templates/sidebar/files.jinja2:36 -#: src/octoprint/templates/sidebar/files.jinja2:46 -msgid "Remove" -msgstr "" - #: src/octoprint/templates/sidebar/files.jinja2:24 msgid "Load" msgstr "" @@ -4141,7 +4175,7 @@ msgid "File list settings" msgstr "" #: src/octoprint/templates/sidebar/files_header.jinja2:6 -#: src/octoprint/templates/tabs/timelapse.jinja2:108 +#: src/octoprint/templates/tabs/timelapse.jinja2:99 msgid "Sort by name" msgstr "" @@ -4150,7 +4184,7 @@ msgid "Sort by upload date" msgstr "" #: src/octoprint/templates/sidebar/files_header.jinja2:8 -#: src/octoprint/templates/tabs/timelapse.jinja2:110 +#: src/octoprint/templates/tabs/timelapse.jinja2:101 msgid "Sort by file size" msgstr "" @@ -4581,6 +4615,18 @@ msgstr "" msgid "Rotate webcam 90 degrees counter clockwise" msgstr "" +#: src/octoprint/templates/snippets/settings/webcam/webcamSnapshotSslValidation.jinja2:4 +msgid "Validate SSL on snapshot URL (if applicable)" +msgstr "" + +#: src/octoprint/templates/snippets/settings/webcam/webcamSnapshotTimeout.jinja2:1 +msgid "Timeout for taking a snapshot" +msgstr "" + +#: src/octoprint/templates/snippets/settings/webcam/webcamSnapshotTimeout.jinja2:2 +msgid "Snapshot timeout" +msgstr "" + #: src/octoprint/templates/snippets/settings/webcam/webcamSnapshotUrl.jinja2:1 msgid "URL to use for retrieving webcam snapshot images for timelapse creation" msgstr "" @@ -4980,25 +5026,15 @@ msgstr "" #: src/octoprint/templates/tabs/timelapse.jinja2:59 msgid "" -"OctoPrint will take additional pictures to add this many seconds to the " -"end of your rendered timelapse." +"OctoPrint will use the final picture to add this many seconds to the end " +"of your rendered timelapse." msgstr "" -#: src/octoprint/templates/tabs/timelapse.jinja2:66 -msgid "Capture post roll images" -msgstr "" - -#: src/octoprint/templates/tabs/timelapse.jinja2:67 -msgid "" -"If this is unchecked, OctoPrint will simply repeat the last frame for the" -" post roll instead of continuing to capture new frames." -msgstr "" - -#: src/octoprint/templates/tabs/timelapse.jinja2:73 +#: src/octoprint/templates/tabs/timelapse.jinja2:64 msgid "Retraction Z-Hop" msgstr "" -#: src/octoprint/templates/tabs/timelapse.jinja2:79 +#: src/octoprint/templates/tabs/timelapse.jinja2:70 msgid "" "Enter the retraction z-hop used in the firmware or the gcode file to " "trigger snapshots for the timelapse only if a real layer change happens. " @@ -5006,69 +5042,69 @@ msgid "" "your layerheight!" msgstr "" -#: src/octoprint/templates/tabs/timelapse.jinja2:86 +#: src/octoprint/templates/tabs/timelapse.jinja2:77 msgid "Save as default" msgstr "" -#: src/octoprint/templates/tabs/timelapse.jinja2:87 +#: src/octoprint/templates/tabs/timelapse.jinja2:78 msgid "" "Check this to make your selected timelapse mode and options persist " "across restarts." msgstr "" -#: src/octoprint/templates/tabs/timelapse.jinja2:94 +#: src/octoprint/templates/tabs/timelapse.jinja2:85 msgid "You have unsaved changes. Don't forget to save them." msgstr "" -#: src/octoprint/templates/tabs/timelapse.jinja2:95 +#: src/octoprint/templates/tabs/timelapse.jinja2:86 msgid "Save changes" msgstr "" -#: src/octoprint/templates/tabs/timelapse.jinja2:96 +#: src/octoprint/templates/tabs/timelapse.jinja2:87 msgid "Reset to active configuration" msgstr "" -#: src/octoprint/templates/tabs/timelapse.jinja2:102 +#: src/octoprint/templates/tabs/timelapse.jinja2:93 msgid "Finished Timelapses" msgstr "" -#: src/octoprint/templates/tabs/timelapse.jinja2:109 +#: src/octoprint/templates/tabs/timelapse.jinja2:100 msgid "Sort by date" msgstr "" -#: src/octoprint/templates/tabs/timelapse.jinja2:118 -#: src/octoprint/templates/tabs/timelapse.jinja2:163 +#: src/octoprint/templates/tabs/timelapse.jinja2:109 +#: src/octoprint/templates/tabs/timelapse.jinja2:154 msgid "Select all on this page" msgstr "" -#: src/octoprint/templates/tabs/timelapse.jinja2:119 -#: src/octoprint/templates/tabs/timelapse.jinja2:164 +#: src/octoprint/templates/tabs/timelapse.jinja2:110 +#: src/octoprint/templates/tabs/timelapse.jinja2:155 msgid "Select all" msgstr "" -#: src/octoprint/templates/tabs/timelapse.jinja2:121 -#: src/octoprint/templates/tabs/timelapse.jinja2:166 +#: src/octoprint/templates/tabs/timelapse.jinja2:112 +#: src/octoprint/templates/tabs/timelapse.jinja2:157 msgid "Clear selection" msgstr "" -#: src/octoprint/templates/tabs/timelapse.jinja2:124 -#: src/octoprint/templates/tabs/timelapse.jinja2:169 +#: src/octoprint/templates/tabs/timelapse.jinja2:115 +#: src/octoprint/templates/tabs/timelapse.jinja2:160 msgid "Delete selected" msgstr "" -#: src/octoprint/templates/tabs/timelapse.jinja2:157 +#: src/octoprint/templates/tabs/timelapse.jinja2:148 msgid "Unrendered Timelapses" msgstr "" -#: src/octoprint/templates/tabs/timelapse.jinja2:176 +#: src/octoprint/templates/tabs/timelapse.jinja2:167 msgid "Frames" msgstr "" -#: src/octoprint/templates/tabs/timelapse.jinja2:189 +#: src/octoprint/templates/tabs/timelapse.jinja2:180 msgid "Delete unrendered timelapse" msgstr "" -#: src/octoprint/templates/tabs/timelapse.jinja2:189 +#: src/octoprint/templates/tabs/timelapse.jinja2:180 msgid "Render timelapse" msgstr "" From e4914b8bd38c81da4b4e34d4dd411625f3949c1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Mon, 19 Mar 2018 17:18:39 +0100 Subject: [PATCH 299/333] staging/maintenance is now 1.3.7rc2.dev --- .versioneer-lookup | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.versioneer-lookup b/.versioneer-lookup index b87b971a57..44d9f682c7 100644 --- a/.versioneer-lookup +++ b/.versioneer-lookup @@ -23,10 +23,10 @@ maintenance 1.3.7 99e1c3ac007a0eee9d6f908e598c6b029f3ae40c pep440-dev fix/.* 1.3.7 99e1c3ac007a0eee9d6f908e598c6b029f3ae40c pep440-dev improve/.* 1.3.7 99e1c3ac007a0eee9d6f908e598c6b029f3ae40c pep440-dev -# staging/maintenance is currently the branch for preparation of 1.3.6rc4 +# staging/maintenance is currently the branch for preparation of 1.3.7rc2 # so is regressionfix/... -staging/maintenance 1.3.6rc4 a8747f7e36a03ff2449b62cdf68b8a26a6fa61b3 pep440-dev -regressionfix/.* 1.3.6rc4 a8747f7e36a03ff2449b62cdf68b8a26a6fa61b3 pep440-dev +staging/maintenance 1.3.7rc2 da2c9632ea54fd1b3c8ae34f8f26bab29dbaf3c6 pep440-dev +regressionfix/.* 1.3.7rc2 da2c9632ea54fd1b3c8ae34f8f26bab29dbaf3c6 pep440-dev # every other branch is a development branch and thus gets resolved to 1.4.0-dev for now .* 1.4.0 7f5d03d0549bcbd26f40e7e4a3297ea5204fb1cc pep440-dev From 022403747825fd8d93549cb27e56d492868cdc30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Tue, 20 Mar 2018 16:19:06 +0100 Subject: [PATCH 300/333] Have OctoPrintJsonEncoder fall back to flask encoder That supports some types we want to keep just in case. --- src/octoprint/server/util/flask.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/octoprint/server/util/flask.py b/src/octoprint/server/util/flask.py index f82b690a62..a5fec34612 100644 --- a/src/octoprint/server/util/flask.py +++ b/src/octoprint/server/util/flask.py @@ -1422,4 +1422,7 @@ def asset_exists(category, asset): class OctoPrintJsonEncoder(flask.json.JSONEncoder): def default(self, obj): - return JsonEncoding.encode(obj) + try: + return JsonEncoding.encode(obj) + except TypeError: + return flask.json.JSONEncoder.default(self, obj) From f981cca47eaef08049a9b0d808e64f4a4dcb08bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Thu, 22 Mar 2018 11:34:39 +0100 Subject: [PATCH 301/333] Forgot a "global" Fixes #2501 --- src/octoprint/server/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/octoprint/server/__init__.py b/src/octoprint/server/__init__.py index da800929a5..6e9edcd889 100644 --- a/src/octoprint/server/__init__.py +++ b/src/octoprint/server/__init__.py @@ -1441,6 +1441,8 @@ def js_bundles_for_plugins(collection, filters=None): def _setup_login_manager(self): util.flask.fix_flask_login_remote_address() + global loginManager + loginManager = LoginManager() loginManager.session_protection = "strong" loginManager.user_callback = load_user From 70254e910cb6da054e35fd4176489c38e1e66c10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Thu, 22 Mar 2018 11:41:31 +0100 Subject: [PATCH 302/333] Use octoprint.util.monotonic_time instead of monotonic --- .../plugins/softwareupdate/updaters/sleep_a_bit.py | 7 ++++--- src/octoprint/util/comm.py | 8 ++++---- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/octoprint/plugins/softwareupdate/updaters/sleep_a_bit.py b/src/octoprint/plugins/softwareupdate/updaters/sleep_a_bit.py index f39ecc7b0a..9740cbdb0c 100644 --- a/src/octoprint/plugins/softwareupdate/updaters/sleep_a_bit.py +++ b/src/octoprint/plugins/softwareupdate/updaters/sleep_a_bit.py @@ -5,9 +5,10 @@ __copyright__ = "Copyright (C) 2018 The OctoPrint Project - Released under terms of the AGPLv3 License" -import monotonic import time +from octoprint.util import monotonic_time + def can_perform_update(target, check, online=True): return True @@ -15,9 +16,9 @@ def can_perform_update(target, check, online=True): def perform_update(target, check, target_version, log_cb=None, online=True): duration = check.get("duration", 30) - now = monotonic.monotonic() + now = monotonic_time() end = now + duration while now < end: log_cb(["{}s left...".format(end - now)], prefix=">", stream="output") time.sleep(5) - now = monotonic.monotonic() + now = monotonic_time() diff --git a/src/octoprint/util/comm.py b/src/octoprint/util/comm.py index b24fc53109..af095b903b 100644 --- a/src/octoprint/util/comm.py +++ b/src/octoprint/util/comm.py @@ -28,7 +28,7 @@ except AttributeError: # pyserial < 3.2: add backported Timeout abstraction, slightly modified since we have monotonic available - import monotonic + from octoprint.util import monotonic_time class _Timeout(object): def __init__(self, duration): """Initialize a timeout with given duration""" @@ -36,7 +36,7 @@ def __init__(self, duration): self.is_non_blocking = (duration == 0) self.duration = duration if duration is not None: - self.target_time = monotonic.monotonic() + duration + self.target_time = monotonic_time() + duration else: self.target_time = None @@ -51,10 +51,10 @@ def time_left(self): elif self.is_infinite: return None else: - delta = self.target_time - monotonic.monotonic() + delta = self.target_time - monotonic_time() if delta > self.duration: # clock jumped, recalculate - self.target_time = monotonic.monotonic() + self.duration + self.target_time = monotonic_time() + self.duration return self.duration else: return max(0, delta) From f782cfe83f174d5d94b87d1320b2a89174182016 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Thu, 22 Mar 2018 12:58:52 +0100 Subject: [PATCH 303/333] Thaw the frozendict on printer.get_current_data We don't want calling code to run into trouble because of that. Fixes #2506 --- src/octoprint/printer/standard.py | 4 ++-- src/octoprint/util/__init__.py | 15 +++++++++++++++ tests/util/test_misc.py | 22 ++++++++++++++++++++++ 3 files changed, 39 insertions(+), 2 deletions(-) diff --git a/src/octoprint/printer/standard.py b/src/octoprint/printer/standard.py index c7f27492de..14fc0326b0 100644 --- a/src/octoprint/printer/standard.py +++ b/src/octoprint/printer/standard.py @@ -550,11 +550,11 @@ def get_state_id(self, state=None, *args, **kwargs): return self._comm.getStateId(state=state) def get_current_data(self, *args, **kwargs): - return self._stateMonitor.get_current_data() + return util.thaw_frozendict(self._stateMonitor.get_current_data()) def get_current_job(self, *args, **kwargs): currentData = self._stateMonitor.get_current_data() - return currentData["job"] + return util.thaw_frozendict(currentData["job"]) def get_current_temperatures(self, *args, **kwargs): if self._comm is not None: diff --git a/src/octoprint/util/__init__.py b/src/octoprint/util/__init__.py index 568e9adb45..e7d68e75a8 100644 --- a/src/octoprint/util/__init__.py +++ b/src/octoprint/util/__init__.py @@ -21,6 +21,7 @@ import warnings import contextlib import collections +import frozendict try: import queue @@ -970,6 +971,20 @@ def glob_escape(pathname): monotonic_time = time.time +def thaw_frozendict(obj): + if not isinstance(obj, (dict, frozendict.frozendict)): + raise ValueError("obj must be a dict or frozendict instance") + + # only true love can thaw a frozen dict + letitgo = dict() + for key, value in obj.items(): + if isinstance(value, frozendict.frozendict): + letitgo[key] = thaw_frozendict(value) + else: + letitgo[key] = value + return letitgo + + def utmify(link, source=None, medium=None, name=None, term=None, content=None): if source is None: return link diff --git a/tests/util/test_misc.py b/tests/util/test_misc.py index 791753a6f4..f07b087f4c 100644 --- a/tests/util/test_misc.py +++ b/tests/util/test_misc.py @@ -7,6 +7,7 @@ import unittest import ddt +from frozendict import frozendict import octoprint.util @@ -45,3 +46,24 @@ def test_get_class_wrongclass(self): def test_utmify(self, link, kwargs, expected): actual = octoprint.util.utmify(link, **kwargs) self.assertEqual(actual, expected) + + @ddt.data( + (frozendict(a=1, b=2, c=3), dict(a=1, b=2, c=3)), + (frozendict(a=1, b=2, c=frozendict(c1=1, c2=2)), dict(a=1, b=2, c=dict(c1=1, c2=2))), + (dict(a=1, b=2, c=3), dict(a=1, b=2, c=3)), + (dict(a=1, b=2, c=frozendict(c1=1, c2=2)), dict(a=1, b=2, c=dict(c1=1, c2=2))), + ) + @ddt.unpack + def test_unfreeze_frozendict(self, input, expected): + result = octoprint.util.thaw_frozendict(input) + self.assertIsInstance(result, dict) + self.assertDictEqual(result, expected) + + @ddt.data(None, "invalid", 3, [1, 2], (3, 4)) + def test_unfreeze_frozendict_invalid(self, input): + try: + octoprint.util.thaw_frozendict(input) + self.fail("expected ValueError") + except ValueError: + # expected + pass From 133850f61cc0691e0982fbbecfdbbcbbeab4947d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Thu, 22 Mar 2018 13:08:01 +0100 Subject: [PATCH 304/333] Actually, make that a deepcopy --- src/octoprint/util/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/octoprint/util/__init__.py b/src/octoprint/util/__init__.py index e7d68e75a8..892a31782e 100644 --- a/src/octoprint/util/__init__.py +++ b/src/octoprint/util/__init__.py @@ -22,6 +22,7 @@ import contextlib import collections import frozendict +import copy try: import queue @@ -981,7 +982,7 @@ def thaw_frozendict(obj): if isinstance(value, frozendict.frozendict): letitgo[key] = thaw_frozendict(value) else: - letitgo[key] = value + letitgo[key] = copy.deepcopy(value) return letitgo From 957ba433201fcb1936aa2d07859a5eba7e7caec3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Thu, 22 Mar 2018 13:36:12 +0100 Subject: [PATCH 305/333] Virtual: Support for simulated reset --- .../plugins/virtual_printer/virtual.py | 78 ++++++++++++++++++- 1 file changed, 74 insertions(+), 4 deletions(-) diff --git a/src/octoprint/plugins/virtual_printer/virtual.py b/src/octoprint/plugins/virtual_printer/virtual.py index 012bef9104..518a7f60ae 100644 --- a/src/octoprint/plugins/virtual_printer/virtual.py +++ b/src/octoprint/plugins/virtual_printer/virtual.py @@ -102,10 +102,12 @@ def __init__(self, seriallog_handler=None, read_timeout=5.0, write_timeout=10.0) self._selectedSdFile = None self._selectedSdFileSize = None self._selectedSdFilePos = None + self._writingToSd = False self._writingToSdHandle = None self._newSdFilePos = None - self._heatupThread = None + + self._heatingUp = False self._okBeforeCommandOutput = settings().getBoolean(["devel", "virtualPrinter", "okBeforeCommandOutput"]) self._supportM112 = settings().getBoolean(["devel", "virtualPrinter", "supportM112"]) @@ -164,6 +166,66 @@ def __str__(self): return "VIRTUAL(read_timeout={read_timeout},write_timeout={write_timeout},options={options})"\ .format(read_timeout=self._read_timeout, write_timeout=self._write_timeout, options=settings().get(["devel", "virtualPrinter"])) + def _reset(self): + self.incoming.queue.clear() + self.outgoing.queue.clear() + self.buffered.queue.clear() + + self._relative = True + self._lastX = 0.0 + self._lastY = 0.0 + self._lastZ = 0.0 + self._lastE = [0.0] * self.extruderCount + self._lastF = 200 + + self._unitModifier = 1 + self._feedrate_multiplier = 100 + self._flowrate_multiplier = 100 + + self._sdCardReady = True + self._sdPrinting = False + if self._sdPrinter: + self._sdPrinting = False + self._sdPrintingSemaphore.set() + self._sdPrinter = None + self._selectedSdFile = None + self._selectedSdFileSize = None + self._selectedSdFilePos = None + + if self._writingToSdHandle: + try: + self._writingToSdHandle.close() + except: + pass + self._writingToSd = False + self._writingToSdHandle = None + self._newSdFilePos = None + + self._heatingUp = False + + self.currentLine = 0 + self.lastN = 0 + + self._debug_awol = False + self._debug_sleep = None + self._sleepAfterNext.clear() + self._sleepAfter.clear() + + self._dont_answer = False + + self._debug_drop_connection = False + + self._killed = False + + self._triggerResendAt100 = True + self._triggerResendWithTimeoutAt105 = True + self._triggerResendWithMissingLinenoAt110 = True + self._triggerResendWithChecksumMismatchAt115 = True + + if settings().getBoolean(["devel", "virtualPrinter", "simulateReset"]): + for item in settings().get(["devel", "virtualPrinter", "resetLines"]): + self._send(item + "\n") + @property def timeout(self): return self._read_timeout @@ -294,7 +356,7 @@ def _processIncoming(self): continue # shortcut for writing to SD - if self._writingToSdHandle is not None and not "M29" in data: + if self._writingToSd and self._writingToSdHandle is not None and not "M29" in data: self._writingToSdHandle.write(data) self._sendOk() continue @@ -662,6 +724,8 @@ def _debugTrigger(self, data): send | Sends back + reset + | Simulates a reset. Internal state will be lost. """ for line in usage.split("\n"): self._send("echo: {}".format(line.strip())) @@ -683,6 +747,8 @@ def _debugTrigger(self, data): self._prepared_errors.append(lambda cur, last, line: self._send(self._error("lineno_missing", last))) elif data == "drop_connection": self._debug_drop_connection = True + elif data == "reset": + self._reset() elif data == "mintemp_error": self._send(self._error("mintemp")) elif data == "maxtemp_error": @@ -764,6 +830,7 @@ def _selectSdFile(self, filename): def _startSdPrint(self): if self._selectedSdFile is not None: if self._sdPrinter is None: + self._sdPrinting = True self._sdPrinter = threading.Thread(target=self._sdPrintingWorker) self._sdPrinter.start() self._sdPrintingSemaphore.set() @@ -1025,7 +1092,7 @@ def _sdPrintingWorker(self): try: with open(self._selectedSdFile, "r") as f: for line in iter(f.readline, ""): - if self._killed: + if self._killed or not self._sdPrinting: break # reset position if requested by client @@ -1038,6 +1105,8 @@ def _sdPrintingWorker(self): # if we are paused, wait for unpausing self._sdPrintingSemaphore.wait() + if self._killed or not self._sdPrinting: + break # set target temps if 'M104' in line or 'M109' in line: @@ -1055,6 +1124,7 @@ def _sdPrintingWorker(self): if not self._killed: self._sdPrintingSemaphore.clear() self._selectedSdFilePos = 0 + self._sdPrinting = False self._sdPrinter = None self._output("Done printing file") @@ -1074,7 +1144,7 @@ def _waitForHeatup(self, heater, only_wait_if_higher): else: return - while not self._killed and test(): + while not self._killed and self._heatingUp and test(): self._simulateTemps(delta=delta) self._output(output()) if self._sendBusy and time.time() - last_busy >= self._busyInterval: From fb834f81ee97f3733bd0d6c61f006d587d91a064 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Thu, 22 Mar 2018 13:46:38 +0100 Subject: [PATCH 306/333] Use pkg_resources to find out local pip version Doing a "import pip; pip.__version__" or "from pip import __version__" poses too much risk with pip's general "no API support", as seen in #2499. --- src/octoprint/environment.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/octoprint/environment.py b/src/octoprint/environment.py index 2a369d5ae6..4d539297e8 100644 --- a/src/octoprint/environment.py +++ b/src/octoprint/environment.py @@ -83,8 +83,8 @@ def _detect_python(self): # try to find pip version try: - import pip - result["pip"] = pip.__version__ + import pkg_resources + result["pip"] = pkg_resources.get_distribution("pip").version except: self._logger.exception("Error detecting pip version") From f10bfa67ca920e4b726e2acf6dd1cf550c320b83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Thu, 22 Mar 2018 13:59:38 +0100 Subject: [PATCH 307/333] Use "import pip" method of calling pip only as last resort Doing that poses too much risk with pip's general "no API support", as seen in #2499. --- src/octoprint/util/pip.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/octoprint/util/pip.py b/src/octoprint/util/pip.py index 7afbad2728..724106ae8f 100644 --- a/src/octoprint/util/pip.py +++ b/src/octoprint/util/pip.py @@ -11,6 +11,7 @@ import logging import site import threading +import os import pkg_resources @@ -237,6 +238,10 @@ def _get_pip_command(self): @classmethod def autodetect_pip(cls): commands = [[sys.executable, "-m", "pip"], + [os.path.join(os.path.dirname(sys.executable), "pip.exe" if sys.platform == "win32" else "pip")], + + # this should be our last resort since it might fail thanks to using pip programmatically like + # that is not officially supported or sanctioned by the pip developers [sys.executable, "-c", "import sys; sys.argv = ['pip'] + sys.argv[1:]; import pip; pip.main()"]] for command in commands: From 972c2823a01477c124ceb15c235f8e2801e0b416 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Thu, 22 Mar 2018 14:21:20 +0100 Subject: [PATCH 308/333] Add missing _sprintf wrapper around messages Fixes #2499 --- src/octoprint/static/js/app/dataupdater.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/octoprint/static/js/app/dataupdater.js b/src/octoprint/static/js/app/dataupdater.js index 406df46900..54074ac8f9 100644 --- a/src/octoprint/static/js/app/dataupdater.js +++ b/src/octoprint/static/js/app/dataupdater.js @@ -245,17 +245,17 @@ function DataUpdater(allViewModels, connectCallback, disconnectCallback) { case "resend": case "timeout": { title = gettext("Communication error"); - text = gettext("There was a communication error while talking to your printer. Please consult the terminal output and octoprint.log for details. Error: %(error)s", payload); + text = _.sprintf(gettext("There was a communication error while talking to your printer. Please consult the terminal output and octoprint.log for details. Error: %(error)s"), payload); break; } case "connection": { title = gettext("Error connecting to printer"); - text = gettext("There was an error while trying to connect to your printer. Error: %(error)s", payload); + text = _.sprintf(gettext("There was an error while trying to connect to your printer. Error: %(error)s"), payload); break; } case "start_print": { title = gettext("Error starting a print"); - text = gettext("There was an error while trying to start a print job. Error: %(error)s", payload); + text = _.sprintf(gettext("There was an error while trying to start a print job. Error: %(error)s"), payload); break; } case "autodetect_port": @@ -265,7 +265,7 @@ function DataUpdater(allViewModels, connectCallback, disconnectCallback) { } default: { title = gettext("Unknown error"); - text = gettext("There was an unknown error while talking to your printer. Please consult the terminal output and octoprint.log for details. Error: %(error)s", payload); + text = _.sprintf(gettext("There was an unknown error while talking to your printer. Please consult the terminal output and octoprint.log for details. Error: %(error)s"), payload); break; } } From 832a0c88aaae786ece1b7711331b9125e14d8a2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Thu, 22 Mar 2018 16:15:56 +0100 Subject: [PATCH 309/333] Virtual: also reset autoreporting & fix heatup --- src/octoprint/plugins/virtual_printer/virtual.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/octoprint/plugins/virtual_printer/virtual.py b/src/octoprint/plugins/virtual_printer/virtual.py index 518a7f60ae..11211e2b56 100644 --- a/src/octoprint/plugins/virtual_printer/virtual.py +++ b/src/octoprint/plugins/virtual_printer/virtual.py @@ -222,6 +222,14 @@ def _reset(self): self._triggerResendWithMissingLinenoAt110 = True self._triggerResendWithChecksumMismatchAt115 = True + if self._temperature_reporter is not None: + self._temperature_reporter.cancel() + self._temperature_reporter = None + + if self._sdstatus_reporter is not None: + self._sdstatus_reporter.cancel() + self._sdstatus_reporter = None + if settings().getBoolean(["devel", "virtualPrinter", "simulateReset"]): for item in settings().get(["devel", "virtualPrinter", "resetLines"]): self._send(item + "\n") @@ -1133,6 +1141,7 @@ def _waitForHeatup(self, heater, only_wait_if_higher): delay = 1 last_busy = time.time() + self._heatingUp = True try: if heater.startswith("tool"): toolNum = int(heater[len("tool"):]) @@ -1154,6 +1163,8 @@ def _waitForHeatup(self, heater, only_wait_if_higher): except AttributeError: if self.outgoing is not None: raise + finally: + self._heatingUp = False def _deleteSdFile(self, filename): if filename.startswith("/"): From c30db1608595123a4a35be9c25ad7356bbc6bbae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Thu, 22 Mar 2018 16:17:25 +0100 Subject: [PATCH 310/333] Fire print failed event on error while printing That will ensure to also stop timelapse --- src/octoprint/printer/standard.py | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/src/octoprint/printer/standard.py b/src/octoprint/printer/standard.py index 14fc0326b0..b85f4e5018 100644 --- a/src/octoprint/printer/standard.py +++ b/src/octoprint/printer/standard.py @@ -1049,17 +1049,22 @@ def on_comm_state_change(self, state): with self._selectedFileMutex: if self._selectedFile is not None: if state == comm.MachineCom.STATE_CLOSED or state == comm.MachineCom.STATE_ERROR or state == comm.MachineCom.STATE_CLOSED_WITH_ERROR: - def log_print(): - self._fileManager.log_print(FileDestinations.SDCARD if self._selectedFile["sd"] else FileDestinations.LOCAL, - self._selectedFile["filename"], - time.time(), - self._comm.getPrintTime(), - False, - self._printerProfileManager.get_current_or_default()["id"]) - - thread = threading.Thread(target=log_print) - thread.daemon = True - thread.start() + payload = self._payload_for_print_job_event() + if payload: + payload["time"] = self._comm.getPrintTime() + + def finalize(): + self._fileManager.log_print(payload["origin"], + payload["path"], + time.time(), + payload["time"], + False, + self._printerProfileManager.get_current_or_default()["id"]) + eventManager().fire(Events.PRINT_FAILED, payload) + + thread = threading.Thread(target=finalize) + thread.daemon = True + thread.start() self._analysisQueue.resume() # printing done, put those cpu cycles to good use elif state == comm.MachineCom.STATE_PRINTING: self._analysisQueue.pause() # do not analyse files while printing From 7cb910a69a806e214fdb5fdf02591b54aa6ecb46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Thu, 22 Mar 2018 17:10:06 +0100 Subject: [PATCH 311/333] Forgot a monotonic.monotonic --- src/octoprint/util/comm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/octoprint/util/comm.py b/src/octoprint/util/comm.py index af095b903b..5bef1e8592 100644 --- a/src/octoprint/util/comm.py +++ b/src/octoprint/util/comm.py @@ -65,7 +65,7 @@ def restart(self, duration): before. """ self.duration = duration - self.target_time = monotonic.monotonic() + duration + self.target_time = monotonic_time() + duration serial.Timeout = _Timeout del _Timeout From 9af6b42bdd5724fb4cf2a872b78c71593eb24989 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Thu, 22 Mar 2018 17:50:48 +0100 Subject: [PATCH 312/333] Virtual: Fix queue clearing on reset --- src/octoprint/plugins/virtual_printer/virtual.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/octoprint/plugins/virtual_printer/virtual.py b/src/octoprint/plugins/virtual_printer/virtual.py index 11211e2b56..3862e455a3 100644 --- a/src/octoprint/plugins/virtual_printer/virtual.py +++ b/src/octoprint/plugins/virtual_printer/virtual.py @@ -167,9 +167,9 @@ def __str__(self): .format(read_timeout=self._read_timeout, write_timeout=self._write_timeout, options=settings().get(["devel", "virtualPrinter"])) def _reset(self): - self.incoming.queue.clear() - self.outgoing.queue.clear() - self.buffered.queue.clear() + self._clearQueue(self.incoming) + self._clearQueue(self.outgoing) + self._clearQueue(self.buffered) self._relative = True self._lastX = 0.0 @@ -252,9 +252,10 @@ def write_timeout(self, value): self._logger.debug("Setting write timeout to {}s".format(value)) self._write_timeout = value - def _clearQueue(self, queue): + def _clearQueue(self, q): try: - while queue.get(block=False): + while q.get(block=False): + q.task_done() continue except queue.Empty: pass @@ -1327,6 +1328,10 @@ def __init__(self, maxsize, name=None): self._size = 0 self._name = name + def clear(self): + with self.mutex: + self.queue.clear() + def put(self, item, block=True, timeout=None, partial=False): self.not_full.acquire() From 23f130da58e2ade555bb9e8de9572757fe68d9de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Fri, 23 Mar 2018 14:17:23 +0100 Subject: [PATCH 313/333] Fix "undefined" settings values not saving Fixes #2494 --- src/octoprint/static/js/app/client/base.js | 14 +++++++++++--- src/octoprint/static/js/app/helpers.js | 16 +++++++++------- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/octoprint/static/js/app/client/base.js b/src/octoprint/static/js/app/client/base.js index 70d47da0e9..2eb51d4d90 100644 --- a/src/octoprint/static/js/app/client/base.js +++ b/src/octoprint/static/js/app/client/base.js @@ -91,6 +91,14 @@ return params; }; + var replaceUndefinedWithNull = function(key, value) { + if (value === undefined) { + return null; + } else { + return value; + } + }; + OctoPrintClient.prototype.getBaseUrl = function() { var url = this.options.baseurl; if (!_.endsWith(url, "/")) { @@ -165,7 +173,7 @@ }; OctoPrintClient.prototype.postJson = function(url, data, opts) { - return this.post(url, JSON.stringify(data), contentTypeJson(opts)); + return this.post(url, JSON.stringify(data, replaceUndefinedWithNull), contentTypeJson(opts)); }; OctoPrintClient.prototype.put = function(url, data, opts) { @@ -173,7 +181,7 @@ }; OctoPrintClient.prototype.putJson = function(url, data, opts) { - return this.put(url, JSON.stringify(data), contentTypeJson(opts)); + return this.put(url, JSON.stringify(data, replaceUndefinedWithNull), contentTypeJson(opts)); }; OctoPrintClient.prototype.patch = function(url, data, opts) { @@ -181,7 +189,7 @@ }; OctoPrintClient.prototype.patchJson = function(url, data, opts) { - return this.patch(url, JSON.stringify(data), contentTypeJson(opts)); + return this.patch(url, JSON.stringify(data, replaceUndefinedWithNull), contentTypeJson(opts)); }; OctoPrintClient.prototype.delete = function(url, opts) { diff --git a/src/octoprint/static/js/app/helpers.js b/src/octoprint/static/js/app/helpers.js index 0781c9b6ff..98a1e57414 100644 --- a/src/octoprint/static/js/app/helpers.js +++ b/src/octoprint/static/js/app/helpers.js @@ -911,15 +911,12 @@ function splitTextToArray(text, sep, stripEmpty, filter) { * and is optimized to check for value changes, not key updates. */ function hasDataChanged(data, oldData) { - if (data == undefined) { + // noinspection EqualityComparisonWithCoercionJS + if (data == oldData && data == undefined) { return false; } - if (oldData == undefined) { - return true; - } - - if (_.isPlainObject(data)) { + if (_.isPlainObject(data) && _.isPlainObject(oldData)) { return _.any(_.keys(data), function(key) {return hasDataChanged(data[key], oldData[key]);}); } else { return !_.isEqual(data, oldData); @@ -956,10 +953,12 @@ function hasDataChanged(data, oldData) { * and is optimized to check for value changes, not key updates. */ function getOnlyChangedData(data, oldData) { + // noinspection EqualityComparisonWithCoercionJS if (data == undefined) { return {}; } + // noinspection EqualityComparisonWithCoercionJS if (oldData == undefined) { return data; } @@ -972,17 +971,20 @@ function getOnlyChangedData(data, oldData) { var retval = {}; _.forOwn(root, function(value, key) { var oldValue = undefined; + // noinspection EqualityComparisonWithCoercionJS if (oldRoot != undefined && oldRoot.hasOwnProperty(key)) { oldValue = oldRoot[key]; } if (_.isPlainObject(value)) { + // noinspection EqualityComparisonWithCoercionJS if (oldValue == undefined) { retval[key] = value; } else if (hasDataChanged(value, oldValue)) { retval[key] = f(value, oldValue); } } else { - if (!_.isEqual(value, oldValue)) { + // noinspection EqualityComparisonWithCoercionJS + if (!(value == oldValue && value == undefined) && !_.isEqual(value, oldValue)) { retval[key] = value; } } From 47267abcd125ff2ea4ff78a468538fbaaeea1a08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Fri, 23 Mar 2018 14:24:10 +0100 Subject: [PATCH 314/333] Fix HTTP 500 in case of unconfigured slicer Fixes #2508 --- src/octoprint/slicing/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/octoprint/slicing/__init__.py b/src/octoprint/slicing/__init__.py index f14ecc1998..f9e60b0a23 100644 --- a/src/octoprint/slicing/__init__.py +++ b/src/octoprint/slicing/__init__.py @@ -558,7 +558,7 @@ def all_profiles(self, slicer, require_configured=False): raise SlicerNotConfigured(slicer) slicer_profile_path = self.get_slicer_profile_path(slicer) - return self.get_slicer(slicer).get_slicer_profiles(slicer_profile_path) + return self.get_slicer(slicer, require_configured=False).get_slicer_profiles(slicer_profile_path) def profiles_last_modified(self, slicer): """ @@ -575,7 +575,7 @@ def profiles_last_modified(self, slicer): raise UnknownSlicer(slicer) slicer_profile_path = self.get_slicer_profile_path(slicer) - return self.get_slicer(slicer).get_slicer_profiles_lastmodified(slicer_profile_path) + return self.get_slicer(slicer, require_configured=False).get_slicer_profiles_lastmodified(slicer_profile_path) def get_slicer_profile_path(self, slicer): """ From 2abe67a24c9faaf12e5002734b28494b3f14c6a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Fri, 23 Mar 2018 15:27:12 +0100 Subject: [PATCH 315/333] Logging: Discourage disabling this plugin --- src/octoprint/plugins/logging/__init__.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/octoprint/plugins/logging/__init__.py b/src/octoprint/plugins/logging/__init__.py index 06fcab6320..91da040c86 100644 --- a/src/octoprint/plugins/logging/__init__.py +++ b/src/octoprint/plugins/logging/__init__.py @@ -11,6 +11,7 @@ from octoprint.server.util.flask import redirect_to_tornado, restricted_access from flask import request, jsonify, url_for, make_response +from flask_babel import gettext from werkzeug.utils import secure_filename from werkzeug.exceptions import BadRequest import yaml @@ -200,4 +201,7 @@ def get_assets(self): __plugin_name__ = "Logging" __plugin_author__ = "Shawn Bruce, based on work by Gina Häußge and Marc Hannappel" __plugin_description__ = "Provides access to OctoPrint's logs and logging configuration." +__plugin_disabling_discouraged__ = gettext("Without this plugin you will no longer be able to retrieve " + "OctoPrint's logs or modify the current logging levels through " + "the web interface.") __plugin_implementation__ = LoggingPlugin() From 339d1df5a920cc7a2dbae2b5f3410180fea4ceb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Fri, 23 Mar 2018 15:29:00 +0100 Subject: [PATCH 316/333] Preparing release of 1.3.7rc2 --- CHANGELOG.md | 17 +++++++++ SUPPORTERS.md | 3 +- src/octoprint/static/css/octoprint.css | 2 +- .../translations/de/LC_MESSAGES/messages.mo | Bin 125348 -> 126108 bytes .../translations/de/LC_MESSAGES/messages.po | 34 ++++++++++++++---- translations/de/LC_MESSAGES/messages.mo | Bin 125348 -> 126108 bytes translations/de/LC_MESSAGES/messages.po | 8 +++-- translations/messages.pot | 10 ++++-- 8 files changed, 61 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 490503c4eb..1a2ff8e6d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,22 @@ # OctoPrint Changelog +## 1.3.7rc2 (2018-03-23) + +### Bugfixes + + * [#1951](http://github.com/foosel/OctoPrint/issues/1951) - Fixed plugins being able to modify internal state data (e.g. progress, job), causing concurrent modification and resulting run time errors in the printer state processing. + * [#2494](https://github.com/foosel/OctoPrint/issues/2494) - Fixed `undefined` values not saving in the settings. + * [#2499](https://github.com/foosel/OctoPrint/issues/2499) - Fixed communication error notification lacking the actual error message. + * [#2501](https://github.com/foosel/OctoPrint/issues/2501) - Fixed a bug causing log downloads to fail with an HTTP 500 error. + * [#2506](https://github.com/foosel/OctoPrint/issues/2506) - Fixed `printer.get_current_data` and `printer.get_current_job` returning `frozendict` instead of `dict` instances, causing issues with plugins relying on being able to modify the returned data (e.g. [dattas/OctoPrint-DetailedProgress#26](https://github.com/dattas/OctoPrint-DetailedProgress/issues/26)). + * [#2508](https://github.com/foosel/OctoPrint/issues/2508) - Fixed HTTP 500 error on `/api/slicing` in case of an unconfigured slicer. + * Have `OctoPrintJsonEncoder` fall back to regular flask JSON encoder, otherwise we might not be able to serialize some data types we need to be able to serialize. + * Use `pkg_resources` to determine pip version during environment check instead of `import pip; pip.__version__` since the latter causes issues with pip version 9.0.2. In the same spirit make `pip.main` approach of calling `pip` in the PipCaller the last resort during auto detection, only after trying `pip` or `pip.exe` inside the same folder as the Python executable. + * Use `octoprint.util.monotonic_time` instead of `monotonic.monotonic` in comm layer. + * Fixed timelapse not stopping on print failure due to firmware error due to missing `PrintFailed` event. + +([Commits](https://github.com/foosel/OctoPrint/compare/1.3.7rc1...1.3.7rc2)) + ## 1.3.7rc1 (2018-03-19) ### Improvements diff --git a/SUPPORTERS.md b/SUPPORTERS.md index 2690df2f64..e204c181b9 100644 --- a/SUPPORTERS.md +++ b/SUPPORTERS.md @@ -11,6 +11,7 @@ thanks to everyone who contributed! * Andrew Moorby * Arnljot Arntsen * BEEVERYCREATIVE + * Boris Hussein * Brad Jackson * Brian E. Tyler * Christian Petropolis @@ -54,4 +55,4 @@ thanks to everyone who contributed! * Thomas Hatley * Trent Shumay -and 1188 more wonderful people pledging on the [Patreon campaign](https://patreon.com/foosel)! \ No newline at end of file +and 1199 more wonderful people pledging on the [Patreon campaign](https://patreon.com/foosel)! \ No newline at end of file diff --git a/src/octoprint/static/css/octoprint.css b/src/octoprint/static/css/octoprint.css index a6f04abec9..65970c593e 100644 --- a/src/octoprint/static/css/octoprint.css +++ b/src/octoprint/static/css/octoprint.css @@ -1 +1 @@ -.btn{display:inline-block;*display:inline;*zoom:1;padding:4px 12px;margin-bottom:0;font-size:14px;line-height:20px;text-align:center;vertical-align:middle;cursor:pointer;color:#333;text-shadow:0 1px 1px rgba(255,255,255,.75);background-color:#f5f5f5;background-image:-moz-linear-gradient(top,#fff,#e6e6e6);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#e6e6e6));background-image:-webkit-linear-gradient(top,#fff,#e6e6e6);background-image:-o-linear-gradient(top,#fff,#e6e6e6);background-image:linear-gradient(to bottom,#fff,#e6e6e6);background-repeat:repeat-x;*background-color:#e6e6e6;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);border:1px solid #ccc;*border:0;border-bottom-color:#b3b3b3;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;*margin-left:.3em;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05)}.btn.active,.btn.disabled,.btn:active,.btn:focus,.btn:hover,.btn[disabled]{color:#333;background-color:#e6e6e6;*background-color:#d9d9d9}.btn.active,.btn:active{background-color:#ccc \9}.btn:first-child{*margin-left:0}.btn:focus,.btn:hover{color:#333;text-decoration:none;background-position:0 -15px;-webkit-transition:background-position .1s linear;-moz-transition:background-position .1s linear;-o-transition:background-position .1s linear;transition:background-position .1s linear}.btn:focus{outline:#333 dotted thin;outline:-webkit-focus-ring-color auto 5px;outline-offset:-2px}.btn.active,.btn:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,.15),0 1px 2px rgba(0,0,0,.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,.15),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 2px 4px rgba(0,0,0,.15),0 1px 2px rgba(0,0,0,.05)}.btn.disabled,.btn[disabled]{cursor:default;background-image:none;opacity:.65;filter:alpha(opacity=65);-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.btn-large{padding:11px 19px;font-size:17.5px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.btn-large [class*=" icon-"],.btn-large [class^=icon-]{margin-top:4px}.btn-small{padding:2px 10px;font-size:11.9px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.btn-small [class*=" icon-"],.btn-small [class^=icon-]{margin-top:0}.btn-mini [class*=" icon-"],.btn-mini [class^=icon-]{margin-top:-1px}.btn-mini{padding:0 6px;font-size:10.5px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.btn-block{display:block;width:100%;padding-left:0;padding-right:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.btn-block+.btn-block{margin-top:5px}input[type=submit].btn-block,input[type=reset].btn-block,input[type=button].btn-block{width:100%}.btn-primary{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,.25);background-color:#006dcc;background-image:-moz-linear-gradient(top,#08c,#04c);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#04c));background-image:-webkit-linear-gradient(top,#08c,#04c);background-image:-o-linear-gradient(top,#08c,#04c);background-image:linear-gradient(to bottom,#08c,#04c);background-repeat:repeat-x;border-color:#04c #04c #002a80;border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);*background-color:#04c;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-primary.active,.btn-primary.disabled,.btn-primary:active,.btn-primary:focus,.btn-primary:hover,.btn-primary[disabled]{color:#fff;background-color:#04c;*background-color:#003bb3}.btn-primary.active,.btn-primary:active{background-color:#039 \9}.btn-warning{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,.25);background-color:#faa732;background-image:-moz-linear-gradient(top,#fbb450,#f89406);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fbb450),to(#f89406));background-image:-webkit-linear-gradient(top,#fbb450,#f89406);background-image:-o-linear-gradient(top,#fbb450,#f89406);background-image:linear-gradient(to bottom,#fbb450,#f89406);background-repeat:repeat-x;border-color:#f89406 #f89406 #ad6704;border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);*background-color:#f89406;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-warning.active,.btn-warning.disabled,.btn-warning:active,.btn-warning:focus,.btn-warning:hover,.btn-warning[disabled]{color:#fff;background-color:#f89406;*background-color:#df8505}.btn-warning.active,.btn-warning:active{background-color:#c67605 \9}.btn-danger{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,.25);background-color:#da4f49;background-image:-moz-linear-gradient(top,#ee5f5b,#bd362f);background-image:-webkit-gradient(linear,0 0,0 100%,from(#ee5f5b),to(#bd362f));background-image:-webkit-linear-gradient(top,#ee5f5b,#bd362f);background-image:-o-linear-gradient(top,#ee5f5b,#bd362f);background-image:linear-gradient(to bottom,#ee5f5b,#bd362f);background-repeat:repeat-x;border-color:#bd362f #bd362f #802420;border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);*background-color:#bd362f;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-danger.active,.btn-danger.disabled,.btn-danger:active,.btn-danger:focus,.btn-danger:hover,.btn-danger[disabled]{color:#fff;background-color:#bd362f;*background-color:#a9302a}.btn-danger.active,.btn-danger:active{background-color:#942a25 \9}.btn-success{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,.25);background-color:#5bb75b;background-image:-moz-linear-gradient(top,#62c462,#51a351);background-image:-webkit-gradient(linear,0 0,0 100%,from(#62c462),to(#51a351));background-image:-webkit-linear-gradient(top,#62c462,#51a351);background-image:-o-linear-gradient(top,#62c462,#51a351);background-image:linear-gradient(to bottom,#62c462,#51a351);background-repeat:repeat-x;border-color:#51a351 #51a351 #387038;border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);*background-color:#51a351;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-success.active,.btn-success.disabled,.btn-success:active,.btn-success:focus,.btn-success:hover,.btn-success[disabled]{color:#fff;background-color:#51a351;*background-color:#499249}.btn-success.active,.btn-success:active{background-color:#408140 \9}.btn-info{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,.25);background-color:#49afcd;background-image:-moz-linear-gradient(top,#5bc0de,#2f96b4);background-image:-webkit-gradient(linear,0 0,0 100%,from(#5bc0de),to(#2f96b4));background-image:-webkit-linear-gradient(top,#5bc0de,#2f96b4);background-image:-o-linear-gradient(top,#5bc0de,#2f96b4);background-image:linear-gradient(to bottom,#5bc0de,#2f96b4);background-repeat:repeat-x;border-color:#2f96b4 #2f96b4 #1f6377;border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);*background-color:#2f96b4;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-info.active,.btn-info.disabled,.btn-info:active,.btn-info:focus,.btn-info:hover,.btn-info[disabled]{color:#fff;background-color:#2f96b4;*background-color:#2a85a0}.btn-info.active,.btn-info:active{background-color:#24748c \9}.btn-inverse{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,.25);background-color:#363636;background-image:-moz-linear-gradient(top,#444,#222);background-image:-webkit-gradient(linear,0 0,0 100%,from(#444),to(#222));background-image:-webkit-linear-gradient(top,#444,#222);background-image:-o-linear-gradient(top,#444,#222);background-image:linear-gradient(to bottom,#444,#222);background-repeat:repeat-x;border-color:#222 #222 #000;border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);*background-color:#222;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-inverse.active,.btn-inverse.disabled,.btn-inverse:active,.btn-inverse:focus,.btn-inverse:hover,.btn-inverse[disabled]{color:#fff;background-color:#222;*background-color:#151515}.btn-inverse.active,.btn-inverse:active{background-color:#080808 \9}button.btn,input[type=submit].btn{*padding-top:3px;*padding-bottom:3px}button.btn::-moz-focus-inner,input[type=submit].btn::-moz-focus-inner{padding:0;border:0}button.btn.btn-large,input[type=submit].btn.btn-large{*padding-top:7px;*padding-bottom:7px}button.btn.btn-small,input[type=submit].btn.btn-small{*padding-top:3px;*padding-bottom:3px}button.btn.btn-mini,input[type=submit].btn.btn-mini{*padding-top:1px;*padding-bottom:1px}.btn-link,.btn-link:active,.btn-link[disabled]{background-color:transparent;background-image:none;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.btn-link{border-color:transparent;cursor:pointer;color:#08c;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.btn-link:focus,.btn-link:hover{color:#005580;text-decoration:underline;background-color:transparent}.btn-link[disabled]:focus,.btn-link[disabled]:hover{color:#333;text-decoration:none}.clearfix{*zoom:1}.clearfix:after,.clearfix:before{display:table;content:"";line-height:0}.clearfix:after{clear:both}.hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.input-block-level{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.nowrap{white-space:nowrap}.actioncol{text-align:center;white-space:nowrap}.actioncol a{text-decoration:none;color:#000}.actioncol a.disabled{color:#ccc;cursor:default}#navbar .navbar-inner{background-color:#ebebeb;background-image:-moz-linear-gradient(top,#fff,#ccc);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#ccc));background-image:-webkit-linear-gradient(top,#fff,#ccc);background-image:-o-linear-gradient(top,#fff,#ccc);background-image:linear-gradient(to bottom,#fff,#ccc);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffcccccc', GradientType=0)}#navbar .navbar-inner .brand,#navbar .navbar-inner .nav>li>a{text-shadow:0 1px 0 #ccc;color:#333}#navbar .navbar-inner .brand .caret,#navbar .navbar-inner .nav>li>a .caret{border-bottom-color:#939393;border-top-color:#939393}#navbar .navbar-inner .brand:focus .caret,#navbar .navbar-inner .brand:hover .caret,#navbar .navbar-inner .nav>li>a:focus .caret,#navbar .navbar-inner .nav>li>a:hover .caret{border-bottom-color:#636363;border-top-color:#636363}#navbar .navbar-inner .brand span{background-image:url(../img/tentacle-20x20.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner .brand span{background-image:url(../img/tentacle-20x20@2x.png)}}#navbar .navbar-inner .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner .nav>li.dropdown.open>.dropdown-toggle{background-color:#e0e0e0;background-image:-moz-linear-gradient(top,#ccc,#fff);background-image:-webkit-gradient(linear,0 0,0 100%,from(#ccc),to(#fff));background-image:-webkit-linear-gradient(top,#ccc,#fff);background-image:-o-linear-gradient(top,#ccc,#fff);background-image:linear-gradient(to bottom,#ccc,#fff);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffcccccc', endColorstr='#ffffffff', GradientType=0)}#navbar .navbar-inner .nav>li>a:hover{background-color:#dedede;background-image:-moz-linear-gradient(top,#f2f2f2,#bfbfbf);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f2f2f2),to(#bfbfbf));background-image:-webkit-linear-gradient(top,#f2f2f2,#bfbfbf);background-image:-o-linear-gradient(top,#f2f2f2,#bfbfbf);background-image:linear-gradient(to bottom,#f2f2f2,#bfbfbf);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2f2f2', endColorstr='#ffbfbfbf', GradientType=0)}#navbar .navbar-inner.transparent{background-color:rgba(235,235,235,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(255,255,255,.6),rgba(204,204,204,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(255,255,255,.6)),to(rgba(204,204,204,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(255,255,255,.6),rgba(204,204,204,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(255,255,255,.6),rgba(204,204,204,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(255,255,255,.6),rgba(204,204,204,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99ffffff', endColorstr='#99cccccc', GradientType=0)}#navbar .navbar-inner.transparent .brand,#navbar .navbar-inner.transparent .nav>li>a{text-shadow:0 1px 0 #ccc;color:#333}#navbar .navbar-inner.transparent .brand .caret,#navbar .navbar-inner.transparent .nav>li>a .caret{border-bottom-color:#939393;border-top-color:#939393}#navbar .navbar-inner.transparent .brand:focus .caret,#navbar .navbar-inner.transparent .brand:hover .caret,#navbar .navbar-inner.transparent .nav>li>a:focus .caret,#navbar .navbar-inner.transparent .nav>li>a:hover .caret{border-bottom-color:#636363;border-top-color:#636363}#navbar .navbar-inner.transparent .brand span{background-image:url(../img/tentacle-20x20.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.transparent .brand span{background-image:url(../img/tentacle-20x20@2x.png)}}#navbar .navbar-inner.transparent .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.transparent .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.transparent .nav>li.dropdown.open>.dropdown-toggle{background-color:rgba(224,224,224,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(204,204,204,.6),rgba(255,255,255,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(204,204,204,.6)),to(rgba(255,255,255,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(204,204,204,.6),rgba(255,255,255,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(204,204,204,.6),rgba(255,255,255,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(204,204,204,.6),rgba(255,255,255,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99cccccc', endColorstr='#99ffffff', GradientType=0)}#navbar .navbar-inner.transparent .nav>li>a:hover{background-color:rgba(222,222,222,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(242,242,242,.6),rgba(191,191,191,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(242,242,242,.6)),to(rgba(191,191,191,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(242,242,242,.6),rgba(191,191,191,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(242,242,242,.6),rgba(191,191,191,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(242,242,242,.6),rgba(191,191,191,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99f2f2f2', endColorstr='#99bfbfbf', GradientType=0)}#navbar .navbar-inner.red{background-color:#bb645f;background-image:-moz-linear-gradient(top,#e28e8a,#802420);background-image:-webkit-gradient(linear,0 0,0 100%,from(#e28e8a),to(#802420));background-image:-webkit-linear-gradient(top,#e28e8a,#802420);background-image:-o-linear-gradient(top,#e28e8a,#802420);background-image:linear-gradient(to bottom,#e28e8a,#802420);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe28e8a', endColorstr='#ff802420', GradientType=0)}#navbar .navbar-inner.red .brand,#navbar .navbar-inner.red .nav>li>a{text-shadow:0 1px 0 #d86761;color:#f2f2f2}#navbar .navbar-inner.red .brand .caret,#navbar .navbar-inner.red .nav>li>a .caret{border-bottom-color:#d89491;border-top-color:#d89491}#navbar .navbar-inner.red .brand:focus .caret,#navbar .navbar-inner.red .brand:hover .caret,#navbar .navbar-inner.red .nav>li>a:focus .caret,#navbar .navbar-inner.red .nav>li>a:hover .caret{border-bottom-color:#e5c3c1;border-top-color:#e5c3c1}#navbar .navbar-inner.red .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.red .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.red .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.red .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.red .nav>li.dropdown.open>.dropdown-toggle{background-color:#a74f4a;background-image:-moz-linear-gradient(top,#802420,#e28e8a);background-image:-webkit-gradient(linear,0 0,0 100%,from(#802420),to(#e28e8a));background-image:-webkit-linear-gradient(top,#802420,#e28e8a);background-image:-o-linear-gradient(top,#802420,#e28e8a);background-image:linear-gradient(to bottom,#802420,#e28e8a);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff802420', endColorstr='#ffe28e8a', GradientType=0)}#navbar .navbar-inner.red .nav>li>a:hover{background-color:#af5651;background-image:-moz-linear-gradient(top,#dd7a75,#6b1f1b);background-image:-webkit-gradient(linear,0 0,0 100%,from(#dd7a75),to(#6b1f1b));background-image:-webkit-linear-gradient(top,#dd7a75,#6b1f1b);background-image:-o-linear-gradient(top,#dd7a75,#6b1f1b);background-image:linear-gradient(to bottom,#dd7a75,#6b1f1b);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdd7a75', endColorstr='#ff6b1f1b', GradientType=0)}#navbar .navbar-inner.red.transparent{background-color:rgba(187,100,95,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(226,142,138,.6),rgba(128,36,32,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(226,142,138,.6)),to(rgba(128,36,32,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(226,142,138,.6),rgba(128,36,32,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(226,142,138,.6),rgba(128,36,32,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(226,142,138,.6),rgba(128,36,32,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99e28e8a', endColorstr='#99802420', GradientType=0)}#navbar .navbar-inner.red.transparent .brand,#navbar .navbar-inner.red.transparent .nav>li>a{text-shadow:0 1px 0 #d86761;color:#f2f2f2}#navbar .navbar-inner.red.transparent .brand .caret,#navbar .navbar-inner.red.transparent .nav>li>a .caret{border-bottom-color:#d89491;border-top-color:#d89491}#navbar .navbar-inner.red.transparent .brand:focus .caret,#navbar .navbar-inner.red.transparent .brand:hover .caret,#navbar .navbar-inner.red.transparent .nav>li>a:focus .caret,#navbar .navbar-inner.red.transparent .nav>li>a:hover .caret{border-bottom-color:#e5c3c1;border-top-color:#e5c3c1}#navbar .navbar-inner.red.transparent .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.red.transparent .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.red.transparent .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.red.transparent .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.red.transparent .nav>li.dropdown.open>.dropdown-toggle{background-color:rgba(167,79,74,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(128,36,32,.6),rgba(226,142,138,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(128,36,32,.6)),to(rgba(226,142,138,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(128,36,32,.6),rgba(226,142,138,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(128,36,32,.6),rgba(226,142,138,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(128,36,32,.6),rgba(226,142,138,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99802420', endColorstr='#99e28e8a', GradientType=0)}#navbar .navbar-inner.red.transparent .nav>li>a:hover{background-color:rgba(175,86,81,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(221,122,117,.6),rgba(107,31,27,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(221,122,117,.6)),to(rgba(107,31,27,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(221,122,117,.6),rgba(107,31,27,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(221,122,117,.6),rgba(107,31,27,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(221,122,117,.6),rgba(107,31,27,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99dd7a75', endColorstr='#996b1f1b', GradientType=0)}#navbar .navbar-inner.orange{background-color:#e39665;background-image:-moz-linear-gradient(top,#f9c3a0,#c2530c);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f9c3a0),to(#c2530c));background-image:-webkit-linear-gradient(top,#f9c3a0,#c2530c);background-image:-o-linear-gradient(top,#f9c3a0,#c2530c);background-image:linear-gradient(to bottom,#f9c3a0,#c2530c);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff9c3a0', endColorstr='#ffc2530c', GradientType=0)}#navbar .navbar-inner.orange .brand,#navbar .navbar-inner.orange .nav>li>a{text-shadow:0 1px 0 #f6a570;color:#f2f2f2}#navbar .navbar-inner.orange .brand .caret,#navbar .navbar-inner.orange .nav>li>a .caret{border-bottom-color:#f2b58d;border-top-color:#f2b58d}#navbar .navbar-inner.orange .brand:focus .caret,#navbar .navbar-inner.orange .brand:hover .caret,#navbar .navbar-inner.orange .nav>li>a:focus .caret,#navbar .navbar-inner.orange .nav>li>a:hover .caret{border-bottom-color:#f2d3bf;border-top-color:#f2d3bf}#navbar .navbar-inner.orange .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.orange .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.orange .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.orange .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.orange .nav>li.dropdown.open>.dropdown-toggle{background-color:#d88047;background-image:-moz-linear-gradient(top,#c2530c,#f9c3a0);background-image:-webkit-gradient(linear,0 0,0 100%,from(#c2530c),to(#f9c3a0));background-image:-webkit-linear-gradient(top,#c2530c,#f9c3a0);background-image:-o-linear-gradient(top,#c2530c,#f9c3a0);background-image:linear-gradient(to bottom,#c2530c,#f9c3a0);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffc2530c', endColorstr='#fff9c3a0', GradientType=0)}#navbar .navbar-inner.orange .nav>li>a:hover{background-color:#d98956;background-image:-moz-linear-gradient(top,#f8b488,#aa490a);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f8b488),to(#aa490a));background-image:-webkit-linear-gradient(top,#f8b488,#aa490a);background-image:-o-linear-gradient(top,#f8b488,#aa490a);background-image:linear-gradient(to bottom,#f8b488,#aa490a);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff8b488', endColorstr='#ffaa490a', GradientType=0)}#navbar .navbar-inner.orange.transparent{background-color:rgba(227,150,101,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(249,195,160,.6),rgba(194,83,12,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(249,195,160,.6)),to(rgba(194,83,12,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(249,195,160,.6),rgba(194,83,12,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(249,195,160,.6),rgba(194,83,12,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(249,195,160,.6),rgba(194,83,12,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99f9c3a0', endColorstr='#99c2530c', GradientType=0)}#navbar .navbar-inner.orange.transparent .brand,#navbar .navbar-inner.orange.transparent .nav>li>a{text-shadow:0 1px 0 #f6a570;color:#f2f2f2}#navbar .navbar-inner.orange.transparent .brand .caret,#navbar .navbar-inner.orange.transparent .nav>li>a .caret{border-bottom-color:#f2b58d;border-top-color:#f2b58d}#navbar .navbar-inner.orange.transparent .brand:focus .caret,#navbar .navbar-inner.orange.transparent .brand:hover .caret,#navbar .navbar-inner.orange.transparent .nav>li>a:focus .caret,#navbar .navbar-inner.orange.transparent .nav>li>a:hover .caret{border-bottom-color:#f2d3bf;border-top-color:#f2d3bf}#navbar .navbar-inner.orange.transparent .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.orange.transparent .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.orange.transparent .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.orange.transparent .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.orange.transparent .nav>li.dropdown.open>.dropdown-toggle{background-color:rgba(216,128,71,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(194,83,12,.6),rgba(249,195,160,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(194,83,12,.6)),to(rgba(249,195,160,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(194,83,12,.6),rgba(249,195,160,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(194,83,12,.6),rgba(249,195,160,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(194,83,12,.6),rgba(249,195,160,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99c2530c', endColorstr='#99f9c3a0', GradientType=0)}#navbar .navbar-inner.orange.transparent .nav>li>a:hover{background-color:rgba(217,137,86,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(248,180,136,.6),rgba(170,73,10,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(248,180,136,.6)),to(rgba(170,73,10,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(248,180,136,.6),rgba(170,73,10,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(248,180,136,.6),rgba(170,73,10,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(248,180,136,.6),rgba(170,73,10,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99f8b488', endColorstr='#99aa490a', GradientType=0)}#navbar .navbar-inner.yellow{background-color:#e3d765;background-image:-moz-linear-gradient(top,#f9f0a0,#c2b00c);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f9f0a0),to(#c2b00c));background-image:-webkit-linear-gradient(top,#f9f0a0,#c2b00c);background-image:-o-linear-gradient(top,#f9f0a0,#c2b00c);background-image:linear-gradient(to bottom,#f9f0a0,#c2b00c);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff9f0a0', endColorstr='#ffc2b00c', GradientType=0)}#navbar .navbar-inner.yellow .brand,#navbar .navbar-inner.yellow .nav>li>a{text-shadow:0 1px 0 #c2b00c;color:#f2f2f2}#navbar .navbar-inner.yellow .brand .caret,#navbar .navbar-inner.yellow .nav>li>a .caret{border-bottom-color:#f2e88d;border-top-color:#f2e88d}#navbar .navbar-inner.yellow .brand:focus .caret,#navbar .navbar-inner.yellow .brand:hover .caret,#navbar .navbar-inner.yellow .nav>li>a:focus .caret,#navbar .navbar-inner.yellow .nav>li>a:hover .caret{border-bottom-color:#f2edbf;border-top-color:#f2edbf}#navbar .navbar-inner.yellow .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.yellow .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.yellow .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.yellow .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.yellow .nav>li.dropdown.open>.dropdown-toggle{background-color:#d8ca47;background-image:-moz-linear-gradient(top,#c2b00c,#f9f0a0);background-image:-webkit-gradient(linear,0 0,0 100%,from(#c2b00c),to(#f9f0a0));background-image:-webkit-linear-gradient(top,#c2b00c,#f9f0a0);background-image:-o-linear-gradient(top,#c2b00c,#f9f0a0);background-image:linear-gradient(to bottom,#c2b00c,#f9f0a0);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffc2b00c', endColorstr='#fff9f0a0', GradientType=0)}#navbar .navbar-inner.yellow .nav>li>a:hover{background-color:#d9cc56;background-image:-moz-linear-gradient(top,#f8ed88,#aa9a0a);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f8ed88),to(#aa9a0a));background-image:-webkit-linear-gradient(top,#f8ed88,#aa9a0a);background-image:-o-linear-gradient(top,#f8ed88,#aa9a0a);background-image:linear-gradient(to bottom,#f8ed88,#aa9a0a);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff8ed88', endColorstr='#ffaa9a0a', GradientType=0)}#navbar .navbar-inner.yellow.transparent{background-color:rgba(227,215,101,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(249,240,160,.6),rgba(194,176,12,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(249,240,160,.6)),to(rgba(194,176,12,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(249,240,160,.6),rgba(194,176,12,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(249,240,160,.6),rgba(194,176,12,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(249,240,160,.6),rgba(194,176,12,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99f9f0a0', endColorstr='#99c2b00c', GradientType=0)}#navbar .navbar-inner.yellow.transparent .brand,#navbar .navbar-inner.yellow.transparent .nav>li>a{text-shadow:0 1px 0 #c2b00c;color:#f2f2f2}#navbar .navbar-inner.yellow.transparent .brand .caret,#navbar .navbar-inner.yellow.transparent .nav>li>a .caret{border-bottom-color:#f2e88d;border-top-color:#f2e88d}#navbar .navbar-inner.yellow.transparent .brand:focus .caret,#navbar .navbar-inner.yellow.transparent .brand:hover .caret,#navbar .navbar-inner.yellow.transparent .nav>li>a:focus .caret,#navbar .navbar-inner.yellow.transparent .nav>li>a:hover .caret{border-bottom-color:#f2edbf;border-top-color:#f2edbf}#navbar .navbar-inner.yellow.transparent .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.yellow.transparent .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.yellow.transparent .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.yellow.transparent .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.yellow.transparent .nav>li.dropdown.open>.dropdown-toggle{background-color:rgba(216,202,71,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(194,176,12,.6),rgba(249,240,160,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(194,176,12,.6)),to(rgba(249,240,160,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(194,176,12,.6),rgba(249,240,160,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(194,176,12,.6),rgba(249,240,160,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(194,176,12,.6),rgba(249,240,160,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99c2b00c', endColorstr='#99f9f0a0', GradientType=0)}#navbar .navbar-inner.yellow.transparent .nav>li>a:hover{background-color:rgba(217,204,86,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(248,237,136,.6),rgba(170,154,10,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(248,237,136,.6)),to(rgba(170,154,10,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(248,237,136,.6),rgba(170,154,10,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(248,237,136,.6),rgba(170,154,10,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(248,237,136,.6),rgba(170,154,10,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99f8ed88', endColorstr='#99aa9a0a', GradientType=0)}#navbar .navbar-inner.green{background-color:#98f064;background-image:-moz-linear-gradient(top,#c8ffa7,#50da00);background-image:-webkit-gradient(linear,0 0,0 100%,from(#c8ffa7),to(#50da00));background-image:-webkit-linear-gradient(top,#c8ffa7,#50da00);background-image:-o-linear-gradient(top,#c8ffa7,#50da00);background-image:linear-gradient(to bottom,#c8ffa7,#50da00);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffc8ffa7', endColorstr='#ff50da00', GradientType=0)}#navbar .navbar-inner.green .brand,#navbar .navbar-inner.green .nav>li>a{text-shadow:0 1px 0 #50da00;color:#333}#navbar .navbar-inner.green .brand .caret,#navbar .navbar-inner.green .nav>li>a .caret{border-bottom-color:#55992e;border-top-color:#55992e}#navbar .navbar-inner.green .brand:focus .caret,#navbar .navbar-inner.green .brand:hover .caret,#navbar .navbar-inner.green .nav>li>a:focus .caret,#navbar .navbar-inner.green .nav>li>a:hover .caret{border-bottom-color:#446630;border-top-color:#446630}#navbar .navbar-inner.green .brand span{background-image:url(../img/tentacle-20x20.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.green .brand span{background-image:url(../img/tentacle-20x20@2x.png)}}#navbar .navbar-inner.green .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.green .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.green .nav>li.dropdown.open>.dropdown-toggle{background-color:#80e943;background-image:-moz-linear-gradient(top,#50da00,#c8ffa7);background-image:-webkit-gradient(linear,0 0,0 100%,from(#50da00),to(#c8ffa7));background-image:-webkit-linear-gradient(top,#50da00,#c8ffa7);background-image:-o-linear-gradient(top,#50da00,#c8ffa7);background-image:linear-gradient(to bottom,#50da00,#c8ffa7);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff50da00', endColorstr='#ffc8ffa7', GradientType=0)}#navbar .navbar-inner.green .nav>li>a:hover{background-color:#8ae655;background-image:-moz-linear-gradient(top,#b8ff8e,#47c100);background-image:-webkit-gradient(linear,0 0,0 100%,from(#b8ff8e),to(#47c100));background-image:-webkit-linear-gradient(top,#b8ff8e,#47c100);background-image:-o-linear-gradient(top,#b8ff8e,#47c100);background-image:linear-gradient(to bottom,#b8ff8e,#47c100);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffb8ff8e', endColorstr='#ff47c100', GradientType=0)}#navbar .navbar-inner.green.transparent{background-color:rgba(152,240,100,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(200,255,167,.6),rgba(80,218,0,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(200,255,167,.6)),to(rgba(80,218,0,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(200,255,167,.6),rgba(80,218,0,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(200,255,167,.6),rgba(80,218,0,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(200,255,167,.6),rgba(80,218,0,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99c8ffa7', endColorstr='#9950da00', GradientType=0)}#navbar .navbar-inner.green.transparent .brand,#navbar .navbar-inner.green.transparent .nav>li>a{text-shadow:0 1px 0 #50da00;color:#333}#navbar .navbar-inner.green.transparent .brand .caret,#navbar .navbar-inner.green.transparent .nav>li>a .caret{border-bottom-color:#55992e;border-top-color:#55992e}#navbar .navbar-inner.green.transparent .brand:focus .caret,#navbar .navbar-inner.green.transparent .brand:hover .caret,#navbar .navbar-inner.green.transparent .nav>li>a:focus .caret,#navbar .navbar-inner.green.transparent .nav>li>a:hover .caret{border-bottom-color:#446630;border-top-color:#446630}#navbar .navbar-inner.green.transparent .brand span{background-image:url(../img/tentacle-20x20.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.green.transparent .brand span{background-image:url(../img/tentacle-20x20@2x.png)}}#navbar .navbar-inner.green.transparent .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.green.transparent .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.green.transparent .nav>li.dropdown.open>.dropdown-toggle{background-color:rgba(128,233,67,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(80,218,0,.6),rgba(200,255,167,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(80,218,0,.6)),to(rgba(200,255,167,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(80,218,0,.6),rgba(200,255,167,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(80,218,0,.6),rgba(200,255,167,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(80,218,0,.6),rgba(200,255,167,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#9950da00', endColorstr='#99c8ffa7', GradientType=0)}#navbar .navbar-inner.green.transparent .nav>li>a:hover{background-color:rgba(138,230,85,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(184,255,142,.6),rgba(71,193,0,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(184,255,142,.6)),to(rgba(71,193,0,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(184,255,142,.6),rgba(71,193,0,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(184,255,142,.6),rgba(71,193,0,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(184,255,142,.6),rgba(71,193,0,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99b8ff8e', endColorstr='#9947c100', GradientType=0)}#navbar .navbar-inner.blue{background-color:#2e63cc;background-image:-moz-linear-gradient(top,#4d88ff,#002b80);background-image:-webkit-gradient(linear,0 0,0 100%,from(#4d88ff),to(#002b80));background-image:-webkit-linear-gradient(top,#4d88ff,#002b80);background-image:-o-linear-gradient(top,#4d88ff,#002b80);background-image:linear-gradient(to bottom,#4d88ff,#002b80);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff4d88ff', endColorstr='#ff002b80', GradientType=0)}#navbar .navbar-inner.blue .brand,#navbar .navbar-inner.blue .nav>li>a{text-shadow:0 1px 0 #1a66ff;color:#f2f2f2}#navbar .navbar-inner.blue .brand .caret,#navbar .navbar-inner.blue .nav>li>a .caret{border-bottom-color:#799bdf;border-top-color:#799bdf}#navbar .navbar-inner.blue .brand:focus .caret,#navbar .navbar-inner.blue .brand:hover .caret,#navbar .navbar-inner.blue .nav>li>a:focus .caret,#navbar .navbar-inner.blue .nav>li>a:hover .caret{border-bottom-color:#b6c7e9;border-top-color:#b6c7e9}#navbar .navbar-inner.blue .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.blue .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.blue .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.blue .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.blue .nav>li.dropdown.open>.dropdown-toggle{background-color:#1f50b3;background-image:-moz-linear-gradient(top,#002b80,#4d88ff);background-image:-webkit-gradient(linear,0 0,0 100%,from(#002b80),to(#4d88ff));background-image:-webkit-linear-gradient(top,#002b80,#4d88ff);background-image:-o-linear-gradient(top,#002b80,#4d88ff);background-image:linear-gradient(to bottom,#002b80,#4d88ff);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff002b80', endColorstr='#ff4d88ff', GradientType=0)}#navbar .navbar-inner.blue .nav>li>a:hover{background-color:#1f55c2;background-image:-moz-linear-gradient(top,#37f,#026);background-image:-webkit-gradient(linear,0 0,0 100%,from(#37f),to(#026));background-image:-webkit-linear-gradient(top,#37f,#026);background-image:-o-linear-gradient(top,#37f,#026);background-image:linear-gradient(to bottom,#37f,#026);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3377ff', endColorstr='#ff002266', GradientType=0)}#navbar .navbar-inner.blue.transparent{background-color:rgba(46,99,204,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(77,136,255,.6),rgba(0,43,128,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(77,136,255,.6)),to(rgba(0,43,128,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(77,136,255,.6),rgba(0,43,128,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(77,136,255,.6),rgba(0,43,128,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(77,136,255,.6),rgba(0,43,128,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#994d88ff', endColorstr='#99002b80', GradientType=0)}#navbar .navbar-inner.blue.transparent .brand,#navbar .navbar-inner.blue.transparent .nav>li>a{text-shadow:0 1px 0 #1a66ff;color:#f2f2f2}#navbar .navbar-inner.blue.transparent .brand .caret,#navbar .navbar-inner.blue.transparent .nav>li>a .caret{border-bottom-color:#799bdf;border-top-color:#799bdf}#navbar .navbar-inner.blue.transparent .brand:focus .caret,#navbar .navbar-inner.blue.transparent .brand:hover .caret,#navbar .navbar-inner.blue.transparent .nav>li>a:focus .caret,#navbar .navbar-inner.blue.transparent .nav>li>a:hover .caret{border-bottom-color:#b6c7e9;border-top-color:#b6c7e9}#navbar .navbar-inner.blue.transparent .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.blue.transparent .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.blue.transparent .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.blue.transparent .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.blue.transparent .nav>li.dropdown.open>.dropdown-toggle{background-color:rgba(31,80,179,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(0,43,128,.6),rgba(77,136,255,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(0,43,128,.6)),to(rgba(77,136,255,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(0,43,128,.6),rgba(77,136,255,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(0,43,128,.6),rgba(77,136,255,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(0,43,128,.6),rgba(77,136,255,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99002b80', endColorstr='#994d88ff', GradientType=0)}#navbar .navbar-inner.blue.transparent .nav>li>a:hover{background-color:rgba(31,85,194,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(51,119,255,.6),rgba(0,34,102,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(51,119,255,.6)),to(rgba(0,34,102,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(51,119,255,.6),rgba(0,34,102,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(51,119,255,.6),rgba(0,34,102,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(51,119,255,.6),rgba(0,34,102,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#993377ff', endColorstr='#99002266', GradientType=0)}#navbar .navbar-inner.violet{background-color:#9864f0;background-image:-moz-linear-gradient(top,#c8a7ff,#5000da);background-image:-webkit-gradient(linear,0 0,0 100%,from(#c8a7ff),to(#5000da));background-image:-webkit-linear-gradient(top,#c8a7ff,#5000da);background-image:-o-linear-gradient(top,#c8a7ff,#5000da);background-image:linear-gradient(to bottom,#c8a7ff,#5000da);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffc8a7ff', endColorstr='#ff5000da', GradientType=0)}#navbar .navbar-inner.violet .brand,#navbar .navbar-inner.violet .nav>li>a{text-shadow:0 1px 0 #a774ff;color:#f2f2f2}#navbar .navbar-inner.violet .brand .caret,#navbar .navbar-inner.violet .nav>li>a .caret{border-bottom-color:#b58df9;border-top-color:#b58df9}#navbar .navbar-inner.violet .brand:focus .caret,#navbar .navbar-inner.violet .brand:hover .caret,#navbar .navbar-inner.violet .nav>li>a:focus .caret,#navbar .navbar-inner.violet .nav>li>a:hover .caret{border-bottom-color:#d3bff5;border-top-color:#d3bff5}#navbar .navbar-inner.violet .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.violet .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.violet .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.violet .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.violet .nav>li.dropdown.open>.dropdown-toggle{background-color:#8043e9;background-image:-moz-linear-gradient(top,#5000da,#c8a7ff);background-image:-webkit-gradient(linear,0 0,0 100%,from(#5000da),to(#c8a7ff));background-image:-webkit-linear-gradient(top,#5000da,#c8a7ff);background-image:-o-linear-gradient(top,#5000da,#c8a7ff);background-image:linear-gradient(to bottom,#5000da,#c8a7ff);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5000da', endColorstr='#ffc8a7ff', GradientType=0)}#navbar .navbar-inner.violet .nav>li>a:hover{background-color:#8a55e6;background-image:-moz-linear-gradient(top,#b88eff,#4700c1);background-image:-webkit-gradient(linear,0 0,0 100%,from(#b88eff),to(#4700c1));background-image:-webkit-linear-gradient(top,#b88eff,#4700c1);background-image:-o-linear-gradient(top,#b88eff,#4700c1);background-image:linear-gradient(to bottom,#b88eff,#4700c1);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffb88eff', endColorstr='#ff4700c1', GradientType=0)}#navbar .navbar-inner.violet.transparent{background-color:rgba(152,100,240,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(200,167,255,.6),rgba(80,0,218,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(200,167,255,.6)),to(rgba(80,0,218,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(200,167,255,.6),rgba(80,0,218,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(200,167,255,.6),rgba(80,0,218,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(200,167,255,.6),rgba(80,0,218,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99c8a7ff', endColorstr='#995000da', GradientType=0)}#navbar .navbar-inner.violet.transparent .brand,#navbar .navbar-inner.violet.transparent .nav>li>a{text-shadow:0 1px 0 #a774ff;color:#f2f2f2}#navbar .navbar-inner.violet.transparent .brand .caret,#navbar .navbar-inner.violet.transparent .nav>li>a .caret{border-bottom-color:#b58df9;border-top-color:#b58df9}#navbar .navbar-inner.violet.transparent .brand:focus .caret,#navbar .navbar-inner.violet.transparent .brand:hover .caret,#navbar .navbar-inner.violet.transparent .nav>li>a:focus .caret,#navbar .navbar-inner.violet.transparent .nav>li>a:hover .caret{border-bottom-color:#d3bff5;border-top-color:#d3bff5}#navbar .navbar-inner.violet.transparent .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.violet.transparent .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.violet.transparent .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.violet.transparent .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.violet.transparent .nav>li.dropdown.open>.dropdown-toggle{background-color:rgba(128,67,233,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(80,0,218,.6),rgba(200,167,255,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(80,0,218,.6)),to(rgba(200,167,255,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(80,0,218,.6),rgba(200,167,255,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(80,0,218,.6),rgba(200,167,255,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(80,0,218,.6),rgba(200,167,255,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#995000da', endColorstr='#99c8a7ff', GradientType=0)}#navbar .navbar-inner.violet.transparent .nav>li>a:hover{background-color:rgba(138,85,230,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(184,142,255,.6),rgba(71,0,193,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(184,142,255,.6)),to(rgba(71,0,193,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(184,142,255,.6),rgba(71,0,193,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(184,142,255,.6),rgba(71,0,193,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(184,142,255,.6),rgba(71,0,193,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99b88eff', endColorstr='#994700c1', GradientType=0)}#navbar .navbar-inner.black{background-color:#4f4f4f;background-image:-moz-linear-gradient(top,#787878,#121212);background-image:-webkit-gradient(linear,0 0,0 100%,from(#787878),to(#121212));background-image:-webkit-linear-gradient(top,#787878,#121212);background-image:-o-linear-gradient(top,#787878,#121212);background-image:linear-gradient(to bottom,#787878,#121212);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff787878', endColorstr='#ff121212', GradientType=0)}#navbar .navbar-inner.black .brand,#navbar .navbar-inner.black .nav>li>a{text-shadow:0 1px 0 #5e5e5e;color:#f2f2f2}#navbar .navbar-inner.black .brand .caret,#navbar .navbar-inner.black .nav>li>a .caret{border-bottom-color:#959595;border-top-color:#959595}#navbar .navbar-inner.black .brand:focus .caret,#navbar .navbar-inner.black .brand:hover .caret,#navbar .navbar-inner.black .nav>li>a:focus .caret,#navbar .navbar-inner.black .nav>li>a:hover .caret{border-bottom-color:#c4c4c4;border-top-color:#c4c4c4}#navbar .navbar-inner.black .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.black .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.black .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.black .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.black .nav>li.dropdown.open>.dropdown-toggle{background-color:#3b3b3b;background-image:-moz-linear-gradient(top,#121212,#787878);background-image:-webkit-gradient(linear,0 0,0 100%,from(#121212),to(#787878));background-image:-webkit-linear-gradient(top,#121212,#787878);background-image:-o-linear-gradient(top,#121212,#787878);background-image:linear-gradient(to bottom,#121212,#787878);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff121212', endColorstr='#ff787878', GradientType=0)}#navbar .navbar-inner.black .nav>li>a:hover{background-color:#424242;background-image:-moz-linear-gradient(top,#6b6b6b,#050505);background-image:-webkit-gradient(linear,0 0,0 100%,from(#6b6b6b),to(#050505));background-image:-webkit-linear-gradient(top,#6b6b6b,#050505);background-image:-o-linear-gradient(top,#6b6b6b,#050505);background-image:linear-gradient(to bottom,#6b6b6b,#050505);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff6b6b6b', endColorstr='#ff050505', GradientType=0)}#navbar .navbar-inner.black.transparent{background-color:rgba(79,79,79,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(120,120,120,.6),rgba(18,18,18,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(120,120,120,.6)),to(rgba(18,18,18,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(120,120,120,.6),rgba(18,18,18,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(120,120,120,.6),rgba(18,18,18,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(120,120,120,.6),rgba(18,18,18,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99787878', endColorstr='#99121212', GradientType=0)}#navbar .navbar-inner.black.transparent .brand,#navbar .navbar-inner.black.transparent .nav>li>a{text-shadow:0 1px 0 #5e5e5e;color:#f2f2f2}#navbar .navbar-inner.black.transparent .brand .caret,#navbar .navbar-inner.black.transparent .nav>li>a .caret{border-bottom-color:#959595;border-top-color:#959595}#navbar .navbar-inner.black.transparent .brand:focus .caret,#navbar .navbar-inner.black.transparent .brand:hover .caret,#navbar .navbar-inner.black.transparent .nav>li>a:focus .caret,#navbar .navbar-inner.black.transparent .nav>li>a:hover .caret{border-bottom-color:#c4c4c4;border-top-color:#c4c4c4}#navbar .navbar-inner.black.transparent .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.black.transparent .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.black.transparent .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.black.transparent .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.black.transparent .nav>li.dropdown.open>.dropdown-toggle{background-color:rgba(59,59,59,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(18,18,18,.6),rgba(120,120,120,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(18,18,18,.6)),to(rgba(120,120,120,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(18,18,18,.6),rgba(120,120,120,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(18,18,18,.6),rgba(120,120,120,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(18,18,18,.6),rgba(120,120,120,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99121212', endColorstr='#99787878', GradientType=0)}#navbar .navbar-inner.black.transparent .nav>li>a:hover{background-color:rgba(66,66,66,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(107,107,107,.6),rgba(5,5,5,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(107,107,107,.6)),to(rgba(5,5,5,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(107,107,107,.6),rgba(5,5,5,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(107,107,107,.6),rgba(5,5,5,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(107,107,107,.6),rgba(5,5,5,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#996b6b6b', endColorstr='#99050505', GradientType=0)}#navbar .navbar-inner.white{background-color:#e9e9e9;background-image:-moz-linear-gradient(top,#fff,#c8c8c8);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#c8c8c8));background-image:-webkit-linear-gradient(top,#fff,#c8c8c8);background-image:-o-linear-gradient(top,#fff,#c8c8c8);background-image:linear-gradient(to bottom,#fff,#c8c8c8);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffc8c8c8', GradientType=0)}#navbar .navbar-inner.white .brand,#navbar .navbar-inner.white .nav>li>a{text-shadow:0 1px 0 #c8c8c8;color:#333}#navbar .navbar-inner.white .brand .caret,#navbar .navbar-inner.white .nav>li>a .caret{border-bottom-color:#919191;border-top-color:#919191}#navbar .navbar-inner.white .brand:focus .caret,#navbar .navbar-inner.white .brand:hover .caret,#navbar .navbar-inner.white .nav>li>a:focus .caret,#navbar .navbar-inner.white .nav>li>a:hover .caret{border-bottom-color:#626262;border-top-color:#626262}#navbar .navbar-inner.white .brand span{background-image:url(../img/tentacle-20x20.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.white .brand span{background-image:url(../img/tentacle-20x20@2x.png)}}#navbar .navbar-inner.white .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.white .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.white .nav>li.dropdown.open>.dropdown-toggle{background-color:#dedede;background-image:-moz-linear-gradient(top,#c8c8c8,#fff);background-image:-webkit-gradient(linear,0 0,0 100%,from(#c8c8c8),to(#fff));background-image:-webkit-linear-gradient(top,#c8c8c8,#fff);background-image:-o-linear-gradient(top,#c8c8c8,#fff);background-image:linear-gradient(to bottom,#c8c8c8,#fff);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffc8c8c8', endColorstr='#ffffffff', GradientType=0)}#navbar .navbar-inner.white .nav>li>a:hover{background-color:#dcdcdc;background-image:-moz-linear-gradient(top,#f2f2f2,#bbb);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f2f2f2),to(#bbb));background-image:-webkit-linear-gradient(top,#f2f2f2,#bbb);background-image:-o-linear-gradient(top,#f2f2f2,#bbb);background-image:linear-gradient(to bottom,#f2f2f2,#bbb);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2f2f2', endColorstr='#ffbbbbbb', GradientType=0)}#navbar .navbar-inner.white.transparent{background-color:rgba(233,233,233,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(255,255,255,.6),rgba(200,200,200,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(255,255,255,.6)),to(rgba(200,200,200,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(255,255,255,.6),rgba(200,200,200,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(255,255,255,.6),rgba(200,200,200,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(255,255,255,.6),rgba(200,200,200,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99ffffff', endColorstr='#99c8c8c8', GradientType=0)}#navbar .navbar-inner.white.transparent .brand,#navbar .navbar-inner.white.transparent .nav>li>a{text-shadow:0 1px 0 #c8c8c8;color:#333}#navbar .navbar-inner.white.transparent .brand .caret,#navbar .navbar-inner.white.transparent .nav>li>a .caret{border-bottom-color:#919191;border-top-color:#919191}#navbar .navbar-inner.white.transparent .brand:focus .caret,#navbar .navbar-inner.white.transparent .brand:hover .caret,#navbar .navbar-inner.white.transparent .nav>li>a:focus .caret,#navbar .navbar-inner.white.transparent .nav>li>a:hover .caret{border-bottom-color:#626262;border-top-color:#626262}#navbar .navbar-inner.white.transparent .brand span{background-image:url(../img/tentacle-20x20.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.white.transparent .brand span{background-image:url(../img/tentacle-20x20@2x.png)}}#navbar .navbar-inner.white.transparent .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.white.transparent .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.white.transparent .nav>li.dropdown.open>.dropdown-toggle{background-color:rgba(222,222,222,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(200,200,200,.6),rgba(255,255,255,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(200,200,200,.6)),to(rgba(255,255,255,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(200,200,200,.6),rgba(255,255,255,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(200,200,200,.6),rgba(255,255,255,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(200,200,200,.6),rgba(255,255,255,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99c8c8c8', endColorstr='#99ffffff', GradientType=0)}#navbar .navbar-inner.white.transparent .nav>li>a:hover{background-color:rgba(220,220,220,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(242,242,242,.6),rgba(187,187,187,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(242,242,242,.6)),to(rgba(187,187,187,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(242,242,242,.6),rgba(187,187,187,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(242,242,242,.6),rgba(187,187,187,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(242,242,242,.6),rgba(187,187,187,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99f2f2f2', endColorstr='#99bbbbbb', GradientType=0)}#navbar .navbar-inner .brand{padding:10px 20px 6px}#navbar .navbar-inner .brand span{padding-left:26px;background-size:20px 20px;background-repeat:no-repeat;display:inline-block;max-width:250px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;vertical-align:top;line-height:20px;height:24px}#navbar_login a.dropdown-toggle span{display:inline-block;max-width:100px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;vertical-align:top}.octoprint-container{margin-top:20px}.octoprint-container .tab-content{padding:9px 15px;border-left:1px solid #DDD;border-right:1px solid #DDD;border-bottom:1px solid #DDD;-webkit-border-bottom-right-radius:4px;-moz-border-radius-bottomright:4px;border-bottom-right-radius:4px;-webkit-border-bottom-left-radius:4px;-moz-border-radius-bottomleft:4px;border-bottom-left-radius:4px}.octoprint-container .nav{margin-bottom:0}.octoprint-container .tab-content h1{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:40px;color:#333;border:0;border-bottom:1px solid #E5E5E5;font-weight:400}.octoprint-container .accordion-heading .accordion-heading-button{float:right}.octoprint-container .accordion-heading .accordion-heading-button a{display:inline-block;padding:8px 15px;font-size:14px;line-height:20px;color:#000;text-decoration:none;background:0 0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.octoprint-container .accordion-heading a.accordion-toggle{display:inline-block}.octoprint-container .accordion-heading [class*=" icon-"],.octoprint-container .accordion-heading [class^=icon-]{color:#000}.print-control .btn{padding-left:4px;padding-right:4px}.upload-buttons .btn{margin-right:0}table{table-layout:fixed}table .popover-title{text-overflow:ellipsis;word-break:break-all}table td,table th{overflow:hidden}table td.gcode_files_name,table th.gcode_files_name{text-overflow:ellipsis;text-align:left;white-space:nowrap}table td.gcode_files_action,table th.gcode_files_action{width:90px;text-align:center;white-space:nowrap}table td.gcode_files_action a,table th.gcode_files_action a{text-decoration:none;color:#000}table td.gcode_files_action a.disabled,table th.gcode_files_action a.disabled{color:#ccc;cursor:default}table td.timelapse_files_checkbox,table td.timelapse_unrendered_checkbox,table th.timelapse_files_checkbox,table th.timelapse_unrendered_checkbox{text-align:center;width:10px}table td.timelapse_files_checkbox input[type=checkbox],table td.timelapse_unrendered_checkbox input[type=checkbox],table th.timelapse_files_checkbox input[type=checkbox],table th.timelapse_unrendered_checkbox input[type=checkbox]{margin-top:0}table td.timelapse_files_name,table td.timelapse_unrendered_name,table th.timelapse_files_name,table th.timelapse_unrendered_name{text-overflow:ellipsis;text-align:left}table td.timelapse_files_size,table td.timelapse_unrendered_size,table th.timelapse_files_size,table th.timelapse_unrendered_size{text-align:right;width:55px}table td.timelapse_unrendered_count,table th.timelapse_unrendered_count{text-align:right;width:45px}table td.timelapse_files_action,table td.timelapse_unrendered_action,table th.timelapse_files_action,table th.timelapse_unrendered_action{width:45px;text-align:center;white-space:nowrap}table td.timelapse_files_action a,table td.timelapse_unrendered_action a,table th.timelapse_files_action a,table th.timelapse_unrendered_action a{text-decoration:none;color:#000}table td.timelapse_files_action a.disabled,table td.timelapse_unrendered_action a.disabled,table th.timelapse_files_action a.disabled,table th.timelapse_unrendered_action a.disabled{color:#ccc;cursor:default}table td.settings_users_name,table th.settings_users_name{text-overflow:ellipsis;text-align:left}table td.settings_users_active,table td.settings_users_admin,table th.settings_users_active,table th.settings_users_admin{text-align:center;width:55px}table td.settings_users_actions,table th.settings_users_actions{width:60px;text-align:center;white-space:nowrap}table td.settings_users_actions a,table th.settings_users_actions a{text-decoration:none;color:#000}table td.settings_users_actions a.disabled,table th.settings_users_actions a.disabled{color:#ccc;cursor:default}table td.settings_printerProfiles_profiles_name,table th.settings_printerProfiles_profiles_name{text-overflow:ellipsis;text-align:left}table td.settings_printerProfiles_profiles_model,table th.settings_printerProfiles_profiles_model{text-align:left;width:250px}table td.settings_printerProfiles_profiles_action,table th.settings_printerProfiles_profiles_action{width:80px;text-align:center;white-space:nowrap}table td.settings_printerProfiles_profiles_action a,table th.settings_printerProfiles_profiles_action a{text-decoration:none;color:#000}table td.settings_printerProfiles_profiles_action a.disabled,table th.settings_printerProfiles_profiles_action a.disabled{color:#ccc;cursor:default}#temperature-graph{height:350px;width:100%;background:url(../img/graph-background.png) center no-repeat}#temperature-table{table-layout:fixed;width:100%;margin-top:20px}#temperature-table td.temperature_actual,#temperature-table td.temperature_offset,#temperature-table td.temperature_target,#temperature-table td.temperature_tool,#temperature-table th.temperature_actual,#temperature-table th.temperature_offset,#temperature-table th.temperature_target,#temperature-table th.temperature_tool{vertical-align:middle;text-align:center}#temperature-table td.temperature_actual form,#temperature-table td.temperature_offset form,#temperature-table td.temperature_target form,#temperature-table td.temperature_tool form,#temperature-table th.temperature_actual form,#temperature-table th.temperature_offset form,#temperature-table th.temperature_target form,#temperature-table th.temperature_tool form{margin:0}#temperature-table td.temperature_actual .dropdown-menu,#temperature-table td.temperature_offset .dropdown-menu,#temperature-table td.temperature_target .dropdown-menu,#temperature-table td.temperature_tool .dropdown-menu,#temperature-table th.temperature_actual .dropdown-menu,#temperature-table th.temperature_offset .dropdown-menu,#temperature-table th.temperature_target .dropdown-menu,#temperature-table th.temperature_tool .dropdown-menu{text-align:left}#temperature-table td.temperature_tool,#temperature-table th.temperature_tool{width:16%;text-align:left;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}#temperature-table td.temperature_actual,#temperature-table th.temperature_actual{width:12%}#temperature-table td.temperature_target,#temperature-table th.temperature_target{width:42%;overflow:visible}#temperature-table td.temperature_offset,#temperature-table th.temperature_offset{width:30%}.tab-content,.tab-pane{overflow:visible}#speed_fill,#speed_innerWall,#speed_outerWall,#speed_support,#temp_newBedTemp,#temp_newTemp,#webcam_timelapse_fps,#webcam_timelapse_interval,#webcam_timelapse_postRoll,#webcam_timelapse_retractionZHop{text-align:right}ul.dropdown-menu li a{cursor:pointer}#connection_baudrates,#connection_ports,#connection_printers{width:100%}#offline_overlay,#reloadui_overlay{position:fixed;top:0;left:0;width:100%;height:100%;display:none}#offline_overlay{z-index:10002}#reloadui_overlay{z-index:10001}#offline_overlay_background,#reloadui_overlay_background{position:fixed;top:0;left:0;width:100%;height:100%;background-color:#000;filter:alpha(opacity=50);-moz-opacity:.5;-khtml-opacity:.5;opacity:.5}#offline_overlay_wrapper,#reloadui_overlay_wrapper{position:absolute;top:0;bottom:0;left:0;right:0;padding-top:60px}#offline_overlay_wrapper .container,#reloadui_overlay_wrapper .container{margin:auto}#webcam_container{width:100%;position:relative;outline:0;background-color:#000}#webcam_container .keycontrol_overlay{position:absolute;left:10px;right:10px;bottom:10px;background:rgba(0,0,0,.5);font-size:85%;color:#fff;padding:0}#webcam_container .keycontrol_overlay kbd{border:1px solid #eee;border-radius:3px;margin-left:2px;margin-right:2px;font-size:90%;padding:2px;min-width:1em}#webcam_container .keycontrol_overlay .keycontrol_overlay_heading{position:relative;padding:10px;font-weight:700}#webcam_container .keycontrol_overlay .keycontrol_overlay_column{position:relative;width:45%;padding:10px;float:left}#webcam_container .nowebcam{position:absolute;top:0;left:0;right:0;bottom:0}#webcam_container .nowebcam .text{color:#fff;text-align:center;position:relative;margin:auto;width:80%;top:50%;transform:translateY(-50%);display:block}#webcam_container .nowebcam .text.webcam_loading{animation:pulsate 3s ease-out;animation-iteration-count:infinite}#webcam_container .webcam_rotated{position:relative;width:100%;padding-bottom:100%;pointer-events:none}#webcam_container .webcam_rotated .webcam_fixed_ratio{position:absolute;transform:rotate(-90deg);top:0;bottom:0;pointer-events:none}#webcam_container .webcam_rotated .webcam_fixed_ratio .webcam_fixed_ratio_inner{width:100%;height:100%;pointer-events:none}#webcam_container .webcam_unrotated .webcam_fixed_ratio{width:100%;pointer-events:none;padding-bottom:100%;position:relative}#webcam_container .webcam_unrotated .webcam_fixed_ratio.ratio43{padding-bottom:75%}#webcam_container .webcam_unrotated .webcam_fixed_ratio.ratio169{padding-bottom:56.25%}#webcam_container .webcam_unrotated .webcam_fixed_ratio.ratio1610{padding-bottom:62.5%}#webcam_container .webcam_unrotated .webcam_fixed_ratio .webcam_fixed_ratio_inner{position:absolute;top:0;bottom:0;left:0;right:0;pointer-events:none}#webcam_container img{width:100%;height:100%;object-fit:contain}#state_wrapper hr{margin:5px 0}#state_wrapper .alert{margin-bottom:5px}#state_wrapper .alert a{text-decoration:underline;color:#c09853}#state_wrapper .alert-danger a,#state_wrapper .alert-error a{color:#b94a48}#state_wrapper .alert-success a{color:#468847}#state_wrapper .alert-info a{color:#3a87ad}#files .gcode_files{padding-right:12px}#files .gcode_files .entry{padding:5px;line-height:20px;border-bottom:1px solid #ddd;position:relative}#files .gcode_files .entry:hover{background-color:#f5f5f5}#files .gcode_files .entry .title{text-overflow:ellipsis;word-break:break-all}#files .gcode_files .entry .additionalInfo,#files .gcode_files .entry .internal,#files .gcode_files .entry .size,#files .gcode_files .entry .uploaded{font-size:85%;color:#999}#files .gcode_files .entry .internal{word-break:break-all}#files .gcode_files .entry .action-buttons{position:absolute;bottom:5px;right:5px}#files .gcode_files .entry .additionalInfo{padding-bottom:22px}@keyframes highlightframes{0%{background:#ff0}100%{background:0 0}}#files .gcode_files .entry.highlight{animation:highlightframes 2s}#files .gcode_files .back .back-path{white-space:nowrap}#files .gcode_files .back .back-path span{word-wrap:break-word;white-space:pre-line}#files .upload-buttons{margin-top:10px}#files .form-search{text-align:center;margin-bottom:5px!important}#control{overflow:hidden}#control .jog-panel{float:left;margin-right:19px}#control h1{text-align:left}#control .jog-panel>div{text-align:center}#control .jog-panel>div.distance{text-align:left}#control .jog-panel .slider{margin-bottom:10px}#control .box{width:30px;height:30px;margin-right:10px;margin-bottom:10px;padding-left:8px}#control .control-box{display:block;height:30px;margin-bottom:10px}#control .btn-group{margin-bottom:10px}#control .btn-group.distance>.btn{width:43px;padding:3px 0;height:30px}#control .slider-handle{width:14px;height:14px;margin-left:-7px;margin-top:-3px}#control .custom_section h1{cursor:pointer}#control .custom_section_horizontal>.custom_control{display:inline-block}#control .custom_section_vertical>.custom_control{display:block}#control .custom_control .slider{margin-left:10px;margin-right:10px;margin-bottom:2px}#gcode .progress{width:588px}#gcode .progress .bar{-webkit-transition:width 0s linear;-moz-transition:width 0s linear;-o-transition:width 0s linear;transition:width 0s linear}#gcode .canvas_container{position:relative}#gcode .canvas_container:active,#gcode .canvas_container:hover{outline:0}#gcode .layer-buttons{padding-top:5px;padding-bottom:7px}#gcode #gcode_layer_slider{position:absolute;right:0;top:0;height:568px;float:right}#gcode #gcode_layer_slider .slider-handle{width:14px;height:14px;margin-left:-3px;margin-top:-7px}#gcode #gcode_command_slider .slider-handle{width:14px;height:14px;margin-left:-7px;margin-top:-3px}#term .terminal{margin-bottom:30px}#term .terminal #terminal-output,#term .terminal #terminal-output-lowfi{min-height:340px;margin-bottom:5px}#settings_dialog .aboutlink{float:left}#settings_dialog_menu,#wizard_dialog_menu{margin-left:0}#wizard_firstrun_acl .acl_decision{margin-top:1em}#wizard_firstrun_end p,#wizard_firstrun_start p{margin-bottom:1.5em;line-height:1.5}#settings_appearance_managelanguagesdialog_emptylist{overflow:hidden;width:100%;height:300px;text-align:center;display:table}#settings_appearance_managelanguagesdialog_emptylist div{display:table-cell;vertical-align:middle}.footer ul{margin:0}.footer ul li{display:inline;margin-left:1em;font-size:85%}.footer ul li:first-child{margin-left:0}.footer ul li a{color:#555}.footer #footer_link,.footer #footer_version{max-width:50%}.ui-pnotify .alert a{color:#c09853}.ui-pnotify .alert-danger a,.ui-pnotify .alert-error a{color:#b94a48}.ui-pnotify .alert-success a{color:#468847}.ui-pnotify .alert-info a{color:#3a87ad}.pnotify_additional_info .pnotify_more{font-size:85%}.text-right{text-align:right}.text-center{text-align:center}.text-block{display:block}.overflow_visible{overflow:visible!important}.clickable{cursor:pointer}.border_box{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.hidden{display:none}textarea.block{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;width:100%}@keyframes pulsate{0%{opacity:.5}50%{opacity:1}100%{opacity:.5}}#drop_overlay{position:fixed;top:0;left:0;width:100%;height:100%;z-index:10000;display:none}#drop_overlay.in{display:block}#drop_overlay #drop_overlay_background{position:fixed;top:0;left:0;width:100%;height:100%;background-color:#000;filter:alpha(opacity=50);-moz-opacity:.5;-khtml-opacity:.5;opacity:.5}#drop_overlay #drop_overlay_wrapper{position:absolute;top:0;bottom:0;left:0;right:0;padding-top:60px}#drop_overlay #drop_overlay_wrapper #drop,#drop_overlay #drop_overlay_wrapper #drop_background{position:absolute;top:0;left:0;margin-left:0;width:100%}#drop_overlay #drop_overlay_wrapper #drop_locally,#drop_overlay #drop_overlay_wrapper #drop_locally_background{position:absolute;top:0;left:50%;margin-left:-50%;width:50%;border-right:2px dashed #ccc}#drop_overlay #drop_overlay_wrapper #drop_sd,#drop_overlay #drop_overlay_wrapper #drop_sd_background{position:absolute;top:0;left:50%;margin-left:0;width:50%;border-left:2px dashed #ccc}#drop_overlay #drop_overlay_wrapper .dropzone{height:100%;z-index:10001;color:#fff;font-size:30px}#drop_overlay #drop_overlay_wrapper .dropzone i{font-size:50px}#drop_overlay #drop_overlay_wrapper .dropzone .text{display:block;text-align:center;line-height:40px;position:absolute;width:100%;bottom:5%;filter:alpha(opacity=100);-moz-opacity:1;-khtml-opacity:1;opacity:1}#drop_overlay #drop_overlay_wrapper .dropzone_background{width:50%;height:100%;background-color:#000;filter:alpha(opacity=25);-moz-opacity:.25;-khtml-opacity:.25;opacity:.25}#drop_overlay #drop_overlay_wrapper .dropzone_background.hover{background-color:#000;filter:alpha(opacity=50);-moz-opacity:.5;-khtml-opacity:.5;opacity:.5}#drop_overlay #drop_overlay_wrapper .dropzone_background.fade{-webkit-transition:all .3s ease-out;-moz-transition:all .3s ease-out;-ms-transition:all .3s ease-out;-o-transition:all .3s ease-out;transition:all .3s ease-out;opacity:1}.icon-sd-black-14{background:url(../img/icon-sd-black-14.png) 0 3px no-repeat;width:11px;height:17px;display:inline-block!important}.center{float:none;margin-left:auto;margin-right:auto}.flipH{-webkit-transform:scaleX(-1);-moz-transform:scaleX(-1);-ms-transform:scaleX(-1);transform:scaleX(-1)}.flipV{-webkit-transform:scaleY(-1);-moz-transform:scaleY(-1);-ms-transform:scaleY(-1);transform:scaleY(-1)}.flipH.flipV{-webkit-transform:scaleX(-1) scaleY(-1);-moz-transform:scaleX(-1) scaleY(-1);-ms-transform:scaleX(-1) scaleY(-1);transform:scaleX(-1) scaleY(-1)}.rotate90{-webkit-transform:rotate(-90deg);transform:rotate(-90deg)}.ui-pnotify a{text-decoration:underline}.btn-mini .caret,.btn-small .caret{margin-top:8px}.dropdown-menu-right{right:0;left:auto}.slider .slider-selection{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,.25);background-color:#006dcc;background-image:-moz-linear-gradient(top,#08c,#04c);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#04c));background-image:-webkit-linear-gradient(top,#08c,#04c);background-image:-o-linear-gradient(top,#08c,#04c);background-image:linear-gradient(to bottom,#08c,#04c);background-repeat:repeat-x;border-color:#04c #04c #002a80;border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);*background-color:#04c;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.slider .slider-selection.active,.slider .slider-selection.disabled,.slider .slider-selection:active,.slider .slider-selection:focus,.slider .slider-selection:hover,.slider .slider-selection[disabled]{color:#fff;background-color:#04c;*background-color:#003bb3}.slider .slider-selection.active,.slider .slider-selection:active{background-color:#039 \9}.slider.slider-disabled .slider-selection{background-image:none;opacity:.65;filter:alpha(opacity=65);-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.slider .slider-track{background-color:#f5f5f5;border:1px solid #e3e3e3;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05)}.slider.slider-disabled .slider-track{background-image:none;opacity:.65;filter:alpha(opacity=65);-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.slider .slider-handle{display:inline-block;*display:inline;*zoom:1;font-size:14px;line-height:20px;text-align:center;vertical-align:middle;cursor:pointer;color:#333;text-shadow:0 1px 1px rgba(255,255,255,.75);background-color:#f5f5f5;background-image:-moz-linear-gradient(top,#fff,#e6e6e6);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#e6e6e6));background-image:-webkit-linear-gradient(top,#fff,#e6e6e6);background-image:-o-linear-gradient(top,#fff,#e6e6e6);background-image:linear-gradient(to bottom,#fff,#e6e6e6);background-repeat:repeat-x;*background-color:#e6e6e6;border:1px solid #ccc;*border:0;border-bottom-color:#b3b3b3;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;*margin-left:.3em;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05);padding:0;margin-bottom:0;opacity:1;filter:alpha(opacity=100)}.slider .slider-handle.active,.slider .slider-handle.disabled,.slider .slider-handle:active,.slider .slider-handle:focus,.slider .slider-handle:hover,.slider .slider-handle[disabled]{color:#333;background-color:#e6e6e6;*background-color:#d9d9d9}.slider .slider-handle.active,.slider .slider-handle:active{background-color:#ccc \9}.slider .slider-handle:first-child{*margin-left:0}.slider .slider-handle:focus,.slider .slider-handle:hover{color:#333;text-decoration:none;background-position:0 -15px;-webkit-transition:background-position .1s linear;-moz-transition:background-position .1s linear;-o-transition:background-position .1s linear;transition:background-position .1s linear}.slider .slider-handle:focus{outline:#333 dotted thin;outline:-webkit-focus-ring-color auto 5px;outline-offset:-2px}.slider .slider-handle.active,.slider .slider-handle:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,.15),0 1px 2px rgba(0,0,0,.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,.15),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 2px 4px rgba(0,0,0,.15),0 1px 2px rgba(0,0,0,.05)}.slider .slider-handle.disabled,.slider .slider-handle[disabled]{cursor:default;background-image:none;opacity:.65;filter:alpha(opacity=65);-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.slider .slider-handle.hide{display:none}.slider .slider-handle.round{-webkit-border-radius:50%;-moz-border-radius:50%;border-radius:50%}.modal.large{width:975px;margin-left:-487px}.full-sized-box{position:absolute;bottom:0;left:0;right:0;top:0;padding:15px}.full-sized-box .row-fluid{height:100%}@media (max-width:979px){.full-sized-box{position:static}}:root .full-sized-box,_::-webkit-full-page-media,_:future{position:static}.scrollable{height:100%;overflow:auto;-webkit-overflow-scrolling:touch}.pre-output span{display:block}.input-append .add-on.add-on-limited,.input-prepend .add-on.add-on-limited{overflow-x:hidden;text-overflow:ellipsis;width:inherit}.input-append .btn-group:first-child .btn:first-child,.input-prepend .btn-group:first-child .btn:first-child{-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.input-append .btn-group .btn:first-child,.input-prepend .btn-group .btn:first-child{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.input-append.input-block-level,.input-prepend.input-block-level{display:table}.input-append.input-block-level .add-on,.input-prepend.input-block-level .add-on{display:table-cell;width:1%}.input-append.input-block-level>input,.input-prepend.input-block-level>input{box-sizing:border-box;display:table;min-height:inherit;width:100%}.input-append.input-block-level :not(:last-child),.input-prepend.input-block-level :not(:last-child){border-right:0}.control-group.error .input-append .fileinput-button,.control-group.error .input-prepend .fileinput-button{border-color:#b94a48}.control-text{padding-top:5px;cursor:default}input[type=number]{text-align:right}input[type=number].input-nospin::-webkit-inner-spin-button,input[type=number].input-nospin::-webkit-outer-spin-button{-webkit-appearance:none;margin:0}input[type=number].input-nospin{-moz-appearance:textfield}.progress-text,.progress-text-centered{position:relative}.progress-text .progress-text-back,.progress-text .progress-text-front,.progress-text-centered .progress-text-back,.progress-text-centered .progress-text-front{white-space:nowrap}.progress-text .progress-text-front,.progress-text-centered .progress-text-front{box-sizing:border-box;padding:0 10px;width:100%;display:block}.progress-text .progress-text-back,.progress-text-centered .progress-text-back{position:absolute;font-size:12px;line-height:20px;display:block;box-sizing:border-box;text-align:center;padding:0 10px}.progress-text .bar,.progress-text-centered .bar{position:absolute;overflow:hidden}.progress-text-centered .progress-text-front{position:absolute;font-size:12px;line-height:20px;display:block;text-align:center;color:#fff}.progress-text-centered .progress-text-back{width:100%}#navbar_login:not(.open) #login_dropdown_loggedout{display:block;z-index:-1;height:0;width:0;padding:0!important;overflow:hidden;border:0;box-shadow:none;left:-9999px}#navbar_login:not(.open) #login_dropdown_loggedout.hide{display:none}#loginForm{margin:0}#loginForm button{margin-top:20px} \ No newline at end of file +.btn{display:inline-block;*display:inline;*zoom:1;padding:4px 12px;margin-bottom:0;font-size:14px;line-height:20px;text-align:center;vertical-align:middle;cursor:pointer;color:#333;text-shadow:0 1px 1px rgba(255,255,255,.75);background-color:#f5f5f5;background-image:-moz-linear-gradient(top,#fff,#e6e6e6);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#e6e6e6));background-image:-webkit-linear-gradient(top,#fff,#e6e6e6);background-image:-o-linear-gradient(top,#fff,#e6e6e6);background-image:linear-gradient(to bottom,#fff,#e6e6e6);background-repeat:repeat-x;*background-color:#e6e6e6;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);border:1px solid #ccc;*border:0;border-bottom-color:#b3b3b3;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;*margin-left:.3em;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05)}.btn.active,.btn.disabled,.btn:active,.btn:focus,.btn:hover,.btn[disabled]{color:#333;background-color:#e6e6e6;*background-color:#d9d9d9}.btn.active,.btn:active{background-color:#ccc \9}.btn:first-child{*margin-left:0}.btn:focus,.btn:hover{color:#333;text-decoration:none;background-position:0 -15px;-webkit-transition:background-position .1s linear;-moz-transition:background-position .1s linear;-o-transition:background-position .1s linear;transition:background-position .1s linear}.btn:focus{outline:#333 dotted thin;outline:-webkit-focus-ring-color auto 5px;outline-offset:-2px}.btn.active,.btn:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,.15),0 1px 2px rgba(0,0,0,.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,.15),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 2px 4px rgba(0,0,0,.15),0 1px 2px rgba(0,0,0,.05)}.btn.disabled,.btn[disabled]{cursor:default;background-image:none;opacity:.65;filter:alpha(opacity=65);-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.btn-large{padding:11px 19px;font-size:17.5px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.btn-large [class*=" icon-"],.btn-large [class^=icon-]{margin-top:4px}.btn-small{padding:2px 10px;font-size:11.9px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.btn-small [class*=" icon-"],.btn-small [class^=icon-]{margin-top:0}.btn-mini [class*=" icon-"],.btn-mini [class^=icon-]{margin-top:-1px}.btn-mini{padding:0 6px;font-size:10.5px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.btn-block{display:block;width:100%;padding-left:0;padding-right:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.btn-block+.btn-block{margin-top:5px}input[type=submit].btn-block,input[type=reset].btn-block,input[type=button].btn-block{width:100%}.btn-primary{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,.25);background-color:#006dcc;background-image:-moz-linear-gradient(top,#08c,#04c);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#04c));background-image:-webkit-linear-gradient(top,#08c,#04c);background-image:-o-linear-gradient(top,#08c,#04c);background-image:linear-gradient(to bottom,#08c,#04c);background-repeat:repeat-x;border-color:#04c #04c #002a80;border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);*background-color:#04c;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-primary.active,.btn-primary.disabled,.btn-primary:active,.btn-primary:focus,.btn-primary:hover,.btn-primary[disabled]{color:#fff;background-color:#04c;*background-color:#003bb3}.btn-primary.active,.btn-primary:active{background-color:#039 \9}.btn-warning{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,.25);background-color:#faa732;background-image:-moz-linear-gradient(top,#fbb450,#f89406);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fbb450),to(#f89406));background-image:-webkit-linear-gradient(top,#fbb450,#f89406);background-image:-o-linear-gradient(top,#fbb450,#f89406);background-image:linear-gradient(to bottom,#fbb450,#f89406);background-repeat:repeat-x;border-color:#f89406 #f89406 #ad6704;border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);*background-color:#f89406;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-warning.active,.btn-warning.disabled,.btn-warning:active,.btn-warning:focus,.btn-warning:hover,.btn-warning[disabled]{color:#fff;background-color:#f89406;*background-color:#df8505}.btn-warning.active,.btn-warning:active{background-color:#c67605 \9}.btn-danger{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,.25);background-color:#da4f49;background-image:-moz-linear-gradient(top,#ee5f5b,#bd362f);background-image:-webkit-gradient(linear,0 0,0 100%,from(#ee5f5b),to(#bd362f));background-image:-webkit-linear-gradient(top,#ee5f5b,#bd362f);background-image:-o-linear-gradient(top,#ee5f5b,#bd362f);background-image:linear-gradient(to bottom,#ee5f5b,#bd362f);background-repeat:repeat-x;border-color:#bd362f #bd362f #802420;border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);*background-color:#bd362f;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-danger.active,.btn-danger.disabled,.btn-danger:active,.btn-danger:focus,.btn-danger:hover,.btn-danger[disabled]{color:#fff;background-color:#bd362f;*background-color:#a9302a}.btn-danger.active,.btn-danger:active{background-color:#942a25 \9}.btn-success{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,.25);background-color:#5bb75b;background-image:-moz-linear-gradient(top,#62c462,#51a351);background-image:-webkit-gradient(linear,0 0,0 100%,from(#62c462),to(#51a351));background-image:-webkit-linear-gradient(top,#62c462,#51a351);background-image:-o-linear-gradient(top,#62c462,#51a351);background-image:linear-gradient(to bottom,#62c462,#51a351);background-repeat:repeat-x;border-color:#51a351 #51a351 #387038;border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);*background-color:#51a351;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-success.active,.btn-success.disabled,.btn-success:active,.btn-success:focus,.btn-success:hover,.btn-success[disabled]{color:#fff;background-color:#51a351;*background-color:#499249}.btn-success.active,.btn-success:active{background-color:#408140 \9}.btn-info{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,.25);background-color:#49afcd;background-image:-moz-linear-gradient(top,#5bc0de,#2f96b4);background-image:-webkit-gradient(linear,0 0,0 100%,from(#5bc0de),to(#2f96b4));background-image:-webkit-linear-gradient(top,#5bc0de,#2f96b4);background-image:-o-linear-gradient(top,#5bc0de,#2f96b4);background-image:linear-gradient(to bottom,#5bc0de,#2f96b4);background-repeat:repeat-x;border-color:#2f96b4 #2f96b4 #1f6377;border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);*background-color:#2f96b4;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-info.active,.btn-info.disabled,.btn-info:active,.btn-info:focus,.btn-info:hover,.btn-info[disabled]{color:#fff;background-color:#2f96b4;*background-color:#2a85a0}.btn-info.active,.btn-info:active{background-color:#24748c \9}.btn-inverse{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,.25);background-color:#363636;background-image:-moz-linear-gradient(top,#444,#222);background-image:-webkit-gradient(linear,0 0,0 100%,from(#444),to(#222));background-image:-webkit-linear-gradient(top,#444,#222);background-image:-o-linear-gradient(top,#444,#222);background-image:linear-gradient(to bottom,#444,#222);background-repeat:repeat-x;border-color:#222 #222 #000;border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);*background-color:#222;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-inverse.active,.btn-inverse.disabled,.btn-inverse:active,.btn-inverse:focus,.btn-inverse:hover,.btn-inverse[disabled]{color:#fff;background-color:#222;*background-color:#151515}.btn-inverse.active,.btn-inverse:active{background-color:#080808 \9}button.btn,input[type=submit].btn{*padding-top:3px;*padding-bottom:3px}button.btn::-moz-focus-inner,input[type=submit].btn::-moz-focus-inner{padding:0;border:0}button.btn.btn-large,input[type=submit].btn.btn-large{*padding-top:7px;*padding-bottom:7px}button.btn.btn-small,input[type=submit].btn.btn-small{*padding-top:3px;*padding-bottom:3px}button.btn.btn-mini,input[type=submit].btn.btn-mini{*padding-top:1px;*padding-bottom:1px}.btn-link,.btn-link:active,.btn-link[disabled]{background-color:transparent;background-image:none;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.btn-link{border-color:transparent;cursor:pointer;color:#08c;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.btn-link:focus,.btn-link:hover{color:#005580;text-decoration:underline;background-color:transparent}.btn-link[disabled]:focus,.btn-link[disabled]:hover{color:#333;text-decoration:none}.clearfix{*zoom:1}.clearfix:after,.clearfix:before{display:table;content:"";line-height:0}.clearfix:after{clear:both}.hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.input-block-level{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.nowrap{white-space:nowrap}.actioncol{text-align:center;white-space:nowrap}.actioncol a{text-decoration:none;color:#000}.actioncol a.disabled{color:#ccc;cursor:default}#navbar .navbar-inner{background-color:#ebebeb;background-image:-moz-linear-gradient(top,#fff,#ccc);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#ccc));background-image:-webkit-linear-gradient(top,#fff,#ccc);background-image:-o-linear-gradient(top,#fff,#ccc);background-image:linear-gradient(to bottom,#fff,#ccc);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffcccccc', GradientType=0)}#navbar .navbar-inner .brand,#navbar .navbar-inner .nav>li>a{text-shadow:0 1px 0 #ccc;color:#333}#navbar .navbar-inner .brand .caret,#navbar .navbar-inner .nav>li>a .caret{border-bottom-color:#939393;border-top-color:#939393}#navbar .navbar-inner .brand:focus .caret,#navbar .navbar-inner .brand:hover .caret,#navbar .navbar-inner .nav>li>a:focus .caret,#navbar .navbar-inner .nav>li>a:hover .caret{border-bottom-color:#636363;border-top-color:#636363}#navbar .navbar-inner .brand span{background-image:url(../img/tentacle-20x20.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner .brand span{background-image:url(../img/tentacle-20x20@2x.png)}}#navbar .navbar-inner .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner .nav>li.dropdown.open>.dropdown-toggle{background-color:#e0e0e0;background-image:-moz-linear-gradient(top,#ccc,#fff);background-image:-webkit-gradient(linear,0 0,0 100%,from(#ccc),to(#fff));background-image:-webkit-linear-gradient(top,#ccc,#fff);background-image:-o-linear-gradient(top,#ccc,#fff);background-image:linear-gradient(to bottom,#ccc,#fff);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffcccccc', endColorstr='#ffffffff', GradientType=0)}#navbar .navbar-inner .nav>li>a:hover{background-color:#dedede;background-image:-moz-linear-gradient(top,#f2f2f2,#bfbfbf);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f2f2f2),to(#bfbfbf));background-image:-webkit-linear-gradient(top,#f2f2f2,#bfbfbf);background-image:-o-linear-gradient(top,#f2f2f2,#bfbfbf);background-image:linear-gradient(to bottom,#f2f2f2,#bfbfbf);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2f2f2', endColorstr='#ffbfbfbf', GradientType=0)}#navbar .navbar-inner.transparent{background-color:rgba(235,235,235,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(255,255,255,.6),rgba(204,204,204,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(255,255,255,.6)),to(rgba(204,204,204,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(255,255,255,.6),rgba(204,204,204,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(255,255,255,.6),rgba(204,204,204,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(255,255,255,.6),rgba(204,204,204,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99ffffff', endColorstr='#99cccccc', GradientType=0)}#navbar .navbar-inner.transparent .brand,#navbar .navbar-inner.transparent .nav>li>a{text-shadow:0 1px 0 #ccc;color:#333}#navbar .navbar-inner.transparent .brand .caret,#navbar .navbar-inner.transparent .nav>li>a .caret{border-bottom-color:#939393;border-top-color:#939393}#navbar .navbar-inner.transparent .brand:focus .caret,#navbar .navbar-inner.transparent .brand:hover .caret,#navbar .navbar-inner.transparent .nav>li>a:focus .caret,#navbar .navbar-inner.transparent .nav>li>a:hover .caret{border-bottom-color:#636363;border-top-color:#636363}#navbar .navbar-inner.transparent .brand span{background-image:url(../img/tentacle-20x20.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.transparent .brand span{background-image:url(../img/tentacle-20x20@2x.png)}}#navbar .navbar-inner.transparent .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.transparent .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.transparent .nav>li.dropdown.open>.dropdown-toggle{background-color:rgba(224,224,224,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(204,204,204,.6),rgba(255,255,255,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(204,204,204,.6)),to(rgba(255,255,255,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(204,204,204,.6),rgba(255,255,255,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(204,204,204,.6),rgba(255,255,255,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(204,204,204,.6),rgba(255,255,255,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99cccccc', endColorstr='#99ffffff', GradientType=0)}#navbar .navbar-inner.transparent .nav>li>a:hover{background-color:rgba(222,222,222,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(242,242,242,.6),rgba(191,191,191,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(242,242,242,.6)),to(rgba(191,191,191,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(242,242,242,.6),rgba(191,191,191,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(242,242,242,.6),rgba(191,191,191,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(242,242,242,.6),rgba(191,191,191,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99f2f2f2', endColorstr='#99bfbfbf', GradientType=0)}#navbar .navbar-inner.red{background-color:#bb645f;background-image:-moz-linear-gradient(top,#e28e8a,#802420);background-image:-webkit-gradient(linear,0 0,0 100%,from(#e28e8a),to(#802420));background-image:-webkit-linear-gradient(top,#e28e8a,#802420);background-image:-o-linear-gradient(top,#e28e8a,#802420);background-image:linear-gradient(to bottom,#e28e8a,#802420);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe28e8a', endColorstr='#ff802420', GradientType=0)}#navbar .navbar-inner.red .brand,#navbar .navbar-inner.red .nav>li>a{text-shadow:0 1px 0 #d86761;color:#f2f2f2}#navbar .navbar-inner.red .brand .caret,#navbar .navbar-inner.red .nav>li>a .caret{border-bottom-color:#d89491;border-top-color:#d89491}#navbar .navbar-inner.red .brand:focus .caret,#navbar .navbar-inner.red .brand:hover .caret,#navbar .navbar-inner.red .nav>li>a:focus .caret,#navbar .navbar-inner.red .nav>li>a:hover .caret{border-bottom-color:#e5c3c1;border-top-color:#e5c3c1}#navbar .navbar-inner.red .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.red .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.red .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.red .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.red .nav>li.dropdown.open>.dropdown-toggle{background-color:#a74f4a;background-image:-moz-linear-gradient(top,#802420,#e28e8a);background-image:-webkit-gradient(linear,0 0,0 100%,from(#802420),to(#e28e8a));background-image:-webkit-linear-gradient(top,#802420,#e28e8a);background-image:-o-linear-gradient(top,#802420,#e28e8a);background-image:linear-gradient(to bottom,#802420,#e28e8a);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff802420', endColorstr='#ffe28e8a', GradientType=0)}#navbar .navbar-inner.red .nav>li>a:hover{background-color:#af5651;background-image:-moz-linear-gradient(top,#dd7a75,#6b1f1b);background-image:-webkit-gradient(linear,0 0,0 100%,from(#dd7a75),to(#6b1f1b));background-image:-webkit-linear-gradient(top,#dd7a75,#6b1f1b);background-image:-o-linear-gradient(top,#dd7a75,#6b1f1b);background-image:linear-gradient(to bottom,#dd7a75,#6b1f1b);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdd7a75', endColorstr='#ff6b1f1b', GradientType=0)}#navbar .navbar-inner.red.transparent{background-color:rgba(187,100,95,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(226,142,138,.6),rgba(128,36,32,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(226,142,138,.6)),to(rgba(128,36,32,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(226,142,138,.6),rgba(128,36,32,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(226,142,138,.6),rgba(128,36,32,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(226,142,138,.6),rgba(128,36,32,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99e28e8a', endColorstr='#99802420', GradientType=0)}#navbar .navbar-inner.red.transparent .brand,#navbar .navbar-inner.red.transparent .nav>li>a{text-shadow:0 1px 0 #d86761;color:#f2f2f2}#navbar .navbar-inner.red.transparent .brand .caret,#navbar .navbar-inner.red.transparent .nav>li>a .caret{border-bottom-color:#d89491;border-top-color:#d89491}#navbar .navbar-inner.red.transparent .brand:focus .caret,#navbar .navbar-inner.red.transparent .brand:hover .caret,#navbar .navbar-inner.red.transparent .nav>li>a:focus .caret,#navbar .navbar-inner.red.transparent .nav>li>a:hover .caret{border-bottom-color:#e5c3c1;border-top-color:#e5c3c1}#navbar .navbar-inner.red.transparent .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.red.transparent .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.red.transparent .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.red.transparent .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.red.transparent .nav>li.dropdown.open>.dropdown-toggle{background-color:rgba(167,79,74,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(128,36,32,.6),rgba(226,142,138,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(128,36,32,.6)),to(rgba(226,142,138,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(128,36,32,.6),rgba(226,142,138,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(128,36,32,.6),rgba(226,142,138,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(128,36,32,.6),rgba(226,142,138,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99802420', endColorstr='#99e28e8a', GradientType=0)}#navbar .navbar-inner.red.transparent .nav>li>a:hover{background-color:rgba(175,86,81,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(221,122,117,.6),rgba(107,31,27,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(221,122,117,.6)),to(rgba(107,31,27,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(221,122,117,.6),rgba(107,31,27,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(221,122,117,.6),rgba(107,31,27,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(221,122,117,.6),rgba(107,31,27,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99dd7a75', endColorstr='#996b1f1b', GradientType=0)}#navbar .navbar-inner.orange{background-color:#e39665;background-image:-moz-linear-gradient(top,#f9c3a0,#c2530c);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f9c3a0),to(#c2530c));background-image:-webkit-linear-gradient(top,#f9c3a0,#c2530c);background-image:-o-linear-gradient(top,#f9c3a0,#c2530c);background-image:linear-gradient(to bottom,#f9c3a0,#c2530c);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff9c3a0', endColorstr='#ffc2530c', GradientType=0)}#navbar .navbar-inner.orange .brand,#navbar .navbar-inner.orange .nav>li>a{text-shadow:0 1px 0 #f6a570;color:#f2f2f2}#navbar .navbar-inner.orange .brand .caret,#navbar .navbar-inner.orange .nav>li>a .caret{border-bottom-color:#f2b58d;border-top-color:#f2b58d}#navbar .navbar-inner.orange .brand:focus .caret,#navbar .navbar-inner.orange .brand:hover .caret,#navbar .navbar-inner.orange .nav>li>a:focus .caret,#navbar .navbar-inner.orange .nav>li>a:hover .caret{border-bottom-color:#f2d3bf;border-top-color:#f2d3bf}#navbar .navbar-inner.orange .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.orange .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.orange .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.orange .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.orange .nav>li.dropdown.open>.dropdown-toggle{background-color:#d88047;background-image:-moz-linear-gradient(top,#c2530c,#f9c3a0);background-image:-webkit-gradient(linear,0 0,0 100%,from(#c2530c),to(#f9c3a0));background-image:-webkit-linear-gradient(top,#c2530c,#f9c3a0);background-image:-o-linear-gradient(top,#c2530c,#f9c3a0);background-image:linear-gradient(to bottom,#c2530c,#f9c3a0);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffc2530c', endColorstr='#fff9c3a0', GradientType=0)}#navbar .navbar-inner.orange .nav>li>a:hover{background-color:#d98956;background-image:-moz-linear-gradient(top,#f8b488,#aa490a);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f8b488),to(#aa490a));background-image:-webkit-linear-gradient(top,#f8b488,#aa490a);background-image:-o-linear-gradient(top,#f8b488,#aa490a);background-image:linear-gradient(to bottom,#f8b488,#aa490a);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff8b488', endColorstr='#ffaa490a', GradientType=0)}#navbar .navbar-inner.orange.transparent{background-color:rgba(227,150,101,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(249,195,160,.6),rgba(194,83,12,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(249,195,160,.6)),to(rgba(194,83,12,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(249,195,160,.6),rgba(194,83,12,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(249,195,160,.6),rgba(194,83,12,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(249,195,160,.6),rgba(194,83,12,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99f9c3a0', endColorstr='#99c2530c', GradientType=0)}#navbar .navbar-inner.orange.transparent .brand,#navbar .navbar-inner.orange.transparent .nav>li>a{text-shadow:0 1px 0 #f6a570;color:#f2f2f2}#navbar .navbar-inner.orange.transparent .brand .caret,#navbar .navbar-inner.orange.transparent .nav>li>a .caret{border-bottom-color:#f2b58d;border-top-color:#f2b58d}#navbar .navbar-inner.orange.transparent .brand:focus .caret,#navbar .navbar-inner.orange.transparent .brand:hover .caret,#navbar .navbar-inner.orange.transparent .nav>li>a:focus .caret,#navbar .navbar-inner.orange.transparent .nav>li>a:hover .caret{border-bottom-color:#f2d3bf;border-top-color:#f2d3bf}#navbar .navbar-inner.orange.transparent .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.orange.transparent .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.orange.transparent .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.orange.transparent .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.orange.transparent .nav>li.dropdown.open>.dropdown-toggle{background-color:rgba(216,128,71,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(194,83,12,.6),rgba(249,195,160,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(194,83,12,.6)),to(rgba(249,195,160,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(194,83,12,.6),rgba(249,195,160,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(194,83,12,.6),rgba(249,195,160,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(194,83,12,.6),rgba(249,195,160,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99c2530c', endColorstr='#99f9c3a0', GradientType=0)}#navbar .navbar-inner.orange.transparent .nav>li>a:hover{background-color:rgba(217,137,86,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(248,180,136,.6),rgba(170,73,10,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(248,180,136,.6)),to(rgba(170,73,10,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(248,180,136,.6),rgba(170,73,10,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(248,180,136,.6),rgba(170,73,10,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(248,180,136,.6),rgba(170,73,10,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99f8b488', endColorstr='#99aa490a', GradientType=0)}#navbar .navbar-inner.yellow{background-color:#e3d765;background-image:-moz-linear-gradient(top,#f9f0a0,#c2b00c);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f9f0a0),to(#c2b00c));background-image:-webkit-linear-gradient(top,#f9f0a0,#c2b00c);background-image:-o-linear-gradient(top,#f9f0a0,#c2b00c);background-image:linear-gradient(to bottom,#f9f0a0,#c2b00c);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff9f0a0', endColorstr='#ffc2b00c', GradientType=0)}#navbar .navbar-inner.yellow .brand,#navbar .navbar-inner.yellow .nav>li>a{text-shadow:0 1px 0 #c2b00c;color:#f2f2f2}#navbar .navbar-inner.yellow .brand .caret,#navbar .navbar-inner.yellow .nav>li>a .caret{border-bottom-color:#f2e88d;border-top-color:#f2e88d}#navbar .navbar-inner.yellow .brand:focus .caret,#navbar .navbar-inner.yellow .brand:hover .caret,#navbar .navbar-inner.yellow .nav>li>a:focus .caret,#navbar .navbar-inner.yellow .nav>li>a:hover .caret{border-bottom-color:#f2edbf;border-top-color:#f2edbf}#navbar .navbar-inner.yellow .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.yellow .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.yellow .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.yellow .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.yellow .nav>li.dropdown.open>.dropdown-toggle{background-color:#d8ca47;background-image:-moz-linear-gradient(top,#c2b00c,#f9f0a0);background-image:-webkit-gradient(linear,0 0,0 100%,from(#c2b00c),to(#f9f0a0));background-image:-webkit-linear-gradient(top,#c2b00c,#f9f0a0);background-image:-o-linear-gradient(top,#c2b00c,#f9f0a0);background-image:linear-gradient(to bottom,#c2b00c,#f9f0a0);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffc2b00c', endColorstr='#fff9f0a0', GradientType=0)}#navbar .navbar-inner.yellow .nav>li>a:hover{background-color:#d9cc56;background-image:-moz-linear-gradient(top,#f8ed88,#aa9a0a);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f8ed88),to(#aa9a0a));background-image:-webkit-linear-gradient(top,#f8ed88,#aa9a0a);background-image:-o-linear-gradient(top,#f8ed88,#aa9a0a);background-image:linear-gradient(to bottom,#f8ed88,#aa9a0a);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff8ed88', endColorstr='#ffaa9a0a', GradientType=0)}#navbar .navbar-inner.yellow.transparent{background-color:rgba(227,215,101,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(249,240,160,.6),rgba(194,176,12,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(249,240,160,.6)),to(rgba(194,176,12,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(249,240,160,.6),rgba(194,176,12,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(249,240,160,.6),rgba(194,176,12,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(249,240,160,.6),rgba(194,176,12,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99f9f0a0', endColorstr='#99c2b00c', GradientType=0)}#navbar .navbar-inner.yellow.transparent .brand,#navbar .navbar-inner.yellow.transparent .nav>li>a{text-shadow:0 1px 0 #c2b00c;color:#f2f2f2}#navbar .navbar-inner.yellow.transparent .brand .caret,#navbar .navbar-inner.yellow.transparent .nav>li>a .caret{border-bottom-color:#f2e88d;border-top-color:#f2e88d}#navbar .navbar-inner.yellow.transparent .brand:focus .caret,#navbar .navbar-inner.yellow.transparent .brand:hover .caret,#navbar .navbar-inner.yellow.transparent .nav>li>a:focus .caret,#navbar .navbar-inner.yellow.transparent .nav>li>a:hover .caret{border-bottom-color:#f2edbf;border-top-color:#f2edbf}#navbar .navbar-inner.yellow.transparent .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.yellow.transparent .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.yellow.transparent .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.yellow.transparent .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.yellow.transparent .nav>li.dropdown.open>.dropdown-toggle{background-color:rgba(216,202,71,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(194,176,12,.6),rgba(249,240,160,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(194,176,12,.6)),to(rgba(249,240,160,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(194,176,12,.6),rgba(249,240,160,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(194,176,12,.6),rgba(249,240,160,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(194,176,12,.6),rgba(249,240,160,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99c2b00c', endColorstr='#99f9f0a0', GradientType=0)}#navbar .navbar-inner.yellow.transparent .nav>li>a:hover{background-color:rgba(217,204,86,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(248,237,136,.6),rgba(170,154,10,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(248,237,136,.6)),to(rgba(170,154,10,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(248,237,136,.6),rgba(170,154,10,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(248,237,136,.6),rgba(170,154,10,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(248,237,136,.6),rgba(170,154,10,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99f8ed88', endColorstr='#99aa9a0a', GradientType=0)}#navbar .navbar-inner.green{background-color:#98f064;background-image:-moz-linear-gradient(top,#c8ffa7,#50da00);background-image:-webkit-gradient(linear,0 0,0 100%,from(#c8ffa7),to(#50da00));background-image:-webkit-linear-gradient(top,#c8ffa7,#50da00);background-image:-o-linear-gradient(top,#c8ffa7,#50da00);background-image:linear-gradient(to bottom,#c8ffa7,#50da00);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffc8ffa7', endColorstr='#ff50da00', GradientType=0)}#navbar .navbar-inner.green .brand,#navbar .navbar-inner.green .nav>li>a{text-shadow:0 1px 0 #50da00;color:#333}#navbar .navbar-inner.green .brand .caret,#navbar .navbar-inner.green .nav>li>a .caret{border-bottom-color:#55992e;border-top-color:#55992e}#navbar .navbar-inner.green .brand:focus .caret,#navbar .navbar-inner.green .brand:hover .caret,#navbar .navbar-inner.green .nav>li>a:focus .caret,#navbar .navbar-inner.green .nav>li>a:hover .caret{border-bottom-color:#446630;border-top-color:#446630}#navbar .navbar-inner.green .brand span{background-image:url(../img/tentacle-20x20.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.green .brand span{background-image:url(../img/tentacle-20x20@2x.png)}}#navbar .navbar-inner.green .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.green .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.green .nav>li.dropdown.open>.dropdown-toggle{background-color:#80e943;background-image:-moz-linear-gradient(top,#50da00,#c8ffa7);background-image:-webkit-gradient(linear,0 0,0 100%,from(#50da00),to(#c8ffa7));background-image:-webkit-linear-gradient(top,#50da00,#c8ffa7);background-image:-o-linear-gradient(top,#50da00,#c8ffa7);background-image:linear-gradient(to bottom,#50da00,#c8ffa7);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff50da00', endColorstr='#ffc8ffa7', GradientType=0)}#navbar .navbar-inner.green .nav>li>a:hover{background-color:#8ae655;background-image:-moz-linear-gradient(top,#b8ff8e,#47c100);background-image:-webkit-gradient(linear,0 0,0 100%,from(#b8ff8e),to(#47c100));background-image:-webkit-linear-gradient(top,#b8ff8e,#47c100);background-image:-o-linear-gradient(top,#b8ff8e,#47c100);background-image:linear-gradient(to bottom,#b8ff8e,#47c100);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffb8ff8e', endColorstr='#ff47c100', GradientType=0)}#navbar .navbar-inner.green.transparent{background-color:rgba(152,240,100,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(200,255,167,.6),rgba(80,218,0,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(200,255,167,.6)),to(rgba(80,218,0,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(200,255,167,.6),rgba(80,218,0,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(200,255,167,.6),rgba(80,218,0,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(200,255,167,.6),rgba(80,218,0,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99c8ffa7', endColorstr='#9950da00', GradientType=0)}#navbar .navbar-inner.green.transparent .brand,#navbar .navbar-inner.green.transparent .nav>li>a{text-shadow:0 1px 0 #50da00;color:#333}#navbar .navbar-inner.green.transparent .brand .caret,#navbar .navbar-inner.green.transparent .nav>li>a .caret{border-bottom-color:#55992e;border-top-color:#55992e}#navbar .navbar-inner.green.transparent .brand:focus .caret,#navbar .navbar-inner.green.transparent .brand:hover .caret,#navbar .navbar-inner.green.transparent .nav>li>a:focus .caret,#navbar .navbar-inner.green.transparent .nav>li>a:hover .caret{border-bottom-color:#446630;border-top-color:#446630}#navbar .navbar-inner.green.transparent .brand span{background-image:url(../img/tentacle-20x20.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.green.transparent .brand span{background-image:url(../img/tentacle-20x20@2x.png)}}#navbar .navbar-inner.green.transparent .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.green.transparent .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.green.transparent .nav>li.dropdown.open>.dropdown-toggle{background-color:rgba(128,233,67,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(80,218,0,.6),rgba(200,255,167,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(80,218,0,.6)),to(rgba(200,255,167,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(80,218,0,.6),rgba(200,255,167,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(80,218,0,.6),rgba(200,255,167,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(80,218,0,.6),rgba(200,255,167,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#9950da00', endColorstr='#99c8ffa7', GradientType=0)}#navbar .navbar-inner.green.transparent .nav>li>a:hover{background-color:rgba(138,230,85,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(184,255,142,.6),rgba(71,193,0,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(184,255,142,.6)),to(rgba(71,193,0,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(184,255,142,.6),rgba(71,193,0,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(184,255,142,.6),rgba(71,193,0,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(184,255,142,.6),rgba(71,193,0,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99b8ff8e', endColorstr='#9947c100', GradientType=0)}#navbar .navbar-inner.blue{background-color:#2e63cc;background-image:-moz-linear-gradient(top,#4d88ff,#002b80);background-image:-webkit-gradient(linear,0 0,0 100%,from(#4d88ff),to(#002b80));background-image:-webkit-linear-gradient(top,#4d88ff,#002b80);background-image:-o-linear-gradient(top,#4d88ff,#002b80);background-image:linear-gradient(to bottom,#4d88ff,#002b80);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff4d88ff', endColorstr='#ff002b80', GradientType=0)}#navbar .navbar-inner.blue .brand,#navbar .navbar-inner.blue .nav>li>a{text-shadow:0 1px 0 #1a66ff;color:#f2f2f2}#navbar .navbar-inner.blue .brand .caret,#navbar .navbar-inner.blue .nav>li>a .caret{border-bottom-color:#799bdf;border-top-color:#799bdf}#navbar .navbar-inner.blue .brand:focus .caret,#navbar .navbar-inner.blue .brand:hover .caret,#navbar .navbar-inner.blue .nav>li>a:focus .caret,#navbar .navbar-inner.blue .nav>li>a:hover .caret{border-bottom-color:#b6c7e9;border-top-color:#b6c7e9}#navbar .navbar-inner.blue .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.blue .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.blue .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.blue .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.blue .nav>li.dropdown.open>.dropdown-toggle{background-color:#1f50b3;background-image:-moz-linear-gradient(top,#002b80,#4d88ff);background-image:-webkit-gradient(linear,0 0,0 100%,from(#002b80),to(#4d88ff));background-image:-webkit-linear-gradient(top,#002b80,#4d88ff);background-image:-o-linear-gradient(top,#002b80,#4d88ff);background-image:linear-gradient(to bottom,#002b80,#4d88ff);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff002b80', endColorstr='#ff4d88ff', GradientType=0)}#navbar .navbar-inner.blue .nav>li>a:hover{background-color:#1f55c2;background-image:-moz-linear-gradient(top,#37f,#026);background-image:-webkit-gradient(linear,0 0,0 100%,from(#37f),to(#026));background-image:-webkit-linear-gradient(top,#37f,#026);background-image:-o-linear-gradient(top,#37f,#026);background-image:linear-gradient(to bottom,#37f,#026);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3377ff', endColorstr='#ff002266', GradientType=0)}#navbar .navbar-inner.blue.transparent{background-color:rgba(46,99,204,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(77,136,255,.6),rgba(0,43,128,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(77,136,255,.6)),to(rgba(0,43,128,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(77,136,255,.6),rgba(0,43,128,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(77,136,255,.6),rgba(0,43,128,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(77,136,255,.6),rgba(0,43,128,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#994d88ff', endColorstr='#99002b80', GradientType=0)}#navbar .navbar-inner.blue.transparent .brand,#navbar .navbar-inner.blue.transparent .nav>li>a{text-shadow:0 1px 0 #1a66ff;color:#f2f2f2}#navbar .navbar-inner.blue.transparent .brand .caret,#navbar .navbar-inner.blue.transparent .nav>li>a .caret{border-bottom-color:#799bdf;border-top-color:#799bdf}#navbar .navbar-inner.blue.transparent .brand:focus .caret,#navbar .navbar-inner.blue.transparent .brand:hover .caret,#navbar .navbar-inner.blue.transparent .nav>li>a:focus .caret,#navbar .navbar-inner.blue.transparent .nav>li>a:hover .caret{border-bottom-color:#b6c7e9;border-top-color:#b6c7e9}#navbar .navbar-inner.blue.transparent .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.blue.transparent .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.blue.transparent .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.blue.transparent .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.blue.transparent .nav>li.dropdown.open>.dropdown-toggle{background-color:rgba(31,80,179,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(0,43,128,.6),rgba(77,136,255,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(0,43,128,.6)),to(rgba(77,136,255,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(0,43,128,.6),rgba(77,136,255,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(0,43,128,.6),rgba(77,136,255,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(0,43,128,.6),rgba(77,136,255,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99002b80', endColorstr='#994d88ff', GradientType=0)}#navbar .navbar-inner.blue.transparent .nav>li>a:hover{background-color:rgba(31,85,194,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(51,119,255,.6),rgba(0,34,102,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(51,119,255,.6)),to(rgba(0,34,102,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(51,119,255,.6),rgba(0,34,102,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(51,119,255,.6),rgba(0,34,102,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(51,119,255,.6),rgba(0,34,102,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#993377ff', endColorstr='#99002266', GradientType=0)}#navbar .navbar-inner.violet{background-color:#9864f0;background-image:-moz-linear-gradient(top,#c8a7ff,#5000da);background-image:-webkit-gradient(linear,0 0,0 100%,from(#c8a7ff),to(#5000da));background-image:-webkit-linear-gradient(top,#c8a7ff,#5000da);background-image:-o-linear-gradient(top,#c8a7ff,#5000da);background-image:linear-gradient(to bottom,#c8a7ff,#5000da);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffc8a7ff', endColorstr='#ff5000da', GradientType=0)}#navbar .navbar-inner.violet .brand,#navbar .navbar-inner.violet .nav>li>a{text-shadow:0 1px 0 #a774ff;color:#f2f2f2}#navbar .navbar-inner.violet .brand .caret,#navbar .navbar-inner.violet .nav>li>a .caret{border-bottom-color:#b58df9;border-top-color:#b58df9}#navbar .navbar-inner.violet .brand:focus .caret,#navbar .navbar-inner.violet .brand:hover .caret,#navbar .navbar-inner.violet .nav>li>a:focus .caret,#navbar .navbar-inner.violet .nav>li>a:hover .caret{border-bottom-color:#d3bff5;border-top-color:#d3bff5}#navbar .navbar-inner.violet .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.violet .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.violet .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.violet .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.violet .nav>li.dropdown.open>.dropdown-toggle{background-color:#8043e9;background-image:-moz-linear-gradient(top,#5000da,#c8a7ff);background-image:-webkit-gradient(linear,0 0,0 100%,from(#5000da),to(#c8a7ff));background-image:-webkit-linear-gradient(top,#5000da,#c8a7ff);background-image:-o-linear-gradient(top,#5000da,#c8a7ff);background-image:linear-gradient(to bottom,#5000da,#c8a7ff);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5000da', endColorstr='#ffc8a7ff', GradientType=0)}#navbar .navbar-inner.violet .nav>li>a:hover{background-color:#8a55e6;background-image:-moz-linear-gradient(top,#b88eff,#4700c1);background-image:-webkit-gradient(linear,0 0,0 100%,from(#b88eff),to(#4700c1));background-image:-webkit-linear-gradient(top,#b88eff,#4700c1);background-image:-o-linear-gradient(top,#b88eff,#4700c1);background-image:linear-gradient(to bottom,#b88eff,#4700c1);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffb88eff', endColorstr='#ff4700c1', GradientType=0)}#navbar .navbar-inner.violet.transparent{background-color:rgba(152,100,240,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(200,167,255,.6),rgba(80,0,218,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(200,167,255,.6)),to(rgba(80,0,218,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(200,167,255,.6),rgba(80,0,218,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(200,167,255,.6),rgba(80,0,218,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(200,167,255,.6),rgba(80,0,218,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99c8a7ff', endColorstr='#995000da', GradientType=0)}#navbar .navbar-inner.violet.transparent .brand,#navbar .navbar-inner.violet.transparent .nav>li>a{text-shadow:0 1px 0 #a774ff;color:#f2f2f2}#navbar .navbar-inner.violet.transparent .brand .caret,#navbar .navbar-inner.violet.transparent .nav>li>a .caret{border-bottom-color:#b58df9;border-top-color:#b58df9}#navbar .navbar-inner.violet.transparent .brand:focus .caret,#navbar .navbar-inner.violet.transparent .brand:hover .caret,#navbar .navbar-inner.violet.transparent .nav>li>a:focus .caret,#navbar .navbar-inner.violet.transparent .nav>li>a:hover .caret{border-bottom-color:#d3bff5;border-top-color:#d3bff5}#navbar .navbar-inner.violet.transparent .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.violet.transparent .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.violet.transparent .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.violet.transparent .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.violet.transparent .nav>li.dropdown.open>.dropdown-toggle{background-color:rgba(128,67,233,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(80,0,218,.6),rgba(200,167,255,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(80,0,218,.6)),to(rgba(200,167,255,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(80,0,218,.6),rgba(200,167,255,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(80,0,218,.6),rgba(200,167,255,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(80,0,218,.6),rgba(200,167,255,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#995000da', endColorstr='#99c8a7ff', GradientType=0)}#navbar .navbar-inner.violet.transparent .nav>li>a:hover{background-color:rgba(138,85,230,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(184,142,255,.6),rgba(71,0,193,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(184,142,255,.6)),to(rgba(71,0,193,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(184,142,255,.6),rgba(71,0,193,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(184,142,255,.6),rgba(71,0,193,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(184,142,255,.6),rgba(71,0,193,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99b88eff', endColorstr='#994700c1', GradientType=0)}#navbar .navbar-inner.black{background-color:#4f4f4f;background-image:-moz-linear-gradient(top,#787878,#121212);background-image:-webkit-gradient(linear,0 0,0 100%,from(#787878),to(#121212));background-image:-webkit-linear-gradient(top,#787878,#121212);background-image:-o-linear-gradient(top,#787878,#121212);background-image:linear-gradient(to bottom,#787878,#121212);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff787878', endColorstr='#ff121212', GradientType=0)}#navbar .navbar-inner.black .brand,#navbar .navbar-inner.black .nav>li>a{text-shadow:0 1px 0 #5e5e5e;color:#f2f2f2}#navbar .navbar-inner.black .brand .caret,#navbar .navbar-inner.black .nav>li>a .caret{border-bottom-color:#959595;border-top-color:#959595}#navbar .navbar-inner.black .brand:focus .caret,#navbar .navbar-inner.black .brand:hover .caret,#navbar .navbar-inner.black .nav>li>a:focus .caret,#navbar .navbar-inner.black .nav>li>a:hover .caret{border-bottom-color:#c4c4c4;border-top-color:#c4c4c4}#navbar .navbar-inner.black .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.black .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.black .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.black .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.black .nav>li.dropdown.open>.dropdown-toggle{background-color:#3b3b3b;background-image:-moz-linear-gradient(top,#121212,#787878);background-image:-webkit-gradient(linear,0 0,0 100%,from(#121212),to(#787878));background-image:-webkit-linear-gradient(top,#121212,#787878);background-image:-o-linear-gradient(top,#121212,#787878);background-image:linear-gradient(to bottom,#121212,#787878);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff121212', endColorstr='#ff787878', GradientType=0)}#navbar .navbar-inner.black .nav>li>a:hover{background-color:#424242;background-image:-moz-linear-gradient(top,#6b6b6b,#050505);background-image:-webkit-gradient(linear,0 0,0 100%,from(#6b6b6b),to(#050505));background-image:-webkit-linear-gradient(top,#6b6b6b,#050505);background-image:-o-linear-gradient(top,#6b6b6b,#050505);background-image:linear-gradient(to bottom,#6b6b6b,#050505);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff6b6b6b', endColorstr='#ff050505', GradientType=0)}#navbar .navbar-inner.black.transparent{background-color:rgba(79,79,79,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(120,120,120,.6),rgba(18,18,18,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(120,120,120,.6)),to(rgba(18,18,18,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(120,120,120,.6),rgba(18,18,18,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(120,120,120,.6),rgba(18,18,18,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(120,120,120,.6),rgba(18,18,18,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99787878', endColorstr='#99121212', GradientType=0)}#navbar .navbar-inner.black.transparent .brand,#navbar .navbar-inner.black.transparent .nav>li>a{text-shadow:0 1px 0 #5e5e5e;color:#f2f2f2}#navbar .navbar-inner.black.transparent .brand .caret,#navbar .navbar-inner.black.transparent .nav>li>a .caret{border-bottom-color:#959595;border-top-color:#959595}#navbar .navbar-inner.black.transparent .brand:focus .caret,#navbar .navbar-inner.black.transparent .brand:hover .caret,#navbar .navbar-inner.black.transparent .nav>li>a:focus .caret,#navbar .navbar-inner.black.transparent .nav>li>a:hover .caret{border-bottom-color:#c4c4c4;border-top-color:#c4c4c4}#navbar .navbar-inner.black.transparent .brand span{background-image:url(../img/tentacle-20x20-light.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.black.transparent .brand span{background-image:url(../img/tentacle-20x20-light@2x.png)}}#navbar .navbar-inner.black.transparent .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.black.transparent .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.black.transparent .nav>li.dropdown.open>.dropdown-toggle{background-color:rgba(59,59,59,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(18,18,18,.6),rgba(120,120,120,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(18,18,18,.6)),to(rgba(120,120,120,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(18,18,18,.6),rgba(120,120,120,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(18,18,18,.6),rgba(120,120,120,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(18,18,18,.6),rgba(120,120,120,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99121212', endColorstr='#99787878', GradientType=0)}#navbar .navbar-inner.black.transparent .nav>li>a:hover{background-color:rgba(66,66,66,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(107,107,107,.6),rgba(5,5,5,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(107,107,107,.6)),to(rgba(5,5,5,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(107,107,107,.6),rgba(5,5,5,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(107,107,107,.6),rgba(5,5,5,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(107,107,107,.6),rgba(5,5,5,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#996b6b6b', endColorstr='#99050505', GradientType=0)}#navbar .navbar-inner.white{background-color:#e9e9e9;background-image:-moz-linear-gradient(top,#fff,#c8c8c8);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#c8c8c8));background-image:-webkit-linear-gradient(top,#fff,#c8c8c8);background-image:-o-linear-gradient(top,#fff,#c8c8c8);background-image:linear-gradient(to bottom,#fff,#c8c8c8);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffc8c8c8', GradientType=0)}#navbar .navbar-inner.white .brand,#navbar .navbar-inner.white .nav>li>a{text-shadow:0 1px 0 #c8c8c8;color:#333}#navbar .navbar-inner.white .brand .caret,#navbar .navbar-inner.white .nav>li>a .caret{border-bottom-color:#919191;border-top-color:#919191}#navbar .navbar-inner.white .brand:focus .caret,#navbar .navbar-inner.white .brand:hover .caret,#navbar .navbar-inner.white .nav>li>a:focus .caret,#navbar .navbar-inner.white .nav>li>a:hover .caret{border-bottom-color:#626262;border-top-color:#626262}#navbar .navbar-inner.white .brand span{background-image:url(../img/tentacle-20x20.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.white .brand span{background-image:url(../img/tentacle-20x20@2x.png)}}#navbar .navbar-inner.white .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.white .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.white .nav>li.dropdown.open>.dropdown-toggle{background-color:#dedede;background-image:-moz-linear-gradient(top,#c8c8c8,#fff);background-image:-webkit-gradient(linear,0 0,0 100%,from(#c8c8c8),to(#fff));background-image:-webkit-linear-gradient(top,#c8c8c8,#fff);background-image:-o-linear-gradient(top,#c8c8c8,#fff);background-image:linear-gradient(to bottom,#c8c8c8,#fff);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffc8c8c8', endColorstr='#ffffffff', GradientType=0)}#navbar .navbar-inner.white .nav>li>a:hover{background-color:#dcdcdc;background-image:-moz-linear-gradient(top,#f2f2f2,#bbb);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f2f2f2),to(#bbb));background-image:-webkit-linear-gradient(top,#f2f2f2,#bbb);background-image:-o-linear-gradient(top,#f2f2f2,#bbb);background-image:linear-gradient(to bottom,#f2f2f2,#bbb);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2f2f2', endColorstr='#ffbbbbbb', GradientType=0)}#navbar .navbar-inner.white.transparent{background-color:rgba(233,233,233,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(255,255,255,.6),rgba(200,200,200,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(255,255,255,.6)),to(rgba(200,200,200,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(255,255,255,.6),rgba(200,200,200,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(255,255,255,.6),rgba(200,200,200,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(255,255,255,.6),rgba(200,200,200,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99ffffff', endColorstr='#99c8c8c8', GradientType=0)}#navbar .navbar-inner.white.transparent .brand,#navbar .navbar-inner.white.transparent .nav>li>a{text-shadow:0 1px 0 #c8c8c8;color:#333}#navbar .navbar-inner.white.transparent .brand .caret,#navbar .navbar-inner.white.transparent .nav>li>a .caret{border-bottom-color:#919191;border-top-color:#919191}#navbar .navbar-inner.white.transparent .brand:focus .caret,#navbar .navbar-inner.white.transparent .brand:hover .caret,#navbar .navbar-inner.white.transparent .nav>li>a:focus .caret,#navbar .navbar-inner.white.transparent .nav>li>a:hover .caret{border-bottom-color:#626262;border-top-color:#626262}#navbar .navbar-inner.white.transparent .brand span{background-image:url(../img/tentacle-20x20.png)}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#navbar .navbar-inner.white.transparent .brand span{background-image:url(../img/tentacle-20x20@2x.png)}}#navbar .navbar-inner.white.transparent .nav>li.dropdown.active>.dropdown-toggle,#navbar .navbar-inner.white.transparent .nav>li.dropdown.open.active>.dropdown-toggle,#navbar .navbar-inner.white.transparent .nav>li.dropdown.open>.dropdown-toggle{background-color:rgba(222,222,222,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(200,200,200,.6),rgba(255,255,255,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(200,200,200,.6)),to(rgba(255,255,255,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(200,200,200,.6),rgba(255,255,255,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(200,200,200,.6),rgba(255,255,255,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(200,200,200,.6),rgba(255,255,255,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99c8c8c8', endColorstr='#99ffffff', GradientType=0)}#navbar .navbar-inner.white.transparent .nav>li>a:hover{background-color:rgba(220,220,220,.6);background-image:"../img/trans-background.png";background-image:-moz-linear-gradient(top,rgba(242,242,242,.6),rgba(187,187,187,.6)),url(../img/trans-background.png);background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(242,242,242,.6)),to(rgba(187,187,187,.6))),url(../img/trans-background.png);background-image:-webkit-linear-gradient(top,rgba(242,242,242,.6),rgba(187,187,187,.6)),url(../img/trans-background.png);background-image:-o-linear-gradient(top,rgba(242,242,242,.6),rgba(187,187,187,.6)),url(../img/trans-background.png);background-image:linear-gradient(to bottom,rgba(242,242,242,.6),rgba(187,187,187,.6)),url(../img/trans-background.png);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#99f2f2f2', endColorstr='#99bbbbbb', GradientType=0)}#navbar .navbar-inner .brand{padding:10px 20px 6px}#navbar .navbar-inner .brand span{padding-left:26px;background-size:20px 20px;background-repeat:no-repeat;display:inline-block;max-width:250px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;vertical-align:top;line-height:20px;height:24px}#navbar_login a.dropdown-toggle span{display:inline-block;max-width:100px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;vertical-align:top}.octoprint-container{margin-top:20px}.octoprint-container .tab-content{padding:9px 15px;border-left:1px solid #DDD;border-right:1px solid #DDD;border-bottom:1px solid #DDD;-webkit-border-bottom-right-radius:4px;-moz-border-radius-bottomright:4px;border-bottom-right-radius:4px;-webkit-border-bottom-left-radius:4px;-moz-border-radius-bottomleft:4px;border-bottom-left-radius:4px}.octoprint-container .nav{margin-bottom:0}.octoprint-container .tab-content h1{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:40px;color:#333;border:0;border-bottom:1px solid #E5E5E5;font-weight:400}.octoprint-container .accordion-heading .accordion-heading-button{float:right}.octoprint-container .accordion-heading .accordion-heading-button a{display:inline-block;padding:8px 15px;font-size:14px;line-height:20px;color:#000;text-decoration:none;background:0 0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.octoprint-container .accordion-heading a.accordion-toggle{display:inline-block}.octoprint-container .accordion-heading [class*=" icon-"],.octoprint-container .accordion-heading [class^=icon-]{color:#000}.print-control .btn{padding-left:4px;padding-right:4px}.upload-buttons .btn{margin-right:0}table{table-layout:fixed}table .popover-title{text-overflow:ellipsis;word-break:break-all}table td,table th{overflow:hidden}table td.gcode_files_name,table th.gcode_files_name{text-overflow:ellipsis;text-align:left;white-space:nowrap}table td.gcode_files_action,table th.gcode_files_action{width:90px;text-align:center;white-space:nowrap}table td.gcode_files_action a,table th.gcode_files_action a{text-decoration:none;color:#000}table td.gcode_files_action a.disabled,table th.gcode_files_action a.disabled{color:#ccc;cursor:default}table td.timelapse_files_checkbox,table td.timelapse_unrendered_checkbox,table th.timelapse_files_checkbox,table th.timelapse_unrendered_checkbox{text-align:center;width:10px}table td.timelapse_files_checkbox input[type=checkbox],table td.timelapse_unrendered_checkbox input[type=checkbox],table th.timelapse_files_checkbox input[type=checkbox],table th.timelapse_unrendered_checkbox input[type=checkbox]{margin-top:0}table td.timelapse_files_name,table td.timelapse_unrendered_name,table th.timelapse_files_name,table th.timelapse_unrendered_name{text-overflow:ellipsis;text-align:left}table td.timelapse_files_size,table td.timelapse_unrendered_size,table th.timelapse_files_size,table th.timelapse_unrendered_size{text-align:right;width:55px}table td.timelapse_unrendered_count,table th.timelapse_unrendered_count{text-align:right;width:45px}table td.timelapse_files_action,table td.timelapse_unrendered_action,table th.timelapse_files_action,table th.timelapse_unrendered_action{width:45px;text-align:center;white-space:nowrap}table td.timelapse_files_action a,table td.timelapse_unrendered_action a,table th.timelapse_files_action a,table th.timelapse_unrendered_action a{text-decoration:none;color:#000}table td.timelapse_files_action a.disabled,table td.timelapse_unrendered_action a.disabled,table th.timelapse_files_action a.disabled,table th.timelapse_unrendered_action a.disabled{color:#ccc;cursor:default}table td.settings_users_name,table th.settings_users_name{text-overflow:ellipsis;text-align:left}table td.settings_users_active,table td.settings_users_admin,table th.settings_users_active,table th.settings_users_admin{text-align:center;width:55px}table td.settings_users_actions,table th.settings_users_actions{width:60px;text-align:center;white-space:nowrap}table td.settings_users_actions a,table th.settings_users_actions a{text-decoration:none;color:#000}table td.settings_users_actions a.disabled,table th.settings_users_actions a.disabled{color:#ccc;cursor:default}table td.settings_printerProfiles_profiles_name,table th.settings_printerProfiles_profiles_name{text-overflow:ellipsis;text-align:left}table td.settings_printerProfiles_profiles_model,table th.settings_printerProfiles_profiles_model{text-align:left;width:250px}table td.settings_printerProfiles_profiles_action,table th.settings_printerProfiles_profiles_action{width:80px;text-align:center;white-space:nowrap}table td.settings_printerProfiles_profiles_action a,table th.settings_printerProfiles_profiles_action a{text-decoration:none;color:#000}table td.settings_printerProfiles_profiles_action a.disabled,table th.settings_printerProfiles_profiles_action a.disabled{color:#ccc;cursor:default}#temperature-graph{height:350px;width:100%;background:url(../img/graph-background.png) center no-repeat}#temperature-table{table-layout:fixed;width:100%;margin-top:20px}#temperature-table td.temperature_actual,#temperature-table td.temperature_offset,#temperature-table td.temperature_target,#temperature-table td.temperature_tool,#temperature-table th.temperature_actual,#temperature-table th.temperature_offset,#temperature-table th.temperature_target,#temperature-table th.temperature_tool{vertical-align:middle;text-align:center}#temperature-table td.temperature_actual form,#temperature-table td.temperature_offset form,#temperature-table td.temperature_target form,#temperature-table td.temperature_tool form,#temperature-table th.temperature_actual form,#temperature-table th.temperature_offset form,#temperature-table th.temperature_target form,#temperature-table th.temperature_tool form{margin:0}#temperature-table td.temperature_actual .dropdown-menu,#temperature-table td.temperature_offset .dropdown-menu,#temperature-table td.temperature_target .dropdown-menu,#temperature-table td.temperature_tool .dropdown-menu,#temperature-table th.temperature_actual .dropdown-menu,#temperature-table th.temperature_offset .dropdown-menu,#temperature-table th.temperature_target .dropdown-menu,#temperature-table th.temperature_tool .dropdown-menu{text-align:left}#temperature-table td.temperature_tool,#temperature-table th.temperature_tool{width:16%;text-align:left;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}#temperature-table td.temperature_actual,#temperature-table th.temperature_actual{width:12%}#temperature-table td.temperature_target,#temperature-table th.temperature_target{width:42%;overflow:visible}#temperature-table td.temperature_offset,#temperature-table th.temperature_offset{width:30%}.tab-content,.tab-pane{overflow:visible}#speed_fill,#speed_innerWall,#speed_outerWall,#speed_support,#temp_newBedTemp,#temp_newTemp,#webcam_timelapse_fps,#webcam_timelapse_interval,#webcam_timelapse_postRoll,#webcam_timelapse_retractionZHop{text-align:right}ul.dropdown-menu li a{cursor:pointer}#connection_baudrates,#connection_ports,#connection_printers{width:100%}#offline_overlay,#reloadui_overlay{position:fixed;top:0;left:0;width:100%;height:100%;display:none}#offline_overlay{z-index:10002}#reloadui_overlay{z-index:10001}#offline_overlay_background,#reloadui_overlay_background{position:fixed;top:0;left:0;width:100%;height:100%;background-color:#000;filter:alpha(opacity=50);-moz-opacity:.5;-khtml-opacity:.5;opacity:.5}#offline_overlay_wrapper,#reloadui_overlay_wrapper{position:absolute;top:0;bottom:0;left:0;right:0;padding-top:60px}#offline_overlay_wrapper .container,#reloadui_overlay_wrapper .container{margin:auto}#webcam_container{width:100%;position:relative;outline:0;background-color:#000}#webcam_container .keycontrol_overlay{position:absolute;left:10px;right:10px;bottom:10px;background:rgba(0,0,0,.5);font-size:85%;color:#fff;padding:0}#webcam_container .keycontrol_overlay kbd{border:1px solid #eee;border-radius:3px;margin-left:2px;margin-right:2px;font-size:90%;padding:2px;min-width:1em}#webcam_container .keycontrol_overlay .keycontrol_overlay_heading{position:relative;padding:10px;font-weight:700}#webcam_container .keycontrol_overlay .keycontrol_overlay_column{position:relative;width:45%;padding:10px;float:left}#webcam_container .nowebcam{position:absolute;top:0;left:0;right:0;bottom:0}#webcam_container .nowebcam .text{color:#fff;text-align:center;position:relative;margin:auto;width:80%;top:50%;transform:translateY(-50%);display:block}#webcam_container .nowebcam .text.webcam_loading{animation:pulsate 3s ease-out;animation-iteration-count:infinite}#webcam_container .webcam_rotated{position:relative;width:100%;padding-bottom:100%;pointer-events:none}#webcam_container .webcam_rotated .webcam_fixed_ratio{position:absolute;transform:rotate(-90deg);top:0;bottom:0;pointer-events:none}#webcam_container .webcam_rotated .webcam_fixed_ratio .webcam_fixed_ratio_inner{width:100%;height:100%;pointer-events:none}#webcam_container .webcam_unrotated .webcam_fixed_ratio{width:100%;pointer-events:none;padding-bottom:100%;position:relative}#webcam_container .webcam_unrotated .webcam_fixed_ratio.ratio43{padding-bottom:75%}#webcam_container .webcam_unrotated .webcam_fixed_ratio.ratio169{padding-bottom:56.25%}#webcam_container .webcam_unrotated .webcam_fixed_ratio.ratio1610{padding-bottom:62.5%}#webcam_container .webcam_unrotated .webcam_fixed_ratio .webcam_fixed_ratio_inner{position:absolute;top:0;bottom:0;left:0;right:0;pointer-events:none}#webcam_container img{width:100%;height:100%;object-fit:contain}#state_wrapper hr{margin:5px 0}#files .gcode_files{padding-right:12px}#files .gcode_files .entry{padding:5px;line-height:20px;border-bottom:1px solid #ddd;position:relative}#files .gcode_files .entry:hover{background-color:#f5f5f5}#files .gcode_files .entry .title{text-overflow:ellipsis;word-break:break-all}#files .gcode_files .entry .additionalInfo,#files .gcode_files .entry .internal,#files .gcode_files .entry .size,#files .gcode_files .entry .uploaded{font-size:85%;color:#999}#files .gcode_files .entry .internal{word-break:break-all}#files .gcode_files .entry .action-buttons{position:absolute;bottom:5px;right:5px}#files .gcode_files .entry .additionalInfo{padding-bottom:22px}@keyframes highlightframes{0%{background:#ff0}100%{background:0 0}}#files .gcode_files .entry.highlight{animation:highlightframes 2s}#files .gcode_files .back .back-path{white-space:nowrap}#files .gcode_files .back .back-path span{word-wrap:break-word;white-space:pre-line}#files .upload-buttons{margin-top:10px}#files .form-search{text-align:center;margin-bottom:5px!important}#control{overflow:hidden}#control .jog-panel{float:left;margin-right:19px}#control h1{text-align:left}#control .jog-panel>div{text-align:center}#control .jog-panel>div.distance{text-align:left}#control .jog-panel .slider{margin-bottom:10px}#control .box{width:30px;height:30px;margin-right:10px;margin-bottom:10px;padding-left:8px}#control .control-box{display:block;height:30px;margin-bottom:10px}#control .btn-group{margin-bottom:10px}#control .btn-group.distance>.btn{width:43px;padding:3px 0;height:30px}#control .slider-handle{width:14px;height:14px;margin-left:-7px;margin-top:-3px}#control .custom_section h1{cursor:pointer}#control .custom_section_horizontal>.custom_control{display:inline-block}#control .custom_section_vertical>.custom_control{display:block}#control .custom_control .slider{margin-left:10px;margin-right:10px;margin-bottom:2px}#gcode .progress{width:588px}#gcode .progress .bar{-webkit-transition:width 0s linear;-moz-transition:width 0s linear;-o-transition:width 0s linear;transition:width 0s linear}#gcode .canvas_container{position:relative}#gcode .canvas_container:active,#gcode .canvas_container:hover{outline:0}#gcode .layer-buttons{padding-top:5px;padding-bottom:7px}#gcode #gcode_layer_slider{position:absolute;right:0;top:0;height:568px;float:right}#gcode #gcode_layer_slider .slider-handle{width:14px;height:14px;margin-left:-3px;margin-top:-7px}#gcode #gcode_command_slider .slider-handle{width:14px;height:14px;margin-left:-7px;margin-top:-3px}#term .terminal{margin-bottom:30px}#term .terminal #terminal-output,#term .terminal #terminal-output-lowfi{min-height:340px;margin-bottom:5px}#settings_dialog .aboutlink{float:left}#settings_dialog_menu,#wizard_dialog_menu{margin-left:0}#wizard_firstrun_acl .acl_decision{margin-top:1em}#wizard_firstrun_end p,#wizard_firstrun_start p{margin-bottom:1.5em;line-height:1.5}#settings_appearance_managelanguagesdialog_emptylist{overflow:hidden;width:100%;height:300px;text-align:center;display:table}#settings_appearance_managelanguagesdialog_emptylist div{display:table-cell;vertical-align:middle}.footer ul{margin:0}.footer ul li{display:inline;margin-left:1em;font-size:85%}.footer ul li:first-child{margin-left:0}.footer ul li a{color:#555}.footer #footer_link,.footer #footer_version{max-width:50%}.ui-pnotify .alert a{color:#c09853}.ui-pnotify .alert-danger a,.ui-pnotify .alert-error a{color:#b94a48}.ui-pnotify .alert-success a{color:#468847}.ui-pnotify .alert-info a{color:#3a87ad}.pnotify_additional_info .pnotify_more{font-size:85%}.text-right{text-align:right}.text-center{text-align:center}.text-block{display:block}.overflow_visible{overflow:visible!important}.clickable{cursor:pointer}.border_box{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.hidden{display:none}textarea.block{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;width:100%}@keyframes pulsate{0%{opacity:.5}50%{opacity:1}100%{opacity:.5}}#drop_overlay{position:fixed;top:0;left:0;width:100%;height:100%;z-index:10000;display:none}#drop_overlay.in{display:block}#drop_overlay #drop_overlay_background{position:fixed;top:0;left:0;width:100%;height:100%;background-color:#000;filter:alpha(opacity=50);-moz-opacity:.5;-khtml-opacity:.5;opacity:.5}#drop_overlay #drop_overlay_wrapper{position:absolute;top:0;bottom:0;left:0;right:0;padding-top:60px}#drop_overlay #drop_overlay_wrapper #drop,#drop_overlay #drop_overlay_wrapper #drop_background{position:absolute;top:0;left:0;margin-left:0;width:100%}#drop_overlay #drop_overlay_wrapper #drop_locally,#drop_overlay #drop_overlay_wrapper #drop_locally_background{position:absolute;top:0;left:50%;margin-left:-50%;width:50%;border-right:2px dashed #ccc}#drop_overlay #drop_overlay_wrapper #drop_sd,#drop_overlay #drop_overlay_wrapper #drop_sd_background{position:absolute;top:0;left:50%;margin-left:0;width:50%;border-left:2px dashed #ccc}#drop_overlay #drop_overlay_wrapper .dropzone{height:100%;z-index:10001;color:#fff;font-size:30px}#drop_overlay #drop_overlay_wrapper .dropzone i{font-size:50px}#drop_overlay #drop_overlay_wrapper .dropzone .text{display:block;text-align:center;line-height:40px;position:absolute;width:100%;bottom:5%;filter:alpha(opacity=100);-moz-opacity:1;-khtml-opacity:1;opacity:1}#drop_overlay #drop_overlay_wrapper .dropzone_background{width:50%;height:100%;background-color:#000;filter:alpha(opacity=25);-moz-opacity:.25;-khtml-opacity:.25;opacity:.25}#drop_overlay #drop_overlay_wrapper .dropzone_background.hover{background-color:#000;filter:alpha(opacity=50);-moz-opacity:.5;-khtml-opacity:.5;opacity:.5}#drop_overlay #drop_overlay_wrapper .dropzone_background.fade{-webkit-transition:all .3s ease-out;-moz-transition:all .3s ease-out;-ms-transition:all .3s ease-out;-o-transition:all .3s ease-out;transition:all .3s ease-out;opacity:1}.icon-sd-black-14{background:url(../img/icon-sd-black-14.png) 0 3px no-repeat;width:11px;height:17px;display:inline-block!important}.center{float:none;margin-left:auto;margin-right:auto}.flipH{-webkit-transform:scaleX(-1);-moz-transform:scaleX(-1);-ms-transform:scaleX(-1);transform:scaleX(-1)}.flipV{-webkit-transform:scaleY(-1);-moz-transform:scaleY(-1);-ms-transform:scaleY(-1);transform:scaleY(-1)}.flipH.flipV{-webkit-transform:scaleX(-1) scaleY(-1);-moz-transform:scaleX(-1) scaleY(-1);-ms-transform:scaleX(-1) scaleY(-1);transform:scaleX(-1) scaleY(-1)}.rotate90{-webkit-transform:rotate(-90deg);transform:rotate(-90deg)}.ui-pnotify a{text-decoration:underline}.btn-mini .caret,.btn-small .caret{margin-top:8px}.dropdown-menu-right{right:0;left:auto}.slider .slider-selection{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,.25);background-color:#006dcc;background-image:-moz-linear-gradient(top,#08c,#04c);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#04c));background-image:-webkit-linear-gradient(top,#08c,#04c);background-image:-o-linear-gradient(top,#08c,#04c);background-image:linear-gradient(to bottom,#08c,#04c);background-repeat:repeat-x;border-color:#04c #04c #002a80;border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);*background-color:#04c;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.slider .slider-selection.active,.slider .slider-selection.disabled,.slider .slider-selection:active,.slider .slider-selection:focus,.slider .slider-selection:hover,.slider .slider-selection[disabled]{color:#fff;background-color:#04c;*background-color:#003bb3}.slider .slider-selection.active,.slider .slider-selection:active{background-color:#039 \9}.slider.slider-disabled .slider-selection{background-image:none;opacity:.65;filter:alpha(opacity=65);-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.slider .slider-track{background-color:#f5f5f5;border:1px solid #e3e3e3;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05)}.slider.slider-disabled .slider-track{background-image:none;opacity:.65;filter:alpha(opacity=65);-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.slider .slider-handle{display:inline-block;*display:inline;*zoom:1;font-size:14px;line-height:20px;text-align:center;vertical-align:middle;cursor:pointer;color:#333;text-shadow:0 1px 1px rgba(255,255,255,.75);background-color:#f5f5f5;background-image:-moz-linear-gradient(top,#fff,#e6e6e6);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#e6e6e6));background-image:-webkit-linear-gradient(top,#fff,#e6e6e6);background-image:-o-linear-gradient(top,#fff,#e6e6e6);background-image:linear-gradient(to bottom,#fff,#e6e6e6);background-repeat:repeat-x;*background-color:#e6e6e6;border:1px solid #ccc;*border:0;border-bottom-color:#b3b3b3;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;*margin-left:.3em;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05);padding:0;margin-bottom:0;opacity:1;filter:alpha(opacity=100)}.slider .slider-handle.active,.slider .slider-handle.disabled,.slider .slider-handle:active,.slider .slider-handle:focus,.slider .slider-handle:hover,.slider .slider-handle[disabled]{color:#333;background-color:#e6e6e6;*background-color:#d9d9d9}.slider .slider-handle.active,.slider .slider-handle:active{background-color:#ccc \9}.slider .slider-handle:first-child{*margin-left:0}.slider .slider-handle:focus,.slider .slider-handle:hover{color:#333;text-decoration:none;background-position:0 -15px;-webkit-transition:background-position .1s linear;-moz-transition:background-position .1s linear;-o-transition:background-position .1s linear;transition:background-position .1s linear}.slider .slider-handle:focus{outline:#333 dotted thin;outline:-webkit-focus-ring-color auto 5px;outline-offset:-2px}.slider .slider-handle.active,.slider .slider-handle:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,.15),0 1px 2px rgba(0,0,0,.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,.15),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 2px 4px rgba(0,0,0,.15),0 1px 2px rgba(0,0,0,.05)}.slider .slider-handle.disabled,.slider .slider-handle[disabled]{cursor:default;background-image:none;opacity:.65;filter:alpha(opacity=65);-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.slider .slider-handle.hide{display:none}.slider .slider-handle.round{-webkit-border-radius:50%;-moz-border-radius:50%;border-radius:50%}.modal.large{width:975px;margin-left:-487px}.full-sized-box{position:absolute;bottom:0;left:0;right:0;top:0;padding:15px}.full-sized-box .row-fluid{height:100%}@media (max-width:979px){.full-sized-box{position:static}}:root .full-sized-box,_::-webkit-full-page-media,_:future{position:static}.scrollable{height:100%;overflow:auto;-webkit-overflow-scrolling:touch}.pre-output span{display:block}.input-append .add-on.add-on-limited,.input-prepend .add-on.add-on-limited{overflow-x:hidden;text-overflow:ellipsis;width:inherit}.input-append .btn-group:first-child .btn:first-child,.input-prepend .btn-group:first-child .btn:first-child{-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.input-append .btn-group .btn:first-child,.input-prepend .btn-group .btn:first-child{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.input-append.input-block-level,.input-prepend.input-block-level{display:table}.input-append.input-block-level .add-on,.input-prepend.input-block-level .add-on{display:table-cell;width:1%}.input-append.input-block-level>input,.input-prepend.input-block-level>input{box-sizing:border-box;display:table;min-height:inherit;width:100%}.input-append.input-block-level :not(:last-child),.input-prepend.input-block-level :not(:last-child){border-right:0}.control-group.error .input-append .fileinput-button,.control-group.error .input-prepend .fileinput-button{border-color:#b94a48}.control-text{padding-top:5px;cursor:default}input[type=number]{text-align:right}input[type=number].input-nospin::-webkit-inner-spin-button,input[type=number].input-nospin::-webkit-outer-spin-button{-webkit-appearance:none;margin:0}input[type=number].input-nospin{-moz-appearance:textfield}.progress-text,.progress-text-centered{position:relative}.progress-text .progress-text-back,.progress-text .progress-text-front,.progress-text-centered .progress-text-back,.progress-text-centered .progress-text-front{white-space:nowrap}.progress-text .progress-text-front,.progress-text-centered .progress-text-front{box-sizing:border-box;padding:0 10px;width:100%;display:block}.progress-text .progress-text-back,.progress-text-centered .progress-text-back{position:absolute;font-size:12px;line-height:20px;display:block;box-sizing:border-box;text-align:center;padding:0 10px}.progress-text .bar,.progress-text-centered .bar{position:absolute;overflow:hidden}.progress-text-centered .progress-text-front{position:absolute;font-size:12px;line-height:20px;display:block;text-align:center;color:#fff}.progress-text-centered .progress-text-back{width:100%}#navbar_login:not(.open) #login_dropdown_loggedout{display:block;z-index:-1;height:0;width:0;padding:0!important;overflow:hidden;border:0;box-shadow:none;left:-9999px}#navbar_login:not(.open) #login_dropdown_loggedout.hide{display:none}#loginForm{margin:0}#loginForm button{margin-top:20px} \ No newline at end of file diff --git a/src/octoprint/translations/de/LC_MESSAGES/messages.mo b/src/octoprint/translations/de/LC_MESSAGES/messages.mo index f779144bdcd5d7286c930131bb90613094b86da1..f4be601cde52c98e20c75e0ec8888b2364d0e55b 100644 GIT binary patch delta 16907 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 GzW)O_!3~uF delta 16362 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 diff --git a/src/octoprint/translations/de/LC_MESSAGES/messages.po b/src/octoprint/translations/de/LC_MESSAGES/messages.po index 3fb9207a20..84c35566aa 100644 --- a/src/octoprint/translations/de/LC_MESSAGES/messages.po +++ b/src/octoprint/translations/de/LC_MESSAGES/messages.po @@ -7,8 +7,8 @@ msgid "" msgstr "" "Project-Id-Version: OctoPrint\n" "Report-Msgid-Bugs-To: i18n@octoprint.org\n" -"POT-Creation-Date: 2018-03-16 13:18+0100\n" -"PO-Revision-Date: 2018-03-16 14:07+0100\n" +"POT-Creation-Date: 2018-03-23 15:23+0100\n" +"PO-Revision-Date: 2018-03-23 15:25+0100\n" "Last-Translator: Gina Häußge \n" "Language: de\n" "Language-Team: German (http://www.transifex.com/projects/p/octoprint/language/de/)\n" @@ -624,6 +624,10 @@ msgstr "Profil entfernen" msgid "Without this plugin your OctoPrint instance will no longer be discoverable on the network via Bonjour and uPnP." msgstr "Ohne dieses Plugin wird deine OctoPrint Instanz nicht mehr in deinem Netzwerk mittels Bonjour oder uPnP automatisch auffindbar sein." +#: src/octoprint/plugins/logging/__init__.py:204 +msgid "Without this plugin you will no longer be able to retrieve OctoPrint's logs or modify the current logging levels through the web interface." +msgstr "Ohne dieses Plugin kannst du OctoPrints Logfiles nicht mehr über das Webinterface herunterladen und auch die aktuellen Loglevel modifizieren." + #: src/octoprint/plugins/logging/templates/logging_settings.jinja2:1 msgid "Logs" msgstr "Logs" @@ -1196,6 +1200,22 @@ msgstr "Abbruch" msgid "Save" msgstr "Speichern" +#: src/octoprint/plugins/printer_safety_check/__init__.py:44 +msgid "Printer Safety Warning" +msgstr "Druckersicherheitswarnung" + +#: src/octoprint/plugins/printer_safety_check/__init__.py:105 +msgid "Without this plugin OctoPrint will no longer be able to check if the printer it is connected to has a known safetyissue and inform you about that fact." +msgstr "Ohne dieses Plugin wird OctoPrint nicht länger in der Lage sein, dich über bekannte Sicherheitsprobleme mit verbundenen Druckern zu informieren." + +#: src/octoprint/plugins/printer_safety_check/templates/printer_safety_check_sidebar.jinja2:1 +msgid "Warning!" +msgstr "Warnung!" + +#: src/octoprint/plugins/printer_safety_check/templates/printer_safety_check_sidebar.jinja2:5 +msgid "Learn more..." +msgstr "Mehr erfahren..." + #: src/octoprint/plugins/softwareupdate/__init__.py:570 #: src/octoprint/plugins/softwareupdate/templates/softwareupdate_wizard.jinja2:1 msgid "Software Update" @@ -1840,7 +1860,7 @@ msgid "off" msgstr "Aus" #: src/octoprint/static/js/app/helpers.js:654 -#: src/octoprint/static/js/app/viewmodels/connection.js:133 +#: src/octoprint/static/js/app/viewmodels/connection.js:135 msgid "Are you sure?" msgstr "Bist Du sicher?" @@ -1902,19 +1922,19 @@ msgid "Connect" msgstr "Verbinden" #: src/octoprint/static/js/app/viewmodels/connection.js:44 -#: src/octoprint/static/js/app/viewmodels/connection.js:142 +#: src/octoprint/static/js/app/viewmodels/connection.js:144 msgid "Disconnect" msgstr "Trennen" -#: src/octoprint/static/js/app/viewmodels/connection.js:134 +#: src/octoprint/static/js/app/viewmodels/connection.js:136 msgid "

You are about to disconnect from the printer while a print is in progress.

Disconnecting while a print is in progress will prevent OctoPrint from completing the print. If you're printing from an SD card attached directly to the printer, any attempt to restart OctoPrint or reconnect to the printer could interrupt the print.

" msgstr "

Du bist im Begriff, die Verbindung zu deinem Drucker zu trennen während ein Druck läuft.

Das Trennen der Verbindung während eines Drucks wird OctoPrint daran hindern, diesen zu vollenden. Falls du von der SD deines Druckers druckst, könnte jeglicher Versuch, OctoPrint neuzustarten oder erneut mit dem Drucker zu verbinden den Druck unterbrechen.

" -#: src/octoprint/static/js/app/viewmodels/connection.js:140 +#: src/octoprint/static/js/app/viewmodels/connection.js:142 msgid "Are you sure you want to disconnect from the printer?" msgstr "Bist Du sicher, dass du die Verbindung zum Drucker trennen willst?" -#: src/octoprint/static/js/app/viewmodels/connection.js:141 +#: src/octoprint/static/js/app/viewmodels/connection.js:143 msgid "Stay Connected" msgstr "Verbunden bleiben" diff --git a/translations/de/LC_MESSAGES/messages.mo b/translations/de/LC_MESSAGES/messages.mo index f779144bdcd5d7286c930131bb90613094b86da1..f4be601cde52c98e20c75e0ec8888b2364d0e55b 100644 GIT binary patch delta 16907 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 GzW)O_!3~uF delta 16362 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 diff --git a/translations/de/LC_MESSAGES/messages.po b/translations/de/LC_MESSAGES/messages.po index 4a3c05f82e..84c35566aa 100644 --- a/translations/de/LC_MESSAGES/messages.po +++ b/translations/de/LC_MESSAGES/messages.po @@ -7,8 +7,8 @@ msgid "" msgstr "" "Project-Id-Version: OctoPrint\n" "Report-Msgid-Bugs-To: i18n@octoprint.org\n" -"POT-Creation-Date: 2018-03-19 13:41+0100\n" -"PO-Revision-Date: 2018-03-19 13:42+0100\n" +"POT-Creation-Date: 2018-03-23 15:23+0100\n" +"PO-Revision-Date: 2018-03-23 15:25+0100\n" "Last-Translator: Gina Häußge \n" "Language: de\n" "Language-Team: German (http://www.transifex.com/projects/p/octoprint/language/de/)\n" @@ -624,6 +624,10 @@ msgstr "Profil entfernen" msgid "Without this plugin your OctoPrint instance will no longer be discoverable on the network via Bonjour and uPnP." msgstr "Ohne dieses Plugin wird deine OctoPrint Instanz nicht mehr in deinem Netzwerk mittels Bonjour oder uPnP automatisch auffindbar sein." +#: src/octoprint/plugins/logging/__init__.py:204 +msgid "Without this plugin you will no longer be able to retrieve OctoPrint's logs or modify the current logging levels through the web interface." +msgstr "Ohne dieses Plugin kannst du OctoPrints Logfiles nicht mehr über das Webinterface herunterladen und auch die aktuellen Loglevel modifizieren." + #: src/octoprint/plugins/logging/templates/logging_settings.jinja2:1 msgid "Logs" msgstr "Logs" diff --git a/translations/messages.pot b/translations/messages.pot index 10fd9c6dcb..dc10155d38 100644 --- a/translations/messages.pot +++ b/translations/messages.pot @@ -6,9 +6,9 @@ #, fuzzy msgid "" msgstr "" -"Project-Id-Version: OctoPrint 1.3.7.dev166+g8a3a87efa.dirty\n" +"Project-Id-Version: OctoPrint 1.3.7rc2.dev16+g47267abcd.dirty\n" "Report-Msgid-Bugs-To: i18n@octoprint.org\n" -"POT-Creation-Date: 2018-03-19 13:41+0100\n" +"POT-Creation-Date: 2018-03-23 15:23+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -699,6 +699,12 @@ msgid "" "discoverable on the network via Bonjour and uPnP." msgstr "" +#: src/octoprint/plugins/logging/__init__.py:204 +msgid "" +"Without this plugin you will no longer be able to retrieve OctoPrint's " +"logs or modify the current logging levels through the web interface." +msgstr "" + #: src/octoprint/plugins/logging/templates/logging_settings.jinja2:1 msgid "Logs" msgstr "" From 028425542fd69efa77d9eb85f55df633e3481108 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Fri, 23 Mar 2018 16:28:47 +0100 Subject: [PATCH 317/333] staging/maintenance is now 1.3.7rc3.dev --- .versioneer-lookup | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.versioneer-lookup b/.versioneer-lookup index 44d9f682c7..eb84a45dda 100644 --- a/.versioneer-lookup +++ b/.versioneer-lookup @@ -23,10 +23,10 @@ maintenance 1.3.7 99e1c3ac007a0eee9d6f908e598c6b029f3ae40c pep440-dev fix/.* 1.3.7 99e1c3ac007a0eee9d6f908e598c6b029f3ae40c pep440-dev improve/.* 1.3.7 99e1c3ac007a0eee9d6f908e598c6b029f3ae40c pep440-dev -# staging/maintenance is currently the branch for preparation of 1.3.7rc2 +# staging/maintenance is currently the branch for preparation of 1.3.7rc3 # so is regressionfix/... -staging/maintenance 1.3.7rc2 da2c9632ea54fd1b3c8ae34f8f26bab29dbaf3c6 pep440-dev -regressionfix/.* 1.3.7rc2 da2c9632ea54fd1b3c8ae34f8f26bab29dbaf3c6 pep440-dev +staging/maintenance 1.3.7rc3 339d1df5a920cc7a2dbae2b5f3410180fea4ceb0 pep440-dev +regressionfix/.* 1.3.7rc3 339d1df5a920cc7a2dbae2b5f3410180fea4ceb0 pep440-dev # every other branch is a development branch and thus gets resolved to 1.4.0-dev for now .* 1.4.0 7f5d03d0549bcbd26f40e7e4a3297ea5204fb1cc pep440-dev From b04aff7d5ed5d1fc5ee4197a85ead52086418fa5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Wed, 28 Mar 2018 15:24:50 +0200 Subject: [PATCH 318/333] Fix command expansion for non-gcode commands --- src/octoprint/util/comm.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/octoprint/util/comm.py b/src/octoprint/util/comm.py index 5bef1e8592..b85ed72f1b 100644 --- a/src/octoprint/util/comm.py +++ b/src/octoprint/util/comm.py @@ -2913,7 +2913,9 @@ def _process_command_phase(self, phase, command, command_type=None, gcode=None, modified = True else: new_results.append((command, command_type, gcode, subcode, tags)) - modified = True + else: + new_results.append((command, command_type, gcode, subcode, tags)) + if modified: if not new_results: # gcode handler returned None or empty list for all commands, so we'll stop here and return a full out empty result From 822f757f9dfd180217d8ac6415aa4f1adb8a1d70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Thu, 29 Mar 2018 14:23:17 +0200 Subject: [PATCH 319/333] Ignore wait if job is on hold See #2524 --- src/octoprint/util/comm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/octoprint/util/comm.py b/src/octoprint/util/comm.py index b85ed72f1b..a82ad6950e 100644 --- a/src/octoprint/util/comm.py +++ b/src/octoprint/util/comm.py @@ -1543,7 +1543,7 @@ def convert_line(line): handled = False # process oks - if line.startswith("ok") or (self.isPrinting() and supportWait and line == "wait"): + if line.startswith("ok") or (self.isPrinting() and not self.job_on_hold and supportWait and line == "wait"): # ok only considered handled if it's alone on the line, might be # a response to an M105 or an M114 self._handle_ok() From b8e7cfb506366380bae11ac7620d9f053d048aa1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Thu, 29 Mar 2018 14:24:58 +0200 Subject: [PATCH 320/333] "Where to find versions" is not on the FAQ --- CONTRIBUTING.md | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f53f77f0ad..861215f4de 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -275,22 +275,11 @@ all requested information or your ticket will be closed. ### Where can I find which version and branch I'm on? -You can find out all of them by taking a look into the lower left corner of the -OctoPrint UI: - -![Current version and git branch info in OctoPrint's UI](http://i.imgur.com/HyHMlY2.png) - -If you don't have access to the UI you can find out that information via the -command line as well. Either `octoprint --version` or `python setup.py version` -in OctoPrint's folder will tell you the version of OctoPrint you are running -(note: if it doesn't then you are running a version older than 1.1.0, -*upgrade now*). A `git branch` in your OctoPrint installation folder will mark -the branch you are on with a little *. `git rev-parse HEAD` will tell you the -current commit. +Please refer to [this FAQ entry](https://discourse.octoprint.org/t/how-can-i-find-out-the-version-of-octoprint-or-octopi-i-am-running/204/1). ### Where can I find those log files you keep talking about? -Please refer to [this FAQ entry](https://discourse.octoprint.org/t/where-can-i-find-octoprints-log-files/299). +Please refer to [this FAQ entry](https://discourse.octoprint.org/t/where-can-i-find-octoprints-and-octopis-log-files/299/1). ### Where can I find my browser's error console? @@ -483,6 +472,7 @@ the local version identifier to allow for an exact determination of the active c * 2017-11-22: Added note on how to run the unit tests * 2018-03-15: Link to new community forum and some clarifications re bug reporting + * 2018-03-29: "Where to find version numbers" is now located on the FAQ ## Footnotes * [1] - If you are wondering why, the problem is that anything that you add From 9c5d996ea2b66a085392981a5e6e0ee341013674 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Thu, 29 Mar 2018 15:47:35 +0200 Subject: [PATCH 321/333] Preparing release of 1.3.7rc3 --- CHANGELOG.md | 8 ++++++++ SUPPORTERS.md | 4 ++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a2ff8e6d7..a1bb983277 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # OctoPrint Changelog +## 1.3.7rc3 (2018-03-29) + +### Bugfixes + + * [#2524](https://github.com/foosel/OctoPrint/issues/2524) Ignore `wait` while job is on hold. + +([Commits](https://github.com/foosel/OctoPrint/compare/1.3.7rc2...1.3.7rc3)) + ## 1.3.7rc2 (2018-03-23) ### Bugfixes diff --git a/SUPPORTERS.md b/SUPPORTERS.md index e204c181b9..de67c9adb3 100644 --- a/SUPPORTERS.md +++ b/SUPPORTERS.md @@ -10,7 +10,6 @@ thanks to everyone who contributed! * Aleph Objects, Inc. * Andrew Moorby * Arnljot Arntsen - * BEEVERYCREATIVE * Boris Hussein * Brad Jackson * Brian E. Tyler @@ -34,6 +33,7 @@ thanks to everyone who contributed! * Kazuhiro Ogura * Kodama Inc. * Makespace Madrid + * Mark Walker * Martin Beattie * Michael Aumock * Miles Flavel @@ -55,4 +55,4 @@ thanks to everyone who contributed! * Thomas Hatley * Trent Shumay -and 1199 more wonderful people pledging on the [Patreon campaign](https://patreon.com/foosel)! \ No newline at end of file +and 1214 more wonderful people pledging on the [Patreon campaign](https://patreon.com/foosel)! \ No newline at end of file From dbfe7fece104d7b48492e4c1b7f8845165c40101 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Thu, 29 Mar 2018 16:58:55 +0200 Subject: [PATCH 322/333] staging/maintenance is now 1.3.7rc4.dev --- .versioneer-lookup | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.versioneer-lookup b/.versioneer-lookup index eb84a45dda..dac4980c5a 100644 --- a/.versioneer-lookup +++ b/.versioneer-lookup @@ -23,10 +23,10 @@ maintenance 1.3.7 99e1c3ac007a0eee9d6f908e598c6b029f3ae40c pep440-dev fix/.* 1.3.7 99e1c3ac007a0eee9d6f908e598c6b029f3ae40c pep440-dev improve/.* 1.3.7 99e1c3ac007a0eee9d6f908e598c6b029f3ae40c pep440-dev -# staging/maintenance is currently the branch for preparation of 1.3.7rc3 +# staging/maintenance is currently the branch for preparation of 1.3.7rc4 # so is regressionfix/... -staging/maintenance 1.3.7rc3 339d1df5a920cc7a2dbae2b5f3410180fea4ceb0 pep440-dev -regressionfix/.* 1.3.7rc3 339d1df5a920cc7a2dbae2b5f3410180fea4ceb0 pep440-dev +staging/maintenance 1.3.7rc4 9c5d996ea2b66a085392981a5e6e0ee341013674 pep440-dev +regressionfix/.* 1.3.7rc4 9c5d996ea2b66a085392981a5e6e0ee341013674 pep440-dev # every other branch is a development branch and thus gets resolved to 1.4.0-dev for now .* 1.4.0 7f5d03d0549bcbd26f40e7e4a3297ea5204fb1cc pep440-dev From 25005b547ca07019a6c0ffe25e8cced301670b4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Tue, 3 Apr 2018 15:38:43 +0200 Subject: [PATCH 323/333] Virtual: Add some debug commands for sd printing --- .../plugins/virtual_printer/virtual.py | 33 +++++++++++++++++-- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/src/octoprint/plugins/virtual_printer/virtual.py b/src/octoprint/plugins/virtual_printer/virtual.py index 3862e455a3..d4f7cc2608 100644 --- a/src/octoprint/plugins/virtual_printer/virtual.py +++ b/src/octoprint/plugins/virtual_printer/virtual.py @@ -29,6 +29,7 @@ class VirtualPrinter(object): prepare_ok_regex = re.compile("prepare_ok (.*)") send_regex = re.compile("send (.*)") set_ambient_regex = re.compile("set_ambient ([-+]?[0-9]*\.?[0-9]+)") + start_sd_regex = re.compile("start_sd (.*)") def __init__(self, seriallog_handler=None, read_timeout=5.0, write_timeout=10.0): import logging @@ -266,6 +267,10 @@ def _processIncoming(self): while self.incoming is not None and not self._killed: self._simulateTemps() + if self._heatingUp: + time.sleep(1) + continue + try: data = self.incoming.get(timeout=0.01) self.incoming.task_done() @@ -729,6 +734,13 @@ def _debugTrigger(self, data): sleep_after_next | Sleeps s after execution of next + # SD printing + + start_sd + | Start printing file from SD + cancel_sd + | Cancels an ongoing SD print + # Misc send @@ -765,6 +777,13 @@ def _debugTrigger(self, data): elif data == "go_awol": self._send("// Going AWOL") self._debug_awol = True + elif data == "cancel_sd": + if self._sdPrinting and self._sdPrinter: + self._pauseSdPrint() + self._sdPrinting = False + self._sdPrintingSemaphore.set() + self._sdPrinter.join() + self._finishSdPrint() else: try: sleep_match = VirtualPrinter.sleep_regex.match(data) @@ -774,6 +793,7 @@ def _debugTrigger(self, data): prepare_ok_match = VirtualPrinter.prepare_ok_regex.match(data) send_match = VirtualPrinter.send_regex.match(data) set_ambient_match = VirtualPrinter.set_ambient_regex.match(data) + start_sd_match = VirtualPrinter.start_sd_regex.match(data) if sleep_match is not None: interval = int(sleep_match.group(1)) @@ -802,6 +822,9 @@ def _debugTrigger(self, data): elif set_ambient_match is not None: self._ambient_temperature = float(set_ambient_match.group(1)) self._send("// set ambient temperature to {}".format(self._ambient_temperature)) + elif start_sd_match is not None: + self._selectSdFile(start_sd_match.group(1)) + self._startSdPrint() except: pass @@ -953,7 +976,7 @@ def _performMove(self, line): matchE = re.search("E([0-9.]+)", line) matchF = re.search("F([0-9.]+)", line) - duration = 0 + duration = 0.0 if matchF is not None: try: self._lastF = float(matchF.group(1)) @@ -1022,6 +1045,7 @@ def _performMove(self, line): pass if duration: + duration *= 0.1 if duration > self._read_timeout: slept = 0 while duration - slept > self._read_timeout and not self._killed: @@ -1119,9 +1143,9 @@ def _sdPrintingWorker(self): # set target temps if 'M104' in line or 'M109' in line: - self._parseHotendCommand(line) + self._parseHotendCommand(line, wait='M109' in line) elif 'M140' in line or 'M190' in line: - self._parseBedCommand(line) + self._parseBedCommand(line, wait='M190' in line) elif line.startswith("G0") or line.startswith("G1") or line.startswith("G2") or line.startswith("G3"): # simulate reprap buffered commands via a Queue with maxsize which internally simulates the moves self.buffered.put(line) @@ -1130,6 +1154,9 @@ def _sdPrintingWorker(self): if self.outgoing is not None: raise + self._finishSdPrint() + + def _finishSdPrint(self): if not self._killed: self._sdPrintingSemaphore.clear() self._selectedSdFilePos = 0 From fcecb5c0f0ad904460842b09694034c0eb345e57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Tue, 3 Apr 2018 15:41:13 +0200 Subject: [PATCH 324/333] Fix heatup flag not resetting on external SD print start If the start of the SD print could only be verified after the blocking heatup was through, the start time was reset to None and hence the heatup flag was not cleared on receival of the first ok, causing no commands to be sent out and things to run into a timeout. Fixes #2536 --- src/octoprint/util/comm.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/octoprint/util/comm.py b/src/octoprint/util/comm.py index a82ad6950e..51eb1393c3 100644 --- a/src/octoprint/util/comm.py +++ b/src/octoprint/util/comm.py @@ -1003,7 +1003,7 @@ def startPrint(self, pos=None, tags=None, external_sd=False): if self._currentFile is None: raise ValueError("No file selected for printing") - self._heatupWaitStartTime = None + self._heatupWaitStartTime = None if not self._heating else time.time() self._heatupWaitTimeLost = 0.0 self._pauseWaitStartTime = 0 self._pauseWaitTimeLost = 0.0 @@ -1631,7 +1631,7 @@ def convert_line(line): if not disable_external_heatup_detection and not self._temperature_autoreporting \ and not line.strip().startswith("ok") and not self._heating \ and self._firmware_info_received: - self._logger.debug("Externally triggered heatup detected") + self._logger.info("Externally triggered heatup detected") self._heating = True self._heatupWaitStartTime = time.time() @@ -2043,9 +2043,10 @@ def _handle_timeout(self): self._clear_to_send.set() def _finish_heatup(self): - if self._heatupWaitStartTime: - self._heatupWaitTimeLost = self._heatupWaitTimeLost + (time.time() - self._heatupWaitStartTime) - self._heatupWaitStartTime = None + if self._heating: + if self._heatupWaitStartTime: + self._heatupWaitTimeLost = self._heatupWaitTimeLost + (time.time() - self._heatupWaitStartTime) + self._heatupWaitStartTime = None self._heating = False def _continue_sending(self): From c1fe8f5a359e943d6a3cc6a46f0c69dd4ea912be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Tue, 3 Apr 2018 16:45:14 +0200 Subject: [PATCH 325/333] Fix RawWebsocketTransport of sockjs Ran into issues due to session wrapping in order to support custom Json encoding. We now make sure to * only wrap non-raw sessions (since only those have send_jsonified defined which we overwrite) * overwrite tornado.escape.json_encode to use our custom encoder as fallback, since that is used for RawWebsocketTransport Fixes an issue with the /sockjs/websocket endpoint hinted at in #2529 --- src/octoprint/server/__init__.py | 1 + src/octoprint/server/util/sockjs.py | 11 ++--------- src/octoprint/server/util/tornado.py | 16 ++++++++++++++++ 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/src/octoprint/server/__init__.py b/src/octoprint/server/__init__.py index 6e9edcd889..296c166e7a 100644 --- a/src/octoprint/server/__init__.py +++ b/src/octoprint/server/__init__.py @@ -223,6 +223,7 @@ def run(self): # monkey patch a bunch of stuff util.tornado.fix_ioloop_scheduling() + util.tornado.fix_json_encode() util.flask.enable_additional_translations(additional_folders=[self._settings.getBaseFolder("translations")]) # setup app diff --git a/src/octoprint/server/util/sockjs.py b/src/octoprint/server/util/sockjs.py index 07783ffd34..84ff1f9fdb 100644 --- a/src/octoprint/server/util/sockjs.py +++ b/src/octoprint/server/util/sockjs.py @@ -57,15 +57,7 @@ def remove_handler(self, handler): class JsonEncodingSessionWrapper(wrapt.ObjectProxy): - def send_message(self, msg, stats=True, binary=False): - """Send or queue outgoing message - - `msg` - Message to send - `stats` - If set to True, will update statistics after operation completes - """ self.send_jsonified(json.dumps(sockjs.tornado.util.bytes_to_str(msg), separators=(',', ':'), default=JsonEncoding.encode), @@ -74,7 +66,8 @@ def send_message(self, msg, stats=True, binary=False): class PrinterStateConnection(sockjs.tornado.SockJSConnection, octoprint.printer.PrinterCallback): def __init__(self, printer, fileManager, analysisQueue, userManager, eventManager, pluginManager, session): - session = JsonEncodingSessionWrapper(session) + if isinstance(session, sockjs.tornado.session.Session): + session = JsonEncodingSessionWrapper(session) sockjs.tornado.SockJSConnection.__init__(self, session) diff --git a/src/octoprint/server/util/tornado.py b/src/octoprint/server/util/tornado.py index f63a1c5f67..e8de7890b0 100644 --- a/src/octoprint/server/util/tornado.py +++ b/src/octoprint/server/util/tornado.py @@ -59,6 +59,22 @@ def _schedule_next(self): tornado.ioloop.PeriodicCallback._schedule_next = _schedule_next +def fix_json_encode(): + """ + This makes tornado.escape.json_encode use octoprint.util.JsonEncoding.encode as fallback in order to allow + serialization of globally registered types like frozendict and others. + """ + + from octoprint.util.json import JsonEncoding + import json + + def fixed_json_encode(value): + return json.dumps(value, default=JsonEncoding.encode).replace(" Date: Tue, 3 Apr 2018 16:56:59 +0200 Subject: [PATCH 326/333] thaw_frozendict should be fully recursive So far it was only deep-diving into frozendicts, not dicts (which might contain frozendicts). In principle we probably should also dive into tuples and lists and other containers, but in our current application we don't put frozendicts into those so let's not overdo it. --- src/octoprint/util/__init__.py | 2 +- tests/util/test_misc.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/octoprint/util/__init__.py b/src/octoprint/util/__init__.py index 892a31782e..7a24670448 100644 --- a/src/octoprint/util/__init__.py +++ b/src/octoprint/util/__init__.py @@ -979,7 +979,7 @@ def thaw_frozendict(obj): # only true love can thaw a frozen dict letitgo = dict() for key, value in obj.items(): - if isinstance(value, frozendict.frozendict): + if isinstance(value, (dict, frozendict.frozendict)): letitgo[key] = thaw_frozendict(value) else: letitgo[key] = copy.deepcopy(value) diff --git a/tests/util/test_misc.py b/tests/util/test_misc.py index f07b087f4c..b0bd8b98d4 100644 --- a/tests/util/test_misc.py +++ b/tests/util/test_misc.py @@ -52,6 +52,7 @@ def test_utmify(self, link, kwargs, expected): (frozendict(a=1, b=2, c=frozendict(c1=1, c2=2)), dict(a=1, b=2, c=dict(c1=1, c2=2))), (dict(a=1, b=2, c=3), dict(a=1, b=2, c=3)), (dict(a=1, b=2, c=frozendict(c1=1, c2=2)), dict(a=1, b=2, c=dict(c1=1, c2=2))), + (dict(a=1, b=2, c=dict(c1=1, c2=2, c3=frozendict(c11=11, c12=12))), dict(a=1, b=2, c=dict(c1=1, c2=2, c3=dict(c11=11, c12=12)))) ) @ddt.unpack def test_unfreeze_frozendict(self, input, expected): From 0ac9ea518c85352da696283920232cb1577065b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Tue, 3 Apr 2018 17:39:53 +0200 Subject: [PATCH 327/333] SWU: Update "How to use release channels" link --- .../snippets/plugins/softwareupdate/releaseChannel.jinja2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/octoprint/plugins/softwareupdate/templates/snippets/plugins/softwareupdate/releaseChannel.jinja2 b/src/octoprint/plugins/softwareupdate/templates/snippets/plugins/softwareupdate/releaseChannel.jinja2 index 57ec4151c1..5506dff4ab 100644 --- a/src/octoprint/plugins/softwareupdate/templates/snippets/plugins/softwareupdate/releaseChannel.jinja2 +++ b/src/octoprint/plugins/softwareupdate/templates/snippets/plugins/softwareupdate/releaseChannel.jinja2 @@ -2,6 +2,6 @@

- {{ _('Make sure you have read "Using Release Channels" before switching to a release channel other than "Stable"', url="https://github.com/foosel/OctoPrint/wiki/Using-Release-Channels") }} + {{ _('Make sure you have read "How to use the release channels to help test release candidates" before switching to a release channel other than "Stable"', url="https://faq.octoprint.org/using-release-channels") }}
From a821346b072a7dc0115b84de7293ff5ef6a2eb6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Wed, 4 Apr 2018 10:29:01 +0200 Subject: [PATCH 328/333] Don't trigger PRINT_FAILED twice on disconnect Fixes #2546 --- src/octoprint/printer/standard.py | 3 ++- src/octoprint/util/comm.py | 5 +---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/octoprint/printer/standard.py b/src/octoprint/printer/standard.py index b85f4e5018..d11e47ffc0 100644 --- a/src/octoprint/printer/standard.py +++ b/src/octoprint/printer/standard.py @@ -1176,7 +1176,8 @@ def log_print(): def on_comm_print_job_failed(self): payload = self._payload_for_print_job_event() - eventManager().fire(Events.PRINT_FAILED, payload) + if payload: + eventManager().fire(Events.PRINT_FAILED, payload) def on_comm_print_job_cancelling(self, firmware_error=None): payload = self._payload_for_print_job_event() diff --git a/src/octoprint/util/comm.py b/src/octoprint/util/comm.py index 51eb1393c3..45975124ab 100644 --- a/src/octoprint/util/comm.py +++ b/src/octoprint/util/comm.py @@ -850,7 +850,6 @@ def deactivate_monitoring_and_send_queue(): self._monitoring_active = False self._send_queue_active = False - printing = self.isPrinting() or self.isPaused() if self._serial is not None: if not is_error: self.sendGcodeScript("beforePrinterDisconnected") @@ -883,6 +882,7 @@ def deactivate_monitoring_and_send_queue(): self._logger.exception("Error while trying to close serial port") is_error = True + # if we are printing, this will also make sure of firing PRINT_FAILED if is_error: self._changeState(self.STATE_CLOSED_WITH_ERROR) else: @@ -894,9 +894,6 @@ def deactivate_monitoring_and_send_queue(): if settings().getBoolean(["feature", "sdSupport"]): self._sdFileList = [] - if printing: - self._callback.on_comm_print_job_failed() - def setTemperatureOffset(self, offsets): self._tempOffsets.update(offsets) From e5caa3d0bf77ed23f04c1d96ab0fa997d19d2006 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Tue, 3 Apr 2018 17:41:52 +0200 Subject: [PATCH 329/333] Preparing release of 1.3.7rc4 --- CHANGELOG.md | 10 ++++++++++ SUPPORTERS.md | 5 ++--- .../translations/de/LC_MESSAGES/messages.mo | Bin 126108 -> 126190 bytes .../translations/de/LC_MESSAGES/messages.po | 8 ++++---- translations/de/LC_MESSAGES/messages.mo | Bin 126108 -> 126190 bytes translations/de/LC_MESSAGES/messages.po | 8 ++++---- translations/messages.pot | 9 +++++---- 7 files changed, 25 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a1bb983277..963eb5425e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ # OctoPrint Changelog +## 1.3.7rc4 (2018-04-04) + +### Bugfixes + + * [#2536](https://github.com/foosel/OctoPrint/issues/2536) - Fix a wrong state tracking when starting an SD print through the controller, causing a disconnect due to a timeout. + * [#2544](https://github.com/foosel/OctoPrint/issues/2544) - Fix an exception when connecting to the raw websocket at `/sockjs/websocket` (instead of `/sockjs///websocket`). + * [#2546](https://github.com/foosel/OctoPrint/issues/2546) - Fix the `PRINT_FAILED` event getting triggered twice on print failure due to disconnect + +([Commits](https://github.com/foosel/OctoPrint/compare/1.3.7rc3...1.3.7rc4)) + ## 1.3.7rc3 (2018-03-29) ### Bugfixes diff --git a/SUPPORTERS.md b/SUPPORTERS.md index de67c9adb3..1de21d6114 100644 --- a/SUPPORTERS.md +++ b/SUPPORTERS.md @@ -6,8 +6,6 @@ thanks to everyone who contributed! ## Patreon Patrons - * 3D Moniak - * Aleph Objects, Inc. * Andrew Moorby * Arnljot Arntsen * Boris Hussein @@ -32,6 +30,7 @@ thanks to everyone who contributed! * Kale Stedman * Kazuhiro Ogura * Kodama Inc. + * LulzBot® * Makespace Madrid * Mark Walker * Martin Beattie @@ -55,4 +54,4 @@ thanks to everyone who contributed! * Thomas Hatley * Trent Shumay -and 1214 more wonderful people pledging on the [Patreon campaign](https://patreon.com/foosel)! \ No newline at end of file +and 1217 more wonderful people pledging on the [Patreon campaign](https://patreon.com/foosel)! \ No newline at end of file diff --git a/src/octoprint/translations/de/LC_MESSAGES/messages.mo b/src/octoprint/translations/de/LC_MESSAGES/messages.mo index f4be601cde52c98e20c75e0ec8888b2364d0e55b..c47cb5b84d44b5104c6f7a6f72f3b7d84e8ba148 100644 GIT binary patch delta 12698 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 delta 12595 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 CHnm!yPk-_Vy(l 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\n" "Language: de\n" "Language-Team: German (http://www.transifex.com/projects/p/octoprint/language/de/)\n" @@ -1536,8 +1536,8 @@ msgstr "OctoPrint Release Channel" #: src/octoprint/plugins/softwareupdate/templates/snippets/plugins/softwareupdate/releaseChannel.jinja2:5 #, python-format -msgid "Make sure you have read \"Using Release Channels\" before switching to a release channel other than \"Stable\"" -msgstr "Stelle sicher, dass du \"Using Release Channels\" gelesen hast, bevor du einen anderen Release Channel als \"Stable\" auswählst." +msgid "Make sure you have read \"How to use the release channels to help test release candidates\" before switching to a release channel other than \"Stable\"" +msgstr "Stelle sicher, dass du \"How to use the release channels to help test release candidates\" gelesen hast, bevor du einen anderen Release Channel als \"Stable\" auswählst." #: src/octoprint/plugins/softwareupdate/templates/snippets/plugins/softwareupdate/versionTracking.jinja2:2 msgid "OctoPrint version tracking" diff --git a/translations/de/LC_MESSAGES/messages.mo b/translations/de/LC_MESSAGES/messages.mo index f4be601cde52c98e20c75e0ec8888b2364d0e55b..c47cb5b84d44b5104c6f7a6f72f3b7d84e8ba148 100644 GIT binary patch delta 12698 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 delta 12595 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 CHnm!yPk-_Vy(l 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\n" "Language: de\n" "Language-Team: German (http://www.transifex.com/projects/p/octoprint/language/de/)\n" @@ -1536,8 +1536,8 @@ msgstr "OctoPrint Release Channel" #: src/octoprint/plugins/softwareupdate/templates/snippets/plugins/softwareupdate/releaseChannel.jinja2:5 #, python-format -msgid "Make sure you have read \"Using Release Channels\" before switching to a release channel other than \"Stable\"" -msgstr "Stelle sicher, dass du \"Using Release Channels\" gelesen hast, bevor du einen anderen Release Channel als \"Stable\" auswählst." +msgid "Make sure you have read \"How to use the release channels to help test release candidates\" before switching to a release channel other than \"Stable\"" +msgstr "Stelle sicher, dass du \"How to use the release channels to help test release candidates\" gelesen hast, bevor du einen anderen Release Channel als \"Stable\" auswählst." #: src/octoprint/plugins/softwareupdate/templates/snippets/plugins/softwareupdate/versionTracking.jinja2:2 msgid "OctoPrint version tracking" diff --git a/translations/messages.pot b/translations/messages.pot index dc10155d38..5709bddc24 100644 --- a/translations/messages.pot +++ b/translations/messages.pot @@ -6,9 +6,9 @@ #, fuzzy msgid "" msgstr "" -"Project-Id-Version: OctoPrint 1.3.7rc2.dev16+g47267abcd.dirty\n" +"Project-Id-Version: OctoPrint 1.3.7rc4.dev6+g2c77614b3.dirty\n" "Report-Msgid-Bugs-To: i18n@octoprint.org\n" -"POT-Creation-Date: 2018-03-23 15:23+0100\n" +"POT-Creation-Date: 2018-04-03 17:40+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -1709,8 +1709,9 @@ msgstr "" #, python-format msgid "" "Make sure you have read \"Using Release Channels\" before " -"switching to a release channel other than \"Stable\"" +"rel=\"noreferrer noopener\">\"How to use the release channels to help " +"test release candidates\" before switching to a release channel other" +" than \"Stable\"" msgstr "" #: src/octoprint/plugins/softwareupdate/templates/snippets/plugins/softwareupdate/versionTracking.jinja2:2 From 440ccf165e2c5a9ed97a2d6842cc4699aaaa9fb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Mon, 9 Apr 2018 11:50:16 +0200 Subject: [PATCH 330/333] [Docs] Add missing image Fixes #2557 --- ...ndledplugins-printer_safety_check-example.png | Bin 0 -> 6695 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 docs/images/bundledplugins-printer_safety_check-example.png diff --git a/docs/images/bundledplugins-printer_safety_check-example.png b/docs/images/bundledplugins-printer_safety_check-example.png new file mode 100644 index 0000000000000000000000000000000000000000..c6bac3a8e0907a853947a28e5d7069d1dcace12c GIT binary patch literal 6695 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 o!(O_v Date: Mon, 9 Apr 2018 12:38:56 +0200 Subject: [PATCH 331/333] Preparing release of 1.3.7 --- CHANGELOG.md | 67 +++++++++++++++++++++++---------------------------- SUPPORTERS.md | 3 ++- 2 files changed, 32 insertions(+), 38 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 963eb5425e..560a28bbff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,45 +1,11 @@ # OctoPrint Changelog -## 1.3.7rc4 (2018-04-04) - -### Bugfixes - - * [#2536](https://github.com/foosel/OctoPrint/issues/2536) - Fix a wrong state tracking when starting an SD print through the controller, causing a disconnect due to a timeout. - * [#2544](https://github.com/foosel/OctoPrint/issues/2544) - Fix an exception when connecting to the raw websocket at `/sockjs/websocket` (instead of `/sockjs///websocket`). - * [#2546](https://github.com/foosel/OctoPrint/issues/2546) - Fix the `PRINT_FAILED` event getting triggered twice on print failure due to disconnect - -([Commits](https://github.com/foosel/OctoPrint/compare/1.3.7rc3...1.3.7rc4)) - -## 1.3.7rc3 (2018-03-29) - -### Bugfixes - - * [#2524](https://github.com/foosel/OctoPrint/issues/2524) Ignore `wait` while job is on hold. - -([Commits](https://github.com/foosel/OctoPrint/compare/1.3.7rc2...1.3.7rc3)) - -## 1.3.7rc2 (2018-03-23) - -### Bugfixes - - * [#1951](http://github.com/foosel/OctoPrint/issues/1951) - Fixed plugins being able to modify internal state data (e.g. progress, job), causing concurrent modification and resulting run time errors in the printer state processing. - * [#2494](https://github.com/foosel/OctoPrint/issues/2494) - Fixed `undefined` values not saving in the settings. - * [#2499](https://github.com/foosel/OctoPrint/issues/2499) - Fixed communication error notification lacking the actual error message. - * [#2501](https://github.com/foosel/OctoPrint/issues/2501) - Fixed a bug causing log downloads to fail with an HTTP 500 error. - * [#2506](https://github.com/foosel/OctoPrint/issues/2506) - Fixed `printer.get_current_data` and `printer.get_current_job` returning `frozendict` instead of `dict` instances, causing issues with plugins relying on being able to modify the returned data (e.g. [dattas/OctoPrint-DetailedProgress#26](https://github.com/dattas/OctoPrint-DetailedProgress/issues/26)). - * [#2508](https://github.com/foosel/OctoPrint/issues/2508) - Fixed HTTP 500 error on `/api/slicing` in case of an unconfigured slicer. - * Have `OctoPrintJsonEncoder` fall back to regular flask JSON encoder, otherwise we might not be able to serialize some data types we need to be able to serialize. - * Use `pkg_resources` to determine pip version during environment check instead of `import pip; pip.__version__` since the latter causes issues with pip version 9.0.2. In the same spirit make `pip.main` approach of calling `pip` in the PipCaller the last resort during auto detection, only after trying `pip` or `pip.exe` inside the same folder as the Python executable. - * Use `octoprint.util.monotonic_time` instead of `monotonic.monotonic` in comm layer. - * Fixed timelapse not stopping on print failure due to firmware error due to missing `PrintFailed` event. - -([Commits](https://github.com/foosel/OctoPrint/compare/1.3.7rc1...1.3.7rc2)) - -## 1.3.7rc1 (2018-03-19) +## 1.3.7 (2018-04-09) ### Improvements * [#324](https://github.com/foosel/OctoPrint/issues/324) & [#2414](https://github.com/foosel/OctoPrint/issues/2414) - Native support for the following [@ commands](http://docs.octoprint.org/en/maintenance/features/atcommands.html): `@pause` (pauses the print), `@resume` (resumes the print), `@cancel` or `@abort` (cancels the print). More commands can be added through the plugin hooks [`octoprint.comm.protocol.atcommand.*`](http://docs.octoprint.org/en/maintenance/plugins/hooks.html#octoprint-comm-protocol-atcommand-phase). + * [#1951](http://github.com/foosel/OctoPrint/issues/1951) - Fixed plugins being able to modify internal state data (e.g. progress, job), causing concurrent modification and resulting run time errors in the printer state processing. * [#2208](https://github.com/foosel/OctoPrint/issues/2208) - New plugin hook [`octoprint.comm.protocol.gcode.error`](http://docs.octoprint.org/en/maintenance/plugins/hooks.html#octoprint-comm-protocol-gcode-error) to allow plugins to override OctoPrint's handling of `Error:`/`!!` messages reported by the firmware. Can be used to "downgrade" errors that aren't actually fatal errors that make the printer halt. * [#2213](https://github.com/foosel/OctoPrint/issues/2213) - Made sure to prevent accidental configuration of a temperature cutoff value less than 1min for the temperature graph. * [#2250](https://github.com/foosel/OctoPrint/pull/2250) - Added [`octoprint.util.ResettableTimer`](http://docs.octoprint.org/en/maintenance/modules/util.html#octoprint.util.ResettableTimer) helper class. @@ -50,6 +16,7 @@ * Firmware must send a "File opened: ..." message on start of the print * Firmware must respond to an immediately sent `M27` with `SD printing byte /` * Firmware must stay responsive during ongoing print to allow for regular M27 polls (or push those automatically) or M25 to pause/cancel the print through OctoPrint. + * Firmware must send `Done printing file` or respond to `M27` with `Not SD printing` when SD printing finishes (either due to being done or to having been cancelled by the user). * [#2362](https://github.com/foosel/OctoPrint/issues/2362) - Added option to configure timelapse snapshot timeout. * [#2367](https://github.com/foosel/OctoPrint/issues/2367) - Added support for `//action:cancel` action command. * [#2378](https://github.com/foosel/OctoPrint/issues/2378) - Made GCODE viewer gracefully handle GCODE subcodes. @@ -104,11 +71,37 @@ * [#2426](https://github.com/foosel/OctoPrint/issues/2426) - Made resend logging to `octoprint.log` unicode safe * [#2442](https://github.com/foosel/OctoPrint/issues/2442) - Don't even import disabled plugins, use a dummy entry for them just like for backlisted plugins. More resilience against misbehaving plugins. * [#2461](https://github.com/foosel/OctoPrint/issues/2461) - Fixed OctoPrint not properly disconnecting in case of a firmware error. + * [#2494](https://github.com/foosel/OctoPrint/issues/2494) - Fixed `undefined` values not saving in the settings. + * [#2499](https://github.com/foosel/OctoPrint/issues/2499) (regression) - Fixed communication error notification lacking the actual error message. + * [#2501](https://github.com/foosel/OctoPrint/issues/2501) (regression) - Fixed a bug causing log downloads to fail with an HTTP 500 error. + * [#2506](https://github.com/foosel/OctoPrint/issues/2506) (regression) - Fixed `printer.get_current_data` and `printer.get_current_job` returning `frozendict` instead of `dict` instances, causing issues with plugins relying on being able to modify the returned data (e.g. [dattas/OctoPrint-DetailedProgress#26](https://github.com/dattas/OctoPrint-DetailedProgress/issues/26)). + * [#2508](https://github.com/foosel/OctoPrint/issues/2508) (regression) - Fixed HTTP 500 error on `/api/slicing` in case of an unconfigured slicer. + * [#2524](https://github.com/foosel/OctoPrint/issues/2524) - Ignore `wait` while job is on hold. + * [#2536](https://github.com/foosel/OctoPrint/issues/2536) - Fix a wrong state tracking when starting an SD print through the controller, causing a disconnect due to a timeout. + * [#2544](https://github.com/foosel/OctoPrint/issues/2544) (regression) - Fix an exception when connecting to the raw websocket at `/sockjs/websocket` (instead of `/sockjs///websocket`). + * [#2546](https://github.com/foosel/OctoPrint/issues/2546) (regression) - Fix the `PRINT_FAILED` event getting triggered twice on print failure due to disconnect + * Use `pkg_resources` to determine pip version during environment check instead of `import pip; pip.__version__` since the latter causes issues with pip version 9.0.2. In the same spirit make `pip.main` approach of calling `pip` in the PipCaller the last resort during auto detection, only after trying `pip` or `pip.exe` inside the same folder as the Python executable. + * Use `octoprint.util.monotonic_time` instead of `monotonic.monotonic` in comm layer. + * Fixed timelapse not stopping on print failure due to firmware error due to missing `PrintFailed` event. * Announcement Plugin: Fixed a missing line break in case of more than three announcements in a notification. * Docs: Documentation for printer profile related bits and pieces * Docs: Various fixes of typos and grammar. + * Have `OctoPrintJsonEncoder` fall back to regular flask JSON encoder, otherwise we might not be able to serialize some data types we need to be able to serialize. (regression) + +### Known bugs + + * [#2547](https://github.com/foosel/OctoPrint/issues/2547) - Error during processing of afterPrintJobDone script will trigger PRINT_FAILED event even though PRINT_DONE event was already triggered -([Commits](https://github.com/foosel/OctoPrint/compare/1.3.6...1.3.7rc1)) +### More Information + + * [Commits](https://github.com/foosel/OctoPrint/compare/1.3.6...1.3.7) + * Release Candidates: + * [1.3.7rc1](https://github.com/foosel/OctoPrint/releases/tag/1.3.7rc1) + * [1.3.7rc2](https://github.com/foosel/OctoPrint/releases/tag/1.3.7rc2) + * [1.3.7rc3](https://github.com/foosel/OctoPrint/releases/tag/1.3.7rc3) + * [1.3.7rc4](https://github.com/foosel/OctoPrint/releases/tag/1.3.7rc4) + * A special **Thank you!** to everyone who reported back on these release candidates this time: + [aaronkeck](https://github.com/aaronkeck), [akurz42](https://github.com/akurz42), [andrivet](https://github.com/andrivet), [anthonyst91](https://github.com/anthonyst91), [arhi](https://github.com/arhi), [b-morgan](https://github.com/b-morgan), [BryanSmithDev](https://github.com/BryanSmithDev), [chippypilot](https://github.com/chippypilot), [ChrisHeerschap](https://github.com/ChrisHeerschap), [Crowlord](https://github.com/Crowlord), [dforsi](https://github.com/dforsi), [drdelaney](https://github.com/drdelaney), [FormerLurker](https://github.com/FormerLurker), [goeland86](https://github.com/goeland86), [inspiredbylife](https://github.com/inspiredbylife), [jbjones27](https://github.com/jbjones27), [jesasi](https://github.com/jesasi), [jneilliii](https://github.com/jneilliii), [JohnOCFII](https://github.com/JohnOCFII), [kantlivelong](https://github.com/kantlivelong), [lnx13](https://github.com/lnx13), [makaper](https://github.com/makaper), [markwal](https://github.com/markwal), [MiquelAdell](https://discourse.octoprint.org/u/MiquelAdell), [ml0w](https://github.com/ml0w), [mmotley999](https://github.com/mmotley999), [mrhanman](https://github.com/mrhanman), [SCiunczyk](https://github.com/SCiunczyk), [tdub415](https://github.com/tdub415), [tedder42](https://discourse.octoprint.org/u/tedder42), [thatjoshguy](https://github.com/thatjoshguy), [wescrockett](https://github.com/wescrockett) ## 1.3.6 (2017-12-12) diff --git a/SUPPORTERS.md b/SUPPORTERS.md index 1de21d6114..6159d8f79f 100644 --- a/SUPPORTERS.md +++ b/SUPPORTERS.md @@ -23,6 +23,7 @@ thanks to everyone who contributed! * günter weber * James Seigel * Jeff Moe + * Jeff Renfer * Jefferson Hunt * Jeremiah Avery * Josh Daniels @@ -54,4 +55,4 @@ thanks to everyone who contributed! * Thomas Hatley * Trent Shumay -and 1217 more wonderful people pledging on the [Patreon campaign](https://patreon.com/foosel)! \ No newline at end of file +and 1233 more wonderful people pledging on the [Patreon campaign](https://patreon.com/foosel)! \ No newline at end of file From 8869748ecc0526e964d0b7dec1bb6a54072f41cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Mon, 9 Apr 2018 15:53:25 +0200 Subject: [PATCH 332/333] Don't trigger ok timeout while heating or in long running command Workaround for printers that don't send busy:processing during heatups like they should. --- src/octoprint/util/comm.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/octoprint/util/comm.py b/src/octoprint/util/comm.py index 45975124ab..3780dbf04d 100644 --- a/src/octoprint/util/comm.py +++ b/src/octoprint/util/comm.py @@ -1554,7 +1554,11 @@ def convert_line(line): handled = True # process timeouts - elif ((line == "" and now > self._timeout) or (self.isPrinting() and not self.isSdPrinting() and not self.job_on_hold and now > self._ok_timeout)) \ + elif ((line == "" and now > self._timeout) or (self.isPrinting() + and not self.isSdPrinting() + and not self.job_on_hold + and not self._long_running_command + and not self._heating and now > self._ok_timeout)) \ and (not self._blockWhileDwelling or not self._dwelling_until or now > self._dwelling_until): # We have two timeout variants: # From 6d7f8a2a64b364504f11dafd5009a85fb785127a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Mon, 9 Apr 2018 15:56:35 +0200 Subject: [PATCH 333/333] Don't raise error when trying to read from finished print Could be caused by timing issues and one `continue_sending` too many, so just ignore this but log a warning. --- src/octoprint/util/comm.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/octoprint/util/comm.py b/src/octoprint/util/comm.py index 3780dbf04d..03d4c32c7d 100644 --- a/src/octoprint/util/comm.py +++ b/src/octoprint/util/comm.py @@ -3547,7 +3547,8 @@ def getNext(self): """ with self._handle_mutex: if self._handle is None: - raise ValueError("File %s is not open for reading" % self._filename) + self._logger.warn(u"File {} is not open for reading".format(self._filename)) + return None, None, None try: offsets = self._offsets_callback() if self._offsets_callback is not None else None