Implemented support for customizable system actions/commands, e.g. for shutting down/rebooting the system OctoPrint's running on from within the UI.
parent
a261896186
commit
eb3832b028
|
@ -9,6 +9,7 @@ import tornadio2
|
||||||
import os
|
import os
|
||||||
import threading
|
import threading
|
||||||
import logging, logging.config
|
import logging, logging.config
|
||||||
|
import subprocess
|
||||||
|
|
||||||
from octoprint.printer import Printer, getConnectionOptions
|
from octoprint.printer import Printer, getConnectionOptions
|
||||||
from octoprint.settings import settings
|
from octoprint.settings import settings
|
||||||
|
@ -30,8 +31,9 @@ def index():
|
||||||
return render_template(
|
return render_template(
|
||||||
"index.html",
|
"index.html",
|
||||||
webcamStream=settings().get(["webcam", "stream"]),
|
webcamStream=settings().get(["webcam", "stream"]),
|
||||||
enableTimelapse=(settings().get(["webcam", "snapshot"]) is not None and settings().get(["webcam", "ffmpeg"]) is not None),
|
enableTimelapse=settings().get(["webcam", "snapshot"]) is not None and settings().get(["webcam", "ffmpeg"]) is not None,
|
||||||
enableGCodeVisualizer=settings().get(["feature", "gCodeVisualizer"])
|
enableGCodeVisualizer=settings().get(["feature", "gCodeVisualizer"]),
|
||||||
|
enableSystemMenu=settings().get(["system"]) is not None and settings().get(["system", "actions"]) is not None and len(settings().get(["system", "actions"])) > 0
|
||||||
)
|
)
|
||||||
|
|
||||||
#~~ Printer state
|
#~~ Printer state
|
||||||
|
@ -364,6 +366,9 @@ def getSettings():
|
||||||
},
|
},
|
||||||
"temperature": {
|
"temperature": {
|
||||||
"profiles": s.get(["temperature", "profiles"])
|
"profiles": s.get(["temperature", "profiles"])
|
||||||
|
},
|
||||||
|
"system": {
|
||||||
|
"actions": s.get(["system", "actions"])
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -403,10 +408,34 @@ def setSettings():
|
||||||
if "temperature" in data.keys():
|
if "temperature" in data.keys():
|
||||||
if "profiles" in data["temperature"].keys(): s.set(["temperature", "profiles"], data["temperature"]["profiles"])
|
if "profiles" in data["temperature"].keys(): s.set(["temperature", "profiles"], data["temperature"]["profiles"])
|
||||||
|
|
||||||
|
if "system" in data.keys():
|
||||||
|
if "actions" in data["system"].keys(): s.set(["system", "actions"], data["system"]["actions"])
|
||||||
|
|
||||||
s.save()
|
s.save()
|
||||||
|
|
||||||
return getSettings()
|
return getSettings()
|
||||||
|
|
||||||
|
#~~ system control
|
||||||
|
|
||||||
|
@app.route(BASEURL + "system", methods=["POST"])
|
||||||
|
def performSystemAction():
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
if request.values.has_key("action"):
|
||||||
|
action = request.values["action"]
|
||||||
|
availableActions = settings().get(["system", "actions"])
|
||||||
|
for availableAction in availableActions:
|
||||||
|
if availableAction["action"] == action:
|
||||||
|
logger.info("Performing command: %s" % availableAction["command"])
|
||||||
|
try:
|
||||||
|
subprocess.check_output(availableAction["command"])
|
||||||
|
except subprocess.CalledProcessError, e:
|
||||||
|
logger.warn("Command failed with return code %i: %s" % (e.returncode, e.message))
|
||||||
|
return app.make_response(("Command failed with return code %i: %s" % (e.returncode, e.message), 500, []))
|
||||||
|
except Exception, ex:
|
||||||
|
logger.exception("Command failed")
|
||||||
|
return app.make_response(("Command failed: %r" % ex, 500, []))
|
||||||
|
return jsonify(SUCCESS)
|
||||||
|
|
||||||
#~~ startup code
|
#~~ startup code
|
||||||
|
|
||||||
def run(host = "0.0.0.0", port = 5000, debug = False):
|
def run(host = "0.0.0.0", port = 5000, debug = False):
|
||||||
|
|
|
@ -61,13 +61,16 @@ default_settings.update({
|
||||||
"z": 200,
|
"z": 200,
|
||||||
"e": 300
|
"e": 300
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"temperature": {
|
"temperature": {
|
||||||
"profiles":
|
"profiles":
|
||||||
[
|
[
|
||||||
{"name": "ABS", "extruder" : 210, "bed" : 100 },
|
{"name": "ABS", "extruder" : 210, "bed" : 100 },
|
||||||
{"name": "PLA", "extruder" : 180, "bed" : 60 }
|
{"name": "PLA", "extruder" : 180, "bed" : 60 }
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"system": {
|
||||||
|
"actions": []
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,83 @@
|
||||||
|
/*
|
||||||
|
Document : jquery.pnotify.default.css
|
||||||
|
Created on : Nov 23, 2009, 3:14:10 PM
|
||||||
|
Author : Hunter Perrin
|
||||||
|
Version : 1.2.0
|
||||||
|
Link : http://pinesframework.org/pnotify/
|
||||||
|
Description:
|
||||||
|
Default styling for Pines Notify jQuery plugin.
|
||||||
|
*/
|
||||||
|
/* -- Notice */
|
||||||
|
.ui-pnotify {
|
||||||
|
top: 25px;
|
||||||
|
right: 25px;
|
||||||
|
position: absolute;
|
||||||
|
height: auto;
|
||||||
|
/* Ensures notices are above everything */
|
||||||
|
z-index: 9999;
|
||||||
|
}
|
||||||
|
/* Hides position: fixed from IE6 */
|
||||||
|
html > body .ui-pnotify {
|
||||||
|
position: fixed;
|
||||||
|
}
|
||||||
|
.ui-pnotify .ui-pnotify-shadow {
|
||||||
|
-webkit-box-shadow: 0px 2px 10px rgba(50, 50, 50, 0.5);
|
||||||
|
-moz-box-shadow: 0px 2px 10px rgba(50, 50, 50, 0.5);
|
||||||
|
box-shadow: 0px 2px 10px rgba(50, 50, 50, 0.5);
|
||||||
|
}
|
||||||
|
.ui-pnotify-container {
|
||||||
|
background-position: 0 0;
|
||||||
|
padding: .8em;
|
||||||
|
height: 100%;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
.ui-pnotify-sharp {
|
||||||
|
-webkit-border-radius: 0;
|
||||||
|
-moz-border-radius: 0;
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
.ui-pnotify-closer, .ui-pnotify-sticker {
|
||||||
|
float: right;
|
||||||
|
margin-left: .2em;
|
||||||
|
}
|
||||||
|
.ui-pnotify-title {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: .4em;
|
||||||
|
}
|
||||||
|
.ui-pnotify-text {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.ui-pnotify-icon, .ui-pnotify-icon span {
|
||||||
|
display: block;
|
||||||
|
float: left;
|
||||||
|
margin-right: .2em;
|
||||||
|
}
|
||||||
|
/* -- History Pulldown */
|
||||||
|
.ui-pnotify-history-container {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 18px;
|
||||||
|
width: 70px;
|
||||||
|
border-top: none;
|
||||||
|
padding: 0;
|
||||||
|
-webkit-border-top-left-radius: 0;
|
||||||
|
-moz-border-top-left-radius: 0;
|
||||||
|
border-top-left-radius: 0;
|
||||||
|
-webkit-border-top-right-radius: 0;
|
||||||
|
-moz-border-top-right-radius: 0;
|
||||||
|
border-top-right-radius: 0;
|
||||||
|
/* Ensures history container is above notices. */
|
||||||
|
z-index: 10000;
|
||||||
|
}
|
||||||
|
.ui-pnotify-history-container .ui-pnotify-history-header {
|
||||||
|
padding: 2px;
|
||||||
|
}
|
||||||
|
.ui-pnotify-history-container button {
|
||||||
|
cursor: pointer;
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.ui-pnotify-history-container .ui-pnotify-history-pulldown {
|
||||||
|
display: block;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
|
@ -23,6 +23,18 @@ body {
|
||||||
.brand, .nav>li>a {
|
.brand, .nav>li>a {
|
||||||
.navbar-inner-text(@base);
|
.navbar-inner-text(@base);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.nav {
|
||||||
|
li.dropdown.open>.dropdown-toggle, li.dropdown.active>.dropdown-toggle, li.dropdown.open.active>.dropdown-toggle {
|
||||||
|
// invert for dropdown
|
||||||
|
background-color: @base; /* fallback color if gradients are not supported */
|
||||||
|
background-image: -webkit-linear-gradient(top, @bottom, @top); /* For Chrome and Safari */
|
||||||
|
background-image: -moz-linear-gradient(top, @bottom, @top); /* For old Fx (3.6 to 15) */
|
||||||
|
background-image: -ms-linear-gradient(top, @bottom, @top); /* For pre-releases of IE 10*/
|
||||||
|
background-image: -o-linear-gradient(top, @bottom, @top); /* For old Opera (11.1 to 12.0) */
|
||||||
|
background-image: linear-gradient(to bottom, @bottom, @top); /* Standard syntax; must be last */
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#navbar .navbar-inner {
|
#navbar .navbar-inner {
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
/*
|
||||||
|
* jQuery Pines Notify (pnotify) Plugin 1.2.0
|
||||||
|
*
|
||||||
|
* http://pinesframework.org/pnotify/
|
||||||
|
* Copyright (c) 2009-2012 Hunter Perrin
|
||||||
|
*
|
||||||
|
* Triple license under the GPL, LGPL, and MPL:
|
||||||
|
* http://www.gnu.org/licenses/gpl.html
|
||||||
|
* http://www.gnu.org/licenses/lgpl.html
|
||||||
|
* http://www.mozilla.org/MPL/MPL-1.1.html
|
||||||
|
*/
|
||||||
|
(function(d){var q,j,r,i=d(window),u={jqueryui:{container:"ui-widget ui-widget-content ui-corner-all",notice:"ui-state-highlight",notice_icon:"ui-icon ui-icon-info",info:"",info_icon:"ui-icon ui-icon-info",success:"ui-state-default",success_icon:"ui-icon ui-icon-circle-check",error:"ui-state-error",error_icon:"ui-icon ui-icon-alert",closer:"ui-icon ui-icon-close",pin_up:"ui-icon ui-icon-pin-w",pin_down:"ui-icon ui-icon-pin-s",hi_menu:"ui-state-default ui-corner-bottom",hi_btn:"ui-state-default ui-corner-all",
|
||||||
|
hi_btnhov:"ui-state-hover",hi_hnd:"ui-icon ui-icon-grip-dotted-horizontal"},bootstrap:{container:"alert",notice:"",notice_icon:"icon-exclamation-sign",info:"alert-info",info_icon:"icon-info-sign",success:"alert-success",success_icon:"icon-ok-sign",error:"alert-error",error_icon:"icon-warning-sign",closer:"icon-remove",pin_up:"icon-pause",pin_down:"icon-play",hi_menu:"well",hi_btn:"btn",hi_btnhov:"",hi_hnd:"icon-chevron-down"}},s=function(){r=d("body");i=d(window);i.bind("resize",function(){j&&clearTimeout(j);
|
||||||
|
j=setTimeout(d.pnotify_position_all,10)})};document.body?s():d(s);d.extend({pnotify_remove_all:function(){var e=i.data("pnotify");e&&e.length&&d.each(e,function(){this.pnotify_remove&&this.pnotify_remove()})},pnotify_position_all:function(){j&&clearTimeout(j);j=null;var e=i.data("pnotify");e&&e.length&&(d.each(e,function(){var d=this.opts.stack;if(d)d.nextpos1=d.firstpos1,d.nextpos2=d.firstpos2,d.addpos2=0,d.animation=true}),d.each(e,function(){this.pnotify_position()}))},pnotify:function(e){var g,
|
||||||
|
a;typeof e!="object"?(a=d.extend({},d.pnotify.defaults),a.text=e):a=d.extend({},d.pnotify.defaults,e);for(var p in a)typeof p=="string"&&p.match(/^pnotify_/)&&(a[p.replace(/^pnotify_/,"")]=a[p]);if(a.before_init&&a.before_init(a)===false)return null;var k,o=function(a,c){b.css("display","none");var f=document.elementFromPoint(a.clientX,a.clientY);b.css("display","block");var e=d(f),g=e.css("cursor");b.css("cursor",g!="auto"?g:"default");if(!k||k.get(0)!=f)k&&(n.call(k.get(0),"mouseleave",a.originalEvent),
|
||||||
|
n.call(k.get(0),"mouseout",a.originalEvent)),n.call(f,"mouseenter",a.originalEvent),n.call(f,"mouseover",a.originalEvent);n.call(f,c,a.originalEvent);k=e},f=u[a.styling],b=d("<div />",{"class":"ui-pnotify "+a.addclass,css:{display:"none"},mouseenter:function(l){a.nonblock&&l.stopPropagation();a.mouse_reset&&g=="out"&&(b.stop(true),g="in",b.css("height","auto").animate({width:a.width,opacity:a.nonblock?a.nonblock_opacity:a.opacity},"fast"));a.nonblock&&b.animate({opacity:a.nonblock_opacity},"fast");
|
||||||
|
a.hide&&a.mouse_reset&&b.pnotify_cancel_remove();a.sticker&&!a.nonblock&&b.sticker.trigger("pnotify_icon").css("visibility","visible");a.closer&&!a.nonblock&&b.closer.css("visibility","visible")},mouseleave:function(l){a.nonblock&&l.stopPropagation();k=null;b.css("cursor","auto");a.nonblock&&g!="out"&&b.animate({opacity:a.opacity},"fast");a.hide&&a.mouse_reset&&b.pnotify_queue_remove();a.sticker_hover&&b.sticker.css("visibility","hidden");a.closer_hover&&b.closer.css("visibility","hidden");d.pnotify_position_all()},
|
||||||
|
mouseover:function(b){a.nonblock&&b.stopPropagation()},mouseout:function(b){a.nonblock&&b.stopPropagation()},mousemove:function(b){a.nonblock&&(b.stopPropagation(),o(b,"onmousemove"))},mousedown:function(b){a.nonblock&&(b.stopPropagation(),b.preventDefault(),o(b,"onmousedown"))},mouseup:function(b){a.nonblock&&(b.stopPropagation(),b.preventDefault(),o(b,"onmouseup"))},click:function(b){a.nonblock&&(b.stopPropagation(),o(b,"onclick"))},dblclick:function(b){a.nonblock&&(b.stopPropagation(),o(b,"ondblclick"))}});
|
||||||
|
b.opts=a;b.container=d("<div />",{"class":f.container+" ui-pnotify-container "+(a.type=="error"?f.error:a.type=="info"?f.info:a.type=="success"?f.success:f.notice)}).appendTo(b);a.cornerclass!=""&&b.container.removeClass("ui-corner-all").addClass(a.cornerclass);a.shadow&&b.container.addClass("ui-pnotify-shadow");b.pnotify_version="1.2.0";b.pnotify=function(l){var c=a;typeof l=="string"?a.text=l:a=d.extend({},a,l);for(var e in a)typeof e=="string"&&e.match(/^pnotify_/)&&(a[e.replace(/^pnotify_/,"")]=
|
||||||
|
a[e]);b.opts=a;a.cornerclass!=c.cornerclass&&b.container.removeClass("ui-corner-all").addClass(a.cornerclass);a.shadow!=c.shadow&&(a.shadow?b.container.addClass("ui-pnotify-shadow"):b.container.removeClass("ui-pnotify-shadow"));a.addclass===false?b.removeClass(c.addclass):a.addclass!==c.addclass&&b.removeClass(c.addclass).addClass(a.addclass);a.title===false?b.title_container.slideUp("fast"):a.title!==c.title&&(a.title_escape?b.title_container.text(a.title).slideDown(200):b.title_container.html(a.title).slideDown(200));
|
||||||
|
a.text===false?b.text_container.slideUp("fast"):a.text!==c.text&&(a.text_escape?b.text_container.text(a.text).slideDown(200):b.text_container.html(a.insert_brs?String(a.text).replace(/\n/g,"<br />"):a.text).slideDown(200));b.pnotify_history=a.history;b.pnotify_hide=a.hide;a.type!=c.type&&b.container.removeClass(f.error+" "+f.notice+" "+f.success+" "+f.info).addClass(a.type=="error"?f.error:a.type=="info"?f.info:a.type=="success"?f.success:f.notice);if(a.icon!==c.icon||a.icon===true&&a.type!=c.type)b.container.find("div.ui-pnotify-icon").remove(),
|
||||||
|
a.icon!==false&&d("<div />",{"class":"ui-pnotify-icon"}).append(d("<span />",{"class":a.icon===true?a.type=="error"?f.error_icon:a.type=="info"?f.info_icon:a.type=="success"?f.success_icon:f.notice_icon:a.icon})).prependTo(b.container);a.width!==c.width&&b.animate({width:a.width});a.min_height!==c.min_height&&b.container.animate({minHeight:a.min_height});a.opacity!==c.opacity&&b.fadeTo(a.animate_speed,a.opacity);!a.closer||a.nonblock?b.closer.css("display","none"):b.closer.css("display","block");
|
||||||
|
!a.sticker||a.nonblock?b.sticker.css("display","none"):b.sticker.css("display","block");b.sticker.trigger("pnotify_icon");a.sticker_hover?b.sticker.css("visibility","hidden"):a.nonblock||b.sticker.css("visibility","visible");a.closer_hover?b.closer.css("visibility","hidden"):a.nonblock||b.closer.css("visibility","visible");a.hide?c.hide||b.pnotify_queue_remove():b.pnotify_cancel_remove();b.pnotify_queue_position();return b};b.pnotify_position=function(a){var c=b.opts.stack;if(c){if(!c.nextpos1)c.nextpos1=
|
||||||
|
c.firstpos1;if(!c.nextpos2)c.nextpos2=c.firstpos2;if(!c.addpos2)c.addpos2=0;var d=b.css("display")=="none";if(!d||a){var f,e={},g;switch(c.dir1){case "down":g="top";break;case "up":g="bottom";break;case "left":g="right";break;case "right":g="left"}a=parseInt(b.css(g));isNaN(a)&&(a=0);if(typeof c.firstpos1=="undefined"&&!d)c.firstpos1=a,c.nextpos1=c.firstpos1;var h;switch(c.dir2){case "down":h="top";break;case "up":h="bottom";break;case "left":h="right";break;case "right":h="left"}f=parseInt(b.css(h));
|
||||||
|
isNaN(f)&&(f=0);if(typeof c.firstpos2=="undefined"&&!d)c.firstpos2=f,c.nextpos2=c.firstpos2;if(c.dir1=="down"&&c.nextpos1+b.height()>i.height()||c.dir1=="up"&&c.nextpos1+b.height()>i.height()||c.dir1=="left"&&c.nextpos1+b.width()>i.width()||c.dir1=="right"&&c.nextpos1+b.width()>i.width())c.nextpos1=c.firstpos1,c.nextpos2+=c.addpos2+(typeof c.spacing2=="undefined"?25:c.spacing2),c.addpos2=0;if(c.animation&&c.nextpos2<f)switch(c.dir2){case "down":e.top=c.nextpos2+"px";break;case "up":e.bottom=c.nextpos2+
|
||||||
|
"px";break;case "left":e.right=c.nextpos2+"px";break;case "right":e.left=c.nextpos2+"px"}else b.css(h,c.nextpos2+"px");switch(c.dir2){case "down":case "up":if(b.outerHeight(true)>c.addpos2)c.addpos2=b.height();break;case "left":case "right":if(b.outerWidth(true)>c.addpos2)c.addpos2=b.width()}if(c.nextpos1)if(c.animation&&(a>c.nextpos1||e.top||e.bottom||e.right||e.left))switch(c.dir1){case "down":e.top=c.nextpos1+"px";break;case "up":e.bottom=c.nextpos1+"px";break;case "left":e.right=c.nextpos1+"px";
|
||||||
|
break;case "right":e.left=c.nextpos1+"px"}else b.css(g,c.nextpos1+"px");(e.top||e.bottom||e.right||e.left)&&b.animate(e,{duration:500,queue:false});switch(c.dir1){case "down":case "up":c.nextpos1+=b.height()+(typeof c.spacing1=="undefined"?25:c.spacing1);break;case "left":case "right":c.nextpos1+=b.width()+(typeof c.spacing1=="undefined"?25:c.spacing1)}}}};b.pnotify_queue_position=function(a){j&&clearTimeout(j);a||(a=10);j=setTimeout(d.pnotify_position_all,a)};b.pnotify_display=function(){b.parent().length||
|
||||||
|
b.appendTo(r);a.before_open&&a.before_open(b)===false||(a.stack.push!="top"&&b.pnotify_position(true),a.animation=="fade"||a.animation.effect_in=="fade"?b.show().fadeTo(0,0).hide():a.opacity!=1&&b.show().fadeTo(0,a.opacity).hide(),b.animate_in(function(){a.after_open&&a.after_open(b);b.pnotify_queue_position();a.hide&&b.pnotify_queue_remove()}))};b.pnotify_remove=function(){if(b.timer)window.clearTimeout(b.timer),b.timer=null;a.before_close&&a.before_close(b)===false||b.animate_out(function(){a.after_close&&
|
||||||
|
a.after_close(b)===false||(b.pnotify_queue_position(),a.remove&&b.detach())})};b.animate_in=function(d){g="in";var c;c=typeof a.animation.effect_in!="undefined"?a.animation.effect_in:a.animation;c=="none"?(b.show(),d()):c=="show"?b.show(a.animate_speed,d):c=="fade"?b.show().fadeTo(a.animate_speed,a.opacity,d):c=="slide"?b.slideDown(a.animate_speed,d):typeof c=="function"?c("in",d,b):b.show(c,typeof a.animation.options_in=="object"?a.animation.options_in:{},a.animate_speed,d)};b.animate_out=function(d){g=
|
||||||
|
"out";var c;c=typeof a.animation.effect_out!="undefined"?a.animation.effect_out:a.animation;c=="none"?(b.hide(),d()):c=="show"?b.hide(a.animate_speed,d):c=="fade"?b.fadeOut(a.animate_speed,d):c=="slide"?b.slideUp(a.animate_speed,d):typeof c=="function"?c("out",d,b):b.hide(c,typeof a.animation.options_out=="object"?a.animation.options_out:{},a.animate_speed,d)};b.pnotify_cancel_remove=function(){b.timer&&window.clearTimeout(b.timer)};b.pnotify_queue_remove=function(){b.pnotify_cancel_remove();b.timer=
|
||||||
|
window.setTimeout(function(){b.pnotify_remove()},isNaN(a.delay)?0:a.delay)};b.closer=d("<div />",{"class":"ui-pnotify-closer",css:{cursor:"pointer",visibility:a.closer_hover?"hidden":"visible"},click:function(){b.pnotify_remove();b.sticker.css("visibility","hidden");b.closer.css("visibility","hidden")}}).append(d("<span />",{"class":f.closer})).appendTo(b.container);(!a.closer||a.nonblock)&&b.closer.css("display","none");b.sticker=d("<div />",{"class":"ui-pnotify-sticker",css:{cursor:"pointer",visibility:a.sticker_hover?
|
||||||
|
"hidden":"visible"},click:function(){a.hide=!a.hide;a.hide?b.pnotify_queue_remove():b.pnotify_cancel_remove();d(this).trigger("pnotify_icon")}}).bind("pnotify_icon",function(){d(this).children().removeClass(f.pin_up+" "+f.pin_down).addClass(a.hide?f.pin_up:f.pin_down)}).append(d("<span />",{"class":f.pin_up})).appendTo(b.container);(!a.sticker||a.nonblock)&&b.sticker.css("display","none");a.icon!==false&&d("<div />",{"class":"ui-pnotify-icon"}).append(d("<span />",{"class":a.icon===true?a.type=="error"?
|
||||||
|
f.error_icon:a.type=="info"?f.info_icon:a.type=="success"?f.success_icon:f.notice_icon:a.icon})).prependTo(b.container);b.title_container=d("<h4 />",{"class":"ui-pnotify-title"}).appendTo(b.container);a.title===false?b.title_container.hide():a.title_escape?b.title_container.text(a.title):b.title_container.html(a.title);b.text_container=d("<div />",{"class":"ui-pnotify-text"}).appendTo(b.container);a.text===false?b.text_container.hide():a.text_escape?b.text_container.text(a.text):b.text_container.html(a.insert_brs?
|
||||||
|
String(a.text).replace(/\n/g,"<br />"):a.text);typeof a.width=="string"&&b.css("width",a.width);typeof a.min_height=="string"&&b.container.css("min-height",a.min_height);b.pnotify_history=a.history;b.pnotify_hide=a.hide;var h=i.data("pnotify");if(h==null||typeof h!="object")h=[];h=a.stack.push=="top"?d.merge([b],h):d.merge(h,[b]);i.data("pnotify",h);a.stack.push=="top"&&b.pnotify_queue_position(1);a.after_init&&a.after_init(b);if(a.history){var m=i.data("pnotify_history");typeof m=="undefined"&&(m=
|
||||||
|
d("<div />",{"class":"ui-pnotify-history-container "+f.hi_menu,mouseleave:function(){m.animate({top:"-"+q+"px"},{duration:100,queue:false})}}).append(d("<div />",{"class":"ui-pnotify-history-header",text:"Redisplay"})).append(d("<button />",{"class":"ui-pnotify-history-all "+f.hi_btn,text:"All",mouseenter:function(){d(this).addClass(f.hi_btnhov)},mouseleave:function(){d(this).removeClass(f.hi_btnhov)},click:function(){d.each(h,function(){this.pnotify_history&&(this.is(":visible")?this.pnotify_hide&&
|
||||||
|
this.pnotify_queue_remove():this.pnotify_display&&this.pnotify_display())});return false}})).append(d("<button />",{"class":"ui-pnotify-history-last "+f.hi_btn,text:"Last",mouseenter:function(){d(this).addClass(f.hi_btnhov)},mouseleave:function(){d(this).removeClass(f.hi_btnhov)},click:function(){var a=-1,b;do{b=a==-1?h.slice(a):h.slice(a,a+1);if(!b[0])break;a--}while(!b[0].pnotify_history||b[0].is(":visible"));if(!b[0])return false;b[0].pnotify_display&&b[0].pnotify_display();return false}})).appendTo(r),
|
||||||
|
q=d("<span />",{"class":"ui-pnotify-history-pulldown "+f.hi_hnd,mouseenter:function(){m.animate({top:"0"},{duration:100,queue:false})}}).appendTo(m).offset().top+2,m.css({top:"-"+q+"px"}),i.data("pnotify_history",m))}a.stack.animation=false;b.pnotify_display();return b}});var t=/^on/,v=/^(dbl)?click$|^mouse(move|down|up|over|out|enter|leave)$|^contextmenu$/,w=/^(focus|blur|select|change|reset)$|^key(press|down|up)$/,x=/^(scroll|resize|(un)?load|abort|error)$/,n=function(e,g){var a,e=e.toLowerCase();
|
||||||
|
document.createEvent&&this.dispatchEvent?(e=e.replace(t,""),e.match(v)?(d(this).offset(),a=document.createEvent("MouseEvents"),a.initMouseEvent(e,g.bubbles,g.cancelable,g.view,g.detail,g.screenX,g.screenY,g.clientX,g.clientY,g.ctrlKey,g.altKey,g.shiftKey,g.metaKey,g.button,g.relatedTarget)):e.match(w)?(a=document.createEvent("UIEvents"),a.initUIEvent(e,g.bubbles,g.cancelable,g.view,g.detail)):e.match(x)&&(a=document.createEvent("HTMLEvents"),a.initEvent(e,g.bubbles,g.cancelable)),a&&this.dispatchEvent(a)):
|
||||||
|
(e.match(t)||(e="on"+e),a=document.createEventObject(g),this.fireEvent(e,a))};d.pnotify.defaults={title:false,title_escape:false,text:false,text_escape:false,styling:"bootstrap",addclass:"",cornerclass:"",nonblock:false,nonblock_opacity:0.2,history:true,width:"300px",min_height:"16px",type:"notice",icon:true,animation:"fade",animate_speed:"slow",opacity:1,shadow:true,closer:true,closer_hover:true,sticker:true,sticker_hover:true,hide:true,delay:8E3,mouse_reset:true,remove:true,insert_brs:true,stack:{dir1:"down",
|
||||||
|
dir2:"left",push:"bottom",spacing1:25,spacing2:25}}})(jQuery);
|
|
@ -202,28 +202,6 @@ function PrinterStateViewModel() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function AppearanceViewModel(settingsViewModel) {
|
|
||||||
var self = this;
|
|
||||||
|
|
||||||
self.name = settingsViewModel.appearance_name;
|
|
||||||
self.color = settingsViewModel.appearance_color;
|
|
||||||
|
|
||||||
self.brand = ko.computed(function() {
|
|
||||||
if (self.name())
|
|
||||||
return "OctoPrint: " + self.name();
|
|
||||||
else
|
|
||||||
return "OctoPrint";
|
|
||||||
})
|
|
||||||
|
|
||||||
self.title = ko.computed(function() {
|
|
||||||
if (self.name())
|
|
||||||
return self.name() + " [OctoPrint]";
|
|
||||||
else
|
|
||||||
return "OctoPrint";
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function TemperatureViewModel(settingsViewModel) {
|
function TemperatureViewModel(settingsViewModel) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
|
@ -989,6 +967,8 @@ function SettingsViewModel() {
|
||||||
|
|
||||||
self.temperature_profiles = ko.observableArray(undefined);
|
self.temperature_profiles = ko.observableArray(undefined);
|
||||||
|
|
||||||
|
self.system_actions = ko.observableArray([]);
|
||||||
|
|
||||||
self.addTemperatureProfile = function() {
|
self.addTemperatureProfile = function() {
|
||||||
self.temperature_profiles.push({name: "New", extruder:0, bed:0});
|
self.temperature_profiles.push({name: "New", extruder:0, bed:0});
|
||||||
};
|
};
|
||||||
|
@ -1030,6 +1010,8 @@ function SettingsViewModel() {
|
||||||
self.folder_logs(response.folder.logs);
|
self.folder_logs(response.folder.logs);
|
||||||
|
|
||||||
self.temperature_profiles(response.temperature.profiles);
|
self.temperature_profiles(response.temperature.profiles);
|
||||||
|
|
||||||
|
self.system_actions(response.system.actions);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.saveData = function() {
|
self.saveData = function() {
|
||||||
|
@ -1063,6 +1045,9 @@ function SettingsViewModel() {
|
||||||
},
|
},
|
||||||
"temperature": {
|
"temperature": {
|
||||||
"profiles": self.temperature_profiles()
|
"profiles": self.temperature_profiles()
|
||||||
|
},
|
||||||
|
"system": {
|
||||||
|
"actions": self.system_actions()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1081,6 +1066,37 @@ function SettingsViewModel() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function NavigationViewModel(appearanceViewModel, settingsViewModel) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
self.appearance = appearanceViewModel;
|
||||||
|
self.systemActions = settingsViewModel.system_actions;
|
||||||
|
|
||||||
|
self.triggerAction = function(action) {
|
||||||
|
var callback = function() {
|
||||||
|
$.ajax({
|
||||||
|
url: AJAX_BASEURL + "system",
|
||||||
|
type: "POST",
|
||||||
|
dataType: "json",
|
||||||
|
data: "action=" + action.action,
|
||||||
|
success: function() {
|
||||||
|
$.pnotify({title: "Success", text: "The command \""+ action.name +"\" executed successfully", type: "success"});
|
||||||
|
},
|
||||||
|
error: function(jqXHR, textStatus, errorThrown) {
|
||||||
|
$.pnotify({title: "Error", text: "<p>The command \"" + action.name + "\" could not be executed.</p><p>Reason: <pre>" + jqXHR.responseText + "</pre></p>", type: "error"});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (action.confirm) {
|
||||||
|
$("#confirmation_dialog .confirmation_dialog_message").text(action.confirm);
|
||||||
|
$("#confirmation_dialog .confirmation_dialog_acknowledge").click(function(e) {e.preventDefault(); $("#confirmation_dialog").modal("hide"); callback(); });
|
||||||
|
$("#confirmation_dialog").modal("show");
|
||||||
|
} else {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function DataUpdater(connectionViewModel, printerStateViewModel, temperatureViewModel, controlsViewModel, speedViewModel, terminalViewModel, gcodeFilesViewModel, webcamViewModel, gcodeViewModel) {
|
function DataUpdater(connectionViewModel, printerStateViewModel, temperatureViewModel, controlsViewModel, speedViewModel, terminalViewModel, gcodeFilesViewModel, webcamViewModel, gcodeViewModel) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
|
@ -1370,6 +1386,27 @@ function ItemListHelper(listType, supportedSorting, supportedFilters, defaultSor
|
||||||
self._loadCurrentSortingFromLocalStorage();
|
self._loadCurrentSortingFromLocalStorage();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function AppearanceViewModel(settingsViewModel) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
self.name = settingsViewModel.appearance_name;
|
||||||
|
self.color = settingsViewModel.appearance_color;
|
||||||
|
|
||||||
|
self.brand = ko.computed(function() {
|
||||||
|
if (self.name())
|
||||||
|
return "OctoPrint: " + self.name();
|
||||||
|
else
|
||||||
|
return "OctoPrint";
|
||||||
|
})
|
||||||
|
|
||||||
|
self.title = ko.computed(function() {
|
||||||
|
if (self.name())
|
||||||
|
return self.name() + " [OctoPrint]";
|
||||||
|
else
|
||||||
|
return "OctoPrint";
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
$(function() {
|
$(function() {
|
||||||
|
|
||||||
//~~ View models
|
//~~ View models
|
||||||
|
@ -1384,6 +1421,7 @@ $(function() {
|
||||||
var gcodeFilesViewModel = new GcodeFilesViewModel();
|
var gcodeFilesViewModel = new GcodeFilesViewModel();
|
||||||
var webcamViewModel = new WebcamViewModel();
|
var webcamViewModel = new WebcamViewModel();
|
||||||
var gcodeViewModel = new GcodeViewModel();
|
var gcodeViewModel = new GcodeViewModel();
|
||||||
|
var navigationViewModel = new NavigationViewModel(appearanceViewModel, settingsViewModel);
|
||||||
|
|
||||||
var dataUpdater = new DataUpdater(
|
var dataUpdater = new DataUpdater(
|
||||||
connectionViewModel,
|
connectionViewModel,
|
||||||
|
@ -1566,7 +1604,7 @@ $(function() {
|
||||||
ko.applyBindings(speedViewModel, document.getElementById("speed"));
|
ko.applyBindings(speedViewModel, document.getElementById("speed"));
|
||||||
ko.applyBindings(gcodeViewModel, document.getElementById("gcode"));
|
ko.applyBindings(gcodeViewModel, document.getElementById("gcode"));
|
||||||
ko.applyBindings(settingsViewModel, document.getElementById("settings_dialog"));
|
ko.applyBindings(settingsViewModel, document.getElementById("settings_dialog"));
|
||||||
ko.applyBindings(appearanceViewModel, document.getElementById("navbar"));
|
ko.applyBindings(navigationViewModel, document.getElementById("navbar"));
|
||||||
ko.applyBindings(appearanceViewModel, document.getElementsByTagName("head")[0]);
|
ko.applyBindings(appearanceViewModel, document.getElementsByTagName("head")[0]);
|
||||||
|
|
||||||
var webcamElement = document.getElementById("webcam");
|
var webcamElement = document.getElementById("webcam");
|
||||||
|
@ -1597,6 +1635,8 @@ $(function() {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
$.pnotify.defaults.history = false;
|
||||||
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
<div id="offline_overlay">
|
||||||
|
<div id="offline_overlay_background"></div>
|
||||||
|
<div id="offline_overlay_wrapper">
|
||||||
|
<div class="container">
|
||||||
|
<div class="hero-unit">
|
||||||
|
<h1>Server is offline</h1>
|
||||||
|
<p id="offline_overlay_message">
|
||||||
|
The server appears to be offline, at least I'm not getting any response from it. I'll try to reconnect
|
||||||
|
automatically <strong>over the next couple of minutes</strong>, however you are welcome to try a manual reconnect
|
||||||
|
anytime using the button below.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<a class="btn btn-primary btn-large" id="offline_overlay_reconnect">Attempt to reconnect</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="confirmation_dialog" class="modal hide fade">
|
||||||
|
<div class="modal-header">
|
||||||
|
<a href="#" class="close" data-dismiss="modal" aria-hidden="true">×</a>
|
||||||
|
<h3>Are you sure?</h3>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<p class="confirmation_dialog_message"></p>
|
||||||
|
<p>Are you sure you want to proceed?</p>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<a href="#" class="btn" data-dismiss="modal" aria-hidden="true">Cancel</a>
|
||||||
|
<a href="#" class="btn btn-danger confirmation_dialog_acknowledge">Proceed</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -9,6 +9,7 @@
|
||||||
|
|
||||||
<link href="{{ url_for('static', filename='css/bootstrap.min.css') }}" rel="stylesheet" media="screen">
|
<link href="{{ url_for('static', filename='css/bootstrap.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/jquery.fileupload-ui.css') }}" rel="stylesheet" media="screen">
|
||||||
|
<link href="{{ url_for('static', filename='css/jquery.pnotify.default.css') }}" rel="stylesheet" media="screen">
|
||||||
<link href="{{ url_for('static', filename='css/octoprint.less') }}" rel="stylesheet/less" type="text/css" media="screen">
|
<link href="{{ url_for('static', filename='css/octoprint.less') }}" rel="stylesheet/less" type="text/css" media="screen">
|
||||||
<link href="{{ url_for('static', filename='gcodeviewer/css/cupertino/jquery-ui-1.9.0.custom.css') }}" rel="stylesheet" media="screen">
|
<link href="{{ url_for('static', filename='gcodeviewer/css/cupertino/jquery-ui-1.9.0.custom.css') }}" rel="stylesheet" media="screen">
|
||||||
<link href="{{ url_for('static', filename='gcodeviewer/css/style.css') }}" rel="stylesheet" media="screen">
|
<link href="{{ url_for('static', filename='gcodeviewer/css/style.css') }}" rel="stylesheet" media="screen">
|
||||||
|
@ -26,12 +27,23 @@
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="navbar" class="navbar navbar-fixed-top">
|
<div id="navbar" class="navbar navbar-fixed-top">
|
||||||
<div class="navbar-inner" data-bind="css: color">
|
<div class="navbar-inner" data-bind="css: appearance.color">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<a class="brand" href="#"><img src="{{ url_for('static', filename='img/tentacle-20x20.png') }}"> <span data-bind="text: brand">OctoPrint</span></a>
|
<a class="brand" href="#"><img src="{{ url_for('static', filename='img/tentacle-20x20.png') }}"> <span data-bind="text: appearance.brand">OctoPrint</span></a>
|
||||||
<div class="nav-collapse">
|
<div class="nav-collapse">
|
||||||
<ul class="nav pull-right">
|
<ul class="nav pull-right">
|
||||||
<li><a id="navbar_show_settings" class="pull-right" href="#settings_dialog"><i class="icon-wrench"></i> Settings</a></li>
|
<li><a id="navbar_show_settings" class="pull-right" href="#settings_dialog"><i class="icon-wrench"></i> Settings</a></li>
|
||||||
|
{% if enableSystemMenu %}
|
||||||
|
<li class="dropdown">
|
||||||
|
<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>
|
||||||
|
{% endif %}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -509,26 +521,8 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="offline_overlay">
|
|
||||||
<div id="offline_overlay_background"></div>
|
|
||||||
<div id="offline_overlay_wrapper">
|
|
||||||
<div class="container">
|
|
||||||
<div class="hero-unit">
|
|
||||||
<h1>Server is offline</h1>
|
|
||||||
<p id="offline_overlay_message">
|
|
||||||
The server appears to be offline, at least I'm not getting any response from it. I'll try to reconnect
|
|
||||||
automatically <strong>over the next couple of minutes</strong>, however you are welcome to try a manual reconnect
|
|
||||||
anytime using the button below.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<a class="btn btn-primary btn-large" id="offline_overlay_reconnect">Attempt to reconnect</a>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{% include 'settings.html' %}
|
{% include 'settings.html' %}
|
||||||
|
{% include 'dialogs.html' %}
|
||||||
|
|
||||||
<script type="text/javascript" src="http://code.jquery.com/jquery-latest.js"></script>
|
<script type="text/javascript" src="http://code.jquery.com/jquery-latest.js"></script>
|
||||||
<script type="text/javascript" src="{{ url_for('static', filename='js/modernizr.custom.js') }}"></script>
|
<script type="text/javascript" src="{{ url_for('static', filename='js/modernizr.custom.js') }}"></script>
|
||||||
|
@ -539,6 +533,7 @@
|
||||||
<script type="text/javascript" src="{{ url_for('static', filename='js/jquery.ui.widget.js') }}"></script>
|
<script type="text/javascript" src="{{ url_for('static', filename='js/jquery.ui.widget.js') }}"></script>
|
||||||
<script type="text/javascript" src="{{ url_for('static', filename='js/jquery.ui.mouse.js') }}"></script>
|
<script type="text/javascript" src="{{ url_for('static', filename='js/jquery.ui.mouse.js') }}"></script>
|
||||||
<script type="text/javascript" src="{{ url_for('static', filename='js/jquery.ui.slider.js') }}"></script>
|
<script type="text/javascript" src="{{ url_for('static', filename='js/jquery.ui.slider.js') }}"></script>
|
||||||
|
<script type="text/javascript" src="{{ url_for('static', filename='js/jquery.pnotify.min.js') }}"></script>
|
||||||
<script type="text/javascript" src="{{ url_for('static', filename='js/jquery.flot.js') }}"></script>
|
<script type="text/javascript" src="{{ url_for('static', filename='js/jquery.flot.js') }}"></script>
|
||||||
<script type="text/javascript" src="{{ url_for('static', filename='js/jquery.iframe-transport.js') }}"></script>
|
<script type="text/javascript" src="{{ url_for('static', filename='js/jquery.iframe-transport.js') }}"></script>
|
||||||
<script type="text/javascript" src="{{ url_for('static', filename='js/jquery.fileupload.js') }}"></script>
|
<script type="text/javascript" src="{{ url_for('static', filename='js/jquery.fileupload.js') }}"></script>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<div id="settings_dialog" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="settings_dialog_label" aria-hidden="true">
|
<div id="settings_dialog" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="settings_dialog_label" aria-hidden="true">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<button type="button" class="close" data-dismiss="modal">×</button>
|
<button type="button" class="close" data-dismiss="modal">×</button>
|
||||||
<h3 id="settings_dialog_label">OctoPrint Settings</h3>
|
<h3 id="settings_dialog_label">OctoPrint Settings</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
|
|
Loading…
Reference in New Issue