Commit 8c24401d authored by kliment's avatar kliment

Merge pull request #356 from D1plo1d/pronserve

Merging awesomeness
parents b4f620a0 4290a3b8
......@@ -2,3 +2,4 @@
.pronsolerc
*.swp
*.bak
uploads
......@@ -3,7 +3,6 @@ Printrun consists of printcore, pronsole and pronterface, and a small collection
* printcore.py is a library that makes writing reprap hosts easy
* pronsole.py is an interactive command-line host software with tabcompletion goodness
* pronterface.py is a graphical host software with the same functionality as pronsole
* webinterface.py is a browser-usable remote control function for Pronterface
# GETTING PRINTRUN
......@@ -51,23 +50,19 @@ The command box recognizes all pronsole commands, but has no tabcompletion.
If you want to load stl files, you need to install a slicing program such as Slic3r and add its path to the settings.
See the Slic3r readme for more details on integration.
# Using the browser interface
To run the web interface, install Cherrypy and run Pronterface as described above.
The www server will start on the port/address you have chosen.
# USING PRONSERVE
## Webinterface Dependencies
Pronserve runs a server for remote controlling your 3D printer over your network. To use pronserve you need:
Cherrypy is required for the web interface. Download and install it by opening a
command prompt in its directory and running python setup.py install.
* python (ideally 2.6.x or 2.7.x),
* pyserial (or python-serial on ubuntu/debian) and
* tornado
* D1plo1d's py-mdns fork (https://github.com/D1plo1d/py-mdns)
* pybonjour
* bonjour for windows (Windows ONLY)
## Webinterface Configuration
* The Web interface port / ip is configurable in http.config
* The Default User / Password can be set in auth.config
## Webinterface Styling
* css/style.css can be modified to change the style of the Web Interface.
When you're done setting up Printrun, you can start `pronserve.py` in the directory you unpacked it. Once the server starts you can verify it's working by going to http://localhost:8888 in your web browser.
# USING PRONSOLE
......
<?php
$pronterfaceIP = "192.168.0.102:8080"; //Format: ip:port
$curl = curl_init();
curl_setopt($curl, CURLINFO_HEADER_OUT, true);
curl_setopt($curl, CURLOPT_HEADER, false);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 1);
curl_setopt($curl,CURLOPT_TIMEOUT, 1);
curl_setopt($curl, CURLOPT_URL, "http://" . $pronterfaceIP . "/status/");
$data = curl_exec($curl);
if (curl_errno($curl) || empty($data))
{
die("Printer offline");
}
curl_close($curl);
try
{
$xml = new SimpleXMLElement($data);
echo "State: " . $xml->state . "<br />";
echo "Hotend: " . round($xml->hotend, 0) . "&deg;c<br />";
echo "Bed: " . round($xml->bed, 0) . "&deg;c<br />";
if ($xml->progress != "NA")
{
echo "Progress: " . $xml->progress . "%";
}
}
catch(Exception $e)
{
echo "ERROR:\n" . $e->getMessage(). " (severity " . $e->getCode() . ")";
}
?>
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -196,21 +196,6 @@ class PronterWindow(MainWindow, pronsole.pronsole):
self.predisconnect_layer = None
self.hsetpoint = 0.0
self.bsetpoint = 0.0
self.webInterface = None
if self.webrequested:
try :
import cherrypy
from printrun import webinterface
try:
self.webInterface = webinterface.WebInterface(self)
self.webThread = threading.Thread(target = webinterface.StartWebInterfaceThread, args = (self.webInterface, ))
self.webThread.start()
except:
print _("Failed to start web interface")
traceback.print_exc(file = sys.stdout)
self.webInterface = None
except:
print _("CherryPy is not installed. Web Interface Disabled.")
if self.filename is not None:
self.do_load(self.filename)
......@@ -364,8 +349,6 @@ class PronterWindow(MainWindow, pronsole.pronsole):
print _("You cannot set negative temperatures. To turn the hotend off entirely, set its temperature to 0.")
except Exception, x:
print _("You must enter a temperature. (%s)") % (repr(x),)
if self.webInterface:
self.webInterface.AddLog("You must enter a temperature. (%s)" % (repr(x),))
def do_bedtemp(self, l = ""):
try:
......@@ -382,16 +365,10 @@ class PronterWindow(MainWindow, pronsole.pronsole):
self.setbedgui(f)
else:
print _("Printer is not online.")
if self.webInterface:
self.webInterface.AddLog("Printer is not online.")
else:
print _("You cannot set negative temperatures. To turn the bed off entirely, set its temperature to 0.")
if self.webInterface:
self.webInterface.AddLog("You cannot set negative temperatures. To turn the bed off entirely, set its temperature to 0.")
except Exception, x:
print _("You must enter a temperature. (%s)") % (repr(x),)
if self.webInterface:
self.webInterface.AddLog("You must enter a temperature.")
def end_macro(self):
pronsole.pronsole.end_macro(self)
......@@ -411,8 +388,6 @@ class PronterWindow(MainWindow, pronsole.pronsole):
self.delete_macro(macro_name)
return
print _("Cancelled.")
if self.webInterface:
self.webInterface.AddLog("Cancelled.")
return
self.cur_macro_name = macro_name
self.cur_macro_def = definition
......@@ -452,8 +427,6 @@ class PronterWindow(MainWindow, pronsole.pronsole):
projectlayer.setframe(self,self.p).Show()
else:
print _("Printer is not online.")
if self.webInterface:
self.webInterface.AddLog("Printer is not online.")
def popmenu(self):
self.menustrip = wx.MenuBar()
......@@ -530,8 +503,6 @@ class PronterWindow(MainWindow, pronsole.pronsole):
old_def = self.macros[macro]
elif len([c for c in macro.encode("ascii", "replace") if not c.isalnum() and c != "_"]):
print _("Macro name may contain only ASCII alphanumeric symbols and underscores")
if self.webInterface:
self.webInterface.AddLog("Macro name may contain only alphanumeric symbols and underscores")
return
elif hasattr(self.__class__, "do_"+macro):
print _("Name '%s' is being used by built-in command") % macro
......@@ -713,8 +684,6 @@ class PronterWindow(MainWindow, pronsole.pronsole):
def help_button(self):
print _('Defines custom button. Usage: button <num> "title" [/c "colour"] command')
if self.webInterface:
self.webInterface.AddLog('Defines custom button. Usage: button <num> "title" [/c "colour"] command')
def do_button(self, argstr):
def nextarg(rest):
......@@ -737,8 +706,6 @@ class PronterWindow(MainWindow, pronsole.pronsole):
command = argstr.strip()
if num<0 or num>=64:
print _("Custom button number should be between 0 and 63")
if self.webInterface:
self.webInterface.AddLog("Custom button number should be between 0 and 63")
return
while num >= len(self.custombuttons):
self.custombuttons.append(None)
......@@ -1004,8 +971,6 @@ class PronterWindow(MainWindow, pronsole.pronsole):
self.cur_button = None
except:
print _("event object missing")
if self.webInterface:
self.webInterface.AddLog("event object missing")
self.cur_button = None
raise
......@@ -1025,9 +990,6 @@ class PronterWindow(MainWindow, pronsole.pronsole):
except:
pass
self.Destroy()
if self.webInterface:
from printrun import webinterface
webinterface.KillWebInterfaceThread()
def do_monitor(self, l = ""):
if l.strip()=="":
......@@ -1040,17 +1002,11 @@ class PronterWindow(MainWindow, pronsole.pronsole):
wx.CallAfter(self.monitorbox.SetValue, self.monitor_interval>0)
except:
print _("Invalid period given.")
if self.webInterface:
self.webInterface.AddLog("Invalid period given.")
self.setmonitor(None)
if self.monitor:
print _("Monitoring printer.")
if self.webInterface:
self.webInterface.AddLog("Monitoring printer.")
else:
print _("Done monitoring.")
if self.webInterface:
self.webInterface.AddLog("Done monitoring.")
def setmonitor(self, e):
self.monitor = self.monitorbox.GetValue()
......@@ -1065,8 +1021,6 @@ class PronterWindow(MainWindow, pronsole.pronsole):
except:
print "attempted to write invalid text to console"
pass
if self.webInterface:
self.webInterface.AppendLog(text)
def setloud(self,e):
self.p.loud=e.IsChecked()
......@@ -1229,8 +1183,6 @@ class PronterWindow(MainWindow, pronsole.pronsole):
import shlex
param = self.expandcommand(self.settings.slicecommand).encode()
print "Slicing: ", param
if self.webInterface:
self.webInterface.AddLog("Slicing: "+param)
pararray = [i.replace("$s", self.filename).replace("$o", self.filename.replace(".stl", "_export.gcode").replace(".STL", "_export.gcode")).encode() for i in shlex.split(param.replace("\\", "\\\\").encode())]
#print pararray
self.skeinp = subprocess.Popen(pararray, stderr = subprocess.STDOUT, stdout = subprocess.PIPE)
......@@ -1242,8 +1194,6 @@ class PronterWindow(MainWindow, pronsole.pronsole):
self.stopsf = 1
except:
print _("Failed to execute slicing software: ")
if self.webInterface:
self.webInterface.AddLog("Failed to execute slicing software: ")
self.stopsf = 1
traceback.print_exc(file = sys.stdout)
......@@ -1336,8 +1286,6 @@ class PronterWindow(MainWindow, pronsole.pronsole):
Xtot, Ytot, Ztot, Xmin, Xmax, Ymin, Ymax, Zmin, Zmax = pronsole.measurements(self.f)
print pronsole.totalelength(self.f), _("mm of filament used in this print\n")
print _("the print goes from %f mm to %f mm in X\nand is %f mm wide\n") % (Xmin, Xmax, Xtot)
if self.webInterface:
self.webInterface.AddLog(_("the print goes from %f mm to %f mm in X\nand is %f mm wide\n") % (Xmin, Xmax, Xtot))
print _("the print goes from %f mm to %f mm in Y\nand is %f mm wide\n") % (Ymin, Ymax, Ytot)
print _("the print goes from %f mm to %f mm in Z\nand is %f mm high\n") % (Zmin, Zmax, Ztot)
try:
......
import tornado.ioloop
import tornado.web
import tornado.websocket
import base64
import logging
import logging.config
import tornado.httpserver
import tornado.ioloop
import tornado.web
log = logging.getLogger("root")
def authenticate(realm, authenticator,user_extractor) :
"""
This is a basic authentication interceptor which
protects the desired URIs and requires
authentication as per configuration
"""
def wrapper(self, transforms, *args, **kwargs):
def _request_basic_auth(self):
if self._headers_written:
raise Exception('headers have already been written')
# If this is a websocket accept parameter-based (user/password) auth:
if hasattr(self, 'stream'):
"""
self.stream.write(tornado.escape.utf8(
"HTTP/1.1 401 Unauthorized\r\n"+
"Date: Wed, 10 Apr 2013 02:09:52 GMT\r\n"+
"Content-Length: 0\r\n"+
"Content-Type: text/html; charset=UTF-8\r\n"+
"Www-Authenticate: Basic realm=\"auth_realm\"\r\n"+
"Server: TornadoServer/3.0.1\r\n\r\n"
))
self.stream.close()
"""
# If this is a restful request use the standard tornado methods:
else:
self.set_status(401)
self.set_header('WWW-Authenticate','Basic realm="%s"' % realm)
self._transforms = []
self.finish()
return False
request = self.request
format = ''
clazz = self.__class__
log.debug('intercepting for class : %s', clazz)
try:
auth_hdr = request.headers.get('Authorization')
if auth_hdr == None:
return _request_basic_auth(self)
if not auth_hdr.startswith('Basic '):
return _request_basic_auth(self)
auth_decoded = base64.decodestring(auth_hdr[6:])
username, password = auth_decoded.split(':', 2)
user_info = authenticator(realm, unicode(username), password)
if user_info :
self._user_info = user_info
self._current_user = user_extractor(user_info)
log.debug('authenticated user is : %s',
str(self._user_info))
else:
return _request_basic_auth(self)
except Exception, e:
return _request_basic_auth(self)
return True
return wrapper
def interceptor(func):
"""
This is a class decorator which is helpful in configuring
one or more interceptors which are able to intercept, inspect,
process and approve or reject further processing of the request
"""
def classwrapper(cls):
def wrapper(old):
def inner(self, transforms, *args, **kwargs):
log.debug('Invoking wrapper %s',func)
ret = func(self,transforms,*args,**kwargs)
if ret :
return old(self,transforms,*args,**kwargs)
else :
return ret
return inner
cls._execute = wrapper(cls._execute)
return cls
return classwrapper
print "moo"
\ No newline at end of file
html, body
{
margin: 0px;
padding: 0px;
height: 100%;
}
body
{
background: url("/static/img/background.jpg");
background-color: black;
background-repeat: no-repeat;
background-position: 50% 50%;
background-size: auto 100%;
}
.lead-box
{
position: absolute;
text-align: right;
top: 80%;
margin-top: -40px;
padding: 0px;
color: white;
padding-left: 100px;
padding-right: 20px;
background: rgba(0, 0, 0, 0.6);
}
.lead-box a, .lead-box a:hover
{
color: #3198EC;
font-weight: bold;
}
.sensors
{
margin-top: 15px;
}
.sensors>*
{
margin-left: 40px;
font-weight: bold;
font-size: 120%;
}
.sensors .val
{
display: inline-block;
width: 50px;
text-align: right;
}
.sensors .val, .sensors .deg
{
font-weight: normal;
}
.console
{
height: 200px;
overflow-y: scroll;
}
.console pre
{
border: 0px;
margin: 0px;
padding: 0px;
}
#temperature-graph
{
height: 200px;
}
#print-job-panel
{
margin: 0px;
}
.job-pogress
{
margin: 80px 0;
font-size: 40px;
line-height: 40px;
text-align: center;
}
\ No newline at end of file
(function() {
var $console;
var windowFocus = true;
$(window).focus(function() {
windowFocus = true;
//if ($console) $console.append("Window refocused, restarting graph.\n");
$(".focus-lost-overlay").addClass("out").removeClass("in").delay(1000).hide();
}).blur(function() {
windowFocus = false;
//if ($console) $console.append("Window's focus, lost stopping graph...\n");
$(".focus-lost-overlay")
.stop(true,true)
.show()
.addClass("in")
.removeClass("out");
}.debounce());
var connect = function() {
// Let us open a web socket
var url = "ws://localhost:8888/socket?user=admin&password=admin";
console.log(url);
var ws = new WebSocket(url);
$(function () {
$consoleWrapper = $(".console");
$console = $(".console pre");
$console.html("Connecting...\n")
onConnect(ws)
});
};
var updateSensorsUi = function() {
$(".sensors .val").each(function() {
$(this).html($(this).data("val")||"xx.x");
})
}.throttle(800);
var graph = null;
var graphData = [];
var graphResolution = 40;
var updateGraphData = function(current) {
current.time = Date.now();
if(graphData.length == graphResolution) graphData.shift();
graphData.push(current);
}
var updateGraphUi = function(current) {
if(graph == null)
{
graph = new Morris.Line({
// ID of the element in which to draw the chart.
element: "temperature-graph",
// Chart data records -- each entry in this array corresponds to a point on
// the chart.
data: graphData,
// The name of the data record attribute that contains x-values.
xkey: 'timestamp',
// A list of names of data record attributes that contain y-values.
ykeys: ['extruder', 'bed'],
// Labels for the ykeys -- will be displayed when you hover over the
// chart.
labels: ['extruder &deg;C', 'bed &deg;C'],
hideHover: 'always',
ymax: 'auto 250',
//pointSize: 0,
//parseTime: false,
xLabels: "decade"
});
}
else
{
graph.setData(graphData);
}
}
var updateUi = function(msg) {
if(windowFocus == false) return;
updateSensorsUi();
updateGraphUi();
}
var onConnect = function(ws) {
ws.onopen = function()
{
$console.append("Connected.\n");
// Web Socket is connected, send data using send()
};
var nextGraphPoint = {};
ws.onmessage = function (evt)
{
msg = JSON.parse(evt.data)
if(msg.sensor_changed != undefined)
{
var sensorNames = ["bed", "extruder"];
for (var i = 0; i < sensorNames.length; i++)
{
var name = msg.sensor_changed.name;
var val = parseFloat(msg.sensor_changed.value);
nextGraphPoint[name] = val;
$("."+name+" .val").data("val", val.format(1))
}
if(nextGraphPoint.bed != undefined && nextGraphPoint.extruder != undefined)
{
nextGraphPoint.timestamp = msg.timestamp
updateGraphData(nextGraphPoint);
nextGraphPoint = {};
}
requestAnimationFrame(updateUi);
}
else if (msg.job_progress_changed != undefined)
{
val = Math.round(parseFloat(msg.job_progress_changed)*10)/10;
$(".job-pogress .val").html(val);
}
else
{
console.log($consoleWrapper.scrollTop() - $console.innerHeight())
var atBottom = $consoleWrapper.scrollTop() - $console.innerHeight() > -220;
$console.append(evt.data + "\n");
if (atBottom)
{
$consoleWrapper.scrollTop($console.innerHeight());
}
}
};
ws.onclose = function()
{
// websocket is closed.
$console.append("\nConnection closed.");
};
};
if ("WebSocket" in window)
{
connect();
}
else
{
// The browser doesn't support WebSocket
alert("Error: WebSocket NOT supported by your Browser!");
}
})();
\ No newline at end of file
<!doctype html>
<html>
<head>
<title>Pronserve</title>
<link href="//netdna.bootstrapcdn.com/twitter-bootstrap/2.3.1/css/bootstrap-combined.min.css" rel="stylesheet">
<link href="/static/css/index.css" rel="stylesheet">
</head>
<body>
<div class="lead-box">
<h1>
Your printer just got a whole lot better.
</h1>
<p class="lead">
Pronserve is ready to print. Why not try it out with
<a href="/inspect">Inspector</a> or
<a href="https://github.com/D1plo1d/ctrlpanel">Ctrl Panel</a>?
</p>
</div>
</body>
</html>
\ No newline at end of file
<!doctype html>
<html>
<head>
<title>Pronserve Inspector</title>
<link href="//netdna.bootstrapcdn.com/twitter-bootstrap/2.3.1/css/bootstrap-combined.min.css" rel="stylesheet">
<link rel="stylesheet" href="http://cdn.oesmith.co.uk/morris-0.4.1.min.css">
<link href="/static/css/inspect.css" rel="stylesheet">
</head>
<body>
<div class="container-fluid">
<div class="row-fluid">
<div class="span12">
<h1>
Pronserve Inspector
</h1>
<h2>Console</h2>
<div class="well console">
<pre>
Connecting...
</pre>
</div>
</div>
<div class="span3" id="print-job-panel">
<h2>Job Progress</h2>
<div class="job-pogress"><span class="val">XX.X</span>%</div>
</div>
<div class="span9">
<div class="sensors pull-right">
<div class="extruder pull-right">
Extruder: <span class="val">xx.x</span><span class="deg">&deg;C</span>
</div>
<div class="bed pull-right">
Bed: <span class="val"/>xx.x</span><span class="deg">&deg;C</span>
</div>
</div>
<h2>Temperature</h2>
<div class="clearfix"></div>
<div id="temperature-graph">
</div>
</div>
</div>
</div>
<div class="focus-lost-overlay modal-backdrop fade out hide"></div>
<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
<script src="//netdna.bootstrapcdn.com/twitter-bootstrap/2.3.1/js/bootstrap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/sugar/1.3.9/sugar.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/raphael/2.1.0/raphael-min.js"></script>
<script src="http://cdn.oesmith.co.uk/morris-0.4.1.min.js"></script>
<script src="/static/js/inspect.js"></script>
</body>
</html>
\ No newline at end of file
; THIS IS A TZST. DO NOT ATTZMPT TO PRINT THIS FILZ.
; gZnZratZd by Slic3r 0.9.3-dZv on 2012-09-02 at 04:02:31
; layZr_hZight = 0.4
; pZrimZtZrs = 3
; solid_layZrs = 3
; fill_dZnsity = 0.4
; pZrimZtZr_spZZd = 30
; infill_spZZd = 60
; travZl_spZZd = 130
; scalZ = 1
; nozzlZ_diamZtZr = 0.5
; filamZnt_diamZtZr = 3
; Zxtrusion_multipliZr = 1
; singlZ wall width = 0.53mm
; first layZr singlZ wall width = 0.80mm
M104 S200 ; sZt tZmpZraturZ
;G28 ; homZ all axZs
;M109 S200 ; wait for tZmpZraturZ to bZ rZachZd
G90 ; usZ absolutZ coordinatZs
G21 ; sZt units to millimZtZrs
G92 Z0
M82 ; usZ absolutZ distancZs for Zxtrusion
G1 Z0.400 F71800.000
G1 X75.725 Y86.681
G1 F1800.000 Z1.00000
G1 X87.905 Y75.241 F1040.000 Z1.69560
G1 X88.365 Y74.871 Z1.72017
G1 X88.865 Y74.541 Z1.74511
G1 X89.395 Y74.261 Z1.77006
G1 X89.945 Y74.031 Z1.79488
G1 X90.225 Y73.931 Z1.80726
G1 X90.805 Y73.771 Z1.83230
G1 X92.375 Y73.501 Z1.89862
G1 X92.935 Y73.471 Z1.92196
G1 X109.165 Y73.961 Z2.59789
G1 X109.475 Y73.991 Z2.61085
G1 X110.105 Y74.101 Z2.63747
G1 X110.715 Y74.271 Z2.66383
G1 X111.795 Y74.681 Z2.71192
G1 X112.355 Y74.951 Z2.73780
G1 X112.875 Y75.271 Z2.76322
G1 X113.135 Y75.451 Z2.77638
G1 X113.615 Y75.841 Z2.80213
G1 X124.855 Y87.841 Z3.48656
G1 X125.485 Y88.631 Z3.52863
G1 X125.975 Y89.351 Z3.56488
G1 X126.385 Y90.111 Z3.60083
G1 X126.605 Y90.651 Z3.62510
G1 X126.775 Y91.201 Z3.64906
G1 X126.905 Y91.771 Z3.67340
G1 X126.975 Y92.341 Z3.69731
G1 X127.005 Y92.921 Z3.72148
G1 X126.325 Y109.851 Z4.42681
G1 X126.255 Y110.391 Z4.44947
G1 X126.145 Y110.921 Z4.47201
G1 X125.995 Y111.441 Z4.49453
G1 X125.805 Y111.951 Z4.51719
G1 X125.575 Y112.441 Z4.53972
G1 X125.165 Y113.131 Z4.57313
G1 X124.835 Y113.571 Z4.59603
G1 X124.485 Y113.971 Z4.61815
G1 X124.095 Y114.351 Z4.64082
G1 X123.885 Y114.531 Z4.65233
G1 X123.005 Y115.151 Z4.69715
G1 X122.525 Y115.401 Z4.71967
G1 X96.195 Y125.661 Z5.89600
G1 X95.515 Y125.881 Z5.92575
G1 X94.385 Y126.141 Z5.97402
G1 X91.335 Y126.551 Z6.10213
G1 X91.055 Y126.561 Z6.11379
G1 X90.775 Y126.561 Z6.12545
G1 X90.215 Y126.521 Z6.14882
G1 X89.375 Y126.371 Z6.18434
G1 X88.835 Y126.221 Z6.20767
G1 X88.305 Y126.021 Z6.23125
G1 X87.795 Y125.781 Z6.25471
G1 X87.075 Y125.341 Z6.28984
G1 X86.415 Y124.811 Z6.32507
G1 X75.155 Y112.801 Z7.01038
G1 X74.955 Y112.561 Z7.02339
G1 X74.595 Y112.041 Z7.04972
G1 X73.825 Y110.701 Z7.11405
G1 X73.695 Y110.441 Z7.12615
G1 X73.475 Y109.911 Z7.15004
G1 X73.305 Y109.361 Z7.17400
G1 X73.175 Y108.801 Z7.19794
G1 X73.095 Y108.231 Z7.22190
G1 X73.065 Y107.651 Z7.24607
G1 X73.615 Y91.111 Z7.93497
G1 X73.725 Y90.281 Z7.96982
G1 X73.785 Y90.011 Z7.98134
G1 X74.035 Y89.211 Z8.01623
G1 X74.255 Y88.701 Z8.03935
G1 X74.515 Y88.201 Z8.06281
G1 X74.815 Y87.731 Z8.08602
G1 X75.335 Y87.081 Z8.12067
G1 X75.640 Y86.766 Z8.13893
#title
{
text-align:center;
color:red;
}
#mainmenu
{
margin: 0;
padding: 0 0 20px 10px;
border-bottom: 1px solid #000;
}
#mainmenu ul, #mainmenu li
{
margin: 0;
padding: 0;
display: inline;
list-style-type: none;
}
#mainmenu a:link, #mainmenu a:visited
{
float: left;
line-height: 14px;
font-weight: bold;
margin: 0 10px 4px 10px;
text-decoration: none;
color: #999;
}
#mainmenu a:link#current, #mainmenu a:visited#current, #mainmenu a:hover
{
border-bottom: 4px solid #000;
padding-bottom: 2px;
background: transparent;
color: #000;
}
#mainmenu a:hover { color: #000; }
#content{
padding-top: 25px;
}
#controls{
float:left;
padding:0 0 1em 0;
overflow:hidden;
width:71%; /* right column content width */
left:102%; /* 100% plus left column left padding */
}
#control_xy{
display:inline;
}
#control_z{
display:inline;
position:absolute;
}
#gui{
float:left;
padding:0 0 1em 0;
overflow:hidden;
width:21%; /* left column content width (column width minus left and right padding) */
left:6%; /* (right column left and right padding) plus (left column left padding) */
}
#controls
{
width:21%; /* Width of left column content (column width minus padding on either side) */
left:31%; /* width of (right column) plus (center column left and right padding) plus (left column left padding) */
}
#controls ul
{
list-style: none;
margin: 0px;
padding: 0px;
border: none;
}
#controls ul li
{
margin: 0px;
padding: 0px;
}
#controls ul li a
{
font-size: 80%;
display: block;
border-bottom: 1px dashed #C39C4E;
padding: 5px 0px 2px 4px;
text-decoration: none;
color: #666666;
width:160px;
}
#controls ul li a:hover, #controls ul li a:focus
{
color: #000000;
background-color: #eeeeee;
}
#settings
{
margin: 0px;
padding-top: 50px;
border: none;
}
#settings table
{
font-family: verdana,arial,sans-serif;
font-size:11px;
color:#333333;
border-width: 1px;
border-color: #999999;
border-collapse: collapse;
}
#settings table th {
background-color:#c3dde0;
border-width: 1px;
padding: 8px;
border-style: solid;
border-color: #a9c6c9;
}
#settings table tr {
background-color:#d4e3e5;
}
#settings table td {
border-width: 1px;
padding: 8px;
border-style: solid;
border-color: #a9c6c9;
}
#status{
}
#console{
}
#file{
position:relative;
float:left;
width:100%;
height:20px; /* Height of the footer */
background:#eee;
}
#logframe{
}
#temp{
}
#tempmenu
{
padding: 0 0 10px 10px;
position: relative;
float: left;
width: 100%;
}
#tempmenu ul, #tempmenu li
{
margin: 0;
display: inline;
list-style-type: none;
}
#tempmenu b
{
padding-top: 4px;
float: left;
line-height: 14px;
font-weight: bold;
color: #888;
margin: 0 10px 4px 10px;
text-decoration: none;
color: #999;
}
#tempmenu a:link, #tempmenu a:visited
{
float: left;
border-bottom: 1px solid #000;
line-height: 14px;
font-weight: bold;
margin: 0 10px 4px 10px;
text-decoration: none;
color: #999;
}
#tempmenu a:link#tempmenu, #tempmenu a:visited#current, #tempmenu a:hover
{
border-bottom: 2px solid #000;
padding-bottom: 2px;
background: transparent;
color: #000;
}
#tempmenu a:hover { color: #000; }
\ No newline at end of file
function pronterfaceWebInterface_setup(){
pronterfaceWebInterface_attachAsync();
}
function pronterfaceWebInterface_attachAsync(){
var list = [];
if(document.getElementsByClassName){
list = document.getElementsByClassName('command');
}else if(document.getElementsByTagName){
list = document.getElementsByTagName('a');
list.concat( document.getElementsByTagName('area') );
//TODO filter list via checking the className attributes
}else{
console && console.error && console.error('unable to gather list of elements');
return false;
}
for(var i=0; i < list.length; i++){
list[i].addEventListener && list[i].addEventListener( 'click', function(e){return pronterfaceWebInterface_asyncCommand(null, e);}, true );
list[i].attachEvent && list[i].attachEvent( 'onclick', function(e){return pronterfaceWebInterface_asyncCommand(null, e);} );
}
return true;
}
function pronterfaceWebInterface_asyncCommand( urlOrElement, event ){
if( ! urlOrElement && event.target)
urlOrElement = event.target;
var url = null;
if( typeof urlOrElement == 'string' ){
url = urlOrElement;
}else{
url = urlOrElement&&urlOrElement.href;
}
if( typeof url != 'string' ){
console && console.error && console.error('url not a string', urlOrElement, url);
return true;
}
var httpRequest;
if (window.XMLHttpRequest) { // Mozilla, Safari, ...
httpRequest = new XMLHttpRequest();
} else if (window.ActiveXObject) { // IE 8 and older
httpRequest = new ActiveXObject("Microsoft.XMLHTTP");
}
if( ! httpRequest ){
alert('no AJAX available?');
// follow link
return true;
}
//onreadystatechange
//onerror
httpRequest.open( 'GET', url, true);
httpRequest.send(null);
// don't follow link
if( event ){
event.stopImmediatePropagation && event.stopImmediatePropagation();
event.defaultPrevented = true;
event.preventDefault && event.preventDefault();
}
return false;
}
if (document.addEventListener) {
document.addEventListener("DOMContentLoaded", pronterfaceWebInterface_setup, false);
} else if (document.attachEvent) {
document.attachEvent("onreadystatechange", pronterfaceWebInterface_setup);
} else {
document.onload = pronterfaceWebInterface_setup;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment