More work on the template refactoring and new template plugin types
This commit is contained in:
parent
73466bb513
commit
23387d6e48
25 changed files with 971 additions and 746 deletions
BIN
docs/images/template-plugin-types-main.png
Normal file
BIN
docs/images/template-plugin-types-main.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 100 KiB |
BIN
docs/images/template-plugin-types-settings.png
Normal file
BIN
docs/images/template-plugin-types-settings.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 47 KiB |
|
|
@ -37,6 +37,9 @@ class PluginInfo(object):
|
|||
|
||||
self._version = version
|
||||
|
||||
if self.name is None:
|
||||
raise ValueError("Plugin {key} doesn't have a name".format(key=key))
|
||||
|
||||
def __str__(self):
|
||||
return "{name} ({version})".format(name=self.name, version=self.version if self.version else "unknown")
|
||||
|
||||
|
|
@ -109,7 +112,8 @@ class PluginManager(object):
|
|||
|
||||
self.plugins = dict()
|
||||
self.plugin_hooks = defaultdict(list)
|
||||
self.plugin_implementations = defaultdict(list)
|
||||
self.plugin_implementations = defaultdict(set)
|
||||
self.plugin_implementations_by_type = defaultdict(list)
|
||||
|
||||
self.registered_clients = []
|
||||
|
||||
|
|
@ -223,10 +227,31 @@ class PluginManager(object):
|
|||
# evaluate registered implementations
|
||||
for plugin_type in self.plugin_types:
|
||||
implementations = plugin.get_implementations(plugin_type)
|
||||
self.plugin_implementations[plugin_type] += ( (name, implementation) for implementation in implementations )
|
||||
self.plugin_implementations_by_type[plugin_type] += ( (name, implementation) for implementation in implementations )
|
||||
self.plugin_implementations[name].update(plugin.get_implementations())
|
||||
|
||||
self.log_registered_plugins()
|
||||
|
||||
def initialize_implementations(self, additional_injects=None):
|
||||
if additional_injects is None:
|
||||
additional_injects = dict()
|
||||
|
||||
for name, implementations in self.plugin_implementations.items():
|
||||
plugin = self.plugins[name]
|
||||
for implementation in implementations:
|
||||
kwargs = dict(additional_injects)
|
||||
kwargs.update(dict(
|
||||
identifier=name,
|
||||
basefolder=os.path.realpath(plugin.location),
|
||||
logger=logging.getLogger("octoprint.plugins." + name),
|
||||
))
|
||||
try:
|
||||
implementation.pre_initialize(**kwargs)
|
||||
except:
|
||||
self.logger.exception("Exception while initializing plugin")
|
||||
|
||||
self.logger.info("Initialized {count} plugin(s)".format(count=len(self.plugin_implementations)))
|
||||
|
||||
def log_registered_plugins(self):
|
||||
if len(self.plugins) <= 0:
|
||||
self.logger.info("No plugins found")
|
||||
|
|
@ -247,7 +272,7 @@ class PluginManager(object):
|
|||
result = None
|
||||
|
||||
for t in types:
|
||||
implementations = self.plugin_implementations[t]
|
||||
implementations = self.plugin_implementations_by_type[t]
|
||||
if result is None:
|
||||
result = set(implementations)
|
||||
else:
|
||||
|
|
@ -283,4 +308,10 @@ class PluginManager(object):
|
|||
|
||||
|
||||
class Plugin(object):
|
||||
pass
|
||||
def pre_initialize(self, **kwargs):
|
||||
for arg, value in kwargs.items():
|
||||
setattr(self, "_" + arg, value)
|
||||
self.initialize()
|
||||
|
||||
def initialize(self):
|
||||
pass
|
||||
|
|
|
|||
|
|
@ -63,20 +63,13 @@ class AssetPlugin(Plugin):
|
|||
|
||||
def get_asset_folder(self):
|
||||
"""
|
||||
Defines the folder where the plugin stores its static assets as defined in ``get_assets``. Usually an
|
||||
implementation such as
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def get_asset_folder(self):
|
||||
import os
|
||||
return os.path.join(os.path.dirname(os.path.realpath(__file__)), "static")
|
||||
|
||||
should be sufficient here. This way, assets can be put into a sub folder ``static`` in the plugin directory.
|
||||
Defines the folder where the plugin stores its static assets as defined in ``get_assets``. Override this if
|
||||
your plugin stores its assets at some other place than the ``static`` sub folder in the plugin base directory.
|
||||
|
||||
:return: the absolute path to the folder where the plugin stores its static assets
|
||||
"""
|
||||
return None
|
||||
import os
|
||||
return os.path.join(self._basefolder, "static")
|
||||
|
||||
def get_assets(self):
|
||||
"""
|
||||
|
|
@ -108,15 +101,168 @@ class AssetPlugin(Plugin):
|
|||
|
||||
:return: a dictionary describing the static assets to publish for the plugin
|
||||
"""
|
||||
return []
|
||||
return dict()
|
||||
|
||||
|
||||
class TemplatePlugin(Plugin):
|
||||
"""
|
||||
Using the ``TemplatePlugin`` mixin plugins may inject their own components into the OctoPrint web interface.
|
||||
|
||||
Currently OctoPrint supports the following types of injections:
|
||||
|
||||
Navbar
|
||||
The right part of the navigation bar located at the top of the UI can be enriched with additional links. Note that
|
||||
with the current implementation, plugins will always be located *to the left* of the existing links.
|
||||
Sidebar
|
||||
The left side bar containing Connection, State and Files sections can be enriched with additional sections. Note
|
||||
that with the current implementations, plugins will always be located *beneath* the existing sections.
|
||||
Tabs
|
||||
The available tabs of the main part of the interface may be extended with additional tabs originating from within
|
||||
plugins. Note that with the current implementation, plugins will always be located *to the right* of the existing
|
||||
tabs.
|
||||
Settings
|
||||
Plugins may inject a dialog into the existing settings view. Note that with the current implementations, plugins
|
||||
will always be listed beneath the "Plugins" header in the settings link list, ordered alphabetically after
|
||||
their displayed name.
|
||||
|
||||
.. figure:: ../images/template-plugin-types-main.png
|
||||
:align: center
|
||||
:alt: Template injection types in the main part of the interface
|
||||
|
||||
Template injection types in the main part of the interface
|
||||
|
||||
.. figure:: ../images/template-plugin-types-settings.png
|
||||
:align: center
|
||||
:alt: Template injection types in the settings
|
||||
|
||||
Template injection types in the settings
|
||||
|
||||
Which components a plugin supplies is controlled by a number of special template variables returned by the overridden
|
||||
``get_template_vars`` method. The following special keys are supported:
|
||||
|
||||
_settings
|
||||
Configures the settings component to inject. The value must be a dictionary, supported values are the following:
|
||||
|
||||
name
|
||||
The name under which to include the settings pane, will be visible in the navigation tree on the left of the
|
||||
settings dialog
|
||||
custom_bindings
|
||||
A boolean value indicating whether the default settings view model should be bound to the settings pane (``false``, default)
|
||||
or if a custom binding will be used by the plugin (``true``)
|
||||
data_bind
|
||||
Additional knockout data bindings to apply to the template container, can be used to add further behaviour to
|
||||
the container based on internal state if necessary. Note that if you include this and set ``custom_bindings``
|
||||
to ``True``, you need to also supply ``allowBindings: true`` as part of your custom data binding, otherwise
|
||||
it won't work.
|
||||
|
||||
The included settings dialog template file must be called ``<plugin name>_settings.jinja2`` (e.g. ``myplugin_settings.jinja2``).
|
||||
The template will be already included in the necessary wrapper divs, you just need to supply the pure content.
|
||||
_tab
|
||||
Configures the tab component to inject. The value must be a dictionary, supported values are the following:
|
||||
|
||||
name
|
||||
The name under which to include the tab, will be visible in the list of tabs on the top of the main view
|
||||
data_bind
|
||||
Additional knockout data bindings to apply to the template container, can be used to add further behaviour to
|
||||
the container based on internal state if necessary. Note that if you include this and set ``custom_bindings``
|
||||
to ``True``, you need to also supply ``allowBindings: true`` as part of your custom data binding, otherwise
|
||||
it won't work.
|
||||
|
||||
The template included into the tab pane section must be called ``<plugin name>_tab.jinja2`` (e.g. ``myplugin_tab.jinja2``).
|
||||
The template will be already included in the necessary wrapper divs, you just need to supply the pure content.
|
||||
_sidebar
|
||||
Configures the sidebar component to inject. The value must be a dictionary, supported values are the following:
|
||||
|
||||
name
|
||||
The name of the sidebar entry
|
||||
icon
|
||||
Icon to use for the sidebar header, should be the name of a Font Awesome icon without the leading ``icon-`` part
|
||||
header_addon
|
||||
Additional template to include in the head section of the sidebar item. For an example of this, see the additional
|
||||
options included in the "Files" section
|
||||
data_bind
|
||||
Additional knockout data bindings to apply to the template container, can be used to add further behaviour to
|
||||
the container based on internal state if necessary. Note that if you include this and set ``custom_bindings``
|
||||
to ``True``, you need to also supply ``allowBindings: true`` as part of your custom data binding, otherwise
|
||||
it won't work.
|
||||
|
||||
The template included into the sidebar section must be called ``<plugin name>_sidebar.jinja2`` (e.g. ``myplugin_sidebar.jinja2``).
|
||||
The template will be already included in the necessary wrapper divs, you just need to supply the pure content.
|
||||
_navbar
|
||||
Configures the navbar component to inject. The value must be an empty dictionary.
|
||||
|
||||
The template included into the sidebar section must be called ``<plugin name>_navbar.jinja2``
|
||||
|
||||
The following is an example for a simple plugin which injects all four types of templates into the interfaces:
|
||||
|
||||
``helloworld/__init__.py``
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import octoprint.plugin
|
||||
|
||||
class HelloWorldPlugin(octoprint.plugin.TemplatePlugin, octoprint.plugin.AssetPlugin):
|
||||
def get_template_vars(self):
|
||||
return dict(
|
||||
_settings=dict(name="Hello World"),
|
||||
_tab=dict(name="Hello World", custom_bindings=True, data_bind="allowBindings: true, visible: self.loginState.isUser()"),
|
||||
_sidebar=dict(name="Hello World", icon="gear"),
|
||||
_navbar=dict()
|
||||
)
|
||||
|
||||
def get_assets(self):
|
||||
return dict(
|
||||
js=["js/helloworld.js"]
|
||||
)
|
||||
|
||||
__plugin_name__ = "Hello World"
|
||||
__plugin_version__ = "1.0"
|
||||
__plugin_implementations__ = [HelloWorldPlugin()]
|
||||
|
||||
``helloworld/templates/helloworld_settings.jinja2``
|
||||
|
||||
.. code-block:: html+jinja
|
||||
|
||||
<h1>Hello World!</h1>
|
||||
|
||||
This is a custom settings pane.
|
||||
|
||||
``helloworld/templates/helloworld_tab.jinja2``
|
||||
|
||||
.. code-block:: html+jinja
|
||||
|
||||
<h1>Hello World!</h1>
|
||||
|
||||
This is a custom tab.
|
||||
|
||||
``helloworld/templates/helloworld_sidebar.jinja2``
|
||||
|
||||
.. code-block:: html+jinja
|
||||
|
||||
Hello World! This is a custom side bar.
|
||||
|
||||
``helloworld/templates/helloworld_navbar.jinja2``
|
||||
|
||||
.. code-block:: html+jinja
|
||||
|
||||
<li>
|
||||
<a class="pull-right" href="http://www.example.com"><i class="icon-gear"></i> {{ _('Hello World!') }}</a>
|
||||
</li>
|
||||
|
||||
"""
|
||||
|
||||
def get_template_vars(self):
|
||||
return dict()
|
||||
|
||||
def get_template_folder(self):
|
||||
return None
|
||||
"""
|
||||
Defines the folder where the plugin stores its templates. Override this if your plugin stores its templates at
|
||||
some other place than the ``templates`` sub folder in the plugin base directory.
|
||||
|
||||
:return: the absolute path to the folder where the plugin stores its jinja2 templates
|
||||
"""
|
||||
import os
|
||||
return os.path.join(self._basefolder, "templates")
|
||||
|
||||
|
||||
class SimpleApiPlugin(Plugin):
|
||||
|
|
|
|||
|
|
@ -111,41 +111,78 @@ def index():
|
|||
template_plugins = pluginManager.get_implementations(octoprint.plugin.TemplatePlugin)
|
||||
|
||||
plugin_vars = dict()
|
||||
plugin_includes = dict()
|
||||
plugin_includes_navbar = []
|
||||
plugin_includes_sidebar = []
|
||||
plugin_includes_tabs = []
|
||||
plugin_includes_settings = []
|
||||
plugin_names = template_plugins.keys()
|
||||
for name, implementation in template_plugins.items():
|
||||
vars = implementation.get_template_vars()
|
||||
if not isinstance(vars, dict):
|
||||
continue
|
||||
|
||||
plugin_data = dict()
|
||||
if "_navbar" in vars and isinstance(vars["_navbar"], dict):
|
||||
data = dict(vars["_navbar"])
|
||||
data["_div"] = "navbar_plugin_" + name
|
||||
data["_template"] = name + "_navbar.jinja2"
|
||||
plugin_includes_navbar.append(data)
|
||||
del vars["_navbar"]
|
||||
|
||||
if "_settings" in vars and "name" in vars["_settings"]:
|
||||
plugin_data["_settings"] = dict(vars["_settings"])
|
||||
plugin_data["_settings"]["_div"] = "settings_plugin_" + name
|
||||
plugin_data["_settings"]["_template"] = name + "_settings.jinja2"
|
||||
del vars["_settings"]
|
||||
|
||||
if "_tab" in vars and "name" in vars["_tab"]:
|
||||
plugin_data["_tab"] = dict(vars["_tab"])
|
||||
plugin_data["_tab"]["_div"] = "tab_plugin_" + name
|
||||
plugin_data["_tab"]["_template"] = name + "_tab.jinja2"
|
||||
del vars["_tab"]
|
||||
|
||||
if "_sidebar" in vars and "name" in vars["_sidebar"]:
|
||||
plugin_data["_sidebar"] = vars["_sidebar"]
|
||||
plugin_data["_sidebar"]["_div"] = "sidebar_plugin_" + name
|
||||
plugin_data["_sidebar"]["_template"] = name + "_sidebar.jinja2"
|
||||
if "_sidebar" in vars and isinstance(vars["_sidebar"], dict) and "name" in vars["_sidebar"]:
|
||||
data = dict(vars["_sidebar"])
|
||||
data["_div"] = "sidebar_plugin_" + name
|
||||
data["_template"] = name + "_sidebar.jinja2"
|
||||
plugin_includes_sidebar.append((data["name"], data))
|
||||
del vars["_sidebar"]
|
||||
|
||||
if len(plugin_data):
|
||||
plugin_includes[name] = plugin_data
|
||||
if "_tab" in vars and isinstance(vars["_tab"], dict) and "name" in vars["_tab"]:
|
||||
data = dict(vars["_tab"])
|
||||
data["_div"] = "tab_plugin_" + name
|
||||
data["_template"] = name + "_tab.jinja2"
|
||||
plugin_includes_tabs.append((data["name"], data))
|
||||
del vars["_tab"]
|
||||
|
||||
if "_settings" in vars and isinstance(vars["_settings"], dict) and "name" in vars["_settings"]:
|
||||
data = dict(vars["_settings"])
|
||||
data["_div"] = "settings_plugin_" + name
|
||||
data["_template"] = name + "_settings.jinja2"
|
||||
plugin_includes_settings.append((data["name"], data))
|
||||
del vars["_settings"]
|
||||
|
||||
for var_name, var_value in vars.items():
|
||||
plugin_vars["plugin_" + name + "_" + var_name] = var_value
|
||||
|
||||
#~~ navbar
|
||||
|
||||
navbar_entries = plugin_includes_navbar + [
|
||||
dict(_template="navbar/settings.jinja2", _div="navbar_settings", styles=["display: none"], data_bind="visible: loginState.isAdmin"),
|
||||
dict(_template="navbar/systemmenu.jinja2", _div="navbar_systemmenu", styles=["display: none"], classes=["dropdown"], data_bind="visible: loginState.isAdmin"),
|
||||
dict(_template="navbar/login.jinja2", _div="navbar_login", classes=["dropdown"])
|
||||
]
|
||||
|
||||
#~~ sidebar
|
||||
|
||||
sidebar_entries = [
|
||||
(gettext("Connection"), dict(_template="sidebar/connection.jinja2", _div="connection", icon="signal", styles_wrapper=["display: none"], data_bind="visible: loginState.isAdmin")),
|
||||
(gettext("State"), dict(_template="sidebar/state.jinja2", _div="state", icon="info-sign")),
|
||||
(gettext("Files"), dict(_template="sidebar/files.jinja2", _div="files", icon="list", classes_content=["overflow_visible"], header_addon="sidebar/files_header.jinja2"))
|
||||
] + plugin_includes_sidebar
|
||||
|
||||
#~~ tabs
|
||||
|
||||
tab_entries = [
|
||||
(gettext("Temperature"), dict(_template="tabs/temperature.jinja2", _div="temp")),
|
||||
(gettext("Control"), dict(_template="tabs/control.jinja2", _div="control")),
|
||||
(gettext("GCode Viewer"), dict(_template="tabs/gcodeviewer.jinja2", _div="gcode")),
|
||||
(gettext("Terminal"), dict(_template="tabs/terminal.jinja2", _div="term")),
|
||||
(gettext("Timelapse"), dict(_template="tabs/timelapse.jinja2", _div="timelapse"))
|
||||
] + plugin_includes_tabs
|
||||
|
||||
#~~ settings dialog
|
||||
|
||||
settings_entries = [
|
||||
(gettext("Printer"), None),
|
||||
(gettext("Serial Connection"), dict(_template="dialogs/settings/serialconnection.jinja2", _div="settings_serialConnection", _active=True)),
|
||||
(gettext("Serial Connection"), dict(_template="dialogs/settings/serialconnection.jinja2", _div="settings_serialConnection")),
|
||||
(gettext("Printer Profiles"), dict(_template="dialogs/settings/printerprofiles.jinja2", _div="settings_printerProfiles")),
|
||||
(gettext("Temperatures"), dict(_template="dialogs/settings/temperatures.jinja2", _div="settings_temperature")),
|
||||
(gettext("Terminal Filters"), dict(_template="dialogs/settings/terminalfilters.jinja2", _div="settings_terminalFilters")),
|
||||
|
|
@ -159,14 +196,9 @@ def index():
|
|||
(gettext("Appearance"), dict(_template="dialogs/settings/appearance.jinja2", _div="settings_appearance")),
|
||||
(gettext("Logs"), dict(_template="dialogs/settings/logs.jinja2", _div="settings_logs", data_bind="allowBindings: false"))
|
||||
]
|
||||
if len(plugin_includes):
|
||||
plugin_tuples = []
|
||||
for name, data in plugin_includes.items():
|
||||
if "_settings" in data:
|
||||
plugin_tuples.append((data["_settings"]["name"], data["_settings"]))
|
||||
if len(plugin_tuples):
|
||||
settings_entries.append((gettext("Plugins"), None))
|
||||
settings_entries.extend(sorted(plugin_tuples, key=lambda x: x[0]))
|
||||
if len(plugin_includes_settings):
|
||||
settings_entries.append((gettext("Plugins"), None))
|
||||
settings_entries.extend(sorted(plugin_includes_settings, key=lambda x: x[0]))
|
||||
|
||||
#~~ prepare full set of template vars for rendering
|
||||
|
||||
|
|
@ -186,7 +218,11 @@ def index():
|
|||
gcodeMobileThreshold=settings().get(["gcodeViewer", "mobileSizeThreshold"]),
|
||||
gcodeThreshold=settings().get(["gcodeViewer", "sizeThreshold"]),
|
||||
uiApiKey=UI_API_KEY,
|
||||
navbarEntries=navbar_entries,
|
||||
sidebarEntries=sidebar_entries,
|
||||
tabEntries=tab_entries,
|
||||
settingsEntries=settings_entries,
|
||||
pluginNames=plugin_names,
|
||||
assetPlugins=asset_plugin_urls,
|
||||
)
|
||||
render_kwargs.update(plugin_vars)
|
||||
|
|
@ -309,6 +345,16 @@ class Server():
|
|||
printer = Printer(fileManager, analysisQueue, printerProfileManager)
|
||||
appSessionManager = util.flask.AppSessionManager()
|
||||
|
||||
pluginManager.initialize_implementations(dict(
|
||||
printer_profile_manager=printerProfileManager,
|
||||
event_manager=eventManager,
|
||||
analysis_queue=analysisQueue,
|
||||
slicing_manager=slicingManager,
|
||||
storage_managers=storage_managers,
|
||||
file_manager=fileManager,
|
||||
app_session_manager=appSessionManager
|
||||
))
|
||||
|
||||
# configure additional template folders for jinja2
|
||||
template_plugins = pluginManager.get_implementations(octoprint.plugin.TemplatePlugin)
|
||||
additional_template_folders = []
|
||||
|
|
|
|||
|
|
@ -418,9 +418,9 @@ $(function() {
|
|||
settingsViewModel.requestData(function() {
|
||||
ko.applyBindings(settingsViewModel, document.getElementById("settings_dialog"));
|
||||
|
||||
ko.applyBindings(connectionViewModel, document.getElementById("connection_accordion"));
|
||||
ko.applyBindings(printerStateViewModel, document.getElementById("state_accordion"));
|
||||
ko.applyBindings(gcodeFilesViewModel, document.getElementById("files_accordion"));
|
||||
ko.applyBindings(connectionViewModel, document.getElementById("connection_wrapper"));
|
||||
ko.applyBindings(printerStateViewModel, document.getElementById("state_wrapper"));
|
||||
ko.applyBindings(gcodeFilesViewModel, document.getElementById("files_wrapper"));
|
||||
ko.applyBindings(temperatureViewModel, document.getElementById("temp"));
|
||||
ko.applyBindings(controlViewModel, document.getElementById("control"));
|
||||
ko.applyBindings(terminalViewModel, document.getElementById("term"));
|
||||
|
|
@ -444,24 +444,31 @@ $(function() {
|
|||
|
||||
// apply bindings and signal startup
|
||||
_.each(additionalViewModels, function(additionalViewModel) {
|
||||
if (additionalViewModel[1] === undefined) {
|
||||
var viewModel = additionalViewModel[0];
|
||||
var targets = additionalViewModel[1];
|
||||
|
||||
if (targets === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (additionalViewModel[0].hasOwnProperty("onBeforeBinding")) {
|
||||
additionalViewModel[0].onBeforeBinding();
|
||||
if (!Array.isArray(targets)) {
|
||||
targets = [targets];
|
||||
}
|
||||
|
||||
// model instance, target container
|
||||
try {
|
||||
ko.applyBindings(additionalViewModel[0], additionalViewModel[1]);
|
||||
} catch (exc) {
|
||||
console.log("Could not apply bindings for additional view model " + additionalViewModel[0] + ": " + exc.message);
|
||||
if (viewModel.hasOwnProperty("onBeforeBinding")) {
|
||||
viewModel.onBeforeBinding();
|
||||
}
|
||||
|
||||
_.each(targets, function(target) {
|
||||
try {
|
||||
ko.applyBindings(viewModel, target);
|
||||
} catch (exc) {
|
||||
console.log("Could not apply bindings for additional view model " + viewModel + ": " + exc.message);
|
||||
}
|
||||
});
|
||||
|
||||
if (additionalViewModel[0].hasOwnProperty("onAfterBinding")) {
|
||||
additionalViewModel[0].onAfterBinding();
|
||||
if (viewModel.hasOwnProperty("onAfterBinding")) {
|
||||
viewModel.onAfterBinding();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
@ -517,14 +524,14 @@ $(function() {
|
|||
}
|
||||
});
|
||||
|
||||
$(".accordion-toggle[href='#files']").click(function() {
|
||||
$(".accordion-toggle[data-target='#files']").click(function() {
|
||||
var files = $("#files");
|
||||
if (files.hasClass("in")) {
|
||||
files.removeClass("overflow_visible");
|
||||
} else {
|
||||
setTimeout(function() {
|
||||
files.addClass("overflow_visible");
|
||||
}, 1000);
|
||||
}, 100);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -665,6 +665,9 @@ ul.dropdown-menu li a {
|
|||
overflow: visible !important;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
#drop_overlay {
|
||||
|
|
|
|||
|
|
@ -10,7 +10,15 @@
|
|||
{% if data is none %}
|
||||
<li class="nav-header">{{ entry }}</li>
|
||||
{% else %}
|
||||
<li {% if "_active" in data and data["_active"] %}class="active"{% endif %}><a href="#{{ data._div }}" data-toggle="tab">{{ entry }}</a></li>
|
||||
{% if "custom_bindings" in data and data["custom_bindings"] %}<!-- ko allowBindings: false -->{% endif %}
|
||||
<li id="{{ data._div }}_link"
|
||||
{% if "data_bind" in data %} data-bind="{{ data.data_bind }}" {% elif "custom_bindings" in data and data["custom_bindings"] %} data-bind="allowBindings: true" {% endif %}
|
||||
class="{% if loop.first %}active{% endif %} {% if "classes_link" in data %}{{ data.classes_link|join(' ') }}{% elif "classes" in data %}{{ data.classes|join(' ') }}{% endif %}"
|
||||
{% if "styles_link" in data %} style="{{ data.styles_link|join(', ') }}" {% elif "styles" in data %} style="{{ data.styles|join(', ') }}" {% endif %}
|
||||
>
|
||||
<a href="#{{ data._div }}" data-toggle="tab">{{ entry }}</a>
|
||||
</li>
|
||||
{% if "custom_bindings" in data and data["custom_bindings"] %}<!-- /ko -->{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
|
@ -19,7 +27,11 @@
|
|||
{% for entry, data in settingsEntries %}
|
||||
{% if data is not none %}
|
||||
{% if "custom_bindings" in data and data["custom_bindings"] %}<!-- ko allowBindings: false -->{% endif %}
|
||||
<div class="tab-pane {% if "_active" in data and data["_active"] %}active{% endif %}" data-bind="{% if "data_bind" in data %}{{ data.data_bind }}{% else %}allowBindings: true{% endif %}" id="{{ data._div }}">
|
||||
<div id="{{ data._div }}"
|
||||
{% if "data_bind" in data %} data-bind="{{ data.data_bind }}" {% elif "custom_bindings" in data and data["custom_bindings"] %} data-bind="allowBindings: true" {% endif %}
|
||||
class="tab-pane {% if loop.first %}active{% endif %} {% if classes_content in data %}{{ data.classes_content|join(' ') }}{% elif classes in data %}{{ data.classes|join(' ') }}{% endif %}"
|
||||
{% if "styles_content" in data %} style="{{ data.styles_content|join(', ') }}" {% elif styles in data %} style="{{ data.styles|join(', ') }}" {% endif %}
|
||||
>
|
||||
{% include data._template ignore missing %}
|
||||
</div>
|
||||
{% if "custom_bindings" in data and data["custom_bindings"] %}<!-- /ko -->{% endif %}
|
||||
|
|
|
|||
|
|
@ -7,68 +7,8 @@
|
|||
<link rel="apple-touch-icon" sizes="114x114" href="{{ url_for('static', filename='img/apple-touch-icon-114x114.png') }}">
|
||||
<link rel="apple-touch-icon" sizes="144x144" href="{{ url_for('static', filename='img/apple-touch-icon-144x144.png') }}">
|
||||
|
||||
<link href="{{ url_for('static', filename='css/bootstrap.min.css') }}" rel="stylesheet" media="screen">
|
||||
<link href="{{ url_for('static', filename='css/bootstrap-modal.css') }}" rel="stylesheet" media="screen">
|
||||
<link href="{{ url_for('static', filename='css/bootstrap-slider.css') }}" rel="stylesheet" media="screen">
|
||||
<link href="{{ url_for('static', filename='css/font-awesome.min.css') }}" rel="stylesheet" media="screen">
|
||||
<link href="{{ url_for('static', filename='css/jquery.fileupload-ui.css') }}" rel="stylesheet" media="screen">
|
||||
<link href="{{ url_for('static', filename='css/pnotify.min.css') }}" rel="stylesheet" media="screen">
|
||||
|
||||
{% if stylesheet == "less" %}
|
||||
<link href="{{ url_for('static', filename='less/octoprint.less') }}" rel="stylesheet/less" type="text/css" media="screen">
|
||||
|
||||
<!-- Plugin files -->
|
||||
{% for name, assets in assetPlugins.items() %}
|
||||
{% if "less" in assets %}
|
||||
{% for asset in assets["less"] %}
|
||||
<link href="{{ url_for('plugin_assets', name=name, filename=asset) }}" rel="stylesheet/less" type="text/css" media="screen">
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
<!-- /Plugin files -->
|
||||
|
||||
<script src="{{ url_for('static', filename='js/lib/less.min.js') }}" type="text/javascript"></script>
|
||||
{% else %}
|
||||
<link href="{{ url_for('static', filename='css/octoprint.css') }}" rel="stylesheet" type="text/css" media="screen">
|
||||
<!-- Plugin files -->
|
||||
{% for name, assets in assetPlugins.items() %}
|
||||
{% if "css" in assets %}
|
||||
{% for asset in assets["css"] %}
|
||||
<link href="{{ url_for('plugin_assets', name=name, filename=asset) }}" rel="stylesheet" type="text/css" media="screen">
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
<!-- /Plugin files -->
|
||||
{% endif %}
|
||||
|
||||
<script lang="javascript">
|
||||
var BASEURL = "{{ url_for('index') }}";
|
||||
var API_BASEURL = BASEURL + "api/";
|
||||
var PLUGIN_BASEURL = BASEURL + "plugin/";
|
||||
var GCODE_WORKER = "{{ url_for('static', filename='gcodeviewer/js/Worker.js') }}";
|
||||
|
||||
var CONFIG_GCODEFILESPERPAGE = 5000;
|
||||
var CONFIG_TIMELAPSEFILESPERPAGE = 10;
|
||||
var CONFIG_LOGFILESPERPAGE = 10;
|
||||
var CONFIG_USERSPERPAGE = 10;
|
||||
var CONFIG_WEBCAM_STREAM = "{{ webcamStream }}";
|
||||
var CONFIG_ACCESS_CONTROL = {% if enableAccessControl -%} true; {% else %} false; {%- endif %}
|
||||
var CONFIG_SD_SUPPORT = {% if enableSdSupport -%} true; {% else %} false; {%- endif %}
|
||||
var CONFIG_FIRST_RUN = {% if firstRun -%} true; {% else %} false; {%- endif %}
|
||||
var CONFIG_TEMPERATURE_GRAPH = {% if enableTemperatureGraph -%} true; {% else %} false; {%- endif %}
|
||||
var CONFIG_GCODE_SIZE_THRESHOLD = {{ gcodeThreshold }};
|
||||
var CONFIG_GCODE_MOBILE_SIZE_THRESHOLD = {{ gcodeMobileThreshold }};
|
||||
|
||||
var SOCKJS_URI = "{{ url_for('index') }}" + "sockjs";
|
||||
var SOCKJS_DEBUG = {% if debug -%} true; {% else %} false; {%- endif %}
|
||||
|
||||
var UI_API_KEY = "{{ uiApiKey }}";
|
||||
var VERSION = "{{ version }}";
|
||||
var DISPLAY_VERSION = "{{ display_version }}";
|
||||
var LOCALE = "{{ g.locale }}";
|
||||
|
||||
var ADDITIONAL_VIEWMODELS = [];
|
||||
</script>
|
||||
{% include 'stylesheets.jinja2' %}
|
||||
{% include 'initscript.jinja2' %}
|
||||
</head>
|
||||
<body>
|
||||
<div id="navbar" class="navbar navbar-static-top">
|
||||
|
|
@ -76,10 +16,19 @@
|
|||
<div class="container">
|
||||
<a class="brand" href="#"> <span data-bind="text: appearance.brand">OctoPrint</span></a>
|
||||
<div class="nav-collapse">
|
||||
<!-- Navbar -->
|
||||
<ul class="nav pull-right">
|
||||
{% include 'navbar/settings.jinja2' %}
|
||||
{% include 'navbar/systemmenu.jinja2' %}
|
||||
{% include 'navbar/login.jinja2' %}
|
||||
{% for data in navbarEntries %}
|
||||
{% if "custom_bindings" in data and data["custom_bindings"] %}<!-- ko allowBindings: false -->{% endif %}
|
||||
<li id="{{ data._div }}"
|
||||
data-bind="{% if "data_bind" in data %}{{ data.data_bind }}{% else %}allowBindings: true{% endif %}"
|
||||
{% if "additional_classes" in data %}class="{{ data.additional_classes|join(' ') }}"{% endif %}
|
||||
{% if "style" in data %}style="{{ data.style }}"{% endif %}
|
||||
>
|
||||
{% include data._template ignore missing %}
|
||||
</li>
|
||||
{% if "custom_bindings" in data and data["custom_bindings"] %}<!-- /ko -->{% endif %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -87,52 +36,64 @@
|
|||
</div>
|
||||
<div class="container octoprint-container">
|
||||
<div class="row">
|
||||
<!-- Sidebar -->
|
||||
<div class="accordion span4">
|
||||
{% include 'sidebar/connection.jinja2' %}
|
||||
{% include 'sidebar/state.jinja2' %}
|
||||
{% include 'sidebar/files.jinja2' %}
|
||||
|
||||
{% if templatePlugins %}
|
||||
{% for plugin_name, vars in templatePlugins.items() %}
|
||||
{% include plugin_name+"_sidebar.jinja2" ignore missing %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% for entry, data in sidebarEntries %}
|
||||
{% if "custom_bindings" in data and data["custom_bindings"] %}<!-- ko allowBindings: false -->{% endif %}
|
||||
<div id="{{ data._div }}_wrapper"
|
||||
class="accordion-group {% if "classes_wrapper" in data %}{{ data.classes_wrapper|join(' ') }}{% elif "classes" in data %}{{ data.classes|join(' ') }}{% endif %}"
|
||||
data-bind="{% if "data_bind" in data %}{{ data.data_bind }}{% elif "custom_bindings" in data and data["custom_bindings"] %}allowBindings: true{% endif %}"
|
||||
{% if "styles_wrapper" in data %} style="{{ data.styles_wrapper|join(', ') }}" {% elif "styles" in data %} style="{{ data.styles|join(', ') }}" {% endif %}
|
||||
>
|
||||
<div class="accordion-heading">
|
||||
<a class="accordion-toggle" data-toggle="collapse" data-target="#{{ data._div }}">
|
||||
{% if "icon" in data %}<i class="icon-{{ data.icon }}"></i> {% endif %}{{ entry }}
|
||||
</a>
|
||||
{% if "header_addon" in data %}
|
||||
{% include data.header_addon ignore missing %}
|
||||
{% endif %}
|
||||
</div>
|
||||
<div id="{{ data._div }}"
|
||||
class="accordion-body collapse in {% if "classes_content" in data %}{{ data.classes_content|join(' ') }}{% elif "classes" in data %}{{ data.classes|join(' ') }}{% endif %}"
|
||||
{% if "styles_content" in data %} style="{{ data.styles_content|join(', ') }}" {% elif "styles" in data %} style="{{ data.styles|join(', ') }}"{% endif %}
|
||||
>
|
||||
<div class="accordion-inner">
|
||||
{% include data._template ignore missing %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% if "custom_bindings" in data and data["custom_bindings"] %}<!-- /ko -->{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<!-- Tabs -->
|
||||
<div class="span8 tabbable">
|
||||
<ul class="nav nav-tabs" id="tabs">
|
||||
<li class="active"><a href="#temp" data-toggle="tab">{{ _('Temperature') }}</a></li>
|
||||
<li><a href="#control" data-toggle="tab">{{ _('Control') }}</a></li>
|
||||
{% if enableGCodeVisualizer %}<li><a href="#gcode" data-toggle="tab">{{ _('GCode Viewer') }}</a></li>{% endif %}
|
||||
<li><a href="#term" data-toggle="tab">{{ _('Terminal') }}</a></li>
|
||||
{% if enableTimelapse %}<li><a href="#timelapse" data-toggle="tab">{{ _('Timelapse') }}</a></li>{% endif %}
|
||||
|
||||
{% if templatePlugins %}
|
||||
{% for plugin_name, vars in templatePlugins.items() %}
|
||||
{% if "_tab_entry" in vars %}
|
||||
<li><a href="#tab_plugin_{{ plugin_name }}" data-toggle="tab">{{ vars._tab_entry }}</a></li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% for entry, data in tabEntries %}
|
||||
{% if "custom_bindings" in data and data["custom_bindings"] %}<!-- ko allowBindings: false -->{% endif %}
|
||||
<li id="{{ data._div }}_link"
|
||||
class="{% if loop.first %}active{% endif %} {% if "classes_link" in data %}{{ data.classes_link|join(' ') }}{% elif "classes" in data %}{{ data.classes|join(' ') }}{% endif %}"
|
||||
data-bind="{% if "data_bind" in data %}{{ data.data_bind }}{% elif "custom_bindings" in data and data["custom_bindings"] %}allowBindings: true{% endif %}"
|
||||
{% if "link_style" in data %} style="{{ data.link_style }}" {% elif "style" in data %} style="{{ data.style }}" {% endif %}
|
||||
>
|
||||
<a href="#{{ data._div }}" data-toggle="tab">{{ entry }}</a>
|
||||
</li>
|
||||
{% if "custom_bindings" in data and data["custom_bindings"] %}<!-- /ko -->{% endif %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
||||
<div class="tab-content">
|
||||
{% include 'tabs/temperature.jinja2' %}
|
||||
{% include 'tabs/control.jinja2' %}
|
||||
{% include 'tabs/gcodeviewer.jinja2' %}
|
||||
{% include 'tabs/terminal.jinja2' %}
|
||||
{% include 'tabs/timelapse.jinja2' %}
|
||||
|
||||
{% if templatePlugins %}
|
||||
{% for plugin_name, vars in templatePlugins.items() %}
|
||||
{% if "_tab_entry" in vars %}
|
||||
<div class="tab-pane" id="tab_plugin_{{ plugin_name }}" data-bind="allowBindings: {% if "_tab_allow_bindings" in vars and vars["_tab_allow_bindings"] %}true{% else %}false{% endif %}">
|
||||
{% include plugin_name + "_tab.jinja2" ignore missing %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
{% for entry, data in tabEntries %}
|
||||
{% if "custom_bindings" in data and data["custom_bindings"] %}<!-- ko allowBindings: false -->{% endif %}
|
||||
<div id="{{ data._div }}"
|
||||
class="tab-pane{% if loop.first %} active{% endif %}{% if "additional_classes" in data %} {{ data.additional_classes|join(' ') }}{% endif %}"
|
||||
data-bind="{% if "data_bind" in data %}{{ data.data_bind }}{% elif "custom_bindings" in data and data["custom_bindings"] %}allowBindings: true{% endif %}"
|
||||
{% if "content_style" in data %} style="{{ data.content_style }}" {% elif "style" in data %} style="{{ data.style }}" {% endif %}
|
||||
>
|
||||
{% include data._template ignore missing %}
|
||||
</div>
|
||||
{% if "custom_bindings" in data and data["custom_bindings"] %}<!-- /ko -->{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -161,85 +122,14 @@
|
|||
{% include 'overlays/offline.jinja2' %}
|
||||
<!-- End of overlays -->
|
||||
|
||||
<!-- Plugin template files -->
|
||||
<!-- Generic plugin template files -->
|
||||
{% if templatePlugins %}
|
||||
{% for plugin_name in templatePlugins %}
|
||||
{% for plugin_name in pluginNames %}
|
||||
{% include plugin_name + ".jinja2" ignore missing %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
<!-- End plugin template files -->
|
||||
<!-- End of generic plugin template files -->
|
||||
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/lib/jquery/jquery.min.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/lib/modernizr.custom.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/lib/underscore-min.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/lib/underscore.string.min.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/lib/knockout.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/lib/knockout.mapping-latest.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/lib/babel.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/lib/avltree.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/lib/bootstrap/bootstrap.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/lib/bootstrap/bootstrap-modalmanager.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/lib/bootstrap/bootstrap-modal.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/lib/bootstrap/bootstrap-slider.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/lib/jquery/jquery.ui.core.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/lib/jquery/jquery.ui.widget.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/lib/jquery/jquery.ui.mouse.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/lib/jquery/jquery.flot.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/lib/jquery/jquery.iframe-transport.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/lib/jquery/jquery.fileupload.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/lib/jquery/jquery.slimscroll.min.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/lib/jquery/jquery.qrcode.min.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/lib/sockjs-0.3.4.min.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/lib/moment-with-locales.min.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/lib/pusher.color.min.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/lib/detectmobilebrowser.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/lib/md5.min.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/lib/pnotify.min.js') }}"></script>
|
||||
|
||||
<!-- Include OctoPrint files -->
|
||||
<!-- TODO: merge/minimize in the future -->
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/app/viewmodels/appearance.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/app/viewmodels/connection.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/app/viewmodels/control.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/app/viewmodels/firstrun.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/app/viewmodels/gcode.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/app/viewmodels/files.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/app/viewmodels/loginstate.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/app/viewmodels/navigation.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/app/viewmodels/printerstate.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/app/viewmodels/printerprofiles.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/app/viewmodels/settings.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/app/viewmodels/slicing.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/app/viewmodels/temperature.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/app/viewmodels/terminal.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/app/viewmodels/timelapse.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/app/viewmodels/users.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/app/viewmodels/log.js') }}"></script>
|
||||
|
||||
<!-- Plugin files -->
|
||||
{% for name, assets in assetPlugins.items() %}
|
||||
{% if "js" in assets %}
|
||||
{% for asset in assets["js"] %}
|
||||
<script type="text/javascript" src="{{ url_for('plugin_assets', name=name, filename=asset) }}"></script>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
<!-- /Plugin files -->
|
||||
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/app/dataupdater.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/app/helpers.js') }}"></script>
|
||||
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/app/main.js') }}"></script>
|
||||
<!-- /Include OctoPrint files -->
|
||||
|
||||
<!-- Include i18n language files -->
|
||||
{% if g.locale != 'en' %}
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/i18n/%s.js' % g.locale) }}"></script>
|
||||
{% endif %}
|
||||
<!-- /Include i18n language files -->
|
||||
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='gcodeviewer/js/ui.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='gcodeviewer/js/gCodeReader.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='gcodeviewer/js/renderer.js') }}"></script>
|
||||
{% include 'javascripts.jinja2' %}
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
28
src/octoprint/templates/initscript.jinja2
Normal file
28
src/octoprint/templates/initscript.jinja2
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
<script lang="javascript">
|
||||
var BASEURL = "{{ url_for('index') }}";
|
||||
var API_BASEURL = BASEURL + "api/";
|
||||
var PLUGIN_BASEURL = BASEURL + "plugin/";
|
||||
var GCODE_WORKER = "{{ url_for('static', filename='gcodeviewer/js/Worker.js') }}";
|
||||
|
||||
var CONFIG_GCODEFILESPERPAGE = 5000;
|
||||
var CONFIG_TIMELAPSEFILESPERPAGE = 10;
|
||||
var CONFIG_LOGFILESPERPAGE = 10;
|
||||
var CONFIG_USERSPERPAGE = 10;
|
||||
var CONFIG_WEBCAM_STREAM = "{{ webcamStream }}";
|
||||
var CONFIG_ACCESS_CONTROL = {% if enableAccessControl -%} true; {% else %} false; {%- endif %}
|
||||
var CONFIG_SD_SUPPORT = {% if enableSdSupport -%} true; {% else %} false; {%- endif %}
|
||||
var CONFIG_FIRST_RUN = {% if firstRun -%} true; {% else %} false; {%- endif %}
|
||||
var CONFIG_TEMPERATURE_GRAPH = {% if enableTemperatureGraph -%} true; {% else %} false; {%- endif %}
|
||||
var CONFIG_GCODE_SIZE_THRESHOLD = {{ gcodeThreshold }};
|
||||
var CONFIG_GCODE_MOBILE_SIZE_THRESHOLD = {{ gcodeMobileThreshold }};
|
||||
|
||||
var SOCKJS_URI = "{{ url_for('index') }}" + "sockjs";
|
||||
var SOCKJS_DEBUG = {% if debug -%} true; {% else %} false; {%- endif %}
|
||||
|
||||
var UI_API_KEY = "{{ uiApiKey }}";
|
||||
var VERSION = "{{ version }}";
|
||||
var DISPLAY_VERSION = "{{ display_version }}";
|
||||
var LOCALE = "{{ g.locale }}";
|
||||
|
||||
var ADDITIONAL_VIEWMODELS = [];
|
||||
</script>
|
||||
72
src/octoprint/templates/javascripts.jinja2
Normal file
72
src/octoprint/templates/javascripts.jinja2
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
<script type="text/javascript" src="{{ url_for('static', filename='js/lib/jquery/jquery.min.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/lib/modernizr.custom.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/lib/underscore-min.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/lib/underscore.string.min.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/lib/knockout.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/lib/knockout.mapping-latest.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/lib/babel.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/lib/avltree.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/lib/bootstrap/bootstrap.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/lib/bootstrap/bootstrap-modalmanager.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/lib/bootstrap/bootstrap-modal.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/lib/bootstrap/bootstrap-slider.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/lib/jquery/jquery.ui.core.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/lib/jquery/jquery.ui.widget.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/lib/jquery/jquery.ui.mouse.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/lib/jquery/jquery.flot.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/lib/jquery/jquery.iframe-transport.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/lib/jquery/jquery.fileupload.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/lib/jquery/jquery.slimscroll.min.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/lib/jquery/jquery.qrcode.min.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/lib/sockjs-0.3.4.min.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/lib/moment-with-locales.min.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/lib/pusher.color.min.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/lib/detectmobilebrowser.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/lib/md5.min.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/lib/pnotify.min.js') }}"></script>
|
||||
|
||||
<!-- Include OctoPrint files -->
|
||||
<!-- TODO: merge/minimize in the future -->
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/app/viewmodels/appearance.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/app/viewmodels/connection.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/app/viewmodels/control.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/app/viewmodels/firstrun.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/app/viewmodels/gcode.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/app/viewmodels/files.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/app/viewmodels/loginstate.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/app/viewmodels/navigation.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/app/viewmodels/printerstate.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/app/viewmodels/printerprofiles.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/app/viewmodels/settings.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/app/viewmodels/slicing.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/app/viewmodels/temperature.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/app/viewmodels/terminal.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/app/viewmodels/timelapse.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/app/viewmodels/users.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/app/viewmodels/log.js') }}"></script>
|
||||
|
||||
<!-- Plugin files -->
|
||||
{% for name, assets in assetPlugins.items() %}
|
||||
{% if "js" in assets %}
|
||||
{% for asset in assets["js"] %}
|
||||
<script type="text/javascript" src="{{ url_for('plugin_assets', name=name, filename=asset) }}"></script>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
<!-- /Plugin files -->
|
||||
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/app/dataupdater.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/app/helpers.js') }}"></script>
|
||||
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/app/main.js') }}"></script>
|
||||
<!-- /Include OctoPrint files -->
|
||||
|
||||
<!-- Include i18n language files -->
|
||||
{% if g.locale != 'en' %}
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/i18n/%s.js' % g.locale) }}"></script>
|
||||
{% endif %}
|
||||
<!-- /Include i18n language files -->
|
||||
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='gcodeviewer/js/ui.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='gcodeviewer/js/gCodeReader.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='gcodeviewer/js/renderer.js') }}"></script>
|
||||
|
|
@ -1,22 +1,20 @@
|
|||
{% if enableAccessControl %}
|
||||
<li class="dropdown">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
|
||||
<i class="icon-user"></i> <span data-bind="text: loginState.userMenuText">{{ _('Login') }}</span>
|
||||
<b class="caret"></b>
|
||||
</a>
|
||||
<div id="login_dropdown_loggedout" style="padding: 15px" class="dropdown-menu" data-bind="css: {hide: loginState.loggedIn(), 'dropdown-menu': !loginState.loggedIn()}">
|
||||
<label for="login_user">{{ _('Username') }}</label>
|
||||
<input type="text" id="login_user" placeholder="{{ _('Username') }}" autocapitalize="none">
|
||||
<label for="login_pass">{{ _('Password') }}</label>
|
||||
<input type="password" id="login_pass" placeholder="{{ _('Password') }}">
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" id="login_remember"> {{ _('Remember me') }}
|
||||
</label>
|
||||
<button class="btn btn-block btn-primary" id="login_button" data-bind="click: loginState.login">{{ _('Login') }}</button>
|
||||
</div>
|
||||
<ul id="login_dropdown_loggedin" class="hide" data-bind="css: {hide: !loginState.loggedIn(), 'dropdown-menu': loginState.loggedIn()}">
|
||||
<li><a href="#" id="change_password_button" data-bind="click: function() { users.showChangePasswordDialog(loginState.currentUser()); }">{{ _('Change Password') }}</a></li>
|
||||
<li><a href="#" id="logout_button" data-bind="click: loginState.logout">{{ _('Logout') }}</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
|
||||
<i class="icon-user"></i> <span data-bind="text: loginState.userMenuText">{{ _('Login') }}</span>
|
||||
<b class="caret"></b>
|
||||
</a>
|
||||
<div id="login_dropdown_loggedout" style="padding: 15px" class="dropdown-menu" data-bind="css: {hide: loginState.loggedIn(), 'dropdown-menu': !loginState.loggedIn()}">
|
||||
<label for="login_user">{{ _('Username') }}</label>
|
||||
<input type="text" id="login_user" placeholder="{{ _('Username') }}" autocapitalize="none">
|
||||
<label for="login_pass">{{ _('Password') }}</label>
|
||||
<input type="password" id="login_pass" placeholder="{{ _('Password') }}">
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" id="login_remember"> {{ _('Remember me') }}
|
||||
</label>
|
||||
<button class="btn btn-block btn-primary" id="login_button" data-bind="click: loginState.login">{{ _('Login') }}</button>
|
||||
</div>
|
||||
<ul id="login_dropdown_loggedin" class="hide" data-bind="css: {hide: !loginState.loggedIn(), 'dropdown-menu': loginState.loggedIn()}">
|
||||
<li><a href="#" id="change_password_button" data-bind="click: function() { users.showChangePasswordDialog(loginState.currentUser()); }">{{ _('Change Password') }}</a></li>
|
||||
<li><a href="#" id="logout_button" data-bind="click: loginState.logout">{{ _('Logout') }}</a></li>
|
||||
</ul>
|
||||
{% endif %}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
<li style="display: none;" data-bind="visible: loginState.isAdmin">
|
||||
<a id="navbar_show_settings" class="pull-right" href="#settings_dialog">
|
||||
<i class="icon-wrench"></i> {{ _('Settings') }}
|
||||
</a>
|
||||
</li>
|
||||
<a id="navbar_show_settings" class="pull-right" href="#settings_dialog">
|
||||
<i class="icon-wrench"></i> {{ _('Settings') }}
|
||||
</a>
|
||||
|
|
|
|||
|
|
@ -1,11 +1,9 @@
|
|||
{% if enableSystemMenu %}
|
||||
<li class="dropdown" style="display: none" data-bind="visible: loginState.isAdmin">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
|
||||
<i class="icon-off"></i> {{ _('System') }}
|
||||
<b class="caret"></b>
|
||||
</a>
|
||||
<ul class="dropdown-menu" data-bind="foreach: systemActions">
|
||||
<li><a href="#" data-bind="click: $root.triggerAction, text: name"></a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
|
||||
<i class="icon-off"></i> {{ _('System') }}
|
||||
<b class="caret"></b>
|
||||
</a>
|
||||
<ul class="dropdown-menu" data-bind="foreach: systemActions">
|
||||
<li><a href="#" data-bind="click: $root.triggerAction, text: name"></a></li>
|
||||
</ul>
|
||||
{% endif %}
|
||||
|
|
|
|||
|
|
@ -1,22 +1,13 @@
|
|||
<div class="accordion-group" data-bind="visible: loginState.isUser" id="connection_accordion">
|
||||
<div class="accordion-heading">
|
||||
<a class="accordion-toggle" data-toggle="collapse" data-target="#connection"><i class="icon-signal"></i> {{ _('Connection') }}</a>
|
||||
</div>
|
||||
<div class="accordion-body collapse in" id="connection">
|
||||
<div class="accordion-inner">
|
||||
<label for="connection_ports" data-bind="css: {disabled: !isErrorOrClosed()}, enable: isErrorOrClosed() && loginState.isUser()">{{ _('Serial Port') }}</label>
|
||||
<select id="connection_ports" data-bind="options: portOptions, optionsCaption: 'AUTO', value: selectedPort, css: {disabled: !isErrorOrClosed()}, enable: isErrorOrClosed() && loginState.isUser()"></select>
|
||||
<label for="connection_baudrates" data-bind="css: {disabled: !isErrorOrClosed()}, enable: isErrorOrClosed() && loginState.isUser()">{{ _('Baudrate') }}</label>
|
||||
<select id="connection_baudrates" data-bind="options: baudrateOptions, optionsCaption: 'AUTO', value: selectedBaudrate, css: {disabled: !isErrorOrClosed()}, enable: isErrorOrClosed() && loginState.isUser()"></select>
|
||||
<label for="connection_printers" data-bind="css: {disabled: !isErrorOrClosed()}, enable: isErrorOrClosed() && loginState.isUser()">{{ _('Printer Profile') }}</label>
|
||||
<select id="connection_printers" data-bind="options: printerOptions, optionsText: 'name', optionsValue: 'id', value: selectedPrinter, css: {disabled: !isErrorOrClosed()}, enable: isErrorOrClosed() && loginState.isUser()"></select>
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" id="connection_save" data-bind="checked: saveSettings, css: {disabled: !isErrorOrClosed()}, enable: isErrorOrClosed() && loginState.isUser()"> {{ _('Save connection settings') }}
|
||||
</label>
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" id="connection_autoconnect" data-bind="checked: settings.serial_autoconnect, css: {disabled: !isErrorOrClosed()}, enable: isErrorOrClosed() && loginState.isUser()"> {{ _('Auto-connect on server startup') }}
|
||||
</label>
|
||||
<button class="btn btn-block" id="printer_connect" data-bind="click: connect, text: buttonText(), enable: loginState.isUser()">{{ _('Connect') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<label for="connection_ports" data-bind="css: {disabled: !isErrorOrClosed()}, enable: isErrorOrClosed() && loginState.isUser()">{{ _('Serial Port') }}</label>
|
||||
<select id="connection_ports" data-bind="options: portOptions, optionsCaption: 'AUTO', value: selectedPort, css: {disabled: !isErrorOrClosed()}, enable: isErrorOrClosed() && loginState.isUser()"></select>
|
||||
<label for="connection_baudrates" data-bind="css: {disabled: !isErrorOrClosed()}, enable: isErrorOrClosed() && loginState.isUser()">{{ _('Baudrate') }}</label>
|
||||
<select id="connection_baudrates" data-bind="options: baudrateOptions, optionsCaption: 'AUTO', value: selectedBaudrate, css: {disabled: !isErrorOrClosed()}, enable: isErrorOrClosed() && loginState.isUser()"></select>
|
||||
<label for="connection_printers" data-bind="css: {disabled: !isErrorOrClosed()}, enable: isErrorOrClosed() && loginState.isUser()">{{ _('Printer Profile') }}</label>
|
||||
<select id="connection_printers" data-bind="options: printerOptions, optionsText: 'name', optionsValue: 'id', value: selectedPrinter, css: {disabled: !isErrorOrClosed()}, enable: isErrorOrClosed() && loginState.isUser()"></select>
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" id="connection_save" data-bind="checked: saveSettings, css: {disabled: !isErrorOrClosed()}, enable: isErrorOrClosed() && loginState.isUser()"> {{ _('Save connection settings') }}
|
||||
</label>
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" id="connection_autoconnect" data-bind="checked: settings.serial_autoconnect, css: {disabled: !isErrorOrClosed()}, enable: isErrorOrClosed() && loginState.isUser()"> {{ _('Auto-connect on server startup') }}
|
||||
</label>
|
||||
<button class="btn btn-block" id="printer_connect" data-bind="click: connect, text: buttonText(), enable: loginState.isUser()">{{ _('Connect') }}</button>
|
||||
|
|
|
|||
|
|
@ -1,108 +1,65 @@
|
|||
<div class="accordion-group" id="files_accordion">
|
||||
<div class="accordion-heading">
|
||||
<a class="accordion-toggle" data-toggle="collapse" href="#files"><i class="icon-list"></i> {{ _('Files') }}</a>
|
||||
<form class="form-search">
|
||||
<input type="text" class="input-block search-query" data-bind="value: searchQuery, valueUpdate: 'input'" placeholder="{{ _('Search...') }}">
|
||||
</form>
|
||||
<div class="gcode_files" data-bind="slimScrolledForeach: listHelper.paginatedItems">
|
||||
<div class="entry" data-bind="attr: { id: $root.getEntryId($data) }, template: { name: $root.templateFor($data), data: $data }"></div>
|
||||
|
||||
<div class="settings-trigger accordion-heading-button btn-group">
|
||||
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
|
||||
<span class="icon-wrench"></span>
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="#" data-bind="click: function() { $root.listHelper.changeSorting('name'); }"><i class="icon-ok" data-bind="style: {visibility: listHelper.currentSorting() == 'name' ? 'visible' : 'hidden'}"></i> {{ _('Sort by name') }} ({{ _('ascending') }})</a></li>
|
||||
<li><a href="#" data-bind="click: function() { $root.listHelper.changeSorting('upload'); }"><i class="icon-ok" data-bind="style: {visibility: listHelper.currentSorting() == 'upload' ? 'visible' : 'hidden'}"></i> {{ _('Sort by upload date') }} ({{ _('descending') }})</a></li>
|
||||
<li><a href="#" data-bind="click: function() { $root.listHelper.changeSorting('size'); }"><i class="icon-ok" data-bind="style: {visibility: listHelper.currentSorting() == 'size' ? 'visible' : 'hidden'}"></i> {{ _('Sort by file size') }} ({{ _('descending') }})</a></li>
|
||||
<li class="divider"></li>
|
||||
<li><a href="#" data-bind="click: function() { $root.listHelper.toggleFilter('machinecode'); }"><i class="icon-ok" data-bind="style: {visibility: _.contains(listHelper.currentFilters(), 'machinecode') ? 'visible' : 'hidden'}"></i> {{ _('Only show GCode files') }}</a></li>
|
||||
<li><a href="#" data-bind="click: function() { $root.listHelper.toggleFilter('model'); }"><i class="icon-ok" data-bind="style: {visibility: _.contains(listHelper.currentFilters(), 'model') ? 'visible' : 'hidden'}"></i> {{ _('Only show STL files') }}</a></li>
|
||||
{% if enableSdSupport %}
|
||||
<li class="divider"></li>
|
||||
<li><a href="#" data-bind="click: function() { $root.listHelper.toggleFilter('local'); }"><i class="icon-ok" data-bind="style: {visibility: _.contains(listHelper.currentFilters(), 'local') ? 'visible' : 'hidden'}"></i> {{ _('Only show files stored locally') }}</a></li>
|
||||
<li><a href="#" data-bind="click: function() { $root.listHelper.toggleFilter('sd'); }"><i class="icon-ok" data-bind="style: {visibility: _.contains(listHelper.currentFilters(), 'sd') ? 'visible' : 'hidden'}"></i> {{ _('Only show files stored on SD') }}</a></li>
|
||||
{% endif %}
|
||||
<li class="divider"></li>
|
||||
<li><a href="#" data-bind="click: function() { $root.listHelper.toggleFilter('printed'); }"><i class="icon-ok" data-bind="style: {visibility: _.contains(listHelper.currentFilters(), 'printed') ? 'visible' : 'hidden'}"></i> {{ _('Hide successfully printed files') }}</a></li>
|
||||
</ul>
|
||||
<script type="text/html" id="files_template_machinecode">
|
||||
<div class="title" data-bind="css: $root.getSuccessClass($data), style: { 'font-weight': $root.listHelper.isSelected($data) ? 'bold' : 'normal' }, text: name"></div>
|
||||
<div class="uploaded">{{ _('Uploaded') }}: <span data-bind="text: formatTimeAgo(date)"></span></div>
|
||||
<div class="size">{{ _('Size') }}: <span data-bind="text: formatSize(size)"></span></div>
|
||||
<div class="additionalInfo hide" data-bind="html: $root.getAdditionalData($data)"></div>
|
||||
<div class="btn-group action-buttons">
|
||||
<div class="btn btn-mini toggleAdditionalData" data-bind="click: function() { if ($root.enableAdditionalData($data)) { $root.toggleAdditionalData($data); } else { return; } }, css: { disabled: !$root.enableAdditionalData($data) }"><i class="icon-chevron-down"></i></div>
|
||||
<a class="btn btn-mini" data-bind="attr: {href: $root.downloadLink($data), css: {disabled: !$root.downloadLink($data)}}"><i class="icon-download-alt" title="{{ _('Download') }}"></i></a>
|
||||
<div class="btn btn-mini" data-bind="click: function() { if ($root.enableRemove($data)) { $root.removeFile($data); } else { return; } }, css: {disabled: !$root.enableRemove($data)}"><i class="icon-trash" title="{{ _('Remove') }}"></i></div>
|
||||
<div class="btn btn-mini" data-bind="click: function() { if ($root.enableSelect($data)) { $root.loadFile($data, false); } else { return; } }, css: {disabled: !$root.enableSelect($data)}"><i class="icon-folder-open" title="{{ _('Load') }}"></i></div>
|
||||
<div class="btn btn-mini" data-bind="click: function() { if ($root.enableSelect($data)) { $root.loadFile($data, true); } else { return; } }, css: {disabled: !$root.enableSelect($data)}"><i class="icon-print" title="{{ _('Load and Print') }}"></i></div>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script type="text/html" id="files_template_model">
|
||||
<div class="title muted" data-bind="text: name"></div>
|
||||
<div class="uploaded">{{ _('Uploaded') }}: <span data-bind="text: formatTimeAgo(date)"></span></div>
|
||||
<div class="size">{{ _('Size') }}: <span data-bind="text: formatSize(size)"></span></div>
|
||||
<div class="btn-group action-buttons">
|
||||
<div class="btn btn-mini" data-bind="click: function() { if ($root.enableRemove($data)) { $root.removeFile($data); } else { return; } }, css: {disabled: !$root.enableRemove($data)}"><i class="icon-trash" title="{{ _('Remove') }}"></i></div>
|
||||
<div class="btn btn-mini" data-bind="click: function() { if ($root.enableSlicing($data)) { $root.sliceFile($data); } else { return; } }, css: {disabled: !$root.enableSlicing($data)}"><i class="icon-magic" title="{{ _('Slice') }}"></i></div>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script type="text/html" id="files_template_folder">
|
||||
<div class="title" data-bind="text: name"></div>
|
||||
</script>
|
||||
</div>
|
||||
<div class="muted text-right">
|
||||
<small>{{ _('Free') }}: <span data-bind="text: freeSpaceString"></span></small>
|
||||
</div>
|
||||
<div style="display: none;" data-bind="visible: loginState.isUser">
|
||||
<div class="row-fluid upload-buttons">
|
||||
{% if enableSdSupport %}
|
||||
<div class="sd-trigger accordion-heading-button btn-group">
|
||||
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
|
||||
<span class="icon-sd-black-14"></span>
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
<li data-bind="visible: !isSdReady()"><a href="#" data-bind="click: function() { $root.initSdCard(); }"><i class="icon-flag"></i> {{ _('Initialize SD card') }}</a></li>
|
||||
<li data-bind="visible: isSdReady()"><a href="#" data-bind="click: function() { $root.refreshSdFiles(); }"><i class="icon-refresh"></i> {{ _('Refresh SD files') }}</a></li>
|
||||
<li data-bind="visible: isSdReady()"><a href="#" data-bind="click: function() { $root.releaseSdCard(); }"><i class="icon-eject"></i> {{ _('Release SD card') }}</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<span class="btn btn-primary fileinput-button span6" data-bind="css: {disabled: !$root.loginState.isUser()}" style="margin-bottom: 10px">
|
||||
<i class="icon-upload-alt icon-white"></i>
|
||||
<span>{{ _('Upload') }}</span>
|
||||
<input id="gcode_upload" type="file" name="file" class="fileinput-button" data-bind="enable: loginState.isUser()">
|
||||
</span>
|
||||
<span class="btn btn-primary fileinput-button span6" data-bind="css: {disabled: !$root.loginState.isUser() || !$root.isSdReady()}" style="margin-bottom: 10px">
|
||||
<i class="icon-upload-alt icon-white"></i>
|
||||
<span>{{ _('Upload to SD') }}</span>
|
||||
<input id="gcode_upload_sd" type="file" name="file" class="fileinput-button" data-bind="enable: loginState.isUser() && isSdReady()">
|
||||
</span>
|
||||
{% else %}
|
||||
<span class="btn btn-primary fileinput-button span12" data-bind="css: {disabled: !$root.loginState.isUser()}" style="margin-bottom: 10px">
|
||||
<i class="icon-upload-alt icon-white"></i>
|
||||
<span>{{ _('Upload') }}</span>
|
||||
<input id="gcode_upload" type="file" name="file" class="fileinput-button" data-bind="enable: loginState.isUser()">
|
||||
</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="accordion-body collapse in overflow_visible" id="files">
|
||||
<div class="accordion-inner">
|
||||
<form class="form-search">
|
||||
<input type="text" class="input-block search-query" data-bind="value: searchQuery, valueUpdate: 'input'" placeholder="{{ _('Search...') }}">
|
||||
</form>
|
||||
<div class="gcode_files" data-bind="slimScrolledForeach: listHelper.paginatedItems">
|
||||
<div class="entry" data-bind="attr: { id: $root.getEntryId($data) }, template: { name: $root.templateFor($data), data: $data }"></div>
|
||||
|
||||
<script type="text/html" id="files_template_machinecode">
|
||||
<div class="title" data-bind="css: $root.getSuccessClass($data), style: { 'font-weight': $root.listHelper.isSelected($data) ? 'bold' : 'normal' }, text: name"></div>
|
||||
<div class="uploaded">{{ _('Uploaded') }}: <span data-bind="text: formatTimeAgo(date)"></span></div>
|
||||
<div class="size">{{ _('Size') }}: <span data-bind="text: formatSize(size)"></span></div>
|
||||
<div class="additionalInfo hide" data-bind="html: $root.getAdditionalData($data)"></div>
|
||||
<div class="btn-group action-buttons">
|
||||
<div class="btn btn-mini toggleAdditionalData" data-bind="click: function() { if ($root.enableAdditionalData($data)) { $root.toggleAdditionalData($data); } else { return; } }, css: { disabled: !$root.enableAdditionalData($data) }"><i class="icon-chevron-down"></i></div>
|
||||
<a class="btn btn-mini" data-bind="attr: {href: $root.downloadLink($data), css: {disabled: !$root.downloadLink($data)}}"><i class="icon-download-alt" title="{{ _('Download') }}"></i></a>
|
||||
<div class="btn btn-mini" data-bind="click: function() { if ($root.enableRemove($data)) { $root.removeFile($data); } else { return; } }, css: {disabled: !$root.enableRemove($data)}"><i class="icon-trash" title="{{ _('Remove') }}"></i></div>
|
||||
<div class="btn btn-mini" data-bind="click: function() { if ($root.enableSelect($data)) { $root.loadFile($data, false); } else { return; } }, css: {disabled: !$root.enableSelect($data)}"><i class="icon-folder-open" title="{{ _('Load') }}"></i></div>
|
||||
<div class="btn btn-mini" data-bind="click: function() { if ($root.enableSelect($data)) { $root.loadFile($data, true); } else { return; } }, css: {disabled: !$root.enableSelect($data)}"><i class="icon-print" title="{{ _('Load and Print') }}"></i></div>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script type="text/html" id="files_template_model">
|
||||
<div class="title muted" data-bind="text: name"></div>
|
||||
<div class="uploaded">{{ _('Uploaded') }}: <span data-bind="text: formatTimeAgo(date)"></span></div>
|
||||
<div class="size">{{ _('Size') }}: <span data-bind="text: formatSize(size)"></span></div>
|
||||
<div class="btn-group action-buttons">
|
||||
<div class="btn btn-mini" data-bind="click: function() { if ($root.enableRemove($data)) { $root.removeFile($data); } else { return; } }, css: {disabled: !$root.enableRemove($data)}"><i class="icon-trash" title="{{ _('Remove') }}"></i></div>
|
||||
<div class="btn btn-mini" data-bind="click: function() { if ($root.enableSlicing($data)) { $root.sliceFile($data); } else { return; } }, css: {disabled: !$root.enableSlicing($data)}"><i class="icon-magic" title="{{ _('Slice') }}"></i></div>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script type="text/html" id="files_template_folder">
|
||||
<div class="title" data-bind="text: name"></div>
|
||||
</script>
|
||||
</div>
|
||||
<div class="muted text-right">
|
||||
<small>{{ _('Free') }}: <span data-bind="text: freeSpaceString"></span></small>
|
||||
</div>
|
||||
<div style="display: none;" data-bind="visible: loginState.isUser">
|
||||
<div class="row-fluid upload-buttons">
|
||||
{% if enableSdSupport %}
|
||||
<span class="btn btn-primary fileinput-button span6" data-bind="css: {disabled: !$root.loginState.isUser()}" style="margin-bottom: 10px">
|
||||
<i class="icon-upload-alt icon-white"></i>
|
||||
<span>{{ _('Upload') }}</span>
|
||||
<input id="gcode_upload" type="file" name="file" class="fileinput-button" data-bind="enable: loginState.isUser()">
|
||||
</span>
|
||||
<span class="btn btn-primary fileinput-button span6" data-bind="css: {disabled: !$root.loginState.isUser() || !$root.isSdReady()}" style="margin-bottom: 10px">
|
||||
<i class="icon-upload-alt icon-white"></i>
|
||||
<span>{{ _('Upload to SD') }}</span>
|
||||
<input id="gcode_upload_sd" type="file" name="file" class="fileinput-button" data-bind="enable: loginState.isUser() && isSdReady()">
|
||||
</span>
|
||||
{% else %}
|
||||
<span class="btn btn-primary fileinput-button span12" data-bind="css: {disabled: !$root.loginState.isUser()}" style="margin-bottom: 10px">
|
||||
<i class="icon-upload-alt icon-white"></i>
|
||||
<span>{{ _('Upload') }}</span>
|
||||
<input id="gcode_upload" type="file" name="file" class="fileinput-button" data-bind="enable: loginState.isUser()">
|
||||
</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div id="gcode_upload_progress" class="progress" style="width: 100%;">
|
||||
<div class="bar" style="width: 0%"></div>
|
||||
</div>
|
||||
<div>
|
||||
<small>{{ _('Hint: You can also drag and drop files on this page to upload them.') }}</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="gcode_upload_progress" class="progress" style="width: 100%;">
|
||||
<div class="bar" style="width: 0%"></div>
|
||||
</div>
|
||||
<div>
|
||||
<small>{{ _('Hint: You can also drag and drop files on this page to upload them.') }}</small>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
33
src/octoprint/templates/sidebar/files_header.jinja2
Normal file
33
src/octoprint/templates/sidebar/files_header.jinja2
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
<div class="settings-trigger accordion-heading-button btn-group">
|
||||
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
|
||||
<span class="icon-wrench"></span>
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="#" data-bind="click: function() { $root.listHelper.changeSorting('name'); }"><i class="icon-ok" data-bind="style: {visibility: listHelper.currentSorting() == 'name' ? 'visible' : 'hidden'}"></i> {{ _('Sort by name') }} ({{ _('ascending') }})</a></li>
|
||||
<li><a href="#" data-bind="click: function() { $root.listHelper.changeSorting('upload'); }"><i class="icon-ok" data-bind="style: {visibility: listHelper.currentSorting() == 'upload' ? 'visible' : 'hidden'}"></i> {{ _('Sort by upload date') }} ({{ _('descending') }})</a></li>
|
||||
<li><a href="#" data-bind="click: function() { $root.listHelper.changeSorting('size'); }"><i class="icon-ok" data-bind="style: {visibility: listHelper.currentSorting() == 'size' ? 'visible' : 'hidden'}"></i> {{ _('Sort by file size') }} ({{ _('descending') }})</a></li>
|
||||
<li class="divider"></li>
|
||||
<li><a href="#" data-bind="click: function() { $root.listHelper.toggleFilter('machinecode'); }"><i class="icon-ok" data-bind="style: {visibility: _.contains(listHelper.currentFilters(), 'machinecode') ? 'visible' : 'hidden'}"></i> {{ _('Only show GCode files') }}</a></li>
|
||||
<li><a href="#" data-bind="click: function() { $root.listHelper.toggleFilter('model'); }"><i class="icon-ok" data-bind="style: {visibility: _.contains(listHelper.currentFilters(), 'model') ? 'visible' : 'hidden'}"></i> {{ _('Only show STL files') }}</a></li>
|
||||
{% if enableSdSupport %}
|
||||
<li class="divider"></li>
|
||||
<li><a href="#" data-bind="click: function() { $root.listHelper.toggleFilter('local'); }"><i class="icon-ok" data-bind="style: {visibility: _.contains(listHelper.currentFilters(), 'local') ? 'visible' : 'hidden'}"></i> {{ _('Only show files stored locally') }}</a></li>
|
||||
<li><a href="#" data-bind="click: function() { $root.listHelper.toggleFilter('sd'); }"><i class="icon-ok" data-bind="style: {visibility: _.contains(listHelper.currentFilters(), 'sd') ? 'visible' : 'hidden'}"></i> {{ _('Only show files stored on SD') }}</a></li>
|
||||
{% endif %}
|
||||
<li class="divider"></li>
|
||||
<li><a href="#" data-bind="click: function() { $root.listHelper.toggleFilter('printed'); }"><i class="icon-ok" data-bind="style: {visibility: _.contains(listHelper.currentFilters(), 'printed') ? 'visible' : 'hidden'}"></i> {{ _('Hide successfully printed files') }}</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{% if enableSdSupport %}
|
||||
<div class="sd-trigger accordion-heading-button btn-group">
|
||||
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
|
||||
<span class="icon-sd-black-14"></span>
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
<li data-bind="visible: !isSdReady()"><a href="#" data-bind="click: function() { $root.initSdCard(); }"><i class="icon-flag"></i> {{ _('Initialize SD card') }}</a></li>
|
||||
<li data-bind="visible: isSdReady()"><a href="#" data-bind="click: function() { $root.refreshSdFiles(); }"><i class="icon-refresh"></i> {{ _('Refresh SD files') }}</a></li>
|
||||
<li data-bind="visible: isSdReady()"><a href="#" data-bind="click: function() { $root.releaseSdCard(); }"><i class="icon-eject"></i> {{ _('Release SD card') }}</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
|
@ -1,29 +1,20 @@
|
|||
<div class="accordion-group" id="state_accordion">
|
||||
<div class="accordion-heading">
|
||||
<a class="accordion-toggle" data-toggle="collapse" href="#state"><i class="icon-info-sign"></i> {{ _('State') }}</a>
|
||||
</div>
|
||||
<div class="accordion-body collapse in" id="state">
|
||||
<div class="accordion-inner">
|
||||
{{ _('Machine State') }}: <strong data-bind="text: stateString"></strong><br>
|
||||
{{ _('File') }}: <strong data-bind="text: filename"></strong> <strong data-bind="visible: sd">(SD)</strong><br>
|
||||
{{ _('Timelapse') }}: <strong data-bind="text: timelapseString"></strong><br>
|
||||
<!-- ko foreach: filament -->
|
||||
<span data-bind="text: 'Filament (' + name() + '): '"></span><strong data-bind="text: formatFilament(data())"></strong><br>
|
||||
<!-- /ko -->
|
||||
{{ _('Approx. Total Print Time') }}: <strong data-bind="text: estimatedPrintTimeString"></strong><br>
|
||||
{{ _('Print Time') }}: <strong data-bind="text: printTimeString"></strong><br>
|
||||
{{ _('Print Time Left') }}: <strong data-bind="text: printTimeLeftString"></strong><br>
|
||||
{{ _('Printed') }}: <strong data-bind="text: byteString"></strong><br>
|
||||
{{ _('Machine State') }}: <strong data-bind="text: stateString"></strong><br>
|
||||
{{ _('File') }}: <strong data-bind="text: filename"></strong> <strong data-bind="visible: sd">(SD)</strong><br>
|
||||
{{ _('Timelapse') }}: <strong data-bind="text: timelapseString"></strong><br>
|
||||
<!-- ko foreach: filament -->
|
||||
<span data-bind="text: 'Filament (' + name() + '): '"></span><strong data-bind="text: formatFilament(data())"></strong><br>
|
||||
<!-- /ko -->
|
||||
{{ _('Approx. Total Print Time') }}: <strong data-bind="text: estimatedPrintTimeString"></strong><br>
|
||||
{{ _('Print Time') }}: <strong data-bind="text: printTimeString"></strong><br>
|
||||
{{ _('Print Time Left') }}: <strong data-bind="text: printTimeLeftString"></strong><br>
|
||||
{{ _('Printed') }}: <strong data-bind="text: byteString"></strong><br>
|
||||
|
||||
<div class="progress">
|
||||
<div class="bar" id="job_progressBar" data-bind="style: { width: progressString() + '%' }"></div>
|
||||
</div>
|
||||
|
||||
<div class="row-fluid print-control" style="display: none;" data-bind="visible: loginState.isUser">
|
||||
<button class="btn btn-primary span4" data-bind="click: print, enable: isOperational() && isReady() && !isPrinting() && loginState.isUser(), css: {'btn-danger': isPaused()}, attr: {title: titlePrintButton}" id="job_print"><i class="icon-white" data-bind="css: {'icon-print': !isPaused(), 'icon-undo': isPaused()}"></i> <span data-bind="text: (isPaused() ? '{{ _('Restart') }}' : '{{ _('Print') }}')">{{ _('Print') }}</span></button>
|
||||
<button class="btn span4" id="job_pause" data-bind="click: pause, enable: isOperational() && (isPrinting() || isPaused()) && loginState.isUser(), css: {active: isPaused()}, attr: {title: titlePauseButton}"><i data-bind="css: {'icon-pause': !isPaused(), 'icon-play': isPaused()}"></i> <span data-bind="visible: !isPaused()">{{ _('Pause') }}</span><span data-bind="visible: isPaused()">{{ _('Resume') }}</span></button>
|
||||
<button class="btn span4" id="job_cancel" data-bind="click: cancel, enable: isOperational() && (isPrinting() || isPaused()) && loginState.isUser()" title="{{ _('Cancels the print job') }}"><i class="icon-stop"></i> {{ _('Cancel') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="progress">
|
||||
<div class="bar" id="job_progressBar" data-bind="style: { width: progressString() + '%' }"></div>
|
||||
</div>
|
||||
|
||||
<div class="row-fluid print-control" style="display: none;" data-bind="visible: loginState.isUser">
|
||||
<button class="btn btn-primary span4" data-bind="click: print, enable: isOperational() && isReady() && !isPrinting() && loginState.isUser(), css: {'btn-danger': isPaused()}, attr: {title: titlePrintButton}" id="job_print"><i class="icon-white" data-bind="css: {'icon-print': !isPaused(), 'icon-undo': isPaused()}"></i> <span data-bind="text: (isPaused() ? '{{ _('Restart') }}' : '{{ _('Print') }}')">{{ _('Print') }}</span></button>
|
||||
<button class="btn span4" id="job_pause" data-bind="click: pause, enable: isOperational() && (isPrinting() || isPaused()) && loginState.isUser(), css: {active: isPaused()}, attr: {title: titlePauseButton}"><i data-bind="css: {'icon-pause': !isPaused(), 'icon-play': isPaused()}"></i> <span data-bind="visible: !isPaused()">{{ _('Pause') }}</span><span data-bind="visible: isPaused()">{{ _('Resume') }}</span></button>
|
||||
<button class="btn span4" id="job_cancel" data-bind="click: cancel, enable: isOperational() && (isPrinting() || isPaused()) && loginState.isUser()" title="{{ _('Cancels the print job') }}"><i class="icon-stop"></i> {{ _('Cancel') }}</button>
|
||||
</div>
|
||||
|
|
|
|||
34
src/octoprint/templates/stylesheets.jinja2
Normal file
34
src/octoprint/templates/stylesheets.jinja2
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
<link href="{{ url_for('static', filename='css/bootstrap.min.css') }}" rel="stylesheet" media="screen">
|
||||
<link href="{{ url_for('static', filename='css/bootstrap-modal.css') }}" rel="stylesheet" media="screen">
|
||||
<link href="{{ url_for('static', filename='css/bootstrap-slider.css') }}" rel="stylesheet" media="screen">
|
||||
<link href="{{ url_for('static', filename='css/font-awesome.min.css') }}" rel="stylesheet" media="screen">
|
||||
<link href="{{ url_for('static', filename='css/jquery.fileupload-ui.css') }}" rel="stylesheet" media="screen">
|
||||
<link href="{{ url_for('static', filename='css/pnotify.min.css') }}" rel="stylesheet" media="screen">
|
||||
|
||||
{% if stylesheet == "less" %}
|
||||
<link href="{{ url_for('static', filename='less/octoprint.less') }}" rel="stylesheet/less" type="text/css" media="screen">
|
||||
|
||||
<!-- Plugin files -->
|
||||
{% for name, assets in assetPlugins.items() %}
|
||||
{% if "less" in assets %}
|
||||
{% for asset in assets["less"] %}
|
||||
<link href="{{ url_for('plugin_assets', name=name, filename=asset) }}" rel="stylesheet/less" type="text/css" media="screen">
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
<!-- /Plugin files -->
|
||||
|
||||
<script src="{{ url_for('static', filename='js/lib/less.min.js') }}" type="text/javascript"></script>
|
||||
{% else %}
|
||||
<link href="{{ url_for('static', filename='css/octoprint.css') }}" rel="stylesheet" type="text/css" media="screen">
|
||||
<!-- Plugin files -->
|
||||
{% for name, assets in assetPlugins.items() %}
|
||||
{% if "css" in assets %}
|
||||
{% for asset in assets["css"] %}
|
||||
<link href="{{ url_for('plugin_assets', name=name, filename=asset) }}" rel="stylesheet" type="text/css" media="screen">
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
<!-- /Plugin files -->
|
||||
{% endif %}
|
||||
|
||||
|
|
@ -1,133 +1,131 @@
|
|||
<div class="tab-pane" id="control">
|
||||
{% if webcamStream %}
|
||||
<div id="webcam_container" tabindex="0" data-bind="event: { keydown: onKeyDown, mouseover: onMouseOver, mouseout: onMouseOut, focus: onFocus }">
|
||||
<img id="webcam_image" data-bind="css: { flipH: settings.webcam_flipH(), flipV: settings.webcam_flipV() }">
|
||||
<div class="keycontrol_overlay" data-bind="visible: showKeycontrols">
|
||||
<div class="keycontrol_overlay_heading">{{ _("Keyboard controls active") }} <a href="#" data-bind="click: toggleKeycontrolHelp"><i data-bind="css: { 'icon-chevron-down': !keycontrolHelpActive(), 'icon-chevron-up': keycontrolHelpActive() }"></i></a></div>
|
||||
<div data-bind="visible: keycontrolHelpActive">
|
||||
<div class="keycontrol_overlay_column">
|
||||
<kbd>→</kbd> / <kbd>←</kbd>: {{ _("X Axis") }} +/-<br>
|
||||
<kbd>↑</kbd> / <kbd>↓</kbd>: {{ _("Y Axis") }} +/-<br>
|
||||
<kbd>W</kbd>, <kbd>{{ _("Page↑") }}</kbd> / <kbd>S</kbd>, <kbd>{{ _("Page↓") }}</kbd>: {{ _("Z Axis") }} +/-
|
||||
</div>
|
||||
<div class="keycontrol_overlay_column">
|
||||
<kbd>Home</kbd>: {{ _("Home X/Y") }}<br>
|
||||
<kbd>End</kbd>: {{ _("Home Z") }}<br>
|
||||
<kbd>1</kbd>, <kbd>2</kbd>, <kbd>3</kbd>, <kbd>4</kbd>: {{ _("Stepsize") }} 0.1, 1, 10, 100mm
|
||||
</div>
|
||||
{% if webcamStream %}
|
||||
<div id="webcam_container" tabindex="0" data-bind="event: { keydown: onKeyDown, mouseover: onMouseOver, mouseout: onMouseOut, focus: onFocus }">
|
||||
<img id="webcam_image" data-bind="css: { flipH: settings.webcam_flipH(), flipV: settings.webcam_flipV() }">
|
||||
<div class="keycontrol_overlay" data-bind="visible: showKeycontrols">
|
||||
<div class="keycontrol_overlay_heading">{{ _("Keyboard controls active") }} <a href="#" data-bind="click: toggleKeycontrolHelp"><i data-bind="css: { 'icon-chevron-down': !keycontrolHelpActive(), 'icon-chevron-up': keycontrolHelpActive() }"></i></a></div>
|
||||
<div data-bind="visible: keycontrolHelpActive">
|
||||
<div class="keycontrol_overlay_column">
|
||||
<kbd>→</kbd> / <kbd>←</kbd>: {{ _("X Axis") }} +/-<br>
|
||||
<kbd>↑</kbd> / <kbd>↓</kbd>: {{ _("Y Axis") }} +/-<br>
|
||||
<kbd>W</kbd>, <kbd>{{ _("Page↑") }}</kbd> / <kbd>S</kbd>, <kbd>{{ _("Page↓") }}</kbd>: {{ _("Z Axis") }} +/-
|
||||
</div>
|
||||
<div class="keycontrol_overlay_column">
|
||||
<kbd>Home</kbd>: {{ _("Home X/Y") }}<br>
|
||||
<kbd>End</kbd>: {{ _("Home Z") }}<br>
|
||||
<kbd>1</kbd>, <kbd>2</kbd>, <kbd>3</kbd>, <kbd>4</kbd>: {{ _("Stepsize") }} 0.1, 1, 10, 100mm
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div data-bind="visible: keycontrolPossible">
|
||||
<small>{{ _("Hint: If you move your mouse over the picture, you enter keyboard control mode.") }}</small>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="jog-panel" style="display: none;" data-bind="visible: loginState.isUser">
|
||||
<!-- XY jogging control panel -->
|
||||
<div class="jog-panel">
|
||||
<h1>X/Y</h1>
|
||||
<div>
|
||||
<button class="btn box" id="control-yinc" data-bind="enable: isOperational() && !isPrinting() && loginState.isUser(), click: function() { $root.sendJogCommand('y',1) }"><i class="icon-arrow-up"></i></button>
|
||||
</div>
|
||||
<div>
|
||||
<button class="btn box pull-left" id="control-xdec" data-bind="enable: isOperational() && !isPrinting() && loginState.isUser(), click: function() { $root.sendJogCommand('x',-1) }"><i class="icon-arrow-left"></i></button>
|
||||
<button class="btn box pull-left" id="control-xyhome" data-bind="enable: isOperational() && !isPrinting() && loginState.isUser(), click: function() { $root.sendHomeCommand(['x', 'y']) }"><i class="icon-home"></i></button>
|
||||
<button class="btn box pull-left" id="control-xinc" data-bind="enable: isOperational() && !isPrinting() && loginState.isUser(), click: function() { $root.sendJogCommand('x',1) }"><i class="icon-arrow-right"></i></button>
|
||||
</div>
|
||||
<div>
|
||||
<button class="btn box" id="control-ydec" data-bind="enable: isOperational() && !isPrinting() && loginState.isUser(), click: function() { $root.sendJogCommand('y',-1) }"><i class="icon-arrow-down"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Z jogging control panel -->
|
||||
<div class="jog-panel">
|
||||
<h1>Z</h1>
|
||||
<div>
|
||||
<button class="btn box" id="control-zinc" data-bind="enable: isOperational() && !isPrinting() && loginState.isUser(), click: function() { $root.sendJogCommand('z',1) }"><i class="icon-arrow-up"></i></button>
|
||||
</div>
|
||||
<div>
|
||||
<button class="btn box" id="control-zhome" data-bind="enable: isOperational() && !isPrinting() && loginState.isUser(), click: function() { $root.sendHomeCommand(['z']) }"><i class="icon-home"></i></button>
|
||||
</div>
|
||||
<div>
|
||||
<button class="btn box" id="control-zdec" data-bind="enable: isOperational() && !isPrinting() && loginState.isUser(), click: function() { $root.sendJogCommand('z',-1) }"><i class="icon-arrow-down"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Jog distance -->
|
||||
<div class="distance">
|
||||
<div class="btn-group" data-toggle="buttons-radio" id="jog_distance">
|
||||
<button type="button" id="control-distance01" class="btn distance" data-distance="0.1" data-bind="enable: loginState.isUser()">0.1</button>
|
||||
<button type="button" id="control-distance1" class="btn distance" data-distance="1" data-bind="enable: loginState.isUser()">1</button>
|
||||
<button type="button" id="control-distance10" class="btn distance active" data-distance="10" data-bind="enable: loginState.isUser()">10</button>
|
||||
<button type="button" id="control-distance100" class="btn distance" data-distance="100" data-bind="enable: loginState.isUser()">100</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<!-- Extrusion control panel -->
|
||||
<div class="jog-panel" style="display: none;" data-bind="visible: loginState.isUser">
|
||||
<h1>Tool (E)</h1>
|
||||
<div data-bind="visible: keycontrolPossible">
|
||||
<small>{{ _("Hint: If you move your mouse over the picture, you enter keyboard control mode.") }}</small>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="jog-panel" style="display: none;" data-bind="visible: loginState.isUser">
|
||||
<!-- XY jogging control panel -->
|
||||
<div class="jog-panel">
|
||||
<h1>X/Y</h1>
|
||||
<div>
|
||||
<div class="btn-group control-box">
|
||||
<button class="btn dropdown-toggle" data-toggle="dropdown" data-bind="enable: isOperational() && !isPrinting() && !isPaused() && loginState.isUser()">
|
||||
{{ _('Select Tool...') }}
|
||||
<span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu" data-bind="foreach: tools">
|
||||
<li><a href="#" data-bind="click: $root.sendSelectToolCommand, text: name(), enable: $root.isOperational() && !$root.isPrinting() && !$root.isPaused() && $root.loginState.isUser()"></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="input-append control-box">
|
||||
<input type="text" class="input-mini text-right" data-bind="value: extrusionAmount, enable: isOperational() && !isPrinting() && loginState.isUser(), attr: {placeholder: settings.printer_defaultExtrusionLength}">
|
||||
<span class="add-on">mm</span>
|
||||
</div>
|
||||
<button class="btn btn-block control-box" data-bind="enable: isOperational() && !isPrinting() && loginState.isUser(), click: function() { $root.sendExtrudeCommand() }">{{ _('Extrude') }}</button>
|
||||
<button class="btn btn-block control-box" data-bind="enable: isOperational() && !isPrinting() && loginState.isUser(), click: function() { $root.sendRetractCommand() }">{{ _('Retract') }}</button>
|
||||
<button class="btn box" id="control-yinc" data-bind="enable: isOperational() && !isPrinting() && loginState.isUser(), click: function() { $root.sendJogCommand('y',1) }"><i class="icon-arrow-up"></i></button>
|
||||
</div>
|
||||
<div>
|
||||
<button class="btn box pull-left" id="control-xdec" data-bind="enable: isOperational() && !isPrinting() && loginState.isUser(), click: function() { $root.sendJogCommand('x',-1) }"><i class="icon-arrow-left"></i></button>
|
||||
<button class="btn box pull-left" id="control-xyhome" data-bind="enable: isOperational() && !isPrinting() && loginState.isUser(), click: function() { $root.sendHomeCommand(['x', 'y']) }"><i class="icon-home"></i></button>
|
||||
<button class="btn box pull-left" id="control-xinc" data-bind="enable: isOperational() && !isPrinting() && loginState.isUser(), click: function() { $root.sendJogCommand('x',1) }"><i class="icon-arrow-right"></i></button>
|
||||
</div>
|
||||
<div>
|
||||
<button class="btn box" id="control-ydec" data-bind="enable: isOperational() && !isPrinting() && loginState.isUser(), click: function() { $root.sendJogCommand('y',-1) }"><i class="icon-arrow-down"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- General control panel -->
|
||||
<div class="jog-panel" style="display: none;" data-bind="visible: loginState.isUser">
|
||||
<h1>{{ _('General') }}</h1>
|
||||
<!-- Z jogging control panel -->
|
||||
<div class="jog-panel">
|
||||
<h1>Z</h1>
|
||||
<div>
|
||||
<button class="btn btn-block control-box" data-bind="enable: isOperational() && !isPrinting() && loginState.isUser(), click: function() { $root.sendCustomCommand({type:'command',command:'M18'}) }">{{ _('Motors off') }}</button>
|
||||
<button class="btn btn-block control-box" data-bind="enable: isOperational() && loginState.isUser(), click: function() { $root.sendCustomCommand({type:'command',command:'M106'}) }">{{ _('Fans on') }}</button>
|
||||
<button class="btn btn-block control-box" data-bind="enable: isOperational() && loginState.isUser(), click: function() { $root.sendCustomCommand({type:'command',command:'M106 S0'}) }">{{ _('Fans off') }}</button>
|
||||
<button class="btn box" id="control-zinc" data-bind="enable: isOperational() && !isPrinting() && loginState.isUser(), click: function() { $root.sendJogCommand('z',1) }"><i class="icon-arrow-up"></i></button>
|
||||
</div>
|
||||
<div>
|
||||
<button class="btn box" id="control-zhome" data-bind="enable: isOperational() && !isPrinting() && loginState.isUser(), click: function() { $root.sendHomeCommand(['z']) }"><i class="icon-home"></i></button>
|
||||
</div>
|
||||
<div>
|
||||
<button class="btn box" id="control-zdec" data-bind="enable: isOperational() && !isPrinting() && loginState.isUser(), click: function() { $root.sendJogCommand('z',-1) }"><i class="icon-arrow-down"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Container for custom controls -->
|
||||
<div style="clear: both; display: none;" data-bind="visible: loginState.isUser, template: { name: $root.displayMode, foreach: controls }"></div>
|
||||
|
||||
<!-- Templates for custom controls -->
|
||||
<script type="text/html" id="customControls_sectionTemplate">
|
||||
<h1 data-bind="text: name"></h1>
|
||||
|
||||
<div data-bind="template: { name: $root.displayMode, foreach: children }"></div>
|
||||
</script>
|
||||
<script type="text/html" id="customControls_commandTemplate">
|
||||
<form class="form-inline">
|
||||
<button class="btn" data-bind="text: name, enable: $root.isOperational() && $root.loginState.isUser(), click: function() { $root.sendCustomCommand($data) }"></button>
|
||||
</form>
|
||||
</script>
|
||||
<script type="text/html" id="customControls_feedbackCommandTemplate">
|
||||
<form class="form-inline">
|
||||
<button class="btn" data-bind="text: name, enable: $root.isOperational() && $root.loginState.isUser(), click: function() { $root.sendCustomCommand($data) }"></button> <span data-bind="text: output"></span>
|
||||
</form>
|
||||
</script>
|
||||
<script type="text/html" id="customControls_feedbackTemplate">
|
||||
<div>
|
||||
<strong data-bind="text: name"></strong>: <span data-bind="text: output"></span>
|
||||
<!-- Jog distance -->
|
||||
<div class="distance">
|
||||
<div class="btn-group" data-toggle="buttons-radio" id="jog_distance">
|
||||
<button type="button" id="control-distance01" class="btn distance" data-distance="0.1" data-bind="enable: loginState.isUser()">0.1</button>
|
||||
<button type="button" id="control-distance1" class="btn distance" data-distance="1" data-bind="enable: loginState.isUser()">1</button>
|
||||
<button type="button" id="control-distance10" class="btn distance active" data-distance="10" data-bind="enable: loginState.isUser()">10</button>
|
||||
<button type="button" id="control-distance100" class="btn distance" data-distance="100" data-bind="enable: loginState.isUser()">100</button>
|
||||
</div>
|
||||
</script>
|
||||
<script type="text/html" id="customControls_parametricCommandTemplate">
|
||||
<form class="form-inline">
|
||||
<!-- ko foreach: input -->
|
||||
<label data-bind="text: name"></label>
|
||||
<input type="text" class="input-small" data-bind="attr: {placeholder: name}, value: value">
|
||||
<!-- /ko -->
|
||||
<button class="btn" data-bind="text: name, enable: $root.isOperational() && $root.loginState.isUser(), click: function() { $root.sendCustomCommand($data) }"></button>
|
||||
</form>
|
||||
</script>
|
||||
<script type="text/html" id="customControls_emptyTemplate"><div></div></script>
|
||||
<!-- End of templates for custom controls -->
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<!-- Extrusion control panel -->
|
||||
<div class="jog-panel" style="display: none;" data-bind="visible: loginState.isUser">
|
||||
<h1>Tool (E)</h1>
|
||||
<div>
|
||||
<div class="btn-group control-box">
|
||||
<button class="btn dropdown-toggle" data-toggle="dropdown" data-bind="enable: isOperational() && !isPrinting() && !isPaused() && loginState.isUser()">
|
||||
{{ _('Select Tool...') }}
|
||||
<span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu" data-bind="foreach: tools">
|
||||
<li><a href="#" data-bind="click: $root.sendSelectToolCommand, text: name(), enable: $root.isOperational() && !$root.isPrinting() && !$root.isPaused() && $root.loginState.isUser()"></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="input-append control-box">
|
||||
<input type="text" class="input-mini text-right" data-bind="value: extrusionAmount, enable: isOperational() && !isPrinting() && loginState.isUser(), attr: {placeholder: settings.printer_defaultExtrusionLength}">
|
||||
<span class="add-on">mm</span>
|
||||
</div>
|
||||
<button class="btn btn-block control-box" data-bind="enable: isOperational() && !isPrinting() && loginState.isUser(), click: function() { $root.sendExtrudeCommand() }">{{ _('Extrude') }}</button>
|
||||
<button class="btn btn-block control-box" data-bind="enable: isOperational() && !isPrinting() && loginState.isUser(), click: function() { $root.sendRetractCommand() }">{{ _('Retract') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- General control panel -->
|
||||
<div class="jog-panel" style="display: none;" data-bind="visible: loginState.isUser">
|
||||
<h1>{{ _('General') }}</h1>
|
||||
<div>
|
||||
<button class="btn btn-block control-box" data-bind="enable: isOperational() && !isPrinting() && loginState.isUser(), click: function() { $root.sendCustomCommand({type:'command',command:'M18'}) }">{{ _('Motors off') }}</button>
|
||||
<button class="btn btn-block control-box" data-bind="enable: isOperational() && loginState.isUser(), click: function() { $root.sendCustomCommand({type:'command',command:'M106'}) }">{{ _('Fans on') }}</button>
|
||||
<button class="btn btn-block control-box" data-bind="enable: isOperational() && loginState.isUser(), click: function() { $root.sendCustomCommand({type:'command',command:'M106 S0'}) }">{{ _('Fans off') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Container for custom controls -->
|
||||
<div style="clear: both; display: none;" data-bind="visible: loginState.isUser, template: { name: $root.displayMode, foreach: controls }"></div>
|
||||
|
||||
<!-- Templates for custom controls -->
|
||||
<script type="text/html" id="customControls_sectionTemplate">
|
||||
<h1 data-bind="text: name"></h1>
|
||||
|
||||
<div data-bind="template: { name: $root.displayMode, foreach: children }"></div>
|
||||
</script>
|
||||
<script type="text/html" id="customControls_commandTemplate">
|
||||
<form class="form-inline">
|
||||
<button class="btn" data-bind="text: name, enable: $root.isOperational() && $root.loginState.isUser(), click: function() { $root.sendCustomCommand($data) }"></button>
|
||||
</form>
|
||||
</script>
|
||||
<script type="text/html" id="customControls_feedbackCommandTemplate">
|
||||
<form class="form-inline">
|
||||
<button class="btn" data-bind="text: name, enable: $root.isOperational() && $root.loginState.isUser(), click: function() { $root.sendCustomCommand($data) }"></button> <span data-bind="text: output"></span>
|
||||
</form>
|
||||
</script>
|
||||
<script type="text/html" id="customControls_feedbackTemplate">
|
||||
<div>
|
||||
<strong data-bind="text: name"></strong>: <span data-bind="text: output"></span>
|
||||
</div>
|
||||
</script>
|
||||
<script type="text/html" id="customControls_parametricCommandTemplate">
|
||||
<form class="form-inline">
|
||||
<!-- ko foreach: input -->
|
||||
<label data-bind="text: name"></label>
|
||||
<input type="text" class="input-small" data-bind="attr: {placeholder: name}, value: value">
|
||||
<!-- /ko -->
|
||||
<button class="btn" data-bind="text: name, enable: $root.isOperational() && $root.loginState.isUser(), click: function() { $root.sendCustomCommand($data) }"></button>
|
||||
</form>
|
||||
</script>
|
||||
<script type="text/html" id="customControls_emptyTemplate"><div></div></script>
|
||||
<!-- End of templates for custom controls -->
|
||||
|
|
|
|||
|
|
@ -1,79 +1,77 @@
|
|||
{% if enableGCodeVisualizer %}
|
||||
<div class="tab-pane" id="gcode">
|
||||
<div data-bind="visible: !waitForApproval()">
|
||||
<input id="gcode_slider_layers" type="text">
|
||||
<canvas id="gcode_canvas" width="568" height="568"></canvas>
|
||||
<input id="gcode_slider_commands" type="text" style="width: 554px">
|
||||
<div data-bind="visible: !waitForApproval()">
|
||||
<input id="gcode_slider_layers" type="text">
|
||||
<canvas id="gcode_canvas" width="568" height="568"></canvas>
|
||||
<input id="gcode_slider_commands" type="text" style="width: 554px">
|
||||
|
||||
<div class="progress" >
|
||||
<div class="bar" style="width: 0%;" data-bind="text: ui_progress_text, style: { width: ui_progress_percentage() + '%' }"></div>
|
||||
</div>
|
||||
|
||||
<div class="row-fluid">
|
||||
<div class="span7">
|
||||
<h1>Model info</h1>
|
||||
<p data-bind="html: ui_modelInfo"></p>
|
||||
|
||||
<h1>Layer info</h1>
|
||||
<p data-bind="html: ui_layerInfo"></p>
|
||||
</div>
|
||||
<div class="span5">
|
||||
<h1>Renderer options</h1>
|
||||
|
||||
<p>
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" data-bind="checked: renderer_syncProgress">{{ _('Sync with job progress') }}
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" data-bind="checked: renderer_centerViewport">{{ _('Center viewport on model') }}
|
||||
</label>
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" data-bind="checked: renderer_zoomOnModel">{{ _('Zoom in on model') }}
|
||||
</label>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" data-bind="checked: renderer_showMoves">{{ _('Show moves') }}
|
||||
</label>
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" data-bind="checked: renderer_showRetracts">{{ _('Show retracts') }}
|
||||
</label>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" data-bind="checked: renderer_showPrevious">{{ _('Also show previous layer') }}
|
||||
</label>
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" data-bind="checked: renderer_showNext">{{ _('Also show next layer') }}
|
||||
</label>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<button class="btn btn-block" data-bind="click: reload, enable: enableReload">{{ _('Reload') }}</button>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="progress" >
|
||||
<div class="bar" style="width: 0%;" data-bind="text: ui_progress_text, style: { width: ui_progress_percentage() + '%' }"></div>
|
||||
</div>
|
||||
<div data-bind="visible: waitForApproval">
|
||||
<h1>Warning</h1>
|
||||
|
||||
<p>
|
||||
You've selected <strong data-bind="text: selectedFile.name"></strong> for printing which has a size of
|
||||
<strong data-bind="text: formatSize(selectedFile.size())"></strong>. Depending on your machine this
|
||||
might be too large for rendering and cause your browser to become unresponsive or crash.
|
||||
</p>
|
||||
<div class="row-fluid">
|
||||
<div class="span7">
|
||||
<h1>Model info</h1>
|
||||
<p data-bind="html: ui_modelInfo"></p>
|
||||
|
||||
<p>
|
||||
Are you sure you want to visualize this file nevertheless?
|
||||
</p>
|
||||
<h1>Layer info</h1>
|
||||
<p data-bind="html: ui_layerInfo"></p>
|
||||
</div>
|
||||
<div class="span5">
|
||||
<h1>Renderer options</h1>
|
||||
|
||||
<button class="btn btn-warning btn-block" data-bind="click: approveLargeFile">
|
||||
Yes, please visualize <span data-bind="text: selectedFile.name"></span> regardless of its size
|
||||
</button>
|
||||
<p>
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" data-bind="checked: renderer_syncProgress">{{ _('Sync with job progress') }}
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" data-bind="checked: renderer_centerViewport">{{ _('Center viewport on model') }}
|
||||
</label>
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" data-bind="checked: renderer_zoomOnModel">{{ _('Zoom in on model') }}
|
||||
</label>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" data-bind="checked: renderer_showMoves">{{ _('Show moves') }}
|
||||
</label>
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" data-bind="checked: renderer_showRetracts">{{ _('Show retracts') }}
|
||||
</label>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" data-bind="checked: renderer_showPrevious">{{ _('Also show previous layer') }}
|
||||
</label>
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" data-bind="checked: renderer_showNext">{{ _('Also show next layer') }}
|
||||
</label>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<button class="btn btn-block" data-bind="click: reload, enable: enableReload">{{ _('Reload') }}</button>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div data-bind="visible: waitForApproval">
|
||||
<h1>Warning</h1>
|
||||
|
||||
<p>
|
||||
You've selected <strong data-bind="text: selectedFile.name"></strong> for printing which has a size of
|
||||
<strong data-bind="text: formatSize(selectedFile.size())"></strong>. Depending on your machine this
|
||||
might be too large for rendering and cause your browser to become unresponsive or crash.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Are you sure you want to visualize this file nevertheless?
|
||||
</p>
|
||||
|
||||
<button class="btn btn-warning btn-block" data-bind="click: approveLargeFile">
|
||||
Yes, please visualize <span data-bind="text: selectedFile.name"></span> regardless of its size
|
||||
</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
|
|
|||
|
|
@ -1,57 +1,55 @@
|
|||
<div class="tab-pane active" id="temp">
|
||||
{% if enableTemperatureGraph %}
|
||||
<div class="row" style="padding-left: 20px">
|
||||
<div id="temperature-graph"></div>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="row-fluid">
|
||||
|
||||
<table class="table table-bordered table-hover" style="table-layout: fixed; width: 100%; margin-top: 20px">
|
||||
<tr>
|
||||
<th style="width: 18%"></th>
|
||||
<th style="width: 12%; text-align: right">{{ _('Actual') }}</th>
|
||||
<th style="width: 35%">{{ _('Target') }}</th>
|
||||
<th style="width: 35%">{{ _('Offset') }}</th>
|
||||
</tr>
|
||||
<!-- ko foreach: tools -->
|
||||
<tr data-bind="template: { name: 'temprow-template' }"></tr>
|
||||
<!-- /ko -->
|
||||
<tr data-bind="template: { name: 'temprow-template', data: bedTemp }, visible: hasBed"></tr>
|
||||
</table>
|
||||
|
||||
<script type="text/html" id="temprow-template">
|
||||
<th style="vertical-align: middle" data-bind="text: name"></th>
|
||||
<td style="text-align: right; vertical-align: middle" data-bind="html: formatTemperature(actual())"></td>
|
||||
<td style="vertical-align: middle; overflow: visible">
|
||||
<div class="input-append">
|
||||
<input type="text" class="input-mini text-right tempInput" data-bind="attr: {placeholder: cleanTemperature(target()) }, value: newTarget, enable: $root.isOperational() && $root.loginState.isUser(), event: { keyup: function(d, e) {$root.handleEnter(e, 'target', $data);} }">
|
||||
<span class="add-on">°C</span>
|
||||
<div class="btn-group">
|
||||
<button type="submit" data-bind="click: $parent.setTarget, enable: $root.isOperational() && $root.loginState.isUser()" class="btn">{{ _('Set') }}</button>
|
||||
<button class="btn dropdown-toggle" data-toggle="dropdown" data-bind="enable: $root.isOperational() && $root.loginState.isUser()">
|
||||
<span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
<!-- ko foreach: $root.temperature_profiles -->
|
||||
<li>
|
||||
<a href="#" data-bind="click: function() {$root.setTargetFromProfile($parent, $data);}, text: 'Set ' + name + ' (' + ($parent.key() == 'bed' ? bed : extruder) + '°C)'"></a>
|
||||
</li>
|
||||
<!-- /ko -->
|
||||
<li class="divider"></li>
|
||||
<li>
|
||||
<a href="#" data-bind="click: $root.setTargetToZero">{{ _('Off') }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td style="vertical-align: middle">
|
||||
<div class="input-append">
|
||||
<input type="number" min="-50" max="50" class="input-mini text-right tempInput" data-bind="attr: {placeholder: offset}, value: newOffset, enable: $root.isOperational() && $root.loginState.isUser(), event: { keyup: function(d, e) {$root.handleEnter(e, 'offset', $data);} }">
|
||||
<span class="add-on">°C</span>
|
||||
<button type="submit" data-bind="click: $root.setOffset, enable: $root.isOperational() && $root.loginState.isUser()" class="btn">{{ _('Set') }}</button>
|
||||
</div>
|
||||
</td>
|
||||
</script>
|
||||
{% if enableTemperatureGraph %}
|
||||
<div class="row" style="padding-left: 20px">
|
||||
<div id="temperature-graph"></div>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="row-fluid">
|
||||
|
||||
<table class="table table-bordered table-hover" style="table-layout: fixed; width: 100%; margin-top: 20px">
|
||||
<tr>
|
||||
<th style="width: 18%"></th>
|
||||
<th style="width: 12%; text-align: right">{{ _('Actual') }}</th>
|
||||
<th style="width: 35%">{{ _('Target') }}</th>
|
||||
<th style="width: 35%">{{ _('Offset') }}</th>
|
||||
</tr>
|
||||
<!-- ko foreach: tools -->
|
||||
<tr data-bind="template: { name: 'temprow-template' }"></tr>
|
||||
<!-- /ko -->
|
||||
<tr data-bind="template: { name: 'temprow-template', data: bedTemp }, visible: hasBed"></tr>
|
||||
</table>
|
||||
|
||||
<script type="text/html" id="temprow-template">
|
||||
<th style="vertical-align: middle" data-bind="text: name"></th>
|
||||
<td style="text-align: right; vertical-align: middle" data-bind="html: formatTemperature(actual())"></td>
|
||||
<td style="vertical-align: middle; overflow: visible">
|
||||
<div class="input-append">
|
||||
<input type="text" class="input-mini text-right tempInput" data-bind="attr: {placeholder: cleanTemperature(target()) }, value: newTarget, enable: $root.isOperational() && $root.loginState.isUser(), event: { keyup: function(d, e) {$root.handleEnter(e, 'target', $data);} }">
|
||||
<span class="add-on">°C</span>
|
||||
<div class="btn-group">
|
||||
<button type="submit" data-bind="click: $parent.setTarget, enable: $root.isOperational() && $root.loginState.isUser()" class="btn">{{ _('Set') }}</button>
|
||||
<button class="btn dropdown-toggle" data-toggle="dropdown" data-bind="enable: $root.isOperational() && $root.loginState.isUser()">
|
||||
<span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
<!-- ko foreach: $root.temperature_profiles -->
|
||||
<li>
|
||||
<a href="#" data-bind="click: function() {$root.setTargetFromProfile($parent, $data);}, text: 'Set ' + name + ' (' + ($parent.key() == 'bed' ? bed : extruder) + '°C)'"></a>
|
||||
</li>
|
||||
<!-- /ko -->
|
||||
<li class="divider"></li>
|
||||
<li>
|
||||
<a href="#" data-bind="click: $root.setTargetToZero">{{ _('Off') }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td style="vertical-align: middle">
|
||||
<div class="input-append">
|
||||
<input type="number" min="-50" max="50" class="input-mini text-right tempInput" data-bind="attr: {placeholder: offset}, value: newOffset, enable: $root.isOperational() && $root.loginState.isUser(), event: { keyup: function(d, e) {$root.handleEnter(e, 'offset', $data);} }">
|
||||
<span class="add-on">°C</span>
|
||||
<button type="submit" data-bind="click: $root.setOffset, enable: $root.isOperational() && $root.loginState.isUser()" class="btn">{{ _('Set') }}</button>
|
||||
</div>
|
||||
</td>
|
||||
</script>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,16 +1,14 @@
|
|||
<div class="tab-pane" id="term">
|
||||
<pre id="terminal-output" class="pre-scrollable"></pre>
|
||||
<pre id="terminal-output" class="pre-scrollable"></pre>
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" id="terminal-autoscroll" data-bind="checked: autoscrollEnabled"> {{ _('Autoscroll') }}
|
||||
</label>
|
||||
<div data-bind="foreach: filters">
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" id="terminal-autoscroll" data-bind="checked: autoscrollEnabled"> {{ _('Autoscroll') }}
|
||||
<input type="checkbox" data-bind="attr: { value: regex }, checked: $parent.activeFilters"> <span data-bind="text: name"></span>
|
||||
</label>
|
||||
<div data-bind="foreach: filters">
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" data-bind="attr: { value: regex }, checked: $parent.activeFilters"> <span data-bind="text: name"></span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="input-append" style="display: none;" data-bind="visible: loginState.isUser">
|
||||
<input type="text" id="terminal-command" data-bind="value: command, event: { keyup: function(d,e) { return handleKeyUp(e); }, keydown: function(d,e) { return handleKeyDown(e); } }, enable: isOperational() && loginState.isUser()">
|
||||
<button class="btn" type="button" id="terminal-send" data-bind="click: sendCommand, enable: isOperational() && loginState.isUser()">{{ _('Send') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="input-append" style="display: none;" data-bind="visible: loginState.isUser">
|
||||
<input type="text" id="terminal-command" data-bind="value: command, event: { keyup: function(d,e) { return handleKeyUp(e); }, keydown: function(d,e) { return handleKeyDown(e); } }, enable: isOperational() && loginState.isUser()">
|
||||
<button class="btn" type="button" id="terminal-send" data-bind="click: sendCommand, enable: isOperational() && loginState.isUser()">{{ _('Send') }}</button>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,71 +1,69 @@
|
|||
{% if enableTimelapse %}
|
||||
<div class="tab-pane" id="timelapse">
|
||||
<div style="display: none;" data-bind="visible: loginState.isUser">
|
||||
<h1>{{ _('Timelapse Configuration') }}</h1>
|
||||
<div style="display: none;" data-bind="visible: loginState.isUser">
|
||||
<h1>{{ _('Timelapse Configuration') }}</h1>
|
||||
|
||||
<label for="webcam_timelapse_mode">{{ _('Timelapse Mode') }}</label>
|
||||
<select id="webcam_timelapse_mode" data-bind="value: timelapseType, enable: isOperational() && !isPrinting() && loginState.isUser()">
|
||||
<option value="off">{{ _('Off') }}</option>
|
||||
<option value="zchange">{{ _('On Z Change') }}</option>
|
||||
<option value="timed">{{ _('Timed') }}</option>
|
||||
</select>
|
||||
<label for="webcam_timelapse_mode">{{ _('Timelapse Mode') }}</label>
|
||||
<select id="webcam_timelapse_mode" data-bind="value: timelapseType, enable: isOperational() && !isPrinting() && loginState.isUser()">
|
||||
<option value="off">{{ _('Off') }}</option>
|
||||
<option value="zchange">{{ _('On Z Change') }}</option>
|
||||
<option value="timed">{{ _('Timed') }}</option>
|
||||
</select>
|
||||
|
||||
<label for="webcam_timelapse_postRoll">{{ _('Timelapse post roll (in rendered seconds)') }}</label>
|
||||
<label for="webcam_timelapse_postRoll">{{ _('Timelapse post roll (in rendered seconds)') }}</label>
|
||||
<div class="input-append">
|
||||
<input type="text" class="input-mini" id="webcam_timelapse_postRoll" data-bind="value: timelapsePostRoll, valueUpdate: 'afterkeydown', enable: isOperational() && !isPrinting() && loginState.isUser() && timelapseTypeSelected()">
|
||||
<span class="add-on">{{ _('sec') }}</span>
|
||||
</div>
|
||||
|
||||
<div id="webcam_timelapse_timedsettings" data-bind="visible: intervalInputEnabled">
|
||||
<label for="webcam_timelapse_interval">{{ _('Interval') }}</label>
|
||||
<div class="input-append">
|
||||
<input type="text" class="input-mini" id="webcam_timelapse_postRoll" data-bind="value: timelapsePostRoll, valueUpdate: 'afterkeydown', enable: isOperational() && !isPrinting() && loginState.isUser() && timelapseTypeSelected()">
|
||||
<input type="text" class="input-mini" id="webcam_timelapse_interval" data-bind="value: timelapseTimedInterval, valueUpdate: 'afterkeydown', enable: isOperational() && !isPrinting() && loginState.isUser()">
|
||||
<span class="add-on">{{ _('sec') }}</span>
|
||||
</div>
|
||||
|
||||
<div id="webcam_timelapse_timedsettings" data-bind="visible: intervalInputEnabled">
|
||||
<label for="webcam_timelapse_interval">{{ _('Interval') }}</label>
|
||||
<div class="input-append">
|
||||
<input type="text" class="input-mini" id="webcam_timelapse_interval" data-bind="value: timelapseTimedInterval, valueUpdate: 'afterkeydown', enable: isOperational() && !isPrinting() && loginState.isUser()">
|
||||
<span class="add-on">{{ _('sec') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div data-bind="visible: loginState.isAdmin">
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" data-bind="checked: persist"> {{ _('Save as default') }}
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<button class="btn" data-bind="click: save, enable: saveButtonEnabled">{{ _('Save config') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h1>{{ _('Finished Timelapses') }}</h1>
|
||||
|
||||
<div class="pull-right">
|
||||
<small>{{ _('Sort by') }}: <a href="#" data-bind="click: function() { listHelper.changeSorting('name'); }">{{ _('Name') }} ({{ _('ascending') }})</a> | <a href="#" data-bind="click: function() { listHelper.changeSorting('creation'); }">{{ _('Creation date') }} ({{ _('descending') }})</a> | <a href="#" data-bind="click: function() { listHelper.changeSorting('size'); }">{{ _('Size') }} ({{ _('descending') }})</a></small>
|
||||
<div data-bind="visible: loginState.isAdmin">
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" data-bind="checked: persist"> {{ _('Save as default') }}
|
||||
</label>
|
||||
</div>
|
||||
<table class="table table-striped table-hover table-condensed table-hover" id="timelapse_files">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="timelapse_files_name">{{ _('Name') }}</th>
|
||||
<th class="timelapse_files_size">{{ _('Size') }}</th>
|
||||
<th class="timelapse_files_action">{{ _('Action') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody data-bind="foreach: listHelper.paginatedItems">
|
||||
<tr data-bind="attr: {title: name}">
|
||||
<td class="timelapse_files_name" data-bind="text: name"></td>
|
||||
<td class="timelapse_files_size" data-bind="text: size"></td>
|
||||
<td class="timelapse_files_action"><a href="#" class="icon-trash" data-bind="click: function() { if ($root.loginState.isUser()) { $parent.removeFile($data.name); } else { return; } }, css: {disabled: !$root.loginState.isUser()}"></a> | <a href="#" class="icon-download" data-bind="attr: {href: url}"></a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="pagination pagination-mini pagination-centered">
|
||||
<ul>
|
||||
<li data-bind="css: {disabled: listHelper.currentPage() === 0}"><a href="#" data-bind="click: listHelper.prevPage">«</a></li>
|
||||
</ul>
|
||||
<ul data-bind="foreach: listHelper.pages">
|
||||
<li data-bind="css: { active: $data.number === $root.listHelper.currentPage(), disabled: $data.number === -1 }"><a href="#" data-bind="text: $data.text, click: function() { $root.listHelper.changePage($data.number); }"></a></li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li data-bind="css: {disabled: listHelper.currentPage() === listHelper.lastPage()}"><a href="#" data-bind="click: listHelper.nextPage">»</a></li>
|
||||
</ul>
|
||||
|
||||
<div>
|
||||
<button class="btn" data-bind="click: save, enable: saveButtonEnabled">{{ _('Save config') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h1>{{ _('Finished Timelapses') }}</h1>
|
||||
|
||||
<div class="pull-right">
|
||||
<small>{{ _('Sort by') }}: <a href="#" data-bind="click: function() { listHelper.changeSorting('name'); }">{{ _('Name') }} ({{ _('ascending') }})</a> | <a href="#" data-bind="click: function() { listHelper.changeSorting('creation'); }">{{ _('Creation date') }} ({{ _('descending') }})</a> | <a href="#" data-bind="click: function() { listHelper.changeSorting('size'); }">{{ _('Size') }} ({{ _('descending') }})</a></small>
|
||||
</div>
|
||||
<table class="table table-striped table-hover table-condensed table-hover" id="timelapse_files">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="timelapse_files_name">{{ _('Name') }}</th>
|
||||
<th class="timelapse_files_size">{{ _('Size') }}</th>
|
||||
<th class="timelapse_files_action">{{ _('Action') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody data-bind="foreach: listHelper.paginatedItems">
|
||||
<tr data-bind="attr: {title: name}">
|
||||
<td class="timelapse_files_name" data-bind="text: name"></td>
|
||||
<td class="timelapse_files_size" data-bind="text: size"></td>
|
||||
<td class="timelapse_files_action"><a href="#" class="icon-trash" data-bind="click: function() { if ($root.loginState.isUser()) { $parent.removeFile($data.name); } else { return; } }, css: {disabled: !$root.loginState.isUser()}"></a> | <a href="#" class="icon-download" data-bind="attr: {href: url}"></a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="pagination pagination-mini pagination-centered">
|
||||
<ul>
|
||||
<li data-bind="css: {disabled: listHelper.currentPage() === 0}"><a href="#" data-bind="click: listHelper.prevPage">«</a></li>
|
||||
</ul>
|
||||
<ul data-bind="foreach: listHelper.pages">
|
||||
<li data-bind="css: { active: $data.number === $root.listHelper.currentPage(), disabled: $data.number === -1 }"><a href="#" data-bind="text: $data.text, click: function() { $root.listHelper.changePage($data.number); }"></a></li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li data-bind="css: {disabled: listHelper.currentPage() === listHelper.lastPage()}"><a href="#" data-bind="click: listHelper.nextPage">»</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
|
|
|||
|
|
@ -21,16 +21,16 @@ class PluginTestCase(unittest.TestCase):
|
|||
def test_plugin_loading(self):
|
||||
self.assertEquals(4, len(self.plugin_manager.plugins))
|
||||
self.assertEquals(1, len(self.plugin_manager.plugin_hooks))
|
||||
self.assertEquals(2, len(self.plugin_manager.plugin_implementations))
|
||||
self.assertEquals(2, len(self.plugin_manager.plugin_implementations_by_type))
|
||||
|
||||
self.assertTrue("octoprint.core.startup" in self.plugin_manager.plugin_hooks)
|
||||
self.assertEquals(1, len(self.plugin_manager.plugin_hooks["octoprint.core.startup"]))
|
||||
|
||||
self.assertTrue(octoprint.plugin.StartupPlugin in self.plugin_manager.plugin_implementations)
|
||||
self.assertEquals(2, len(self.plugin_manager.plugin_implementations[octoprint.plugin.StartupPlugin]))
|
||||
self.assertTrue(octoprint.plugin.StartupPlugin in self.plugin_manager.plugin_implementations_by_type)
|
||||
self.assertEquals(2, len(self.plugin_manager.plugin_implementations_by_type[octoprint.plugin.StartupPlugin]))
|
||||
|
||||
self.assertTrue(octoprint.plugin.SettingsPlugin in self.plugin_manager.plugin_implementations)
|
||||
self.assertEquals(2, len(self.plugin_manager.plugin_implementations[octoprint.plugin.SettingsPlugin]))
|
||||
self.assertTrue(octoprint.plugin.SettingsPlugin in self.plugin_manager.plugin_implementations_by_type)
|
||||
self.assertEquals(2, len(self.plugin_manager.plugin_implementations_by_type[octoprint.plugin.SettingsPlugin]))
|
||||
|
||||
def test_get_plugin(self):
|
||||
plugin = self.plugin_manager.get_plugin("hook_plugin")
|
||||
|
|
|
|||
Loading…
Reference in a new issue