diff --git a/VERSION b/VERSION index dedcc7d..37989bd 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.9.1 +2.10 diff --git a/rpimonitor/rpimonitord b/rpimonitor/rpimonitord index 1955014..950bb6a 100755 --- a/rpimonitor/rpimonitord +++ b/rpimonitor/rpimonitord @@ -16,7 +16,7 @@ # along with this program. If not, see . # - #print encode_json \%{$configuration->{'config'}->{'web'}}; + #print to_json \%{$configuration->{'config'}->{'web'}}; #print Data::Dumper->Dump([$configuration]); #use diagnostics; @@ -41,10 +41,6 @@ sub new $this->Debug(3,""); $this->{'rrd'}=(); $this->{'daemon'}->{'confFiles'} = []; - #$this->{'counter'}->{'static'} = 0; - #$this->{'counter'}->{'dynamic'} = 0; - #$this->{'counter'}->{'status'} = 0; - #$this->{'counter'}->{'statistics'} = 0; return $this; } @@ -65,13 +61,14 @@ sub Load $_ = abs_path($0); my ($path,$file) = /(.*)\/([^\/]*)$/; - if ( scalar(@{$this->{'daemon'}->{'confFiles'}}) == 0 ) { - @{$this->{'daemon'}->{'confFiles'}} = ( @{$this->{'daemon'}->{'confFiles'}}, glob "/etc/rpimonitor/*.conf" ) ; - } + push(@{$this->{'daemon'}->{'confFiles'}},"/etc/rpimonitor/data.conf"); + push(@{$this->{'daemon'}->{'confFiles'}},"/etc/rpimonitor/daemon.conf"); foreach ( @{$this->{'daemon'}->{'confFiles'}} ) { + #print "$_\n"; $this->LoadFile($_); } + delete($this->{'daemon'}->{'confFiles'}); # Set version (used by web pagescache mechanism) $this->{'version'} = localtime(); @@ -123,10 +120,13 @@ sub Load # manage menu foreach (@{$this->{'web'}->{'status'}}) { - $_->{'name'} and push(@{$this->{'menu'}->{'status'}}, $_->{'name'}); + $_->{'name'} and push(@{$this->{'web'}->{'menu'}->{'status'}}, $_->{'name'}); } foreach (@{$this->{'web'}->{'statistics'}}) { - $_->{'name'} and push(@{$this->{'menu'}->{'statistics'}}, $_->{'name'}); + $_->{'name'} and push(@{$this->{'web'}->{'menu'}->{'statistics'}}, $_->{'name'}); + } + foreach (@{$this->{'web'}->{'addons'}}) { + $_->{'name'} and push(@{$this->{'web'}->{'menu'}->{'addons'}}, $_->{'name'}); } $this->{'sharedmem'} = IPC::ShareLite->new( @@ -267,7 +267,7 @@ use POSIX; use IO::Handle; use HTTP::Daemon; use HTTP::Status; -use JSON; +use JSON -convert_blessed_universally; #use Data::Dumper; sub new @@ -278,6 +278,8 @@ sub new # List of files to be delivered my @paths = ( "/", + "/all.json", + "/addons.json", "/static.json", "/dynamic.json", "/status.json", @@ -289,9 +291,9 @@ sub new "/favicon.ico", "/index.html", + "/addons.html", "/statistics.html", "/status.html", - "/shellinabox.html", "/cacert.pem", "/certificate.p12", "/shellinabox", @@ -311,6 +313,8 @@ sub new "/js/rpimonitor.shellinabox.js", "/js/rpimonitor.statistics.js", "/js/rpimonitor.status.js", + "/js/rpimonitor.utils.js", + "/js/rpimonitor.addons.js", "/js/rpimonitor.index.js", "/js/rpimonitor.js", "/js/jsqrencode.min.js", @@ -443,12 +447,14 @@ sub DoGET #The file need to be known or we return an error my $isvalid; foreach(@{$this->{'paths'}}) { - if ( $path =~ /$_$/) { + if ( $path =~ /$_$/ ){ $isvalid=1; $path=$_; last; } } + $isvalid ||= ( $path =~ /\/addons\// ); + #$isvalid or $connection->send_error(404,"
$path not in
". join ('
',@{$this->{'paths'}} )) and return; $isvalid or $this->SendError(404); @@ -485,12 +491,17 @@ sub Run unshift ( @{$this->{'paths'}}, $1); } @{$this->{'paths'}} = ( @{ $configuration->{'rrdlist'}}, @{$this->{'paths'}} ); - $this->{'status'} = encode_json(\@{$configuration->{'web'}->{'status'}}); - $this->{'statistics'} = encode_json(\@{$configuration->{'web'}->{'statistics'}}); - $this->{'friends'} = encode_json(\@{$configuration->{'web'}->{'friends'}}); - $this->{'page'} = encode_json(\%{$configuration->{'web'}->{'page'}}); - $this->{'static'} = encode_json(\%{$monitor->{'static'}}); - $this->{'menu'} = encode_json(\%{$configuration->{'menu'}}); + $this->{'status'} = to_json(\@{$configuration->{'web'}->{'status'}}); + $this->{'statistics'} = to_json(\@{$configuration->{'web'}->{'statistics'}}); + $this->{'friends'} = to_json(\@{$configuration->{'web'}->{'friends'}}); + $this->{'page'} = to_json(\%{$configuration->{'web'}->{'page'}}); + $this->{'static'} = to_json(\%{$monitor->{'static'}}); + $this->{'menu'} = to_json(\%{$configuration->{'web'}->{'menu'}}); + $this->{'addons'} = to_json(\@{$configuration->{'web'}->{'addons'}}); + my $json = JSON->new; + $json = $json->allow_blessed([$configuration]); + $json = $json->convert_blessed([$configuration]); + $this->{'all'} = $json->encode( \%{$configuration} ); $this->{'version'} = "{\"version\":\"$configuration->{'version'}\"}"; #print Data::Dumper->Dump([$this->{'paths'}]); @@ -563,27 +574,31 @@ sub Run # write json if server is not running open(FILE, "> $configuration->{'daemon'}->{'webroot'}/static.json") or warn $!; - print FILE encode_json \%{$this->{'static'}} ; + print FILE to_json \%{$this->{'static'}} ; close(FILE); open(FILE, "> $configuration->{'daemon'}->{'webroot'}/status.json") or warn $!; - print FILE encode_json \@{$configuration->{'web'}->{'status'}} ; + print FILE to_json \@{$configuration->{'web'}->{'status'}} ; close(FILE); open(FILE, "> $configuration->{'daemon'}->{'webroot'}/page.json") or warn $!; - print FILE encode_json \%{$configuration->{'web'}->{'page'}} ; + print FILE to_json \%{$configuration->{'web'}->{'page'}} ; close(FILE); open(FILE, "> $configuration->{'daemon'}->{'webroot'}/statistics.json") or warn $!; - print FILE encode_json \@{$configuration->{'web'}->{'statistics'}} ; + print FILE to_json \@{$configuration->{'web'}->{'statistics'}} ; close(FILE); open(FILE, "> $configuration->{'daemon'}->{'webroot'}/friends.json") or warn $!; - print FILE encode_json(\@{$configuration->{'web'}->{'friends'}}); + print FILE to_json(\@{$configuration->{'web'}->{'friends'}}); close(FILE); open(FILE, "> $configuration->{'daemon'}->{'webroot'}/menu.json") or warn $!; - print FILE encode_json(\%{$configuration->{'menu'}}); + print FILE to_json(\%{$configuration->{'web'}->{'menu'}}); + close(FILE); + open(FILE, "> $configuration->{'daemon'}->{'webroot'}/addons.json") + or warn $!; + print FILE to_json(\@{$configuration->{'web'}->{'addons'}}); close(FILE); open(FILE, "> $configuration->{'daemon'}->{'webroot'}/version.json") or warn $!; @@ -668,7 +683,7 @@ sub Status my ($sec,$min,$hour,$mday,$mon,$year) = (localtime)[0,1,2,3,4,5]; @{$this->{'dynamic'}->{'localtime'}}=($year+1900,$mon+1,$mday,$hour,$min,$sec); - my $json=encode_json \%{$this->{'dynamic'}}; + my $json=to_json \%{$this->{'dynamic'}}; $this->Debug(4,"\n$json"); # if embeded server is not used, we write the json file else the diff --git a/rpimonitor/template/bananian.conf b/rpimonitor/template/bananian.conf new file mode 100644 index 0000000..8271352 --- /dev/null +++ b/rpimonitor/template/bananian.conf @@ -0,0 +1,16 @@ +web.page.icon='img/logo.png' +web.page.menutitle='BPi-Monitor ('+data.hostname+')' +web.page.pagetitle='BPi-Monitor ('+data.hostname+')' + +web.status.1.name=Banana Pi +web.statistics.1.name=Banana Pi + +include=/etc/rpimonitor/template/version.conf +include=/etc/rpimonitor/template/uptime.conf +include=/etc/rpimonitor/template/cpu_bananian.conf +include=/etc/rpimonitor/template/pmu_bananian.conf +include=/etc/rpimonitor/template/temperature_bananian.conf +include=/etc/rpimonitor/template/memory.conf +include=/etc/rpimonitor/template/swap.conf +include=/etc/rpimonitor/template/sdcard.conf +include=/etc/rpimonitor/template/network.conf diff --git a/rpimonitor/template/cpu.conf b/rpimonitor/template/cpu.conf index a4eba49..1235fd2 100644 --- a/rpimonitor/template/cpu.conf +++ b/rpimonitor/template/cpu.conf @@ -37,6 +37,7 @@ web.status.1.content.1.icon=cpu.png web.status.1.content.1.line.1=JustGageBar("Load", "1min", 0, data.load1, 3, 100, 80)+" "+JustGageBar("Load", "5min", 0, data.load5, 3, 100, 80)+" "+JustGageBar("Load", "15min", 0, data.load15, 3, 100, 80) web.status.1.content.1.line.2="CPU frequency: " + data.cpu_frequency + "MHz Voltage: " + data.cpu_voltage + "V" web.status.1.content.1.line.3="Scaling governor: " + data.scaling_governor + "" +#web.status.1.content.1.line.4=InsertHTML("/addons/top3/top3.html") web.statistics.1.content.1.name=CPU Loads web.statistics.1.content.1.graph.1=load1 diff --git a/rpimonitor/template/cpu_bananian.conf b/rpimonitor/template/cpu_bananian.conf new file mode 100644 index 0000000..0877641 --- /dev/null +++ b/rpimonitor/template/cpu_bananian.conf @@ -0,0 +1,69 @@ +######################################################################## +# Extract CPU information +# Page: 1 +# Information Status Statistics +# - cpu frequency - yes - yes +# - pmu voltage - no - yes +# - cpu load 1, 5, 15 - yes - yes +# - cpu scaling governor - yes - no +# - pmu current - yes - yes +# - pmu consumption (V * A) - no - yes +######################################################################## +dynamic.1.name=cpu_frequency +dynamic.1.source=/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_cur_freq +dynamic.1.regexp=(.*) +dynamic.1.postprocess=$1/1000 +dynamic.1.rrd=GAUGE + +dynamic.2.name=pmu_voltage +dynamic.2.source=/sys/devices/platform/sunxi-i2c.0/i2c-0/0-0034/axp20-supplyer.28/power_supply/ac/voltage_now +dynamic.2.regexp=(.*) +dynamic.2.postprocess=$1/1000000 +dynamic.2.rrd=GAUGE + +dynamic.3.name=load1,load5,load15 +dynamic.3.source=/proc/loadavg +dynamic.3.regexp=^(\S+)\s(\S+)\s(\S+) +dynamic.3.postprocess= +dynamic.3.rrd=GAUGE + +dynamic.4.name=scaling_governor +dynamic.4.source=/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor +dynamic.4.regexp=(.*) +dynamic.4.postprocess= +dynamic.4.rrd= + +dynamic.5.name=pmu_current +dynamic.5.source=/sys/devices/platform/sunxi-i2c.0/i2c-0/0-0034/axp20-supplyer.28/power_supply/ac/current_now +dynamic.5.regexp=(.*) +dynamic.5.postprocess=$1/1000 +dynamic.5.rrd=GAUGE + +dynamic.6.name=pmu_consumption +dynamic.6.source=/sys/devices/platform/sunxi-i2c.0/i2c-0/0-0034/axp20-supplyer.28/power_supply/ac/current_now +dynamic.6.regexp=(.*) +dynamic.6.postprocess=$1/1000000 * $this->{'dynamic'}->{'pmu_voltage'} +dynamic.6.rrd=GAUGE + +web.status.1.content.1.name=CPU / PMU +web.status.1.content.1.icon=cpu.png +web.status.1.content.1.line.1="Loads: " + data.load1 + " [1min] - " + data.load5 + " [5min] - " + data.load15 + " [15min]" +web.status.1.content.1.line.2="CPU frequency: " + data.cpu_frequency + "MHz PMU Current: " + data.pmu_current + "mA" +web.status.1.content.1.line.3="Scaling governor: " + data.scaling_governor + "" + +web.statistics.1.content.1.name=Load / Clock speed / PMU +web.statistics.1.content.1.graph.1=load1 +web.statistics.1.content.1.graph.2=load5 +web.statistics.1.content.1.graph.3=load15 +web.statistics.1.content.1.graph.4=pmu_consumption +web.statistics.1.content.1.graph.5=cpu_frequency +web.statistics.1.content.1.graph.6=pmu_current +web.statistics.1.content.1.ds_graph_options.load1.label=Load 1 min +web.statistics.1.content.1.ds_graph_options.load5.label=Load 5 min +web.statistics.1.content.1.ds_graph_options.load15.label=Load 15 min +web.statistics.1.content.1.ds_graph_options.pmu_consumption.label=PMU consumption (W) +web.statistics.1.content.1.ds_graph_options.cpu_frequency.label=Clock speed (MHz) +web.statistics.1.content.1.ds_graph_options.pmu_current.label=PMU Current (mA) +web.statistics.1.content.1.ds_graph_options.cpu_frequency.yaxis=2 +web.statistics.1.content.1.ds_graph_options.pmu_current.yaxis=2 +web.statistics.1.content.1.graph_options.y2axis={ position: "right" } diff --git a/rpimonitor/template/pmu_bananian.conf b/rpimonitor/template/pmu_bananian.conf new file mode 100644 index 0000000..04e46e3 --- /dev/null +++ b/rpimonitor/template/pmu_bananian.conf @@ -0,0 +1,84 @@ +######################################################################## +# Extract PMU information +# Page: 1 +# Information Status Statistics +# - pmu usb voltage - no - yes +# - pmu pwr voltage - no - yes +# - pmu usb current - no - yes +# - pmu pwr current - no - yes +# - PWR consumption - yes - no +# - USB consumption - yes - no +# +# logo from http://www.apkdad.com/wp-content/uploads/2013/01/Battery-Icon1.png +######################################################################## +dynamic.1.name=pmu_usb_voltage +dynamic.1.source=/sys/devices/platform/sunxi-i2c.0/i2c-0/0-0034/axp20-supplyer.28/power_supply/usb/voltage_now +dynamic.1.regexp=(.*) +dynamic.1.postprocess=$1/1000000 +dynamic.1.rrd=GAUGE + +dynamic.2.name=pmu_ac_voltage +dynamic.2.source=/sys/devices/platform/sunxi-i2c.0/i2c-0/0-0034/axp20-supplyer.28/power_supply/ac/voltage_now +dynamic.2.regexp=(.*) +dynamic.2.postprocess=$1/1000000 +dynamic.2.rrd=GAUGE + +dynamic.3.name=pmu_usb_current +dynamic.3.source=/sys/devices/platform/sunxi-i2c.0/i2c-0/0-0034/axp20-supplyer.28/power_supply/usb/current_now +dynamic.3.regexp=(.*) +dynamic.3.postprocess=$1/1000 +dynamic.3.rrd=GAUGE + +dynamic.4.name=pmu_ac_current +dynamic.4.source=/sys/devices/platform/sunxi-i2c.0/i2c-0/0-0034/axp20-supplyer.28/power_supply/ac/current_now +dynamic.4.regexp=(.*) +dynamic.4.postprocess=$1/1000 +dynamic.4.rrd=GAUGE + +dynamic.5.name=pmu_usb_consumption +dynamic.5.source=/sys/devices/platform/sunxi-i2c.0/i2c-0/0-0034/axp20-supplyer.28/power_supply/usb/current_now +dynamic.5.regexp=(.*) +dynamic.5.postprocess=$this->{'dynamic'}->{'pmu_usb_voltage'} * $1 / 1000 +dynamic.5.rrd=GAUGE + +dynamic.6.name=pmu_ac_consumption +dynamic.6.source=/sys/devices/platform/sunxi-i2c.0/i2c-0/0-0034/axp20-supplyer.28/power_supply/ac/current_now +dynamic.6.regexp=(.*) +dynamic.6.postprocess=$this->{'dynamic'}->{'pmu_ac_voltage'} * $1 / 1000 +dynamic.6.rrd=GAUGE + +dynamic.7.name=pmu_cur_temp +dynamic.7.source=/sys/devices/platform/sunxi-i2c.0/i2c-0/0-0034/temp1_input +dynamic.7.regexp=(.*) +dynamic.7.postprocess=$1/1000 +dynamic.7.rrd=GAUGE + +web.status.1.content.1.name=Consumption +web.status.1.content.1.icon=pmu.png +web.status.1.content.1.line.1="PWR in: " + data.pmu_ac_consumption + " mW USB OTG in: " + data.pmu_usb_consumption + " mW" + +web.statistics.1.content.1.name=PMU current/voltage +web.statistics.1.content.1.graph.1=pmu_ac_voltage +web.statistics.1.content.1.graph.2=pmu_usb_voltage +web.statistics.1.content.1.graph.3=pmu_ac_current +web.statistics.1.content.1.graph.4=pmu_usb_current +web.statistics.1.content.1.ds_graph_options.pmu_ac_voltage.label=Voltage PWR in (V) +web.statistics.1.content.1.ds_graph_options.pmu_usb_voltage.label=Voltage USB in (V) +web.statistics.1.content.1.ds_graph_options.pmu_ac_current.label=Current PWR in (mA) +web.statistics.1.content.1.ds_graph_options.pmu_usb_current.label=Current USB in (mA) +web.statistics.1.content.1.ds_graph_options.pmu_ac_voltage.yaxis=1 +web.statistics.1.content.1.ds_graph_options.pmu_usb_voltage.yaxis=1 +web.statistics.1.content.1.ds_graph_options.pmu_ac_current.yaxis=2 +web.statistics.1.content.1.ds_graph_options.pmu_usb_current.yaxis=2 +web.statistics.1.content.1.graph_options.y1axis={ position: "left", min: 4.5, max: 5.5 } +web.statistics.1.content.1.graph_options.y2axis={ position: "right" } + +web.statistics.1.content.2.name=Consumption / temperature +web.statistics.1.content.2.graph.1=pmu_ac_consumption +web.statistics.1.content.2.graph.2=pmu_usb_consumption +web.statistics.1.content.2.graph.3=pmu_cur_temp +web.statistics.1.content.2.ds_graph_options.pmu_ac_consumption.label=Consumption (W) +web.statistics.1.content.2.ds_graph_options.pmu_usb_consumption.label=USB devices (W) +web.statistics.1.content.2.ds_graph_options.pmu_cur_temp.label="Current PMU temp (°C)" +web.statistics.1.content.2.ds_graph_options.pmu_cur_temp.yaxis=2 +web.statistics.1.content.2.graph_options.y2axis={ position: "right" } diff --git a/rpimonitor/template/raspbian.conf b/rpimonitor/template/raspbian.conf index 07a1996..6f70f39 100644 --- a/rpimonitor/template/raspbian.conf +++ b/rpimonitor/template/raspbian.conf @@ -209,6 +209,10 @@ # level : 'default' (grey), 'primary' (blue label/grey badge), # 'success' (green), 'info' (cyan), 'warning' (orange) # or 'danger' (red) +# +# InsertHTML(url) +# This function is used to insert an HTML information inside +# a page. (Ref Top3 example showing top most process cpu usage) # # The statistic page is fully configurable. # It is possible to define multiple pages. Each page is identified by @@ -260,7 +264,31 @@ web.page.menutitle='RPi-Monitor ('+data.hostname+')' web.page.pagetitle='RPi-Monitor ('+data.hostname+')' web.status.1.name=Raspberry Pi +#web.status.2.name=Home web.statistics.1.name=Raspberry Pi +#web.statistics.2.name=page 2 + +web.addons.1.name=Addons +web.addons.1.addons=about +#web.addons.1.name=Shellinabox +#web.addons.1.addons=shellinabox + +#web.addons.2.name=Addons example +#web.addons.2.addons=example +#web.addons.2.showTitle=0 + +#web.addons.3.name=Webcam - Hawkeye +#web.addons.3.showTitle=0 +#web.addons.3.addons=custom +#web.addons.3.url=http://0:8000/ + +#web.addons.4.name=Custom addons +#web.addons.4.addons=custom +#web.addons.4.showTitle=0 +#web.addons.4.url=/addons/custom/custominfo.html + +#web.addons.5.name=Top3 +#web.addons.5.addons=top3 #include=/etc/rpimonitor/template/example.justgage.conf #include=/etc/rpimonitor/template/example.badge_and_label.conf diff --git a/rpimonitor/template/services.conf b/rpimonitor/template/services.conf index 4d25da7..ad55067 100644 --- a/rpimonitor/template/services.conf +++ b/rpimonitor/template/services.conf @@ -6,6 +6,7 @@ # - rpimonotor port (8888) - yes - no # - http port (80) - yes - yes # - https port (443) - yes - yes +# - mysql port (3306) - yes - no ######################################################################## dynamic.1.name=ssh dynamic.1.source=netstat -nlt @@ -23,6 +24,10 @@ dynamic.4.name=https dynamic.4.source=netstat -nlt dynamic.4.regexp=tcp .*:(443).*LISTEN +dynamic.5.name=mysql +dynamic.5.source=netstat -nlt +dynamic.5.regexp=tcp .*:(3306).*LISTEN + web.status.1.content.1.name=Servers web.status.1.content.1.icon=daemons.png -web.status.1.content.1.line.1="ssh : "+Label(data.ssh,"==22","OK","success")+Label(data.ssh,"!=22","KO","danger")+" rpimonitor : "+Label(data.rpimonitor,"==8888","OK","success")+Label(data.rpimonitor,"!=8888","KO","danger")+" nginx http : "+Label(data.http,"==80","OK","success")+Label(data.http,"!=80","KO","danger")+" nginx https : "+Label(data.https,"==443","OK","success")+Label(data.https,"!=443","KO","danger") +web.status.1.content.1.line.1="ssh : "+Label(data.ssh,"==22","OK","success")+Label(data.ssh,"!=22","KO","danger")+" rpimonitor : "+Label(data.rpimonitor,"==8888","OK","success")+Label(data.rpimonitor,"!=8888","KO","danger")+" nginx http : "+Label(data.http,"==80","OK","success")+Label(data.http,"!=80","KO","danger")+" nginx https : "+Label(data.https,"==443","OK","success")+Label(data.https,"!=443","KO","danger")+" mysql : "+Label(data.mysql,"==3306","OK","success")+Label(data.mysql,"!=3306","KO","danger" diff --git a/rpimonitor/template/temperature.conf b/rpimonitor/template/temperature.conf index 7b878a4..76e421c 100644 --- a/rpimonitor/template/temperature.conf +++ b/rpimonitor/template/temperature.conf @@ -13,9 +13,9 @@ dynamic.12.rrd=GAUGE web.status.1.content.4.name=Temperature web.status.1.content.4.icon=cpu_temp.png #web.status.1.content.4.line.1="CPU Temperature: "+data.soc_temp+"°C" -#web.status.1.content.4.line.1=JustGageBar("CPU Temperature", data.soc_temp+"°C", 40, data.soc_temp, 80, 100, 80) -web.status.1.content.4.line.1=JustGageBar("Temperature", "°C", 40, data.soc_temp, 80, 100, 80) +#web.status.1.content.4.line.1=JustGageBar("CPU Temperature", data.soc_temp+"°C", 40, data.soc_temp, 80, 100, 80) +web.status.1.content.4.line.1=JustGageBar("Temperature", "°C", 40, data.soc_temp, 80, 100, 80) web.statistics.1.content.8.name=Temperature web.statistics.1.content.8.graph.1=soc_temp -web.statistics.1.content.8.ds_graph_options.soc_temp.label=Core temperature (°C) +web.statistics.1.content.8.ds_graph_options.soc_temp.label=Core temperature (°C) diff --git a/rpimonitor/template/temperature_bananian.conf b/rpimonitor/template/temperature_bananian.conf new file mode 100644 index 0000000..3a5ad40 --- /dev/null +++ b/rpimonitor/template/temperature_bananian.conf @@ -0,0 +1,37 @@ +######################################################################## +# Extract CPU Temperature information +# Page: 1 +# Information Status Statistics +# - soc temperature - yes - yes +# - pmu temperature - yes - yes +# - disk temperature - yes - yes +######################################################################## +dynamic.1.name=soc_temp +dynamic.1.source=/sys/devices/platform/sunxi-i2c.0/i2c-0/0-0034/temp1_input +dynamic.1.regexp=(.*) +dynamic.1.postprocess=$1/1000 +dynamic.1.rrd=GAUGE + +dynamic.2.name=hddtemp +dynamic.2.source=/run/hdd-temp +dynamic.2.regexp=(.*) +dynamic.2.postprocess= +dynamic.2.rrd=GAUGE + +dynamic.3.name=cputemp +dynamic.3.source=/run/soc-temp +dynamic.3.regexp=(.*) +dynamic.3.postprocess= +dynamic.3.rrd=GAUGE + +web.status.1.content.4.name=Temperature +web.status.1.content.4.icon=cpu_temp.png +web.status.1.content.4.line.1=JustGageBar("CPU", "°C",0, data.cputemp , 100,100,80,percentColors,50,60)+" "+JustGageBar("PMU", "°C",0, data.soc_temp , 100,100,80,percentColors,35,45)+" "+JustGageBar("Disk", "°C",0, data.hddtemp , 100,100,80,percentColors,40,45) + +web.statistics.1.content.8.name=Temperature +web.statistics.1.content.8.graph.1=soc_temp +web.statistics.1.content.8.graph.2=hddtemp +web.statistics.1.content.8.graph.3=cputemp +web.statistics.1.content.8.ds_graph_options.soc_temp.label=PMU temperature (deg C) +web.statistics.1.content.8.ds_graph_options.hddtemp.label=HDD temperature (deg C) +web.statistics.1.content.8.ds_graph_options.cputemp.label=CPU temperature (deg C) diff --git a/rpimonitor/web/shellinabox.html b/rpimonitor/web/addons.html similarity index 80% rename from rpimonitor/web/shellinabox.html rename to rpimonitor/web/addons.html index f6fae7d..1f5c1cc 100644 --- a/rpimonitor/web/shellinabox.html +++ b/rpimonitor/web/addons.html @@ -35,23 +35,26 @@ - -
+ +
- -
- -
+ +
+
+
+ -
+ +
- + + diff --git a/rpimonitor/web/addons/about/about.css b/rpimonitor/web/addons/about/about.css new file mode 100644 index 0000000..e69de29 diff --git a/rpimonitor/web/addons/about/about.html b/rpimonitor/web/addons/about/about.html new file mode 100644 index 0000000..6ede61c --- /dev/null +++ b/rpimonitor/web/addons/about/about.html @@ -0,0 +1,32 @@ + + + + + + + +
+
+

About addons

+
+
+ Addon is design to let you add more functionnality to + RPi-Monitor.
+
+ The realease of RPi-Monitor provides the following addons:
+
    +
  • about - this addons
  • +
  • custom - custom addons using <iframe> to display external html page
  • +
  • example - expamle of addons showing how to develop and addons fully integrated to RPi-Monitor
  • +
  • shellinabox - Allow ssh access to your computer through your browser
  • +
  • top3 - Show the top 3 processes using cpu and rpimonitord cpu usage
  • +
+
+ Addons are configured into /etc/rpimonitor/data.conf. You can customise + RPi-Monitor configuration to add or remove addons.
+
+ Refer to manage and/or comments in configuration file to see in detail how to use addons. +
+ + + diff --git a/rpimonitor/web/addons/about/about.js b/rpimonitor/web/addons/about/about.js new file mode 100644 index 0000000..e69de29 diff --git a/rpimonitor/web/addons/custom/custom.css b/rpimonitor/web/addons/custom/custom.css new file mode 100644 index 0000000..333e801 --- /dev/null +++ b/rpimonitor/web/addons/custom/custom.css @@ -0,0 +1,8 @@ +#customframe, +#customdiv { + margin-top: 8px; + padding: 0px 0px 0px 0px; + min-width: 100%; + min-height: 100%; + border:0px; +} diff --git a/rpimonitor/web/addons/custom/custom.html b/rpimonitor/web/addons/custom/custom.html new file mode 100644 index 0000000..53149a1 --- /dev/null +++ b/rpimonitor/web/addons/custom/custom.html @@ -0,0 +1,4 @@ +
+ +
+ diff --git a/rpimonitor/web/addons/custom/custom.js b/rpimonitor/web/addons/custom/custom.js new file mode 100644 index 0000000..ca4c872 --- /dev/null +++ b/rpimonitor/web/addons/custom/custom.js @@ -0,0 +1,69 @@ +var customwarning = true; +var customuri; +var activePage = GetURLParameter('activePage'); + + +$(function () { + resize_frame(); + + data = getData('addons'); + + options = '

'+ + 'Custom addons
'+ + '

'+ + 'Custom url: '+ + '
'+ + ''+ + ''+ + ''+ + ''+ + '

'+ + ' Show warning on page close or refresh'+ + '
'+ + '

' + $(options).insertBefore("#optionsInsertionPoint") + + $("#defaulturi").click(function(){ + customuri = data[activePage].url;//"/addons/custom/custominfo.html"; + $('#customuri'+activePage).val(customuri); + localStorage.setItem('customuri'+activePage, customuri); + $("#customframe").attr('src', customuri); + }); + + customwarning=(localStorage.getItem('customwarning'+activePage) === 'true' ); + $('#customwarning'+activePage).attr('checked', customwarning ); + + $('#customwarning'+activePage).click(function(){ + customwarning = $('#customwarning'+activePage).is(":checked"); + localStorage.setItem('customwarning'+activePage, customwarning); + }); + + customuri=(localStorage.getItem('customuri'+activePage) || data[activePage].url || '/addons/custom/custominfo.html'); + $('#customuri'+activePage).val(customuri); + $('#customuri'+activePage).keyup(function(){ + customuri = $('#customuri'+activePage).val(); + localStorage.setItem('customuri'+activePage, customuri); + $("#customframe").attr('src', customuri); + }); + + $("#customframe").attr('src', customuri); +}); + +function resize_frame(){ + var window_height = $(window).height() - 100; + $('#customdiv').css('height',window_height+'px'); +} + +$(window).resize(function() { + resize_frame(); +}); + +window.onbeforeunload = function (e) { + if ( !customwarning ) return; + e = e || window.event; + message="Closing or refreshing this page will also close your connection."; + if (e) { + e.returnValue = message; + } + return message; +}; diff --git a/rpimonitor/web/addons/custom/custominfo.html b/rpimonitor/web/addons/custom/custominfo.html new file mode 100644 index 0000000..3bf3da5 --- /dev/null +++ b/rpimonitor/web/addons/custom/custominfo.html @@ -0,0 +1,28 @@ + + + + + + + +
+
+

Custom addon

+
+
+ Custom addon is design to let you add custom pages to + RPi-Monitor easily.
+
+ This addons is using <iframe> to display external + html page, web site or anything else that can be displayed by your + browser. The url to be displayed is configurable in + RPi-Monitor configuration file and/or in Options dialog box.
+ You can use custom addon many time in the same configuration and + configure each addons individually.
+
+ Refer to RPi-Experiences web site to + see example of usages. +
+ + + diff --git a/rpimonitor/web/addons/example/example.css b/rpimonitor/web/addons/example/example.css new file mode 100644 index 0000000..2d98fac --- /dev/null +++ b/rpimonitor/web/addons/example/example.css @@ -0,0 +1,3 @@ +#text{ + font-size: 51px; +} diff --git a/rpimonitor/web/addons/example/example.html b/rpimonitor/web/addons/example/example.html new file mode 100644 index 0000000..22b32f8 --- /dev/null +++ b/rpimonitor/web/addons/example/example.html @@ -0,0 +1,31 @@ +
+
+
+

Example addon

+
+
+ Example addon shows how you can develop your own addon for + RPi-Monitor.
+
+ An addon (named for this example example) is composed + by a set of 3 files stored in the subdirectory of the same name
+ /usr/share/rpimonitor/web/addons/example/: + + These 3 files are directly used by RPi-Monitor to render a page. + Javascript of the addon have a direct access to RPi-Monitor + data using the functions in rpimonitor.utils.js and the + functions getData('json_data').
+
+ This example is showing how to get uptime information and display it + using the function Uptime( value ):
+
+
addonInsertionPoint

+ Refer to RPi-Experiences web site to + see example of usages. +
+
+
diff --git a/rpimonitor/web/addons/example/example.js b/rpimonitor/web/addons/example/example.js new file mode 100644 index 0000000..ce662c8 --- /dev/null +++ b/rpimonitor/web/addons/example/example.js @@ -0,0 +1,36 @@ +var activePage = GetURLParameter('activePage'); + +function UpdateAddon () { + $.getJSON('dynamic.json', function(data) { + // Concatenate dynamic.json and static.json into data variable + $.extend(data, getData('static')); + + //ADD YOUR JAVASCRIPT HERE + //Example using data from RPi-Monitor and function provided by rpimonitor.utils.js + textToDisplay = "Uptime: " + Uptime(data.uptime); + $("#addonInsertionPoint").html(textToDisplay) + + }) +} + +$(function () { + alert('Example addons is showing how to dynamically display Raspberry Pi uptime.\n'+ + 'Have a look to "addonInsertionPoint" bellow and click OK') + + options = '

'+ + 'Example
'+ + '

'+ + ' Add addons option(s)'+ + '
'+ + '

' + $(options).insertBefore("#optionsInsertionPoint") + UpdateAddon(); + + //if ( statusautorefresh ) { + // refreshTimerId = setInterval( UpdateAddon , 10000 ) + // clockId=setInterval(Tick,1000); + //} + +}); + + diff --git a/rpimonitor/web/addons/shellinabox/shellinabox.css b/rpimonitor/web/addons/shellinabox/shellinabox.css new file mode 100644 index 0000000..7fc5c43 --- /dev/null +++ b/rpimonitor/web/addons/shellinabox/shellinabox.css @@ -0,0 +1,7 @@ +#shellinaboxframe, +#shellinaboxdiv { + margin-top: 8px; + padding: 0px 0px 0px 0px; + min-width: 100%; + min-height: 100%; +} diff --git a/rpimonitor/web/addons/shellinabox/shellinabox.html b/rpimonitor/web/addons/shellinabox/shellinabox.html new file mode 100644 index 0000000..b9a6b73 --- /dev/null +++ b/rpimonitor/web/addons/shellinabox/shellinabox.html @@ -0,0 +1,4 @@ +
+ +
+ diff --git a/rpimonitor/web/addons/shellinabox/shellinabox.js b/rpimonitor/web/addons/shellinabox/shellinabox.js new file mode 100644 index 0000000..144a08b --- /dev/null +++ b/rpimonitor/web/addons/shellinabox/shellinabox.js @@ -0,0 +1,69 @@ +var shellinaboxwarning = true; +var shellinaboxuri; +var activePage = GetURLParameter('activePage'); + + +$(function () { + $('#pagetitle').remove(); + + resize_frame(); + + options = '

'+ + 'Shellinabox
'+ + '

'+ + 'Shellinabox url: '+ + '
'+ + ''+ + ''+ + ''+ + ''+ + '

'+ + ' Show warning on page close or refresh'+ + '
'+ + '

' + $(options).insertBefore("#optionsInsertionPoint") + + $("#defaulturi").click(function(){ + shellinaboxuri = "/shellinabox"; + $('#shellinaboxuri'+activePage).val(shellinaboxuri); + localStorage.setItem('shellinaboxuri'+activePage, shellinaboxuri); + $("#shellinaboxframe").attr('src', shellinaboxuri); + }); + + shellinaboxwarning=(localStorage.getItem('shellinaboxwarning'+activePage) === 'true' ); + $('#shellinaboxwarning'+activePage).attr('checked', shellinaboxwarning ); + + $('#shellinaboxwarning'+activePage).click(function(){ + shellinaboxwarning = $('#shellinaboxwarning'+activePage).is(":checked"); + localStorage.setItem('shellinaboxwarning'+activePage, shellinaboxwarning); + }); + + shellinaboxuri=(localStorage.getItem('shellinaboxuri'+activePage) || '/shellinabox'); + $('#shellinaboxuri'+activePage).val(shellinaboxuri); + $('#shellinaboxuri'+activePage).keyup(function(){ + shellinaboxuri = $('#shellinaboxuri'+activePage).val(); + localStorage.setItem('shellinaboxuri'+activePage, shellinaboxuri); + $("#shellinaboxframe").attr('src', shellinaboxuri); + }); + + $("#shellinaboxframe").attr('src', shellinaboxuri); +}); + +function resize_frame(){ + var window_height = $(window).height() - 100; + $('#shellinaboxdiv').css('height',window_height+'px'); +} + +$(window).resize(function() { + resize_frame(); +}); + +window.onbeforeunload = function (e) { + if ( !shellinaboxwarning ) return; + e = e || window.event; + message="Closing or refreshing this page will also close your connection."; + if (e) { + e.returnValue = message; + } + return message; +}; diff --git a/rpimonitor/web/addons/top3/top3 b/rpimonitor/web/addons/top3/top3 new file mode 100755 index 0000000..b533112 --- /dev/null +++ b/rpimonitor/web/addons/top3/top3 @@ -0,0 +1,68 @@ +#!/usr/bin/perl +# +# Copyright 2013-2014 - Xavier Berger - http://rpi-experiences.blogspot.fr/ +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# web.status.1.content.1.line.4=InsertHTML("/addons/top3/top3.html") +# +my $process='rpimonitor'; +my $web=1; +my $nbtop=3; +my @top=[]; +my $monitored; + +#Get uptime +my $uptime; +open ( FILE, "/proc/uptime") or die "Can't open /proc/uptime \n"; +while (){ + /(\S+) \S+/; + $uptime = $_; +} + +my $idx=0; +open (PS, 'ps -e -o etimes,time,comm --sort -time |') or die; +while () +{ + /TIME/ and next; + $idx++; + /$process/ and $monitored->{'idx'} ||= $idx; + /$process/ or $idx <= $nbtop or next; + /(\S+) (\S+):(\S+):(\S+) (\S+)/; + my $start=100*($2*60*60+$3*60+$4)/$1; + my $total=100*($2*60*60+$3*60+$4)/$uptime; + $web + and $top[$idx] = sprintf("%3d%02d:%02d:%02d%-12s( %.2f% / %.2f% )\n",$idx, $2, $3, $4, $5, $start, $total) + or $top[$idx] = sprintf("%3d %02d:%02d:%02d %-12s ( %.2f% / %.2f% ) \n",$idx, $2, $3, $4, $5, $start, $total); +} +close PS or die; +my $iloop; + + $web + and print "\n" + and print "\n"; + for ($iloop=1; $iloop<=$nbtop; $iloop++) + { + print $top[$iloop]; + } + if ($iloop < $monitored->{'idx'}){ + $web + and print "\n" + or print " ...\n"; + } + if ($iloop <= $monitored->{'idx'}){ + print $top[$monitored->{'idx'}]; + } + $web + and print "
#CPU usageProcess% start / total
...
\n"; diff --git a/rpimonitor/web/addons/top3/top3.cron b/rpimonitor/web/addons/top3/top3.cron new file mode 100644 index 0000000..2e6d65f --- /dev/null +++ b/rpimonitor/web/addons/top3/top3.cron @@ -0,0 +1,3 @@ +# +* * * * * root cd /usr/share/rpimonitor/web/addons/top3; ./top3 > top3.html + diff --git a/rpimonitor/web/addons/top3/top3.html b/rpimonitor/web/addons/top3/top3.html new file mode 100644 index 0000000..beb7fa1 --- /dev/null +++ b/rpimonitor/web/addons/top3/top3.html @@ -0,0 +1,24 @@ + + + + + + +
+
+

Top3 addons

+
+
+ Top3 is design show the 3 process consuming the CPU time of + CPU in addition to cunsumption of rpimonitord process.
+
+ To activate this addon, copy /usr/share/rpimonitor/web/addons/top3.cron + to /etc/cron.d/top3.
+
+ Adding the top3 information into status page can be done by uncommenting + the following line into cpu.conf
+ #web.status.1.content.1.line.4=InsertHTML("/addons/top3/top3.html") +
+ + + diff --git a/rpimonitor/web/img/pmu.png b/rpimonitor/web/img/pmu.png new file mode 100644 index 0000000..9e6c20e Binary files /dev/null and b/rpimonitor/web/img/pmu.png differ diff --git a/rpimonitor/web/index.html b/rpimonitor/web/index.html index 4231820..acbab5d 100644 --- a/rpimonitor/web/index.html +++ b/rpimonitor/web/index.html @@ -16,6 +16,7 @@ # along with this program. If not, see . # --> + RPi-Monitor @@ -35,21 +36,23 @@ - -
+ +
- -  
-
-
-

RPi-Monitor

-

Welcome to RPi-Monitor web interface.

-

Start

-
+ +  
+
+
+

RPi-Monitor

+

Welcome to RPi-Monitor web interface.

+

Start

+
+ - + +
diff --git a/rpimonitor/web/js/javascriptrrd/binaryXHR.js b/rpimonitor/web/js/javascriptrrd/binaryXHR.js index d91ad72..4129719 100644 --- a/rpimonitor/web/js/javascriptrrd/binaryXHR.js +++ b/rpimonitor/web/js/javascriptrrd/binaryXHR.js @@ -1,234 +1,234 @@ - -/* - * BinaryFile over XMLHttpRequest - * Part of the javascriptRRD package - * Copyright (c) 2009 Frank Wuerthwein, fkw@ucsd.edu - * MIT License [http://www.opensource.org/licenses/mit-license.php] - * - * Original repository: http://javascriptrrd.sourceforge.net/ - * - * Based on: - * Binary Ajax 0.1.5 - * Copyright (c) 2008 Jacob Seidelin, cupboy@gmail.com, http://blog.nihilogic.dk/ - * MIT License [http://www.opensource.org/licenses/mit-license.php] - */ - -// ============================================================ -// Exception class -function InvalidBinaryFile(msg) { - this.message=msg; - this.name="Invalid BinaryFile"; -} - -// pretty print -InvalidBinaryFile.prototype.toString = function() { - return this.name + ': "' + this.message + '"'; -} - -// ===================================================================== -// BinaryFile class -// Allows access to element inside a binary stream -function BinaryFile(strData, iDataOffset, iDataLength) { - var data = strData; - var dataOffset = iDataOffset || 0; - var dataLength = 0; - // added - var doubleMantExpHi=Math.pow(2,-28); - var doubleMantExpLo=Math.pow(2,-52); - var doubleMantExpFast=Math.pow(2,-20); - - this.getRawData = function() { - return data; - } - - if (typeof strData == "string") { - dataLength = iDataLength || data.length; - - this.getByteAt = function(iOffset) { - return data.charCodeAt(iOffset + dataOffset) & 0xFF; - } - } else if (typeof strData == "unknown") { - dataLength = iDataLength || IEBinary_getLength(data); - - this.getByteAt = function(iOffset) { - return IEBinary_getByteAt(data, iOffset + dataOffset); - } - } else { - throw new InvalidBinaryFile("Unsupported type " + (typeof strData)); - } - - this.getLength = function() { - return dataLength; - } - - this.getSByteAt = function(iOffset) { - var iByte = this.getByteAt(iOffset); - if (iByte > 127) - return iByte - 256; - else - return iByte; - } - - this.getShortAt = function(iOffset) { - var iShort = (this.getByteAt(iOffset + 1) << 8) + this.getByteAt(iOffset) - if (iShort < 0) iShort += 65536; - return iShort; - } - this.getSShortAt = function(iOffset) { - var iUShort = this.getShortAt(iOffset); - if (iUShort > 32767) - return iUShort - 65536; - else - return iUShort; - } - this.getLongAt = function(iOffset) { - var iByte1 = this.getByteAt(iOffset), - iByte2 = this.getByteAt(iOffset + 1), - iByte3 = this.getByteAt(iOffset + 2), - iByte4 = this.getByteAt(iOffset + 3); - - var iLong = (((((iByte4 << 8) + iByte3) << 8) + iByte2) << 8) + iByte1; - if (iLong < 0) iLong += 4294967296; - return iLong; - } - this.getSLongAt = function(iOffset) { - var iULong = this.getLongAt(iOffset); - if (iULong > 2147483647) - return iULong - 4294967296; - else - return iULong; - } - this.getStringAt = function(iOffset, iLength) { - var aStr = []; - for (var i=iOffset,j=0;i0);i++,j++) { - aStr[j] = String.fromCharCode(this.getByteAt(i)); - } - return aStr.join(""); - } - - // Added - this.getDoubleAt = function(iOffset) { - var iByte1 = this.getByteAt(iOffset), - iByte2 = this.getByteAt(iOffset + 1), - iByte3 = this.getByteAt(iOffset + 2), - iByte4 = this.getByteAt(iOffset + 3), - iByte5 = this.getByteAt(iOffset + 4), - iByte6 = this.getByteAt(iOffset + 5), - iByte7 = this.getByteAt(iOffset + 6), - iByte8 = this.getByteAt(iOffset + 7); - var iSign=iByte8 >> 7; - var iExpRaw=((iByte8 & 0x7F)<< 4) + (iByte7 >> 4); - var iMantHi=((((((iByte7 & 0x0F) << 8) + iByte6) << 8) + iByte5) << 8) + iByte4; - var iMantLo=((((iByte3) << 8) + iByte2) << 8) + iByte1; - - if (iExpRaw==0) return 0.0; - if (iExpRaw==0x7ff) return undefined; - - var iExp=(iExpRaw & 0x7FF)-1023; - - var dDouble = ((iSign==1)?-1:1)*Math.pow(2,iExp)*(1.0 + iMantLo*doubleMantExpLo + iMantHi*doubleMantExpHi); - return dDouble; - } - // added - // Extracts only 4 bytes out of 8, loosing in precision (20 bit mantissa) - this.getFastDoubleAt = function(iOffset) { - var iByte5 = this.getByteAt(iOffset + 4), - iByte6 = this.getByteAt(iOffset + 5), - iByte7 = this.getByteAt(iOffset + 6), - iByte8 = this.getByteAt(iOffset + 7); - var iSign=iByte8 >> 7; - var iExpRaw=((iByte8 & 0x7F)<< 4) + (iByte7 >> 4); - var iMant=((((iByte7 & 0x0F) << 8) + iByte6) << 8) + iByte5; - - if (iExpRaw==0) return 0.0; - if (iExpRaw==0x7ff) return undefined; - - var iExp=(iExpRaw & 0x7FF)-1023; - - var dDouble = ((iSign==1)?-1:1)*Math.pow(2,iExp)*(1.0 + iMant*doubleMantExpFast); - return dDouble; - } - - this.getCharAt = function(iOffset) { - return String.fromCharCode(this.getByteAt(iOffset)); - } -} - - -document.write( - "\r\n" -); - - - -// =============================================================== -// Load a binary file from the specified URL -// Will return an object of type BinaryFile -function FetchBinaryURL(url) { - var request = new XMLHttpRequest(); - request.open("GET", url,false); - try { - request.overrideMimeType('text/plain; charset=x-user-defined'); - } catch (err) { - // ignore any error, just to make both FF and IE work - } - request.send(null); - - var response=request.responseBody; - if (response==undefined){ // responseBody is non standard, but the only way to make it work in IE - response=request.responseText; - } - var bf=new BinaryFile(response); - return bf; -} - - -// =============================================================== -// Asyncronously load a binary file from the specified URL -// -// callback must be a function with one or two arguments: -// - bf = an object of type BinaryFile -// - optional argument object (used only if callback_arg not undefined) -function FetchBinaryURLAsync(url, callback, callback_arg) { - var callback_wrapper = function() { - if(this.readyState == 4) { - var response=this.responseBody; - if (response==undefined){ // responseBody is non standard, but the only way to make it work in IE - response=this.responseText; - } - var bf=new BinaryFile(response); - if (callback_arg!=null) { - callback(bf,callback_arg); - } else { - callback(bf); - } - } - } - - var request = new XMLHttpRequest(); - request.onreadystatechange = callback_wrapper; - request.open("GET", url,true); - try { - request.overrideMimeType('text/plain; charset=x-user-defined'); - } catch (err) { - // ignore any error, just to make both FF and IE work - } - request.send(null); - return request -} + +/* + * BinaryFile over XMLHttpRequest + * Part of the javascriptRRD package + * Copyright (c) 2009 Frank Wuerthwein, fkw@ucsd.edu + * MIT License [http://www.opensource.org/licenses/mit-license.php] + * + * Original repository: http://javascriptrrd.sourceforge.net/ + * + * Based on: + * Binary Ajax 0.1.5 + * Copyright (c) 2008 Jacob Seidelin, cupboy@gmail.com, http://blog.nihilogic.dk/ + * MIT License [http://www.opensource.org/licenses/mit-license.php] + */ + +// ============================================================ +// Exception class +function InvalidBinaryFile(msg) { + this.message=msg; + this.name="Invalid BinaryFile"; +} + +// pretty print +InvalidBinaryFile.prototype.toString = function() { + return this.name + ': "' + this.message + '"'; +} + +// ===================================================================== +// BinaryFile class +// Allows access to element inside a binary stream +function BinaryFile(strData, iDataOffset, iDataLength) { + var data = strData; + var dataOffset = iDataOffset || 0; + var dataLength = 0; + // added + var doubleMantExpHi=Math.pow(2,-28); + var doubleMantExpLo=Math.pow(2,-52); + var doubleMantExpFast=Math.pow(2,-20); + + this.getRawData = function() { + return data; + } + + if (typeof strData == "string") { + dataLength = iDataLength || data.length; + + this.getByteAt = function(iOffset) { + return data.charCodeAt(iOffset + dataOffset) & 0xFF; + } + } else if (typeof strData == "unknown") { + dataLength = iDataLength || IEBinary_getLength(data); + + this.getByteAt = function(iOffset) { + return IEBinary_getByteAt(data, iOffset + dataOffset); + } + } else { + throw new InvalidBinaryFile("Unsupported type " + (typeof strData)); + } + + this.getLength = function() { + return dataLength; + } + + this.getSByteAt = function(iOffset) { + var iByte = this.getByteAt(iOffset); + if (iByte > 127) + return iByte - 256; + else + return iByte; + } + + this.getShortAt = function(iOffset) { + var iShort = (this.getByteAt(iOffset + 1) << 8) + this.getByteAt(iOffset) + if (iShort < 0) iShort += 65536; + return iShort; + } + this.getSShortAt = function(iOffset) { + var iUShort = this.getShortAt(iOffset); + if (iUShort > 32767) + return iUShort - 65536; + else + return iUShort; + } + this.getLongAt = function(iOffset) { + var iByte1 = this.getByteAt(iOffset), + iByte2 = this.getByteAt(iOffset + 1), + iByte3 = this.getByteAt(iOffset + 2), + iByte4 = this.getByteAt(iOffset + 3); + + var iLong = (((((iByte4 << 8) + iByte3) << 8) + iByte2) << 8) + iByte1; + if (iLong < 0) iLong += 4294967296; + return iLong; + } + this.getSLongAt = function(iOffset) { + var iULong = this.getLongAt(iOffset); + if (iULong > 2147483647) + return iULong - 4294967296; + else + return iULong; + } + this.getStringAt = function(iOffset, iLength) { + var aStr = []; + for (var i=iOffset,j=0;i0);i++,j++) { + aStr[j] = String.fromCharCode(this.getByteAt(i)); + } + return aStr.join(""); + } + + // Added + this.getDoubleAt = function(iOffset) { + var iByte1 = this.getByteAt(iOffset), + iByte2 = this.getByteAt(iOffset + 1), + iByte3 = this.getByteAt(iOffset + 2), + iByte4 = this.getByteAt(iOffset + 3), + iByte5 = this.getByteAt(iOffset + 4), + iByte6 = this.getByteAt(iOffset + 5), + iByte7 = this.getByteAt(iOffset + 6), + iByte8 = this.getByteAt(iOffset + 7); + var iSign=iByte8 >> 7; + var iExpRaw=((iByte8 & 0x7F)<< 4) + (iByte7 >> 4); + var iMantHi=((((((iByte7 & 0x0F) << 8) + iByte6) << 8) + iByte5) << 8) + iByte4; + var iMantLo=((((iByte3) << 8) + iByte2) << 8) + iByte1; + + if (iExpRaw==0) return 0.0; + if (iExpRaw==0x7ff) return undefined; + + var iExp=(iExpRaw & 0x7FF)-1023; + + var dDouble = ((iSign==1)?-1:1)*Math.pow(2,iExp)*(1.0 + iMantLo*doubleMantExpLo + iMantHi*doubleMantExpHi); + return dDouble; + } + // added + // Extracts only 4 bytes out of 8, loosing in precision (20 bit mantissa) + this.getFastDoubleAt = function(iOffset) { + var iByte5 = this.getByteAt(iOffset + 4), + iByte6 = this.getByteAt(iOffset + 5), + iByte7 = this.getByteAt(iOffset + 6), + iByte8 = this.getByteAt(iOffset + 7); + var iSign=iByte8 >> 7; + var iExpRaw=((iByte8 & 0x7F)<< 4) + (iByte7 >> 4); + var iMant=((((iByte7 & 0x0F) << 8) + iByte6) << 8) + iByte5; + + if (iExpRaw==0) return 0.0; + if (iExpRaw==0x7ff) return undefined; + + var iExp=(iExpRaw & 0x7FF)-1023; + + var dDouble = ((iSign==1)?-1:1)*Math.pow(2,iExp)*(1.0 + iMant*doubleMantExpFast); + return dDouble; + } + + this.getCharAt = function(iOffset) { + return String.fromCharCode(this.getByteAt(iOffset)); + } +} + + +document.write( + "\r\n" +); + + + +// =============================================================== +// Load a binary file from the specified URL +// Will return an object of type BinaryFile +function FetchBinaryURL(url) { + var request = new XMLHttpRequest(); + request.open("GET", url,false); + try { + request.overrideMimeType('text/plain; charset=x-user-defined'); + } catch (err) { + // ignore any error, just to make both FF and IE work + } + request.send(null); + + var response=request.responseBody; + if (response==undefined){ // responseBody is non standard, but the only way to make it work in IE + response=request.responseText; + } + var bf=new BinaryFile(response); + return bf; +} + + +// =============================================================== +// Asyncronously load a binary file from the specified URL +// +// callback must be a function with one or two arguments: +// - bf = an object of type BinaryFile +// - optional argument object (used only if callback_arg not undefined) +function FetchBinaryURLAsync(url, callback, callback_arg) { + var callback_wrapper = function() { + if(this.readyState == 4) { + var response=this.responseBody; + if (response==undefined){ // responseBody is non standard, but the only way to make it work in IE + response=this.responseText; + } + var bf=new BinaryFile(response); + if (callback_arg!=null) { + callback(bf,callback_arg); + } else { + callback(bf); + } + } + } + + var request = new XMLHttpRequest(); + request.onreadystatechange = callback_wrapper; + request.open("GET", url,true); + try { + request.overrideMimeType('text/plain; charset=x-user-defined'); + } catch (err) { + // ignore any error, just to make both FF and IE work + } + request.send(null); + return request +} diff --git a/rpimonitor/web/js/javascriptrrd/rrdFile.js b/rpimonitor/web/js/javascriptrrd/rrdFile.js index 81f7731..90142e4 100644 --- a/rpimonitor/web/js/javascriptrrd/rrdFile.js +++ b/rpimonitor/web/js/javascriptrrd/rrdFile.js @@ -1,432 +1,432 @@ -/* - * Client library for access to RRD archive files - * Part of the javascriptRRD package - * Copyright (c) 2009-2010 Frank Wuerthwein, fkw@ucsd.edu - * Igor Sfiligoi, isfiligoi@ucsd.edu - * - * Original repository: http://javascriptrrd.sourceforge.net/ - * - * MIT License [http://www.opensource.org/licenses/mit-license.php] - * - */ - -/* - * - * RRDTool has been developed and is maintained by - * Tobias Oether [http://oss.oetiker.ch/rrdtool/] - * - * This software can be used to read files produced by the RRDTool - * but has been developed independently. - * - * Limitations: - * - * This version of the module assumes RRD files created on linux - * with intel architecture and supports both 32 and 64 bit CPUs. - * All integers in RRD files are suppoes to fit in 32bit values. - * - * Only versions 3 and 4 of the RRD archive are supported. - * - * Only AVERAGE,MAXIMUM,MINIMUM and LAST consolidation functions are - * supported. For all others, the behaviour is at the moment undefined. - * - */ - -/* - * Dependencies: - * - * The data provided to this module require an object of a class - * that implements the following methods: - * getByteAt(idx) - Return a 8 bit unsigned integer at offset idx - * getLongAt(idx) - Return a 32 bit unsigned integer at offset idx - * getDoubleAt(idx) - Return a double float at offset idx - * getFastDoubleAt(idx) - Similar to getDoubleAt but with less precision - * getCStringAt(idx,maxsize) - Return a string of at most maxsize characters - * that was 0-terminated in the source - * - * The BinaryFile from binaryXHR.js implements this interface. - * - */ - - -// ============================================================ -// Exception class -function InvalidRRD(msg) { - this.message=msg; - this.name="Invalid RRD"; -} - -// pretty print -InvalidRRD.prototype.toString = function() { - return this.name + ': "' + this.message + '"'; -} - - -// ============================================================ -// RRD DS Info class -function RRDDS(rrd_data,rrd_data_idx,my_idx) { - this.rrd_data=rrd_data; - this.rrd_data_idx=rrd_data_idx; - this.my_idx=my_idx; -} - -RRDDS.prototype.getIdx = function() { - return this.my_idx; -} -RRDDS.prototype.getName = function() { - return this.rrd_data.getCStringAt(this.rrd_data_idx,20); -} -RRDDS.prototype.getType = function() { - return this.rrd_data.getCStringAt(this.rrd_data_idx+20,20); -} -RRDDS.prototype.getMin = function() { - return this.rrd_data.getDoubleAt(this.rrd_data_idx+48); -} -RRDDS.prototype.getMax = function() { - return this.rrd_data.getDoubleAt(this.rrd_data_idx+56); -} - - -// ============================================================ -// RRD RRA Info class -function RRDRRAInfo(rrd_data,rra_def_idx, - int_align,row_cnt,pdp_step,my_idx) { - this.rrd_data=rrd_data; - this.rra_def_idx=rra_def_idx; - this.int_align=int_align; - this.row_cnt=row_cnt; - this.pdp_step=pdp_step; - this.my_idx=my_idx; - - // char nam[20], uint row_cnt, uint pdp_cnt - this.rra_pdp_cnt_idx=rra_def_idx+Math.ceil(20/int_align)*int_align+int_align; -} - -RRDRRAInfo.prototype.getIdx = function() { - return this.my_idx; -} - -// Get number of rows -RRDRRAInfo.prototype.getNrRows = function() { - return this.row_cnt; -} - -// Get number of slots used for consolidation -// Mostly for internal use -RRDRRAInfo.prototype.getPdpPerRow = function() { - return this.rrd_data.getLongAt(this.rra_pdp_cnt_idx); -} - -// Get RRA step (expressed in seconds) -RRDRRAInfo.prototype.getStep = function() { - return this.pdp_step*this.getPdpPerRow(); -} - -// Get consolidation function name -RRDRRAInfo.prototype.getCFName = function() { - return this.rrd_data.getCStringAt(this.rra_def_idx,20); -} - - -// ============================================================ -// RRD RRA handling class -function RRDRRA(rrd_data,rra_ptr_idx, - rra_info, - header_size,prev_row_cnts,ds_cnt) { - this.rrd_data=rrd_data; - this.rra_info=rra_info; - this.row_cnt=rra_info.row_cnt; - this.ds_cnt=ds_cnt; - - var row_size=ds_cnt*8; - - this.base_rrd_db_idx=header_size+prev_row_cnts*row_size; - - // get imediately, since it will be needed often - this.cur_row=rrd_data.getLongAt(rra_ptr_idx); - - // calculate idx relative to base_rrd_db_idx - // mostly used internally - this.calc_idx = function(row_idx,ds_idx) { - if ((row_idx>=0) && (row_idx=0) && (ds_idx=this.row_cnt) real_row_idx-=this.row_cnt; - return row_size*real_row_idx+ds_idx*8; - } else { - throw RangeError("DS idx ("+ row_idx +") out of range [0-" + ds_cnt +")."); - } - } else { - throw RangeError("Row idx ("+ row_idx +") out of range [0-" + this.row_cnt +")."); - } - } -} - -RRDRRA.prototype.getIdx = function() { - return this.rra_info.getIdx(); -} - -// Get number of rows/columns -RRDRRA.prototype.getNrRows = function() { - return this.row_cnt; -} -RRDRRA.prototype.getNrDSs = function() { - return this.ds_cnt; -} - -// Get RRA step (expressed in seconds) -RRDRRA.prototype.getStep = function() { - return this.rra_info.getStep(); -} - -// Get consolidation function name -RRDRRA.prototype.getCFName = function() { - return this.rra_info.getCFName(); -} - -RRDRRA.prototype.getEl = function(row_idx,ds_idx) { - return this.rrd_data.getDoubleAt(this.base_rrd_db_idx+this.calc_idx(row_idx,ds_idx)); -} - -// Low precision version of getEl -// Uses getFastDoubleAt -RRDRRA.prototype.getElFast = function(row_idx,ds_idx) { - return this.rrd_data.getFastDoubleAt(this.base_rrd_db_idx+this.calc_idx(row_idx,ds_idx)); -} - -// ============================================================ -// RRD Header handling class -function RRDHeader(rrd_data) { - this.rrd_data=rrd_data; - this.validate_rrd(); - this.calc_idxs(); -} - -// Internal, used for initialization -RRDHeader.prototype.validate_rrd = function() { - if (this.rrd_data.getLength()<1) throw new InvalidRRD("Empty file."); - if (this.rrd_data.getLength()<16) throw new InvalidRRD("File too short."); - if (this.rrd_data.getCStringAt(0,4)!=="RRD") throw new InvalidRRD("Wrong magic id."); - - this.rrd_version=this.rrd_data.getCStringAt(4,5); - if ((this.rrd_version!=="0003")&&(this.rrd_version!=="0004")&&(this.rrd_version!=="0001")) { - throw new InvalidRRD("Unsupported RRD version "+this.rrd_version+"."); - } - - this.float_width=8; - if (this.rrd_data.getLongAt(12)==0) { - // not a double here... likely 64 bit - this.float_align=8; - if (this.rrd_data.getDoubleAt(16)==8.642135e+130) { - // now, is it all 64bit or only float 64 bit? - if (this.rrd_data.getLongAt(28)==0) { - // true 64 bit align - this.int_align=8; - this.int_width=8; - } else { - // integers are 32bit aligned - this.int_align=4; - this.int_width=4; - } - } else { - throw new InvalidRRD("Magic float not found at 16."); - } - } else { - /// should be 32 bit alignment - if (this.rrd_data.getDoubleAt(12)==8.642135e+130) { - this.float_align=4; - this.int_align=4; - this.int_width=4; - } else { - throw new InvalidRRD("Magic float not found at 12."); - } - } - this.unival_width=this.float_width; - this.unival_align=this.float_align; - - // process the header here, since I need it for validation - - // char magic[4], char version[5], double magic_float - - // long ds_cnt, long rra_cnt, long pdp_step, unival par[10] - this.ds_cnt_idx=Math.ceil((4+5)/this.float_align)*this.float_align+this.float_width; - this.rra_cnt_idx=this.ds_cnt_idx+this.int_width; - this.pdp_step_idx=this.rra_cnt_idx+this.int_width; - - //always get only the low 32 bits, the high 32 on 64 bit archs should always be 0 - this.ds_cnt=this.rrd_data.getLongAt(this.ds_cnt_idx); - if (this.ds_cnt<1) { - throw new InvalidRRD("ds count less than 1."); - } - - this.rra_cnt=this.rrd_data.getLongAt(this.rra_cnt_idx); - if (this.ds_cnt<1) { - throw new InvalidRRD("rra count less than 1."); - } - - this.pdp_step=this.rrd_data.getLongAt(this.pdp_step_idx); - if (this.pdp_step<1) { - throw new InvalidRRD("pdp step less than 1."); - } - - // best guess, assuming no weird align problems - this.top_header_size=Math.ceil((this.pdp_step_idx+this.int_width)/this.unival_align)*this.unival_align+10*this.unival_width; - var t=this.rrd_data.getLongAt(this.top_header_size); - if (t==0) { - throw new InvalidRRD("Could not find first DS name."); - } -} - -// Internal, used for initialization -RRDHeader.prototype.calc_idxs = function() { - this.ds_def_idx=this.top_header_size; - // char ds_nam[20], char dst[20], unival par[10] - this.ds_el_size=Math.ceil((20+20)/this.unival_align)*this.unival_align+10*this.unival_width; - - this.rra_def_idx=this.ds_def_idx+this.ds_el_size*this.ds_cnt; - // char cf_nam[20], uint row_cnt, uint pdp_cnt, unival par[10] - this.row_cnt_idx=Math.ceil(20/this.int_align)*this.int_align; - this.rra_def_el_size=Math.ceil((this.row_cnt_idx+2*this.int_width)/this.unival_align)*this.unival_align+10*this.unival_width; - - this.live_head_idx=this.rra_def_idx+this.rra_def_el_size*this.rra_cnt; - // time_t last_up, int last_up_usec - this.live_head_size=2*this.int_width; - - this.pdp_prep_idx=this.live_head_idx+this.live_head_size; - // char last_ds[30], unival scratch[10] - this.pdp_prep_el_size=Math.ceil(30/this.unival_align)*this.unival_align+10*this.unival_width; - - this.cdp_prep_idx=this.pdp_prep_idx+this.pdp_prep_el_size*this.ds_cnt; - // unival scratch[10] - this.cdp_prep_el_size=10*this.unival_width; - - this.rra_ptr_idx=this.cdp_prep_idx+this.cdp_prep_el_size*this.ds_cnt*this.rra_cnt; - // uint cur_row - this.rra_ptr_el_size=1*this.int_width; - - this.header_size=this.rra_ptr_idx+this.rra_ptr_el_size*this.rra_cnt; -} - -// Optional initialization -// Read and calculate row counts -RRDHeader.prototype.load_row_cnts = function() { - this.rra_def_row_cnts=[]; - this.rra_def_row_cnt_sums=[]; // how many rows before me - for (var i=0; i=0) && (idx=0) && (idx=0) && (row_idx=0) && (ds_idx=this.row_cnt) real_row_idx-=this.row_cnt; + return row_size*real_row_idx+ds_idx*8; + } else { + throw RangeError("DS idx ("+ row_idx +") out of range [0-" + ds_cnt +")."); + } + } else { + throw RangeError("Row idx ("+ row_idx +") out of range [0-" + this.row_cnt +")."); + } + } +} + +RRDRRA.prototype.getIdx = function() { + return this.rra_info.getIdx(); +} + +// Get number of rows/columns +RRDRRA.prototype.getNrRows = function() { + return this.row_cnt; +} +RRDRRA.prototype.getNrDSs = function() { + return this.ds_cnt; +} + +// Get RRA step (expressed in seconds) +RRDRRA.prototype.getStep = function() { + return this.rra_info.getStep(); +} + +// Get consolidation function name +RRDRRA.prototype.getCFName = function() { + return this.rra_info.getCFName(); +} + +RRDRRA.prototype.getEl = function(row_idx,ds_idx) { + return this.rrd_data.getDoubleAt(this.base_rrd_db_idx+this.calc_idx(row_idx,ds_idx)); +} + +// Low precision version of getEl +// Uses getFastDoubleAt +RRDRRA.prototype.getElFast = function(row_idx,ds_idx) { + return this.rrd_data.getFastDoubleAt(this.base_rrd_db_idx+this.calc_idx(row_idx,ds_idx)); +} + +// ============================================================ +// RRD Header handling class +function RRDHeader(rrd_data) { + this.rrd_data=rrd_data; + this.validate_rrd(); + this.calc_idxs(); +} + +// Internal, used for initialization +RRDHeader.prototype.validate_rrd = function() { + if (this.rrd_data.getLength()<1) throw new InvalidRRD("Empty file."); + if (this.rrd_data.getLength()<16) throw new InvalidRRD("File too short."); + if (this.rrd_data.getCStringAt(0,4)!=="RRD") throw new InvalidRRD("Wrong magic id."); + + this.rrd_version=this.rrd_data.getCStringAt(4,5); + if ((this.rrd_version!=="0003")&&(this.rrd_version!=="0004")&&(this.rrd_version!=="0001")) { + throw new InvalidRRD("Unsupported RRD version "+this.rrd_version+"."); + } + + this.float_width=8; + if (this.rrd_data.getLongAt(12)==0) { + // not a double here... likely 64 bit + this.float_align=8; + if (this.rrd_data.getDoubleAt(16)==8.642135e+130) { + // now, is it all 64bit or only float 64 bit? + if (this.rrd_data.getLongAt(28)==0) { + // true 64 bit align + this.int_align=8; + this.int_width=8; + } else { + // integers are 32bit aligned + this.int_align=4; + this.int_width=4; + } + } else { + throw new InvalidRRD("Magic float not found at 16."); + } + } else { + /// should be 32 bit alignment + if (this.rrd_data.getDoubleAt(12)==8.642135e+130) { + this.float_align=4; + this.int_align=4; + this.int_width=4; + } else { + throw new InvalidRRD("Magic float not found at 12."); + } + } + this.unival_width=this.float_width; + this.unival_align=this.float_align; + + // process the header here, since I need it for validation + + // char magic[4], char version[5], double magic_float + + // long ds_cnt, long rra_cnt, long pdp_step, unival par[10] + this.ds_cnt_idx=Math.ceil((4+5)/this.float_align)*this.float_align+this.float_width; + this.rra_cnt_idx=this.ds_cnt_idx+this.int_width; + this.pdp_step_idx=this.rra_cnt_idx+this.int_width; + + //always get only the low 32 bits, the high 32 on 64 bit archs should always be 0 + this.ds_cnt=this.rrd_data.getLongAt(this.ds_cnt_idx); + if (this.ds_cnt<1) { + throw new InvalidRRD("ds count less than 1."); + } + + this.rra_cnt=this.rrd_data.getLongAt(this.rra_cnt_idx); + if (this.ds_cnt<1) { + throw new InvalidRRD("rra count less than 1."); + } + + this.pdp_step=this.rrd_data.getLongAt(this.pdp_step_idx); + if (this.pdp_step<1) { + throw new InvalidRRD("pdp step less than 1."); + } + + // best guess, assuming no weird align problems + this.top_header_size=Math.ceil((this.pdp_step_idx+this.int_width)/this.unival_align)*this.unival_align+10*this.unival_width; + var t=this.rrd_data.getLongAt(this.top_header_size); + if (t==0) { + throw new InvalidRRD("Could not find first DS name."); + } +} + +// Internal, used for initialization +RRDHeader.prototype.calc_idxs = function() { + this.ds_def_idx=this.top_header_size; + // char ds_nam[20], char dst[20], unival par[10] + this.ds_el_size=Math.ceil((20+20)/this.unival_align)*this.unival_align+10*this.unival_width; + + this.rra_def_idx=this.ds_def_idx+this.ds_el_size*this.ds_cnt; + // char cf_nam[20], uint row_cnt, uint pdp_cnt, unival par[10] + this.row_cnt_idx=Math.ceil(20/this.int_align)*this.int_align; + this.rra_def_el_size=Math.ceil((this.row_cnt_idx+2*this.int_width)/this.unival_align)*this.unival_align+10*this.unival_width; + + this.live_head_idx=this.rra_def_idx+this.rra_def_el_size*this.rra_cnt; + // time_t last_up, int last_up_usec + this.live_head_size=2*this.int_width; + + this.pdp_prep_idx=this.live_head_idx+this.live_head_size; + // char last_ds[30], unival scratch[10] + this.pdp_prep_el_size=Math.ceil(30/this.unival_align)*this.unival_align+10*this.unival_width; + + this.cdp_prep_idx=this.pdp_prep_idx+this.pdp_prep_el_size*this.ds_cnt; + // unival scratch[10] + this.cdp_prep_el_size=10*this.unival_width; + + this.rra_ptr_idx=this.cdp_prep_idx+this.cdp_prep_el_size*this.ds_cnt*this.rra_cnt; + // uint cur_row + this.rra_ptr_el_size=1*this.int_width; + + this.header_size=this.rra_ptr_idx+this.rra_ptr_el_size*this.rra_cnt; +} + +// Optional initialization +// Read and calculate row counts +RRDHeader.prototype.load_row_cnts = function() { + this.rra_def_row_cnts=[]; + this.rra_def_row_cnt_sums=[]; // how many rows before me + for (var i=0; i=0) && (idx=0) && (idx%s Value: %y.3" } - * } - * - * ds_graph_options is a dictionary of DS_name, - * with each element being a graph_option - * The defaults for each element are - * { - * title: label or ds_name // this is what is displayed in the checkboxes - * checked: first_ds_in_list? // boolean - * label: title or ds_name // this is what is displayed in the legend - * color: ds_index // see Flot docs for details - * lines: { show:true } // see Flot docs for details - * yaxis: 1 // can be 1 or 2 - * stack: 'none' // other options are 'positive' and 'negative' - * } - * - * //overwrites other defaults; mostly used for linking via the URL - * rrdflot_defaults defaults (see Flot docs for details) - * { - * graph_only: false // If true, limit the display to the graph only - * legend: "Top" //Starting location of legend. Options are: - * // "Top","Bottom","TopRight","BottomRight","None". - * num_cb_rows: 12 //How many rows of DS checkboxes per column. - * use_elem_buttons: false //To be used in conjunction with num_cb_rows: This option - * // creates a button above every column, which selects - * // every element in the column. - * multi_ds: false //"true" appends the name of the aggregation function to the - * // name of the DS element. - * multi_rra: false //"true" appends the name of the RRA consolidation function (CF) - * // (AVERAGE, MIN, MAX or LAST) to the name of the RRA. Useful - * // for RRAs over the same interval with different CFs. - * use_checked_DSs: false //Use the list checked_DSs below. - * checked_DSs: [] //List of elements to be checked by default when graph is loaded. - * // Overwrites graph options. - * use_rra: false //Whether to use the rra index specified below. - * rra: 0 //RRA (rra index in rrd) to be selected when graph is loaded. - * use_windows: false //Whether to use the window zoom specifications below. - * window_min: 0 //Sets minimum for window zoom. X-axis usually in unix time. - * window_max: 0 //Sets maximum for window zoom. - * graph_height: "300px" //Height of main graph. - * graph_width: "500px" //Width of main graph. - * scale_height: "110px" //Height of small scaler graph. - * scale_width: "250px" //Width of small scaler graph. - * timezone: local //timezone. - * } - */ - -var local_checked_DSs = []; -var selected_rra = 0; -var window_min=0; -var window_max=0; -var elem_group=null; -var timezone_shift=0; - -function rrdFlot(html_id, rrd_file, graph_options, ds_graph_options, rrdflot_defaults) { - this.html_id=html_id; - this.rrd_file=rrd_file; - this.graph_options=graph_options; - if (rrdflot_defaults==null) { - this.rrdflot_defaults=new Object(); // empty object, just not to be null - } else { - this.rrdflot_defaults=rrdflot_defaults; - } - if (ds_graph_options==null) { - this.ds_graph_options=new Object(); // empty object, just not to be null - } else { - this.ds_graph_options=ds_graph_options; - } - this.selection_range=new rrdFlotSelection(); - - graph_info={}; - this.createHTML(); - this.populateRes(); - this.populateDScb(); - this.drawFlotGraph(); - - if (this.rrdflot_defaults.graph_only==true) { - this.cleanHTMLCruft(); - } -} - - -// =============================================== -// Create the HTML tags needed to host the graphs -rrdFlot.prototype.createHTML = function() { - var rf_this=this; // use obj inside other functions - - var base_el=document.getElementById(this.html_id); - - this.res_id=this.html_id+"_res"; - this.ds_cb_id=this.html_id+"_ds_cb"; - this.graph_id=this.html_id+"_graph"; - this.res_title_id=this.html_id+"_res_title"; - this.scale_id=this.html_id+"_scale"; - this.legend_sel_id=this.html_id+"_legend_sel"; - this.time_sel_id=this.html_id+"_time_sel"; - this.elem_group_id=this.html_id+"_elem_group"; - - // First clean up anything in the element - while (base_el.lastChild!=null) base_el.removeChild(base_el.lastChild); - - // Now create the layout - var external_table=document.createElement("Table"); - this.external_table=external_table; - - // Header two: resulution select and DS selection title - var rowHeader=external_table.insertRow(-1); - var cellRes=rowHeader.insertCell(-1); - cellRes.colSpan=3; - // Insert "Resolution:" into a span to allow dynamic modification - var resTitleSpan=document.createElement('span'); - resTitleSpan.id=this.res_title_id; - resTitleSpan.appendChild(document.createTextNode("Resolution:")); - cellRes.appendChild(resTitleSpan); - //cellRes.appendChild(document.createTextNode("Resolution:")); - var forRes=document.createElement("Select"); - forRes.id=this.res_id; - //forRes.onChange= this.callback_res_changed; - forRes.onchange= function () {rf_this.callback_res_changed();}; - cellRes.appendChild(forRes); - - var cellDSTitle=rowHeader.insertCell(-1); - cellDSTitle.appendChild(document.createTextNode("Select elements to plot:")); - - // Graph row: main graph and DS selection block - var rowGraph=external_table.insertRow(-1); - var cellGraph=rowGraph.insertCell(-1); - cellGraph.colSpan=3; - var elGraph=document.createElement("Div"); - if(this.rrdflot_defaults.graph_width!=null) { - elGraph.style.width=this.rrdflot_defaults.graph_width; - } else {elGraph.style.width="500px";} - if(this.rrdflot_defaults.graph_height!=null) { - elGraph.style.height=this.rrdflot_defaults.graph_height; - } else {elGraph.style.height="300px";} - elGraph.id=this.graph_id; - cellGraph.appendChild(elGraph); - - var cellDScb=rowGraph.insertCell(-1); - - - cellDScb.vAlign="top"; - var formDScb=document.createElement("Form"); - formDScb.id=this.ds_cb_id; - formDScb.onchange= function () {rf_this.callback_ds_cb_changed();}; - cellDScb.appendChild(formDScb); - - // Scale row: scaled down selection graph - var rowScale=external_table.insertRow(-1); - - var cellScaleLegend=rowScale.insertCell(-1); - cellScaleLegend.id='Legend'; - cellScaleLegend.vAlign="top"; - cellScaleLegend.appendChild(document.createTextNode("Legend:")); - cellScaleLegend.appendChild(document.createElement('br')); - - var forScaleLegend=document.createElement("Select"); - forScaleLegend.id=this.legend_sel_id; - forScaleLegend.appendChild(new Option("Top","nw",this.rrdflot_defaults.legend=="Top",this.rrdflot_defaults.legend=="Top")); - forScaleLegend.appendChild(new Option("Bottom","sw",this.rrdflot_defaults.legend=="Bottom",this.rrdflot_defaults.legend=="Bottom")); - forScaleLegend.appendChild(new Option("TopRight","ne",this.rrdflot_defaults.legend=="TopRight",this.rrdflot_defaults.legend=="TopRight")); - forScaleLegend.appendChild(new Option("BottomRight","se",this.rrdflot_defaults.legend=="BottomRight",this.rrdflot_defaults.legend=="BottomRight")); - forScaleLegend.appendChild(new Option("None","None",this.rrdflot_defaults.legend=="None",this.rrdflot_defaults.legend=="None")); - forScaleLegend.onchange= function () {rf_this.callback_legend_changed();}; - cellScaleLegend.appendChild(forScaleLegend); - - - cellScaleLegend.appendChild(document.createElement('br')); - cellScaleLegend.appendChild(document.createTextNode("Timezone:")); - cellScaleLegend.appendChild(document.createElement('br')); - - var timezone=document.createElement("Select"); - timezone.id=this.time_sel_id; - - var timezones = ["+12","+11","+10","+9","+8","+7","+6","+5","+4","+3","+2","+1","0", - "-1","-2","-3","-4","-5","-6","-7","-8","-9","-10","-11","-12"]; - var tz_found=false; - var true_tz; - for(var j=0; j<24; j++) { - if (Math.ceil(this.rrdflot_defaults.timezone)==Math.ceil(timezones[j])) { - tz_found=true; - true_tz=Math.ceil(this.rrdflot_defaults.timezone); - break; - } - } - if (!tz_found) { - // the passed timezone does not make sense - // find the local time - var d= new Date(); - true_tz=-Math.ceil(d.getTimezoneOffset()/60); - } - for(var j=0; j<24; j++) { - timezone.appendChild(new Option(timezones[j],timezones[j],true_tz==Math.ceil(timezones[j]),true_tz==Math.ceil(timezones[j]))); - } - timezone.onchange= function () {rf_this.callback_timezone_changed();}; - - cellScaleLegend.appendChild(timezone); - - var cellScale=rowScale.insertCell(-1); - cellScale.align="right"; - var elScale=document.createElement("Div"); - if(this.rrdflot_defaults.scale_width!=null) { - elScale.style.width=this.rrdflot_defaults.scale_width; - } else {elScale.style.width="250px";} - if(this.rrdflot_defaults.scale_height!=null) { - elScale.style.height=this.rrdflot_defaults.scale_height; - } else {elScale.style.height="110px";} - elScale.id=this.scale_id; - cellScale.appendChild(elScale); - - var cellScaleReset=rowScale.insertCell(-1); - cellScaleReset.vAlign="top"; - cellScaleReset.appendChild(document.createTextNode(" ")); - cellScaleReset.appendChild(document.createElement('br')); - var elScaleReset=document.createElement("input"); - elScaleReset.type = "button"; - elScaleReset.value = "Reset selection"; - elScaleReset.onclick = function () {rf_this.callback_scale_reset();} - - cellScaleReset.appendChild(elScaleReset); - - base_el.appendChild(external_table); -}; - -// =============================================== -// Remove all HTMl elements but the graph -rrdFlot.prototype.cleanHTMLCruft = function() { - var rf_this=this; // use obj inside other functions - - // delete top and bottom rows... graph is in the middle - this.external_table.deleteRow(-1); - this.external_table.deleteRow(0); - - var ds_el=document.getElementById(this.ds_cb_id); - ds_el.removeChild(ds_el.lastChild); -} - -// ====================================== -// Populate RRA and RD info -rrdFlot.prototype.populateRes = function() { - var form_el=document.getElementById(this.res_id); - - // First clean up anything in the element - while (form_el.lastChild!=null) form_el.removeChild(form_el.lastChild); - - // now populate with RRA info - var nrRRAs=this.rrd_file.getNrRRAs(); - for (var i=0; ithis.rrdflot_defaults.num_cb_rows) { //if only one column, no need for a button - elem_group_number = (i/this.rrdflot_defaults.num_cb_rows)+1; - var elGroupSelect = document.createElement("input"); - elGroupSelect.type = "button"; - elGroupSelect.value = "Group "+elem_group_number; - elGroupSelect.onclick = (function(e) { //lambda function!! - return function() {rf_this.callback_elem_group_changed(e);};})(elem_group_number); - - cell_el.appendChild(elGroupSelect); - cell_el.appendChild(document.createElement('br')); //add space between the two - } - } else { - //just make next element column - cell_el=row_el.insertCell(-1); - } - } - var ds=this.rrd_file.getDS(i); - if (this.rrdflot_defaults.multi_ds) { //null==false in boolean ops - var name=ds.getName()+"-"+ds.getType(); - var name2=ds.getName(); - } - else {var name=ds.getName(); var name2=ds.getName();} - var title=name; - if(this.rrdflot_defaults.use_checked_DSs) { - if(this.rrdflot_defaults.checked_DSs.length==0) { - var checked=(i==0); // only first checked by default - } else{checked=false;} - } else {var checked=(i==0);} - if (this.ds_graph_options[name]!=null) { - var dgo=this.ds_graph_options[name]; - if (dgo['title']!=null) { - // if the user provided the title, use it - title=dgo['title']; - } else if (dgo['label']!=null) { - // use label as a second choiceit - title=dgo['label']; - } // else leave the ds name - if(this.rrdflot_defaults.use_checked_DSs) { - if(this.rrdflot_defaults.checked_DSs.length==0) { - // if the user provided the title, use it - checked=dgo['checked']; - } - } else { - if (dgo['checked']!=null) { - checked=dgo['checked']; - } - } - } - if(this.rrdflot_defaults.use_checked_DSs) { - if(this.rrdflot_defaults.checked_DSs==null) {continue;} - for(var j=0;j0) { - for (var i=0; i%s Value: %y.3" }, - grid: { hoverable: true }, - }; - - if (legend_id=="None") { - // do nothing - } else { - graph_options.legend.show=true; - graph_options.legend.position=legend_id; - } - - if (this.graph_options!=null) { - graph_options=populateGraphOptions(graph_options,this.graph_options); - } - - if (graph_options.tooltip==false) { - // avoid the need for the caller specify both - graph_options.grid.hoverable=false; - } - - if (this.selection_range.isSet()) { - var selection_range=this.selection_range.getFlotRanges(); - if(this.rrdflot_defaults.use_windows) { - graph_options.xaxis.min = this.rrdflot_defaults.window_min; - graph_options.xaxis.max = this.rrdflot_defaults.window_max; - } else { - graph_options.xaxis.min=selection_range.xaxis.from; - graph_options.xaxis.max=selection_range.xaxis.to; - } - } else if(this.rrdflot_defaults.use_windows) { - graph_options.xaxis.min = this.rrdflot_defaults.window_min; - graph_options.xaxis.max = this.rrdflot_defaults.window_max; - } else { - graph_options.xaxis.min=flot_obj.min; - graph_options.xaxis.max=flot_obj.max; - } - - var scale_options = { - legend: {show:false}, - lines: {show:true}, - xaxis: {mode: "time", min:flot_obj.min, max:flot_obj.max }, - yaxis: graph_options.yaxis, - selection: { mode: "x" }, - }; - - //this.selection_range.selection_min=flot_obj.min; - //this.selection_range.selection_max=flot_obj.max; - - var flot_data=flot_obj.data; - var graph_data=this.selection_range.trim_flot_data(flot_data); - var scale_data=flot_data; - - this.graph = $.plot($(graph_jq_id), graph_data, graph_options); - this.scale = $.plot($(scale_jq_id), scale_data, scale_options); - - - if(this.rrdflot_defaults.use_windows) { - ranges = {}; - ranges.xaxis = []; - ranges.xaxis.from = this.rrdflot_defaults.window_min; - ranges.xaxis.to = this.rrdflot_defaults.window_max; - rf_this.scale.setSelection(ranges,true); - window_min = ranges.xaxis.from; - window_max = ranges.xaxis.to; - } - - if (this.selection_range.isSet()) { - this.scale.setSelection(this.selection_range.getFlotRanges(),true); //don't fire event, no need - } - - // now connect the two - $(graph_jq_id).unbind("plotselected"); // but first remove old function - $(graph_jq_id).bind("plotselected", function (event, ranges) { - // do the zooming - rf_this.selection_range.setFromFlotRanges(ranges); - graph_options.xaxis.min=ranges.xaxis.from; - graph_options.xaxis.max=ranges.xaxis.to; - window_min = ranges.xaxis.from; - window_max = ranges.xaxis.to; - rf_this.graph = $.plot($(graph_jq_id), rf_this.selection_range.trim_flot_data(flot_data), graph_options); - - // don't fire event on the scale to prevent eternal loop - rf_this.scale.setSelection(ranges, true); //puts the transparent window on minigraph - }); - - $(scale_jq_id).unbind("plotselected"); //same here - $(scale_jq_id).bind("plotselected", function (event, ranges) { - rf_this.graph.setSelection(ranges); - }); - - // only the scale has a selection - // so when that is cleared, redraw also the graph - $(scale_jq_id).bind("plotunselected", function() { - rf_this.selection_range.reset(); - graph_options.xaxis.min=flot_obj.min; - graph_options.xaxis.max=flot_obj.max; - rf_this.graph = $.plot($(graph_jq_id), rf_this.selection_range.trim_flot_data(flot_data), graph_options); - window_min = 0; - window_max = 0; - }); -}; - -// callback functions that are called when one of the selections changes -rrdFlot.prototype.callback_res_changed = function() { - this.rrdflot_defaults.use_rra = false; - this.drawFlotGraph(); -}; - -rrdFlot.prototype.callback_ds_cb_changed = function() { - this.drawFlotGraph(); -}; - -rrdFlot.prototype.callback_scale_reset = function() { - this.scale.clearSelection(); -}; - -rrdFlot.prototype.callback_legend_changed = function() { - this.drawFlotGraph(); -}; - -rrdFlot.prototype.callback_timezone_changed = function() { - this.drawFlotGraph(); -}; - -rrdFlot.prototype.callback_elem_group_changed = function(num) { //,window_min,window_max) { - - var oCB=document.getElementById(this.ds_cb_id); - var nrDSs=oCB.ds.length; - if (oCB.ds.length>0) { - for (var i=0; i%s Value: %y.3" } + * } + * + * ds_graph_options is a dictionary of DS_name, + * with each element being a graph_option + * The defaults for each element are + * { + * title: label or ds_name // this is what is displayed in the checkboxes + * checked: first_ds_in_list? // boolean + * label: title or ds_name // this is what is displayed in the legend + * color: ds_index // see Flot docs for details + * lines: { show:true } // see Flot docs for details + * yaxis: 1 // can be 1 or 2 + * stack: 'none' // other options are 'positive' and 'negative' + * } + * + * //overwrites other defaults; mostly used for linking via the URL + * rrdflot_defaults defaults (see Flot docs for details) + * { + * graph_only: false // If true, limit the display to the graph only + * legend: "Top" //Starting location of legend. Options are: + * // "Top","Bottom","TopRight","BottomRight","None". + * num_cb_rows: 12 //How many rows of DS checkboxes per column. + * use_elem_buttons: false //To be used in conjunction with num_cb_rows: This option + * // creates a button above every column, which selects + * // every element in the column. + * multi_ds: false //"true" appends the name of the aggregation function to the + * // name of the DS element. + * multi_rra: false //"true" appends the name of the RRA consolidation function (CF) + * // (AVERAGE, MIN, MAX or LAST) to the name of the RRA. Useful + * // for RRAs over the same interval with different CFs. + * use_checked_DSs: false //Use the list checked_DSs below. + * checked_DSs: [] //List of elements to be checked by default when graph is loaded. + * // Overwrites graph options. + * use_rra: false //Whether to use the rra index specified below. + * rra: 0 //RRA (rra index in rrd) to be selected when graph is loaded. + * use_windows: false //Whether to use the window zoom specifications below. + * window_min: 0 //Sets minimum for window zoom. X-axis usually in unix time. + * window_max: 0 //Sets maximum for window zoom. + * graph_height: "300px" //Height of main graph. + * graph_width: "500px" //Width of main graph. + * scale_height: "110px" //Height of small scaler graph. + * scale_width: "250px" //Width of small scaler graph. + * timezone: local //timezone. + * } + */ + +var local_checked_DSs = []; +var selected_rra = 0; +var window_min=0; +var window_max=0; +var elem_group=null; +var timezone_shift=0; + +function rrdFlot(html_id, rrd_file, graph_options, ds_graph_options, rrdflot_defaults) { + this.html_id=html_id; + this.rrd_file=rrd_file; + this.graph_options=graph_options; + if (rrdflot_defaults==null) { + this.rrdflot_defaults=new Object(); // empty object, just not to be null + } else { + this.rrdflot_defaults=rrdflot_defaults; + } + if (ds_graph_options==null) { + this.ds_graph_options=new Object(); // empty object, just not to be null + } else { + this.ds_graph_options=ds_graph_options; + } + this.selection_range=new rrdFlotSelection(); + + graph_info={}; + this.createHTML(); + this.populateRes(); + this.populateDScb(); + this.drawFlotGraph(); + + if (this.rrdflot_defaults.graph_only==true) { + this.cleanHTMLCruft(); + } +} + + +// =============================================== +// Create the HTML tags needed to host the graphs +rrdFlot.prototype.createHTML = function() { + var rf_this=this; // use obj inside other functions + + var base_el=document.getElementById(this.html_id); + + this.res_id=this.html_id+"_res"; + this.ds_cb_id=this.html_id+"_ds_cb"; + this.graph_id=this.html_id+"_graph"; + this.res_title_id=this.html_id+"_res_title"; + this.scale_id=this.html_id+"_scale"; + this.legend_sel_id=this.html_id+"_legend_sel"; + this.time_sel_id=this.html_id+"_time_sel"; + this.elem_group_id=this.html_id+"_elem_group"; + + // First clean up anything in the element + while (base_el.lastChild!=null) base_el.removeChild(base_el.lastChild); + + // Now create the layout + var external_table=document.createElement("Table"); + this.external_table=external_table; + + // Header two: resulution select and DS selection title + var rowHeader=external_table.insertRow(-1); + var cellRes=rowHeader.insertCell(-1); + cellRes.colSpan=3; + // Insert "Resolution:" into a span to allow dynamic modification + var resTitleSpan=document.createElement('span'); + resTitleSpan.id=this.res_title_id; + resTitleSpan.appendChild(document.createTextNode("Resolution:")); + cellRes.appendChild(resTitleSpan); + //cellRes.appendChild(document.createTextNode("Resolution:")); + var forRes=document.createElement("Select"); + forRes.id=this.res_id; + //forRes.onChange= this.callback_res_changed; + forRes.onchange= function () {rf_this.callback_res_changed();}; + cellRes.appendChild(forRes); + + var cellDSTitle=rowHeader.insertCell(-1); + cellDSTitle.appendChild(document.createTextNode("Select elements to plot:")); + + // Graph row: main graph and DS selection block + var rowGraph=external_table.insertRow(-1); + var cellGraph=rowGraph.insertCell(-1); + cellGraph.colSpan=3; + var elGraph=document.createElement("Div"); + if(this.rrdflot_defaults.graph_width!=null) { + elGraph.style.width=this.rrdflot_defaults.graph_width; + } else {elGraph.style.width="500px";} + if(this.rrdflot_defaults.graph_height!=null) { + elGraph.style.height=this.rrdflot_defaults.graph_height; + } else {elGraph.style.height="300px";} + elGraph.id=this.graph_id; + cellGraph.appendChild(elGraph); + + var cellDScb=rowGraph.insertCell(-1); + + + cellDScb.vAlign="top"; + var formDScb=document.createElement("Form"); + formDScb.id=this.ds_cb_id; + formDScb.onchange= function () {rf_this.callback_ds_cb_changed();}; + cellDScb.appendChild(formDScb); + + // Scale row: scaled down selection graph + var rowScale=external_table.insertRow(-1); + + var cellScaleLegend=rowScale.insertCell(-1); + cellScaleLegend.id='Legend'; + cellScaleLegend.vAlign="top"; + cellScaleLegend.appendChild(document.createTextNode("Legend:")); + cellScaleLegend.appendChild(document.createElement('br')); + + var forScaleLegend=document.createElement("Select"); + forScaleLegend.id=this.legend_sel_id; + forScaleLegend.appendChild(new Option("Top","nw",this.rrdflot_defaults.legend=="Top",this.rrdflot_defaults.legend=="Top")); + forScaleLegend.appendChild(new Option("Bottom","sw",this.rrdflot_defaults.legend=="Bottom",this.rrdflot_defaults.legend=="Bottom")); + forScaleLegend.appendChild(new Option("TopRight","ne",this.rrdflot_defaults.legend=="TopRight",this.rrdflot_defaults.legend=="TopRight")); + forScaleLegend.appendChild(new Option("BottomRight","se",this.rrdflot_defaults.legend=="BottomRight",this.rrdflot_defaults.legend=="BottomRight")); + forScaleLegend.appendChild(new Option("None","None",this.rrdflot_defaults.legend=="None",this.rrdflot_defaults.legend=="None")); + forScaleLegend.onchange= function () {rf_this.callback_legend_changed();}; + cellScaleLegend.appendChild(forScaleLegend); + + + cellScaleLegend.appendChild(document.createElement('br')); + cellScaleLegend.appendChild(document.createTextNode("Timezone:")); + cellScaleLegend.appendChild(document.createElement('br')); + + var timezone=document.createElement("Select"); + timezone.id=this.time_sel_id; + + var timezones = ["+12","+11","+10","+9","+8","+7","+6","+5","+4","+3","+2","+1","0", + "-1","-2","-3","-4","-5","-6","-7","-8","-9","-10","-11","-12"]; + var tz_found=false; + var true_tz; + for(var j=0; j<24; j++) { + if (Math.ceil(this.rrdflot_defaults.timezone)==Math.ceil(timezones[j])) { + tz_found=true; + true_tz=Math.ceil(this.rrdflot_defaults.timezone); + break; + } + } + if (!tz_found) { + // the passed timezone does not make sense + // find the local time + var d= new Date(); + true_tz=-Math.ceil(d.getTimezoneOffset()/60); + } + for(var j=0; j<24; j++) { + timezone.appendChild(new Option(timezones[j],timezones[j],true_tz==Math.ceil(timezones[j]),true_tz==Math.ceil(timezones[j]))); + } + timezone.onchange= function () {rf_this.callback_timezone_changed();}; + + cellScaleLegend.appendChild(timezone); + + var cellScale=rowScale.insertCell(-1); + cellScale.align="right"; + var elScale=document.createElement("Div"); + if(this.rrdflot_defaults.scale_width!=null) { + elScale.style.width=this.rrdflot_defaults.scale_width; + } else {elScale.style.width="250px";} + if(this.rrdflot_defaults.scale_height!=null) { + elScale.style.height=this.rrdflot_defaults.scale_height; + } else {elScale.style.height="110px";} + elScale.id=this.scale_id; + cellScale.appendChild(elScale); + + var cellScaleReset=rowScale.insertCell(-1); + cellScaleReset.vAlign="top"; + cellScaleReset.appendChild(document.createTextNode(" ")); + cellScaleReset.appendChild(document.createElement('br')); + var elScaleReset=document.createElement("input"); + elScaleReset.type = "button"; + elScaleReset.value = "Reset selection"; + elScaleReset.onclick = function () {rf_this.callback_scale_reset();} + + cellScaleReset.appendChild(elScaleReset); + + base_el.appendChild(external_table); +}; + +// =============================================== +// Remove all HTMl elements but the graph +rrdFlot.prototype.cleanHTMLCruft = function() { + var rf_this=this; // use obj inside other functions + + // delete top and bottom rows... graph is in the middle + this.external_table.deleteRow(-1); + this.external_table.deleteRow(0); + + var ds_el=document.getElementById(this.ds_cb_id); + ds_el.removeChild(ds_el.lastChild); +} + +// ====================================== +// Populate RRA and RD info +rrdFlot.prototype.populateRes = function() { + var form_el=document.getElementById(this.res_id); + + // First clean up anything in the element + while (form_el.lastChild!=null) form_el.removeChild(form_el.lastChild); + + // now populate with RRA info + var nrRRAs=this.rrd_file.getNrRRAs(); + for (var i=0; ithis.rrdflot_defaults.num_cb_rows) { //if only one column, no need for a button + elem_group_number = (i/this.rrdflot_defaults.num_cb_rows)+1; + var elGroupSelect = document.createElement("input"); + elGroupSelect.type = "button"; + elGroupSelect.value = "Group "+elem_group_number; + elGroupSelect.onclick = (function(e) { //lambda function!! + return function() {rf_this.callback_elem_group_changed(e);};})(elem_group_number); + + cell_el.appendChild(elGroupSelect); + cell_el.appendChild(document.createElement('br')); //add space between the two + } + } else { + //just make next element column + cell_el=row_el.insertCell(-1); + } + } + var ds=this.rrd_file.getDS(i); + if (this.rrdflot_defaults.multi_ds) { //null==false in boolean ops + var name=ds.getName()+"-"+ds.getType(); + var name2=ds.getName(); + } + else {var name=ds.getName(); var name2=ds.getName();} + var title=name; + if(this.rrdflot_defaults.use_checked_DSs) { + if(this.rrdflot_defaults.checked_DSs.length==0) { + var checked=(i==0); // only first checked by default + } else{checked=false;} + } else {var checked=(i==0);} + if (this.ds_graph_options[name]!=null) { + var dgo=this.ds_graph_options[name]; + if (dgo['title']!=null) { + // if the user provided the title, use it + title=dgo['title']; + } else if (dgo['label']!=null) { + // use label as a second choiceit + title=dgo['label']; + } // else leave the ds name + if(this.rrdflot_defaults.use_checked_DSs) { + if(this.rrdflot_defaults.checked_DSs.length==0) { + // if the user provided the title, use it + checked=dgo['checked']; + } + } else { + if (dgo['checked']!=null) { + checked=dgo['checked']; + } + } + } + if(this.rrdflot_defaults.use_checked_DSs) { + if(this.rrdflot_defaults.checked_DSs==null) {continue;} + for(var j=0;j0) { + for (var i=0; i%s Value: %y.3" }, + grid: { hoverable: true }, + }; + + if (legend_id=="None") { + // do nothing + } else { + graph_options.legend.show=true; + graph_options.legend.position=legend_id; + } + + if (this.graph_options!=null) { + graph_options=populateGraphOptions(graph_options,this.graph_options); + } + + if (graph_options.tooltip==false) { + // avoid the need for the caller specify both + graph_options.grid.hoverable=false; + } + + if (this.selection_range.isSet()) { + var selection_range=this.selection_range.getFlotRanges(); + if(this.rrdflot_defaults.use_windows) { + graph_options.xaxis.min = this.rrdflot_defaults.window_min; + graph_options.xaxis.max = this.rrdflot_defaults.window_max; + } else { + graph_options.xaxis.min=selection_range.xaxis.from; + graph_options.xaxis.max=selection_range.xaxis.to; + } + } else if(this.rrdflot_defaults.use_windows) { + graph_options.xaxis.min = this.rrdflot_defaults.window_min; + graph_options.xaxis.max = this.rrdflot_defaults.window_max; + } else { + graph_options.xaxis.min=flot_obj.min; + graph_options.xaxis.max=flot_obj.max; + } + + var scale_options = { + legend: {show:false}, + lines: {show:true}, + xaxis: {mode: "time", min:flot_obj.min, max:flot_obj.max }, + yaxis: graph_options.yaxis, + selection: { mode: "x" }, + }; + + //this.selection_range.selection_min=flot_obj.min; + //this.selection_range.selection_max=flot_obj.max; + + var flot_data=flot_obj.data; + var graph_data=this.selection_range.trim_flot_data(flot_data); + var scale_data=flot_data; + + this.graph = $.plot($(graph_jq_id), graph_data, graph_options); + this.scale = $.plot($(scale_jq_id), scale_data, scale_options); + + + if(this.rrdflot_defaults.use_windows) { + ranges = {}; + ranges.xaxis = []; + ranges.xaxis.from = this.rrdflot_defaults.window_min; + ranges.xaxis.to = this.rrdflot_defaults.window_max; + rf_this.scale.setSelection(ranges,true); + window_min = ranges.xaxis.from; + window_max = ranges.xaxis.to; + } + + if (this.selection_range.isSet()) { + this.scale.setSelection(this.selection_range.getFlotRanges(),true); //don't fire event, no need + } + + // now connect the two + $(graph_jq_id).unbind("plotselected"); // but first remove old function + $(graph_jq_id).bind("plotselected", function (event, ranges) { + // do the zooming + rf_this.selection_range.setFromFlotRanges(ranges); + graph_options.xaxis.min=ranges.xaxis.from; + graph_options.xaxis.max=ranges.xaxis.to; + window_min = ranges.xaxis.from; + window_max = ranges.xaxis.to; + rf_this.graph = $.plot($(graph_jq_id), rf_this.selection_range.trim_flot_data(flot_data), graph_options); + + // don't fire event on the scale to prevent eternal loop + rf_this.scale.setSelection(ranges, true); //puts the transparent window on minigraph + }); + + $(scale_jq_id).unbind("plotselected"); //same here + $(scale_jq_id).bind("plotselected", function (event, ranges) { + rf_this.graph.setSelection(ranges); + }); + + // only the scale has a selection + // so when that is cleared, redraw also the graph + $(scale_jq_id).bind("plotunselected", function() { + rf_this.selection_range.reset(); + graph_options.xaxis.min=flot_obj.min; + graph_options.xaxis.max=flot_obj.max; + rf_this.graph = $.plot($(graph_jq_id), rf_this.selection_range.trim_flot_data(flot_data), graph_options); + window_min = 0; + window_max = 0; + }); +}; + +// callback functions that are called when one of the selections changes +rrdFlot.prototype.callback_res_changed = function() { + this.rrdflot_defaults.use_rra = false; + this.drawFlotGraph(); +}; + +rrdFlot.prototype.callback_ds_cb_changed = function() { + this.drawFlotGraph(); +}; + +rrdFlot.prototype.callback_scale_reset = function() { + this.scale.clearSelection(); +}; + +rrdFlot.prototype.callback_legend_changed = function() { + this.drawFlotGraph(); +}; + +rrdFlot.prototype.callback_timezone_changed = function() { + this.drawFlotGraph(); +}; + +rrdFlot.prototype.callback_elem_group_changed = function(num) { //,window_min,window_max) { + + var oCB=document.getElementById(this.ds_cb_id); + var nrDSs=oCB.ds.length; + if (oCB.ds.length>0) { + for (var i=0; i0) { - for (var i=0; i=1) { - // wraparound, change them a little - idiv=Math.floor(i/std_colors.length); - c1=parseInt(color[1]+color[2],16); - c2=parseInt(color[3]+color[4],16); - c3=parseInt(color[5]+color[6],16); - m1=Math.floor((c1-128)/Math.sqrt(idiv+1))+128; - m2=Math.floor((c2-128)/Math.sqrt(idiv+1))+128; - m3=Math.floor((c3-128)/Math.sqrt(idiv+1))+128; - if (m1>15) s1=(m1).toString(16); else s1="0"+(m1).toString(16); - if (m2>15) s2=(m2).toString(16); else s2="0"+(m2).toString(16); - if (m3>15) s3=(m3).toString(16); else s3="0"+(m3).toString(16); - color="#"+s1+s2+s3; - } - rrd_colors.push(color); - } - } - } else { // single element is not treated as an array - if (oCB.rrd.checked==true) { - // no sense trying to stack a single element - rrd_list.push(this.rrd_files[0]); - rrd_colors.push(std_colors[0]); - } - } - - // then extract RRA data about those DSs... to be finished - var flot_obj=rrdRRAMultiStackFlotObj(rrd_list,rra_idx,ds_id); - - // fix the colors, based on the position in the RRD - for (var i=0; i%s Value: %y.3" }, - grid: { hoverable: true }, - }; - - if (this.graph_options!=null) { - if (typeof(this.graph_options.tooltip)=='boolean'&&this.graph_options.tooltip==true) { - //nothing - } - else if(typeof(this.graph_options.tooltip)=='boolean'&&this.graph_options.tooltip==false) { - graph_options.grid.hoverable=false; - graph_options.tooltip=false; - } - else if(typeof(this.graph_options.tooltip)=='undefined') { - //defaults to true - } - else { - graph_options.grid.hoverable=false; - graph_options.tooltip=false; - } - } - - if (legend_id=="None") { - // do nothing - } else { - graph_options.legend.show=true; - graph_options.legend.position=legend_id; - } - - if (this.selection_range.isSet()) { - var selection_range=this.selection_range.getFlotRanges(); - graph_options.xaxis.min=selection_range.xaxis.from; - graph_options.xaxis.max=selection_range.xaxis.to; - } else { - graph_options.xaxis.min=flot_obj.min; - graph_options.xaxis.max=flot_obj.max; - } - - if (this.graph_options!=null) { - if (this.graph_options.legend!=null) { - if (this.graph_options.legend.position!=null) { - graph_options.legend.position=this.graph_options.legend.position; - } - if (this.graph_options.legend.noColumns!=null) { - graph_options.legend.noColumns=this.graph_options.legend.noColumns; - } - } - if (this.graph_options.yaxis!=null) { - if (this.graph_options.yaxis.autoscaleMargin!=null) { - graph_options.yaxis.autoscaleMargin=this.graph_options.yaxis.autoscaleMargin; - } - } - if (this.graph_options.lines!=null) { - graph_options.lines=this.graph_options.lines; - } - } - - var scale_options = { - legend: {show:false}, - lines: {show:true}, - xaxis: { mode: "time", min:flot_obj.min, max:flot_obj.max }, - selection: { mode: "x" }, - }; - - var flot_data=flot_obj.data; - - var graph_data=this.selection_range.trim_flot_data(flot_data); - var scale_data=flot_data; - - this.graph = $.plot($(graph_jq_id), graph_data, graph_options); - this.scale = $.plot($(scale_jq_id), scale_data, scale_options); - - if (this.selection_range.isSet()) { - this.scale.setSelection(this.selection_range.getFlotRanges(),true); //don't fire event, no need - } - - // now connect the two - $(graph_jq_id).bind("plotselected", function (event, ranges) { - // do the zooming - rf_this.selection_range.setFromFlotRanges(ranges); - graph_options.xaxis.min=ranges.xaxis.from; - graph_options.xaxis.max=ranges.xaxis.to; - rf_this.graph = $.plot($(graph_jq_id), rf_this.selection_range.trim_flot_data(flot_data), graph_options); - - // don't fire event on the scale to prevent eternal loop - rf_this.scale.setSelection(ranges, true); - }); - - $(scale_jq_id).bind("plotselected", function (event, ranges) { - rf_this.graph.setSelection(ranges); - }); - - // only the scale has a selection - // so when that is cleared, redraw also the graph - $(scale_jq_id).bind("plotunselected", function() { - rf_this.selection_range.reset(); - graph_options.xaxis.min=flot_obj.min; - graph_options.xaxis.max=flot_obj.max; - rf_this.graph = $.plot($(graph_jq_id), rf_this.selection_range.trim_flot_data(flot_data), graph_options); - }); -}; - -// callback functions that are called when one of the selections changes -rrdFlotMatrix.prototype.callback_res_changed = function() { - this.drawFlotGraph(); -}; - -rrdFlotMatrix.prototype.callback_ds_changed = function() { - this.drawFlotGraph(); -}; - -rrdFlotMatrix.prototype.callback_rrd_cb_changed = function() { - this.drawFlotGraph(); -}; - -rrdFlotMatrix.prototype.callback_scale_reset = function() { - this.scale.clearSelection(); -}; - -rrdFlotMatrix.prototype.callback_legend_changed = function() { - this.drawFlotGraph(); -}; - +/* + * RRD graphing libraries, based on Flot + * Part of the javascriptRRD package + * Copyright (c) 2010 Frank Wuerthwein, fkw@ucsd.edu + * Igor Sfiligoi, isfiligoi@ucsd.edu + * + * Original repository: http://javascriptrrd.sourceforge.net/ + * + * MIT License [http://www.opensource.org/licenses/mit-license.php] + * + */ + +/* + * + * Flot is a javascript plotting library developed and maintained by + * Ole Laursen [http://www.flotcharts.org/] + * + */ + +/* + * The rrd_files is a list of + * [rrd_id,rrd_file] pairs + * All rrd_files must have the same step, the same DSes and the same number of RRAs. + * + */ + +/* + * The ds_list is a list of + * [ds_id, ds_title] pairs + * If not defined, the list will be created from the RRDs + * + */ + +/* + * Local dependencies: + * rrdFlotSupport.py + * + * External dependencies: + * [Flot]/jquery.py + * [Flot]/jquery.flot.js + * [Flot]/jquery.flot.selection.js + */ + +/* graph_options defaults (see Flot docs for details) + * { + * legend: { position:"nw",noColumns:3}, + * lines: { show:true }, + * yaxis: { autoscaleMargin: 0.20} + * } + * + * rrd_graph_options is a dictionary of rrd_id, + * with each element being a graph_option + * The defaults for each element are + * { + * title: label or rrd_name // this is what is displayed in the checkboxes + * checked: true // boolean + * label: title or rrd_name // this is what is displayed in the legend + * color: rrd_index // see Flot docs for details + * lines: { show:true, fill: true, fillColor:color } // see Flot docs for details + * } + */ + +function rrdFlotMatrix(html_id, rrd_files, ds_list, graph_options, rrd_graph_options,rrdflot_defaults) { + this.html_id=html_id; + this.rrd_files=rrd_files; + if (rrdflot_defaults==null) { + this.rrdflot_defaults=new Object(); + } + else {this.rrdflot_defaults=rrdflot_defaults;} + if (ds_list==null) { + this.ds_list=[]; + var rrd_file=this.rrd_files[0][1]; // get the first one... they are all the same + var nrDSs=rrd_file.getNrDSs(); + for (var i=0; i0) { + for (var i=0; i=1) { + // wraparound, change them a little + idiv=Math.floor(i/std_colors.length); + c1=parseInt(color[1]+color[2],16); + c2=parseInt(color[3]+color[4],16); + c3=parseInt(color[5]+color[6],16); + m1=Math.floor((c1-128)/Math.sqrt(idiv+1))+128; + m2=Math.floor((c2-128)/Math.sqrt(idiv+1))+128; + m3=Math.floor((c3-128)/Math.sqrt(idiv+1))+128; + if (m1>15) s1=(m1).toString(16); else s1="0"+(m1).toString(16); + if (m2>15) s2=(m2).toString(16); else s2="0"+(m2).toString(16); + if (m3>15) s3=(m3).toString(16); else s3="0"+(m3).toString(16); + color="#"+s1+s2+s3; + } + rrd_colors.push(color); + } + } + } else { // single element is not treated as an array + if (oCB.rrd.checked==true) { + // no sense trying to stack a single element + rrd_list.push(this.rrd_files[0]); + rrd_colors.push(std_colors[0]); + } + } + + // then extract RRA data about those DSs... to be finished + var flot_obj=rrdRRAMultiStackFlotObj(rrd_list,rra_idx,ds_id); + + // fix the colors, based on the position in the RRD + for (var i=0; i%s Value: %y.3" }, + grid: { hoverable: true }, + }; + + if (this.graph_options!=null) { + if (typeof(this.graph_options.tooltip)=='boolean'&&this.graph_options.tooltip==true) { + //nothing + } + else if(typeof(this.graph_options.tooltip)=='boolean'&&this.graph_options.tooltip==false) { + graph_options.grid.hoverable=false; + graph_options.tooltip=false; + } + else if(typeof(this.graph_options.tooltip)=='undefined') { + //defaults to true + } + else { + graph_options.grid.hoverable=false; + graph_options.tooltip=false; + } + } + + if (legend_id=="None") { + // do nothing + } else { + graph_options.legend.show=true; + graph_options.legend.position=legend_id; + } + + if (this.selection_range.isSet()) { + var selection_range=this.selection_range.getFlotRanges(); + graph_options.xaxis.min=selection_range.xaxis.from; + graph_options.xaxis.max=selection_range.xaxis.to; + } else { + graph_options.xaxis.min=flot_obj.min; + graph_options.xaxis.max=flot_obj.max; + } + + if (this.graph_options!=null) { + if (this.graph_options.legend!=null) { + if (this.graph_options.legend.position!=null) { + graph_options.legend.position=this.graph_options.legend.position; + } + if (this.graph_options.legend.noColumns!=null) { + graph_options.legend.noColumns=this.graph_options.legend.noColumns; + } + } + if (this.graph_options.yaxis!=null) { + if (this.graph_options.yaxis.autoscaleMargin!=null) { + graph_options.yaxis.autoscaleMargin=this.graph_options.yaxis.autoscaleMargin; + } + } + if (this.graph_options.lines!=null) { + graph_options.lines=this.graph_options.lines; + } + } + + var scale_options = { + legend: {show:false}, + lines: {show:true}, + xaxis: { mode: "time", min:flot_obj.min, max:flot_obj.max }, + selection: { mode: "x" }, + }; + + var flot_data=flot_obj.data; + + var graph_data=this.selection_range.trim_flot_data(flot_data); + var scale_data=flot_data; + + this.graph = $.plot($(graph_jq_id), graph_data, graph_options); + this.scale = $.plot($(scale_jq_id), scale_data, scale_options); + + if (this.selection_range.isSet()) { + this.scale.setSelection(this.selection_range.getFlotRanges(),true); //don't fire event, no need + } + + // now connect the two + $(graph_jq_id).bind("plotselected", function (event, ranges) { + // do the zooming + rf_this.selection_range.setFromFlotRanges(ranges); + graph_options.xaxis.min=ranges.xaxis.from; + graph_options.xaxis.max=ranges.xaxis.to; + rf_this.graph = $.plot($(graph_jq_id), rf_this.selection_range.trim_flot_data(flot_data), graph_options); + + // don't fire event on the scale to prevent eternal loop + rf_this.scale.setSelection(ranges, true); + }); + + $(scale_jq_id).bind("plotselected", function (event, ranges) { + rf_this.graph.setSelection(ranges); + }); + + // only the scale has a selection + // so when that is cleared, redraw also the graph + $(scale_jq_id).bind("plotunselected", function() { + rf_this.selection_range.reset(); + graph_options.xaxis.min=flot_obj.min; + graph_options.xaxis.max=flot_obj.max; + rf_this.graph = $.plot($(graph_jq_id), rf_this.selection_range.trim_flot_data(flot_data), graph_options); + }); +}; + +// callback functions that are called when one of the selections changes +rrdFlotMatrix.prototype.callback_res_changed = function() { + this.drawFlotGraph(); +}; + +rrdFlotMatrix.prototype.callback_ds_changed = function() { + this.drawFlotGraph(); +}; + +rrdFlotMatrix.prototype.callback_rrd_cb_changed = function() { + this.drawFlotGraph(); +}; + +rrdFlotMatrix.prototype.callback_scale_reset = function() { + this.scale.clearSelection(); +}; + +rrdFlotMatrix.prototype.callback_legend_changed = function() { + this.drawFlotGraph(); +}; + diff --git a/rpimonitor/web/js/javascriptrrd/rrdFlotSupport.js b/rpimonitor/web/js/javascriptrrd/rrdFlotSupport.js index 85f6118..d5ae5ec 100644 --- a/rpimonitor/web/js/javascriptrrd/rrdFlotSupport.js +++ b/rpimonitor/web/js/javascriptrrd/rrdFlotSupport.js @@ -1,431 +1,431 @@ -/* - * Support library aimed at providing commonly used functions and classes - * that may be used while plotting RRD files with Flot - * - * Part of the javascriptRRD package - * Copyright (c) 2009 Frank Wuerthwein, fkw@ucsd.edu - * - * Original repository: http://javascriptrrd.sourceforge.net/ - * - * MIT License [http://www.opensource.org/licenses/mit-license.php] - * - */ - -/* - * - * Flot is a javascript plotting library developed and maintained by - * Ole Laursen [http://www.flotcharts.org/] - * - */ - -// Return a Flot-like data structure -// Since Flot does not properly handle empty elements, min and max are returned, too -function rrdDS2FlotSeries(rrd_file,ds_id,rra_idx,want_rounding) { - var ds=rrd_file.getDS(ds_id); - var ds_name=ds.getName(); - var ds_idx=ds.getIdx(); - var rra=rrd_file.getRRA(rra_idx); - var rra_rows=rra.getNrRows(); - var last_update=rrd_file.getLastUpdate(); - var step=rra.getStep(); - - if (want_rounding!=false) { - // round last_update to step - // so that all elements are sync - last_update-=(last_update%step); - } - - var first_el=last_update-(rra_rows-1)*step; - var timestamp=first_el; - var flot_series=[]; - for (var i=0;imax_ts)) { - max_ts=rrd_last_update; - } - if ((min_ts==null) || (rrd_min_ts=0) && (row_delta no filtering - - var out_data=[]; - for (var i=0; i=this.selection_min) && (nr<=this.selection_max)) { - out_data.push(data_list[i]); - } - } - return out_data; -}; - - -// Given an array of flot lines, limit to the selection -rrdFlotSelection.prototype.trim_flot_timezone_data = function(flot_data,shift) { - var out_data=[]; - for (var i=0; i no filtering - - var out_data=[]; - for (var i=0; i=this.selection_min) && (nr<=this.selection_max)) { - out_data.push(data_list[i]); - } - } - return out_data; -}; - - -// ====================================== -// Miscelaneous helper functions -// ====================================== - -function rfs_format_time(s) { - if (s<120) { - return s+"s"; - } else { - var s60=s%60; - var m=(s-s60)/60; - if ((m<10) && (s60>9)) { - return m+":"+s60+"min"; - } if (m<120) { - return m+"min"; - } else { - var m60=m%60; - var h=(m-m60)/60; - if ((h<12) && (m60>9)) { - return h+":"+m60+"h"; - } if (h<48) { - return h+"h"; - } else { - var h24=h%24; - var d=(h-h24)/24; - if ((d<7) && (h24>0)) { - return d+" days "+h24+"h"; - } if (d<60) { - return d+" days"; - } else { - var d30=d%30; - var mt=(d-d30)/30; - return mt+" months"; - } - } - } - - } -} +/* + * Support library aimed at providing commonly used functions and classes + * that may be used while plotting RRD files with Flot + * + * Part of the javascriptRRD package + * Copyright (c) 2009 Frank Wuerthwein, fkw@ucsd.edu + * + * Original repository: http://javascriptrrd.sourceforge.net/ + * + * MIT License [http://www.opensource.org/licenses/mit-license.php] + * + */ + +/* + * + * Flot is a javascript plotting library developed and maintained by + * Ole Laursen [http://www.flotcharts.org/] + * + */ + +// Return a Flot-like data structure +// Since Flot does not properly handle empty elements, min and max are returned, too +function rrdDS2FlotSeries(rrd_file,ds_id,rra_idx,want_rounding) { + var ds=rrd_file.getDS(ds_id); + var ds_name=ds.getName(); + var ds_idx=ds.getIdx(); + var rra=rrd_file.getRRA(rra_idx); + var rra_rows=rra.getNrRows(); + var last_update=rrd_file.getLastUpdate(); + var step=rra.getStep(); + + if (want_rounding!=false) { + // round last_update to step + // so that all elements are sync + last_update-=(last_update%step); + } + + var first_el=last_update-(rra_rows-1)*step; + var timestamp=first_el; + var flot_series=[]; + for (var i=0;imax_ts)) { + max_ts=rrd_last_update; + } + if ((min_ts==null) || (rrd_min_ts=0) && (row_delta no filtering + + var out_data=[]; + for (var i=0; i=this.selection_min) && (nr<=this.selection_max)) { + out_data.push(data_list[i]); + } + } + return out_data; +}; + + +// Given an array of flot lines, limit to the selection +rrdFlotSelection.prototype.trim_flot_timezone_data = function(flot_data,shift) { + var out_data=[]; + for (var i=0; i no filtering + + var out_data=[]; + for (var i=0; i=this.selection_min) && (nr<=this.selection_max)) { + out_data.push(data_list[i]); + } + } + return out_data; +}; + + +// ====================================== +// Miscelaneous helper functions +// ====================================== + +function rfs_format_time(s) { + if (s<120) { + return s+"s"; + } else { + var s60=s%60; + var m=(s-s60)/60; + if ((m<10) && (s60>9)) { + return m+":"+s60+"min"; + } if (m<120) { + return m+"min"; + } else { + var m60=m%60; + var h=(m-m60)/60; + if ((h<12) && (m60>9)) { + return h+":"+m60+"h"; + } if (h<48) { + return h+"h"; + } else { + var h24=h%24; + var d=(h-h24)/24; + if ((d<7) && (h24>0)) { + return d+" days "+h24+"h"; + } if (d<60) { + return d+" days"; + } else { + var d30=d%30; + var mt=(d-d30)/30; + return mt+" months"; + } + } + } + + } +} diff --git a/rpimonitor/web/js/javascriptrrd/rrdMultiFile.js b/rpimonitor/web/js/javascriptrrd/rrdMultiFile.js index 1e2af85..b4126a7 100644 --- a/rpimonitor/web/js/javascriptrrd/rrdMultiFile.js +++ b/rpimonitor/web/js/javascriptrrd/rrdMultiFile.js @@ -1,173 +1,173 @@ -/* - * Combine multiple rrdFiles into one object - * It implements the same interface, but changing the content - * - * Part of the javascriptRRD package - * Copyright (c) 2010 Igor Sfiligoi, isfiligoi@ucsd.edu - * - * Original repository: http://javascriptrrd.sourceforge.net/ - * - * MIT License [http://www.opensource.org/licenses/mit-license.php] - * - */ - -// ============================================================ -// RRD RRA handling class -function RRDRRASum(rra_list,offset_list,treat_undefined_as_zero) { - this.rra_list=rra_list; - this.offset_list=offset_list; - this.treat_undefined_as_zero=treat_undefined_as_zero; - this.row_cnt= this.rra_list[0].getNrRows(); -} - -RRDRRASum.prototype.getIdx = function() { - return this.rra_list[0].getIdx(); -} - -// Get number of rows/columns -RRDRRASum.prototype.getNrRows = function() { - return this.row_cnt; -} -RRDRRASum.prototype.getNrDSs = function() { - return this.rra_list[0].getNrDSs(); -} - -// Get RRA step (expressed in seconds) -RRDRRASum.prototype.getStep = function() { - return this.rra_list[0].getStep(); -} - -// Get consolidation function name -RRDRRASum.prototype.getCFName = function() { - return this.rra_list[0].getCFName(); -} - -RRDRRASum.prototype.getEl = function(row_idx,ds_idx) { - var outSum=0.0; - for (var i in this.rra_list) { - var offset=this.offset_list[i]; - if ((row_idx+offset) undefined*/ - val=undefined; - } - /* treat all undefines as 0 for now */ - if (val==undefined) { - if (this.treat_undefined_as_zero) { - val=0; - } else { - /* if even one element is undefined, the whole sum is undefined */ - outSum=undefined; - break; - } - } - outSum+=val; - } - return outSum; -} - -// Low precision version of getEl -// Uses getFastDoubleAt -RRDRRASum.prototype.getElFast = function(row_idx,ds_idx) { - var outSum=0.0; - for (var i in this.rra_list) { - var offset=this.offset_list[i]; - if ((row_id+offset) undefined*/ - val=undefined; - } - /* treat all undefines as 0 for now */ - if (val==undefined) { - if (this.treat_undefined_as_zero) { - val=0; - } else { - /* if even one element is undefined, the whole sum is undefined */ - outSum=undefined; - break; - } - } - outSum+=val; - } - return outSum; -} - -/*** INTERNAL ** sort by lastupdate, descending ***/ - -function rrdFileSort(f1, f2) { - return f2.getLastUpdate()-f1.getLastUpdate(); -} - -/* - * Sum several RRDfiles together - * They must all have the same DSes and the same RRAs - */ - -function RRDFileSum(file_list,treat_undefined_as_zero) { - if (treat_undefined_as_zero==undefined) { - this.treat_undefined_as_zero=true; - } else { - this.treat_undefined_as_zero=treat_undefined_as_zero; - } - this.file_list=file_list; - this.file_list.sort(); - - // =================================== - // Start of user functions - - this.getMinStep = function() { - return this.file_list[0].getMinStep(); - } - this.getLastUpdate = function() { - return this.file_list[0].getLastUpdate(); - } - - this.getNrDSs = function() { - return this.file_list[0].getNrDSs(); - } - - this.getDSNames = function() { - return this.file_list[0].getDSNames(); - } - - this.getDS = function(id) { - return this.file_list[0].getDS(id); - } - - this.getNrRRAs = function() { - return this.file_list[0].getNrRRAs(); - } - - this.getRRAInfo = function(idx) { - return this.file_list[0].getRRAInfo(idx); - } - - this.getRRA = function(idx) { - var rra_info=this.getRRAInfo(idx); - var rra_step=rra_info.getStep(); - var realLastUpdate=undefined; - - var rra_list=new Array(); - var offset_list=new Array(); - for (var i in this.file_list) { - file=file_list[i]; - fileLastUpdate=file.getLastUpdate(); - if (realLastUpdate!=undefined) { - fileSkrew=Math.floor((realLastUpdate-fileLastUpdate)/rra_step); - } else { - fileSkrew=0; - firstLastUpdate=fileLastUpdate; - } - offset_list.push(fileSkrew); - fileRRA=file.getRRA(idx); - rra_list.push(fileRRA); - } - - return new RRDRRASum(rra_list,offset_list,this.treat_undefined_as_zero); - } - -} +/* + * Combine multiple rrdFiles into one object + * It implements the same interface, but changing the content + * + * Part of the javascriptRRD package + * Copyright (c) 2010 Igor Sfiligoi, isfiligoi@ucsd.edu + * + * Original repository: http://javascriptrrd.sourceforge.net/ + * + * MIT License [http://www.opensource.org/licenses/mit-license.php] + * + */ + +// ============================================================ +// RRD RRA handling class +function RRDRRASum(rra_list,offset_list,treat_undefined_as_zero) { + this.rra_list=rra_list; + this.offset_list=offset_list; + this.treat_undefined_as_zero=treat_undefined_as_zero; + this.row_cnt= this.rra_list[0].getNrRows(); +} + +RRDRRASum.prototype.getIdx = function() { + return this.rra_list[0].getIdx(); +} + +// Get number of rows/columns +RRDRRASum.prototype.getNrRows = function() { + return this.row_cnt; +} +RRDRRASum.prototype.getNrDSs = function() { + return this.rra_list[0].getNrDSs(); +} + +// Get RRA step (expressed in seconds) +RRDRRASum.prototype.getStep = function() { + return this.rra_list[0].getStep(); +} + +// Get consolidation function name +RRDRRASum.prototype.getCFName = function() { + return this.rra_list[0].getCFName(); +} + +RRDRRASum.prototype.getEl = function(row_idx,ds_idx) { + var outSum=0.0; + for (var i in this.rra_list) { + var offset=this.offset_list[i]; + if ((row_idx+offset) undefined*/ + val=undefined; + } + /* treat all undefines as 0 for now */ + if (val==undefined) { + if (this.treat_undefined_as_zero) { + val=0; + } else { + /* if even one element is undefined, the whole sum is undefined */ + outSum=undefined; + break; + } + } + outSum+=val; + } + return outSum; +} + +// Low precision version of getEl +// Uses getFastDoubleAt +RRDRRASum.prototype.getElFast = function(row_idx,ds_idx) { + var outSum=0.0; + for (var i in this.rra_list) { + var offset=this.offset_list[i]; + if ((row_id+offset) undefined*/ + val=undefined; + } + /* treat all undefines as 0 for now */ + if (val==undefined) { + if (this.treat_undefined_as_zero) { + val=0; + } else { + /* if even one element is undefined, the whole sum is undefined */ + outSum=undefined; + break; + } + } + outSum+=val; + } + return outSum; +} + +/*** INTERNAL ** sort by lastupdate, descending ***/ + +function rrdFileSort(f1, f2) { + return f2.getLastUpdate()-f1.getLastUpdate(); +} + +/* + * Sum several RRDfiles together + * They must all have the same DSes and the same RRAs + */ + +function RRDFileSum(file_list,treat_undefined_as_zero) { + if (treat_undefined_as_zero==undefined) { + this.treat_undefined_as_zero=true; + } else { + this.treat_undefined_as_zero=treat_undefined_as_zero; + } + this.file_list=file_list; + this.file_list.sort(); + + // =================================== + // Start of user functions + + this.getMinStep = function() { + return this.file_list[0].getMinStep(); + } + this.getLastUpdate = function() { + return this.file_list[0].getLastUpdate(); + } + + this.getNrDSs = function() { + return this.file_list[0].getNrDSs(); + } + + this.getDSNames = function() { + return this.file_list[0].getDSNames(); + } + + this.getDS = function(id) { + return this.file_list[0].getDS(id); + } + + this.getNrRRAs = function() { + return this.file_list[0].getNrRRAs(); + } + + this.getRRAInfo = function(idx) { + return this.file_list[0].getRRAInfo(idx); + } + + this.getRRA = function(idx) { + var rra_info=this.getRRAInfo(idx); + var rra_step=rra_info.getStep(); + var realLastUpdate=undefined; + + var rra_list=new Array(); + var offset_list=new Array(); + for (var i in this.file_list) { + file=file_list[i]; + fileLastUpdate=file.getLastUpdate(); + if (realLastUpdate!=undefined) { + fileSkrew=Math.floor((realLastUpdate-fileLastUpdate)/rra_step); + } else { + fileSkrew=0; + firstLastUpdate=fileLastUpdate; + } + offset_list.push(fileSkrew); + fileRRA=file.getRRA(idx); + rra_list.push(fileRRA); + } + + return new RRDRRASum(rra_list,offset_list,this.treat_undefined_as_zero); + } + +} diff --git a/rpimonitor/web/js/justgage.1.0.1.js b/rpimonitor/web/js/justgage.1.0.1.js index 2ec1896..0f8ed43 100644 --- a/rpimonitor/web/js/justgage.1.0.1.js +++ b/rpimonitor/web/js/justgage.1.0.1.js @@ -1,474 +1,474 @@ -/** - * JustGage - a handy JavaScript plugin for generating and animating nice & clean dashboard gauges. - * Copyright (c) 2012 Bojan Djuricic - pindjur(at)gmail(dot)com | http://www.madcog.com - * Licensed under MIT. - * Date: 31/07/2012 - * @author Bojan Djuricic (@Toorshia) - * @version 1.0 - * - * http://www.justgage.com - */ - -JustGage = function(config) { - - if (!config.id) {alert("Missing id parameter for gauge!"); return false;} - if (!document.getElementById(config.id)) {alert("No element with id: \""+config.id+"\" found!"); return false;} - - // configurable parameters - this.config = - { - // id : string - // this is container element id - id : config.id, - - // title : string - // gauge title - title : (config.title) ? config.title : "Title", - - // titleFontColor : string - // color of gauge title - titleFontColor : (config.titleFontColor) ? config.titleFontColor : "#999999", - - // value : int - // value gauge is showing - value : (config.value) ? config.value : 0, - - // valueFontColor : string - // color of label showing current value - valueFontColor : (config.valueFontColor) ? config.valueFontColor : "#010101", - - // min : int - // min value - min : (config.min) ? config.min : 0, - - // max : int - // max value - max : (config.max) ? config.max : 100, - - // showMinMax : bool - // hide or display min and max values - showMinMax : (config.showMinMax != null) ? config.showMinMax : true, - - // gaugeWidthScale : float - // width of the gauge element - gaugeWidthScale : (config.gaugeWidthScale) ? config.gaugeWidthScale : 1.0, - - // gaugeColor : string - // background color of gauge element - gaugeColor : (config.gaugeColor) ? config.gaugeColor : "#edebeb", - - // label : string - // text to show below value - label : (config.label) ? config.label : "", - - // showInnerShadow : bool - // give gauge element small amount of inner shadow - showInnerShadow : (config.showInnerShadow != null) ? config.showInnerShadow : true, - - // shadowOpacity : int - // 0 ~ 1 - shadowOpacity : (config.shadowOpacity) ? config.shadowOpacity : 0.2, - - // shadowSize: int - // inner shadow size - shadowSize : (config.shadowSize) ? config.shadowSize : 5, - - // shadowVerticalOffset : int - // how much shadow is offset from top - shadowVerticalOffset : (config.shadowVerticalOffset) ? config.shadowVerticalOffset : 3, - - // levelColors : string[] - // colors of indicator, from lower to upper, in RGB format - levelColors : (config.levelColors) ? config.levelColors : percentColors, - - // levelColorsGradient : bool - // whether to use gradual color change for value, or sector-based - levelColorsGradient : (config.levelColorsGradient != null) ? config.levelColorsGradient : true, - - // labelFontColor : string - // color of label showing label under value - labelFontColor : (config.labelFontColor) ? config.labelFontColor : "#b3b3b3", - - // startAnimationTime : int - // length of initial animation - startAnimationTime : (config.startAnimationTime) ? config.startAnimationTime : 700, - - // startAnimationType : string - // type of initial animation (linear, >, <, <>, bounce) - startAnimationType : (config.startAnimationType) ? config.startAnimationType : ">", - - // refreshAnimationTime : int - // length of refresh animation - refreshAnimationTime : (config.refreshAnimationTime) ? config.refreshAnimationTime : 700, - - // refreshAnimationType : string - // type of refresh animation (linear, >, <, <>, bounce) - refreshAnimationType : (config.refreshAnimationType) ? config.refreshAnimationType : ">" - }; - - // overflow values - if (config.value > this.config.max) this.config.value = this.config.max; - if (config.value < this.config.min) this.config.value = this.config.min; - this.originalValue = config.value; - - // canvas - this.canvas = Raphael(this.config.id, "100%", "100%"); - - // canvas dimensions - //var canvasW = document.getElementById(this.config.id).clientWidth; - //var canvasH = document.getElementById(this.config.id).clientHeight; - var canvasW = getStyle(document.getElementById(this.config.id), "width").slice(0, -2) * 1; - var canvasH = getStyle(document.getElementById(this.config.id), "height").slice(0, -2) * 1; - - // widget dimensions - var widgetW, widgetH; - if ((canvasW / canvasH) > 1.25) { - widgetW = 1.25 * canvasH; - widgetH = canvasH; - } else { - widgetW = canvasW; - widgetH = canvasW / 1.25; - } - - // delta - var dx = (canvasW - widgetW)/2; - var dy = (canvasH - widgetH)/2; - - // title - var titleFontSize = ((widgetH / 8) > 10) ? (widgetH / 10) : 10; - var titleX = dx + widgetW / 2; - var titleY = dy + widgetH / 6.5; - - // value - var valueFontSize = ((widgetH / 6.4) > 16) ? (widgetH / 6.4) : 16; - var valueX = dx + widgetW / 2; - var valueY = dy + widgetH / 1.4; - - // label - var labelFontSize = ((widgetH / 16) > 10) ? (widgetH / 16) : 10; - var labelX = dx + widgetW / 2; - //var labelY = dy + widgetH / 1.126760563380282; - var labelY = valueY + valueFontSize / 2 + 6; - - // min - var minFontSize = ((widgetH / 16) > 10) ? (widgetH / 16) : 10; - var minX = dx + (widgetW / 10) + (widgetW / 6.666666666666667 * this.config.gaugeWidthScale) / 2 ; - var minY = dy + widgetH / 1.126760563380282; - - // max - var maxFontSize = ((widgetH / 16) > 10) ? (widgetH / 16) : 10; - var maxX = dx + widgetW - (widgetW / 10) - (widgetW / 6.666666666666667 * this.config.gaugeWidthScale) / 2 ; - var maxY = dy + widgetH / 1.126760563380282; - - // parameters - this.params = { - canvasW : canvasW, - canvasH : canvasH, - widgetW : widgetW, - widgetH : widgetH, - dx : dx, - dy : dy, - titleFontSize : titleFontSize, - titleX : titleX, - titleY : titleY, - valueFontSize : valueFontSize, - valueX : valueX, - valueY : valueY, - labelFontSize : labelFontSize, - labelX : labelX, - labelY : labelY, - minFontSize : minFontSize, - minX : minX, - minY : minY, - maxFontSize : maxFontSize, - maxX : maxX, - maxY : maxY - }; - - // pki - custom attribute for generating gauge paths - this.canvas.customAttributes.pki = function (value, min, max, w, h, dx, dy, gws) { - - var alpha = (1 - (value - min) / (max - min)) * Math.PI , - Ro = w / 2 - w / 10, - Ri = Ro - w / 6.666666666666667 * gws, - - Cx = w / 2 + dx, - Cy = h / 1.25 + dy, - - Xo = w / 2 + dx + Ro * Math.cos(alpha), - Yo = h - (h - Cy) + dy - Ro * Math.sin(alpha), - Xi = w / 2 + dx + Ri * Math.cos(alpha), - Yi = h - (h - Cy) + dy - Ri * Math.sin(alpha), - path; - - path += "M" + (Cx - Ri) + "," + Cy + " "; - path += "L" + (Cx - Ro) + "," + Cy + " "; - path += "A" + Ro + "," + Ro + " 0 0,1 " + Xo + "," + Yo + " "; - path += "L" + Xi + "," + Yi + " "; - path += "A" + Ri + "," + Ri + " 0 0,0 " + (Cx - Ri) + "," + Cy + " "; - path += "z "; - return { path: path }; - } - - // gauge - this.gauge = this.canvas.path().attr({ - "stroke": "none", - "fill": this.config.gaugeColor, - pki: [this.config.max, this.config.min, this.config.max, this.params.widgetW, this.params.widgetH, this.params.dx, this.params.dy, this.config.gaugeWidthScale] - }); - this.gauge.id = this.config.id+"-gauge"; - - // level - this.level = this.canvas.path().attr({ - "stroke": "none", - "fill": getColorForPercentage((this.config.value - this.config.min) / (this.config.max - this.config.min), this.config.levelColors, this.config.levelColorsGradient), - pki: [this.config.min, this.config.min, this.config.max, this.params.widgetW, this.params.widgetH, this.params.dx, this.params.dy, this.config.gaugeWidthScale] - }); - this.level.id = this.config.id+"-level"; - - // title - this.txtTitle = this.canvas.text(this.params.titleX, this.params.titleY, this.config.title); - this.txtTitle. attr({ - "font-size":this.params.titleFontSize, - "font-weight":"bold", - "font-family":"Arial", - "fill":this.config.titleFontColor, - "fill-opacity":"1" - }); - this.txtTitle.id = this.config.id+"-txttitle"; - - // value - this.txtValue = this.canvas.text(this.params.valueX, this.params.valueY, this.originalValue); - this.txtValue. attr({ - "font-size":this.params.valueFontSize, - "font-weight":"bold", - "font-family":"Arial", - "fill":this.config.valueFontColor, - "fill-opacity":"0" - }); - this.txtValue.id = this.config.id+"-txtvalue"; - - // label - this.txtLabel = this.canvas.text(this.params.labelX, this.params.labelY, this.config.label); - this.txtLabel. attr({ - "font-size":this.params.labelFontSize, - "font-weight":"normal", - "font-family":"Arial", - "fill":this.config.labelFontColor, - "fill-opacity":"0" - }); - this.txtLabel.id = this.config.id+"-txtlabel"; - - // min - this.txtMin = this.canvas.text(this.params.minX, this.params.minY, this.config.min); - this.txtMin. attr({ - "font-size":this.params.minFontSize, - "font-weight":"normal", - "font-family":"Arial", - "fill":this.config.labelFontColor, - "fill-opacity": (this.config.showMinMax == true)? "1" : "0" - }); - this.txtMin.id = this.config.id+"-txtmin"; - - // max - this.txtMax = this.canvas.text(this.params.maxX, this.params.maxY, this.config.max); - this.txtMax. attr({ - "font-size":this.params.maxFontSize, - "font-weight":"normal", - "font-family":"Arial", - "fill":this.config.labelFontColor, - "fill-opacity": (this.config.showMinMax == true)? "1" : "0" - }); - this.txtMax.id = this.config.id+"-txtmax"; - - var defs = this.canvas.canvas.childNodes[1]; - var svg = "http://www.w3.org/2000/svg"; - - - - if (ie < 9) { - onCreateElementNsReady(function() { - this.generateShadow(); - }); - } else { - this.generateShadow(svg, defs); - } - - // animate - this.level.animate({pki: [this.config.value, this.config.min, this.config.max, this.params.widgetW, this.params.widgetH, this.params.dx, this.params.dy, this.config.gaugeWidthScale]}, this.config.startAnimationTime, this.config.startAnimationType); - - this.txtValue.animate({"fill-opacity":"1"}, this.config.startAnimationTime, this.config.startAnimationType); - this.txtLabel.animate({"fill-opacity":"1"}, this.config.startAnimationTime, this.config.startAnimationType); -}; - -// refresh gauge level -JustGage.prototype.refresh = function(val) { - // overflow values - originalVal = val; - if (val > this.config.max) {val = this.config.max;} - if (val < this.config.min) {val = this.config.min;} - - var color = getColorForPercentage((val - this.config.min) / (this.config.max - this.config.min), this.config.levelColors, this.config.levelColorsGradient); - this.canvas.getById(this.config.id+"-txtvalue").attr({"text":originalVal}); - this.canvas.getById(this.config.id+"-level").animate({pki: [val, this.config.min, this.config.max, this.params.widgetW, this.params.widgetH, this.params.dx, this.params.dy, this.config.gaugeWidthScale], "fill":color}, this.config.refreshAnimationTime, this.config.refreshAnimationType); -}; - -var percentColors = [ - "#a9d70b", - "#f9c802", - "#ff0000" -] - -JustGage.prototype.generateShadow = function(svg, defs) { - // FILTER - var gaussFilter=document.createElementNS(svg,"filter"); - gaussFilter.setAttribute("id", this.config.id + "-inner-shadow"); - defs.appendChild(gaussFilter); - - // offset - var feOffset = document.createElementNS(svg,"feOffset"); - feOffset.setAttribute("dx", 0); - feOffset.setAttribute("dy", this.config.shadowVerticalOffset); - gaussFilter.appendChild(feOffset); - - // blur - var feGaussianBlur = document.createElementNS(svg,"feGaussianBlur"); - feGaussianBlur.setAttribute("result","offset-blur"); - feGaussianBlur.setAttribute("stdDeviation", this.config.shadowSize); - gaussFilter.appendChild(feGaussianBlur); - - // composite 1 - var feComposite1 = document.createElementNS(svg,"feComposite"); - feComposite1.setAttribute("operator","out"); - feComposite1.setAttribute("in", "SourceGraphic"); - feComposite1.setAttribute("in2","offset-blur"); - feComposite1.setAttribute("result","inverse"); - gaussFilter.appendChild(feComposite1); - - // flood - var feFlood = document.createElementNS(svg,"feFlood"); - feFlood.setAttribute("flood-color","black"); - feFlood.setAttribute("flood-opacity", this.config.shadowOpacity); - feFlood.setAttribute("result","color"); - gaussFilter.appendChild(feFlood); - - // composite 2 - var feComposite2 = document.createElementNS(svg,"feComposite"); - feComposite2.setAttribute("operator","in"); - feComposite2.setAttribute("in", "color"); - feComposite2.setAttribute("in2","inverse"); - feComposite2.setAttribute("result","shadow"); - gaussFilter.appendChild(feComposite2); - - // composite 3 - var feComposite3 = document.createElementNS(svg,"feComposite"); - feComposite3.setAttribute("operator","over"); - feComposite3.setAttribute("in", "shadow"); - feComposite3.setAttribute("in2","SourceGraphic"); - gaussFilter.appendChild(feComposite3); - - // set shadow - if (this.config.showInnerShadow == true) { - this.canvas.canvas.childNodes[2].setAttribute("filter", "url(#" + this.config.id + "-inner-shadow)"); - this.canvas.canvas.childNodes[3].setAttribute("filter", "url(#" + this.config.id + "-inner-shadow)"); - } -} - -var getColorForPercentage = function(pct, col, grad) { - - var no = col.length; - if (no === 1) return col[0]; - var inc = (grad) ? (1 / (no - 1)) : (1 / no); - var colors = new Array(); - for (var i = 0; i < col.length; i++) { - var percentage = (grad) ? (inc * i) : (inc * (i + 1)); - var rval = parseInt((cutHex(col[i])).substring(0,2),16); - var gval = parseInt((cutHex(col[i])).substring(2,4),16); - var bval = parseInt((cutHex(col[i])).substring(4,6),16); - colors[i] = { pct: percentage, color: { r: rval, g: gval, b: bval } }; - } - - if(pct == 0) return 'rgb(' + [colors[0].color.r, colors[0].color.g, colors[0].color.b].join(',') + ')'; - for (var i = 0; i < colors.length; i++) { - if (pct <= colors[i].pct) { - if (grad == true) { - var lower = colors[i - 1]; - var upper = colors[i]; - var range = upper.pct - lower.pct; - var rangePct = (pct - lower.pct) / range; - var pctLower = 1 - rangePct; - var pctUpper = rangePct; - var color = { - r: Math.floor(lower.color.r * pctLower + upper.color.r * pctUpper), - g: Math.floor(lower.color.g * pctLower + upper.color.g * pctUpper), - b: Math.floor(lower.color.b * pctLower + upper.color.b * pctUpper) - }; - return 'rgb(' + [color.r, color.g, color.b].join(',') + ')'; - } else { - return 'rgb(' + [colors[i].color.r, colors[i].color.g, colors[i].color.b].join(',') + ')'; - } - } - } -} - -function getRandomInt (min, max) { - return Math.floor(Math.random() * (max - min + 1)) + min; -} - -function cutHex(str) {return (str.charAt(0)=="#") ? str.substring(1,7):str} - -function getStyle(oElm, strCssRule){ - var strValue = ""; - if(document.defaultView && document.defaultView.getComputedStyle){ - strValue = document.defaultView.getComputedStyle(oElm, "").getPropertyValue(strCssRule); - } - else if(oElm.currentStyle){ - strCssRule = strCssRule.replace(/\-(\w)/g, function (strMatch, p1){ - return p1.toUpperCase(); - }); - strValue = oElm.currentStyle[strCssRule]; - } - return strValue; -} - - function onCreateElementNsReady(func) { - if (document.createElementNS != undefined) { - func(); - } else { - setTimeout(function() { onCreateElementNsReady(func); }, 100); - } -} - -// ---------------------------------------------------------- -// A short snippet for detecting versions of IE in JavaScript -// without resorting to user-agent sniffing -// ---------------------------------------------------------- -// If you're not in IE (or IE version is less than 5) then: -// ie === undefined -// If you're in IE (>=5) then you can determine which version: -// ie === 7; // IE7 -// Thus, to detect IE: -// if (ie) {} -// And to detect the version: -// ie === 6 // IE6 -// ie > 7 // IE8, IE9 ... -// ie < 9 // Anything less than IE9 -// ---------------------------------------------------------- - -// UPDATE: Now using Live NodeList idea from @jdalton - -var ie = (function(){ - - var undef, - v = 3, - div = document.createElement('div'), - all = div.getElementsByTagName('i'); - - while ( - div.innerHTML = '', - all[0] - ); - - return v > 4 ? v : undef; - -}()); \ No newline at end of file +/** + * JustGage - a handy JavaScript plugin for generating and animating nice & clean dashboard gauges. + * Copyright (c) 2012 Bojan Djuricic - pindjur(at)gmail(dot)com | http://www.madcog.com + * Licensed under MIT. + * Date: 31/07/2012 + * @author Bojan Djuricic (@Toorshia) + * @version 1.0 + * + * http://www.justgage.com + */ + +JustGage = function(config) { + + if (!config.id) {alert("Missing id parameter for gauge!"); return false;} + if (!document.getElementById(config.id)) {alert("No element with id: \""+config.id+"\" found!"); return false;} + + // configurable parameters + this.config = + { + // id : string + // this is container element id + id : config.id, + + // title : string + // gauge title + title : (config.title) ? config.title : "Title", + + // titleFontColor : string + // color of gauge title + titleFontColor : (config.titleFontColor) ? config.titleFontColor : "#999999", + + // value : int + // value gauge is showing + value : (config.value) ? config.value : 0, + + // valueFontColor : string + // color of label showing current value + valueFontColor : (config.valueFontColor) ? config.valueFontColor : "#010101", + + // min : int + // min value + min : (config.min) ? config.min : 0, + + // max : int + // max value + max : (config.max) ? config.max : 100, + + // showMinMax : bool + // hide or display min and max values + showMinMax : (config.showMinMax != null) ? config.showMinMax : true, + + // gaugeWidthScale : float + // width of the gauge element + gaugeWidthScale : (config.gaugeWidthScale) ? config.gaugeWidthScale : 1.0, + + // gaugeColor : string + // background color of gauge element + gaugeColor : (config.gaugeColor) ? config.gaugeColor : "#edebeb", + + // label : string + // text to show below value + label : (config.label) ? config.label : "", + + // showInnerShadow : bool + // give gauge element small amount of inner shadow + showInnerShadow : (config.showInnerShadow != null) ? config.showInnerShadow : true, + + // shadowOpacity : int + // 0 ~ 1 + shadowOpacity : (config.shadowOpacity) ? config.shadowOpacity : 0.2, + + // shadowSize: int + // inner shadow size + shadowSize : (config.shadowSize) ? config.shadowSize : 5, + + // shadowVerticalOffset : int + // how much shadow is offset from top + shadowVerticalOffset : (config.shadowVerticalOffset) ? config.shadowVerticalOffset : 3, + + // levelColors : string[] + // colors of indicator, from lower to upper, in RGB format + levelColors : (config.levelColors) ? config.levelColors : percentColors, + + // levelColorsGradient : bool + // whether to use gradual color change for value, or sector-based + levelColorsGradient : (config.levelColorsGradient != null) ? config.levelColorsGradient : true, + + // labelFontColor : string + // color of label showing label under value + labelFontColor : (config.labelFontColor) ? config.labelFontColor : "#b3b3b3", + + // startAnimationTime : int + // length of initial animation + startAnimationTime : (config.startAnimationTime) ? config.startAnimationTime : 700, + + // startAnimationType : string + // type of initial animation (linear, >, <, <>, bounce) + startAnimationType : (config.startAnimationType) ? config.startAnimationType : ">", + + // refreshAnimationTime : int + // length of refresh animation + refreshAnimationTime : (config.refreshAnimationTime) ? config.refreshAnimationTime : 700, + + // refreshAnimationType : string + // type of refresh animation (linear, >, <, <>, bounce) + refreshAnimationType : (config.refreshAnimationType) ? config.refreshAnimationType : ">" + }; + + // overflow values + if (config.value > this.config.max) this.config.value = this.config.max; + if (config.value < this.config.min) this.config.value = this.config.min; + this.originalValue = config.value; + + // canvas + this.canvas = Raphael(this.config.id, "100%", "100%"); + + // canvas dimensions + //var canvasW = document.getElementById(this.config.id).clientWidth; + //var canvasH = document.getElementById(this.config.id).clientHeight; + var canvasW = getStyle(document.getElementById(this.config.id), "width").slice(0, -2) * 1; + var canvasH = getStyle(document.getElementById(this.config.id), "height").slice(0, -2) * 1; + + // widget dimensions + var widgetW, widgetH; + if ((canvasW / canvasH) > 1.25) { + widgetW = 1.25 * canvasH; + widgetH = canvasH; + } else { + widgetW = canvasW; + widgetH = canvasW / 1.25; + } + + // delta + var dx = (canvasW - widgetW)/2; + var dy = (canvasH - widgetH)/2; + + // title + var titleFontSize = ((widgetH / 8) > 10) ? (widgetH / 10) : 10; + var titleX = dx + widgetW / 2; + var titleY = dy + widgetH / 6.5; + + // value + var valueFontSize = ((widgetH / 6.4) > 16) ? (widgetH / 6.4) : 16; + var valueX = dx + widgetW / 2; + var valueY = dy + widgetH / 1.4; + + // label + var labelFontSize = ((widgetH / 16) > 10) ? (widgetH / 16) : 10; + var labelX = dx + widgetW / 2; + //var labelY = dy + widgetH / 1.126760563380282; + var labelY = valueY + valueFontSize / 2 + 6; + + // min + var minFontSize = ((widgetH / 16) > 10) ? (widgetH / 16) : 10; + var minX = dx + (widgetW / 10) + (widgetW / 6.666666666666667 * this.config.gaugeWidthScale) / 2 ; + var minY = dy + widgetH / 1.126760563380282; + + // max + var maxFontSize = ((widgetH / 16) > 10) ? (widgetH / 16) : 10; + var maxX = dx + widgetW - (widgetW / 10) - (widgetW / 6.666666666666667 * this.config.gaugeWidthScale) / 2 ; + var maxY = dy + widgetH / 1.126760563380282; + + // parameters + this.params = { + canvasW : canvasW, + canvasH : canvasH, + widgetW : widgetW, + widgetH : widgetH, + dx : dx, + dy : dy, + titleFontSize : titleFontSize, + titleX : titleX, + titleY : titleY, + valueFontSize : valueFontSize, + valueX : valueX, + valueY : valueY, + labelFontSize : labelFontSize, + labelX : labelX, + labelY : labelY, + minFontSize : minFontSize, + minX : minX, + minY : minY, + maxFontSize : maxFontSize, + maxX : maxX, + maxY : maxY + }; + + // pki - custom attribute for generating gauge paths + this.canvas.customAttributes.pki = function (value, min, max, w, h, dx, dy, gws) { + + var alpha = (1 - (value - min) / (max - min)) * Math.PI , + Ro = w / 2 - w / 10, + Ri = Ro - w / 6.666666666666667 * gws, + + Cx = w / 2 + dx, + Cy = h / 1.25 + dy, + + Xo = w / 2 + dx + Ro * Math.cos(alpha), + Yo = h - (h - Cy) + dy - Ro * Math.sin(alpha), + Xi = w / 2 + dx + Ri * Math.cos(alpha), + Yi = h - (h - Cy) + dy - Ri * Math.sin(alpha), + path; + + path += "M" + (Cx - Ri) + "," + Cy + " "; + path += "L" + (Cx - Ro) + "," + Cy + " "; + path += "A" + Ro + "," + Ro + " 0 0,1 " + Xo + "," + Yo + " "; + path += "L" + Xi + "," + Yi + " "; + path += "A" + Ri + "," + Ri + " 0 0,0 " + (Cx - Ri) + "," + Cy + " "; + path += "z "; + return { path: path }; + } + + // gauge + this.gauge = this.canvas.path().attr({ + "stroke": "none", + "fill": this.config.gaugeColor, + pki: [this.config.max, this.config.min, this.config.max, this.params.widgetW, this.params.widgetH, this.params.dx, this.params.dy, this.config.gaugeWidthScale] + }); + this.gauge.id = this.config.id+"-gauge"; + + // level + this.level = this.canvas.path().attr({ + "stroke": "none", + "fill": getColorForPercentage((this.config.value - this.config.min) / (this.config.max - this.config.min), this.config.levelColors, this.config.levelColorsGradient), + pki: [this.config.min, this.config.min, this.config.max, this.params.widgetW, this.params.widgetH, this.params.dx, this.params.dy, this.config.gaugeWidthScale] + }); + this.level.id = this.config.id+"-level"; + + // title + this.txtTitle = this.canvas.text(this.params.titleX, this.params.titleY, this.config.title); + this.txtTitle. attr({ + "font-size":this.params.titleFontSize, + "font-weight":"bold", + "font-family":"Arial", + "fill":this.config.titleFontColor, + "fill-opacity":"1" + }); + this.txtTitle.id = this.config.id+"-txttitle"; + + // value + this.txtValue = this.canvas.text(this.params.valueX, this.params.valueY, this.originalValue); + this.txtValue. attr({ + "font-size":this.params.valueFontSize, + "font-weight":"bold", + "font-family":"Arial", + "fill":this.config.valueFontColor, + "fill-opacity":"0" + }); + this.txtValue.id = this.config.id+"-txtvalue"; + + // label + this.txtLabel = this.canvas.text(this.params.labelX, this.params.labelY, this.config.label); + this.txtLabel. attr({ + "font-size":this.params.labelFontSize, + "font-weight":"normal", + "font-family":"Arial", + "fill":this.config.labelFontColor, + "fill-opacity":"0" + }); + this.txtLabel.id = this.config.id+"-txtlabel"; + + // min + this.txtMin = this.canvas.text(this.params.minX, this.params.minY, this.config.min); + this.txtMin. attr({ + "font-size":this.params.minFontSize, + "font-weight":"normal", + "font-family":"Arial", + "fill":this.config.labelFontColor, + "fill-opacity": (this.config.showMinMax == true)? "1" : "0" + }); + this.txtMin.id = this.config.id+"-txtmin"; + + // max + this.txtMax = this.canvas.text(this.params.maxX, this.params.maxY, this.config.max); + this.txtMax. attr({ + "font-size":this.params.maxFontSize, + "font-weight":"normal", + "font-family":"Arial", + "fill":this.config.labelFontColor, + "fill-opacity": (this.config.showMinMax == true)? "1" : "0" + }); + this.txtMax.id = this.config.id+"-txtmax"; + + var defs = this.canvas.canvas.childNodes[1]; + var svg = "http://www.w3.org/2000/svg"; + + + + if (ie < 9) { + onCreateElementNsReady(function() { + this.generateShadow(); + }); + } else { + this.generateShadow(svg, defs); + } + + // animate + this.level.animate({pki: [this.config.value, this.config.min, this.config.max, this.params.widgetW, this.params.widgetH, this.params.dx, this.params.dy, this.config.gaugeWidthScale]}, this.config.startAnimationTime, this.config.startAnimationType); + + this.txtValue.animate({"fill-opacity":"1"}, this.config.startAnimationTime, this.config.startAnimationType); + this.txtLabel.animate({"fill-opacity":"1"}, this.config.startAnimationTime, this.config.startAnimationType); +}; + +// refresh gauge level +JustGage.prototype.refresh = function(val) { + // overflow values + originalVal = val; + if (val > this.config.max) {val = this.config.max;} + if (val < this.config.min) {val = this.config.min;} + + var color = getColorForPercentage((val - this.config.min) / (this.config.max - this.config.min), this.config.levelColors, this.config.levelColorsGradient); + this.canvas.getById(this.config.id+"-txtvalue").attr({"text":originalVal}); + this.canvas.getById(this.config.id+"-level").animate({pki: [val, this.config.min, this.config.max, this.params.widgetW, this.params.widgetH, this.params.dx, this.params.dy, this.config.gaugeWidthScale], "fill":color}, this.config.refreshAnimationTime, this.config.refreshAnimationType); +}; + +var percentColors = [ + "#a9d70b", + "#f9c802", + "#ff0000" +] + +JustGage.prototype.generateShadow = function(svg, defs) { + // FILTER + var gaussFilter=document.createElementNS(svg,"filter"); + gaussFilter.setAttribute("id", this.config.id + "-inner-shadow"); + defs.appendChild(gaussFilter); + + // offset + var feOffset = document.createElementNS(svg,"feOffset"); + feOffset.setAttribute("dx", 0); + feOffset.setAttribute("dy", this.config.shadowVerticalOffset); + gaussFilter.appendChild(feOffset); + + // blur + var feGaussianBlur = document.createElementNS(svg,"feGaussianBlur"); + feGaussianBlur.setAttribute("result","offset-blur"); + feGaussianBlur.setAttribute("stdDeviation", this.config.shadowSize); + gaussFilter.appendChild(feGaussianBlur); + + // composite 1 + var feComposite1 = document.createElementNS(svg,"feComposite"); + feComposite1.setAttribute("operator","out"); + feComposite1.setAttribute("in", "SourceGraphic"); + feComposite1.setAttribute("in2","offset-blur"); + feComposite1.setAttribute("result","inverse"); + gaussFilter.appendChild(feComposite1); + + // flood + var feFlood = document.createElementNS(svg,"feFlood"); + feFlood.setAttribute("flood-color","black"); + feFlood.setAttribute("flood-opacity", this.config.shadowOpacity); + feFlood.setAttribute("result","color"); + gaussFilter.appendChild(feFlood); + + // composite 2 + var feComposite2 = document.createElementNS(svg,"feComposite"); + feComposite2.setAttribute("operator","in"); + feComposite2.setAttribute("in", "color"); + feComposite2.setAttribute("in2","inverse"); + feComposite2.setAttribute("result","shadow"); + gaussFilter.appendChild(feComposite2); + + // composite 3 + var feComposite3 = document.createElementNS(svg,"feComposite"); + feComposite3.setAttribute("operator","over"); + feComposite3.setAttribute("in", "shadow"); + feComposite3.setAttribute("in2","SourceGraphic"); + gaussFilter.appendChild(feComposite3); + + // set shadow + if (this.config.showInnerShadow == true) { + this.canvas.canvas.childNodes[2].setAttribute("filter", "url(#" + this.config.id + "-inner-shadow)"); + this.canvas.canvas.childNodes[3].setAttribute("filter", "url(#" + this.config.id + "-inner-shadow)"); + } +} + +var getColorForPercentage = function(pct, col, grad) { + + var no = col.length; + if (no === 1) return col[0]; + var inc = (grad) ? (1 / (no - 1)) : (1 / no); + var colors = new Array(); + for (var i = 0; i < col.length; i++) { + var percentage = (grad) ? (inc * i) : (inc * (i + 1)); + var rval = parseInt((cutHex(col[i])).substring(0,2),16); + var gval = parseInt((cutHex(col[i])).substring(2,4),16); + var bval = parseInt((cutHex(col[i])).substring(4,6),16); + colors[i] = { pct: percentage, color: { r: rval, g: gval, b: bval } }; + } + + if(pct == 0) return 'rgb(' + [colors[0].color.r, colors[0].color.g, colors[0].color.b].join(',') + ')'; + for (var i = 0; i < colors.length; i++) { + if (pct <= colors[i].pct) { + if (grad == true) { + var lower = colors[i - 1]; + var upper = colors[i]; + var range = upper.pct - lower.pct; + var rangePct = (pct - lower.pct) / range; + var pctLower = 1 - rangePct; + var pctUpper = rangePct; + var color = { + r: Math.floor(lower.color.r * pctLower + upper.color.r * pctUpper), + g: Math.floor(lower.color.g * pctLower + upper.color.g * pctUpper), + b: Math.floor(lower.color.b * pctLower + upper.color.b * pctUpper) + }; + return 'rgb(' + [color.r, color.g, color.b].join(',') + ')'; + } else { + return 'rgb(' + [colors[i].color.r, colors[i].color.g, colors[i].color.b].join(',') + ')'; + } + } + } +} + +function getRandomInt (min, max) { + return Math.floor(Math.random() * (max - min + 1)) + min; +} + +function cutHex(str) {return (str.charAt(0)=="#") ? str.substring(1,7):str} + +function getStyle(oElm, strCssRule){ + var strValue = ""; + if(document.defaultView && document.defaultView.getComputedStyle){ + strValue = document.defaultView.getComputedStyle(oElm, "").getPropertyValue(strCssRule); + } + else if(oElm.currentStyle){ + strCssRule = strCssRule.replace(/\-(\w)/g, function (strMatch, p1){ + return p1.toUpperCase(); + }); + strValue = oElm.currentStyle[strCssRule]; + } + return strValue; +} + + function onCreateElementNsReady(func) { + if (document.createElementNS != undefined) { + func(); + } else { + setTimeout(function() { onCreateElementNsReady(func); }, 100); + } +} + +// ---------------------------------------------------------- +// A short snippet for detecting versions of IE in JavaScript +// without resorting to user-agent sniffing +// ---------------------------------------------------------- +// If you're not in IE (or IE version is less than 5) then: +// ie === undefined +// If you're in IE (>=5) then you can determine which version: +// ie === 7; // IE7 +// Thus, to detect IE: +// if (ie) {} +// And to detect the version: +// ie === 6 // IE6 +// ie > 7 // IE8, IE9 ... +// ie < 9 // Anything less than IE9 +// ---------------------------------------------------------- + +// UPDATE: Now using Live NodeList idea from @jdalton + +var ie = (function(){ + + var undef, + v = 3, + div = document.createElement('div'), + all = div.getElementsByTagName('i'); + + while ( + div.innerHTML = '', + all[0] + ); + + return v > 4 ? v : undef; + +}()); diff --git a/rpimonitor/web/js/justgage.1.0.1.min.js b/rpimonitor/web/js/justgage.1.0.1.min.js index 5e0e534..3c1990c 100644 --- a/rpimonitor/web/js/justgage.1.0.1.min.js +++ b/rpimonitor/web/js/justgage.1.0.1.min.js @@ -1,12 +1,12 @@ -/** - * JustGage - a handy JavaScript plugin for generating and animating nice & clean dashboard gauges. - * Copyright (c) 2012 Bojan Djuricic - pindjur(at)gmail(dot)com | http://www.madcog.com - * Licensed under MIT. - * Date: 31/07/2012 - * @author Bojan Djuricic (@Toorshia) - * @version 1.0 - * - * http://www.justgage.com - */ - -JustGage=function(x){if(!x.id){alert("Missing id parameter for gauge!");return false}if(!document.getElementById(x.id)){alert('No element with id: "'+x.id+'" found!');return false}this.config={id:x.id,title:(x.title)?x.title:"Title",titleFontColor:(x.titleFontColor)?x.titleFontColor:"#999999",value:(x.value)?x.value:0,valueFontColor:(x.valueFontColor)?x.valueFontColor:"#010101",min:(x.min)?x.min:0,max:(x.max)?x.max:100,showMinMax:(x.showMinMax!=null)?x.showMinMax:true,gaugeWidthScale:(x.gaugeWidthScale)?x.gaugeWidthScale:1,gaugeColor:(x.gaugeColor)?x.gaugeColor:"#edebeb",label:(x.label)?x.label:"",showInnerShadow:(x.showInnerShadow!=null)?x.showInnerShadow:true,shadowOpacity:(x.shadowOpacity)?x.shadowOpacity:0.2,shadowSize:(x.shadowSize)?x.shadowSize:5,shadowVerticalOffset:(x.shadowVerticalOffset)?x.shadowVerticalOffset:3,levelColors:(x.levelColors)?x.levelColors:percentColors,levelColorsGradient:(x.levelColorsGradient!=null)?x.levelColorsGradient:true,labelFontColor:(x.labelFontColor)?x.labelFontColor:"#b3b3b3",startAnimationTime:(x.startAnimationTime)?x.startAnimationTime:700,startAnimationType:(x.startAnimationType)?x.startAnimationType:">",refreshAnimationTime:(x.refreshAnimationTime)?x.refreshAnimationTime:700,refreshAnimationType:(x.refreshAnimationType)?x.refreshAnimationType:">"};if(x.value>this.config.max){this.config.value=this.config.max}if(x.value1.25){c=1.25*q;l=q}else{c=d;l=d/1.25}var i=(d-c)/2;var g=(q-l)/2;var s=((l/8)>10)?(l/10):10;var n=i+c/2;var m=g+l/6.5;var k=((l/6.4)>16)?(l/6.4):16;var r=i+c/2;var p=g+l/1.4;var b=((l/16)>10)?(l/16):10;var f=i+c/2;var e=p+k/2+6;var j=((l/16)>10)?(l/16):10;var w=i+(c/10)+(c/6.666666666666667*this.config.gaugeWidthScale)/2;var v=g+l/1.126760563380282;var h=((l/16)>10)?(l/16):10;var u=i+c-(c/10)-(c/6.666666666666667*this.config.gaugeWidthScale)/2;var t=g+l/1.126760563380282;this.params={canvasW:d,canvasH:q,widgetW:c,widgetH:l,dx:i,dy:g,titleFontSize:s,titleX:n,titleY:m,valueFontSize:k,valueX:r,valueY:p,labelFontSize:b,labelX:f,labelY:e,minFontSize:j,minX:w,minY:v,maxFontSize:h,maxX:u,maxY:t};this.canvas.customAttributes.pki=function(K,L,N,E,O,F,D,y){var B=(1-(K-L)/(N-L))*Math.PI,G=E/2-E/10,J=G-E/6.666666666666667*y,C=E/2+F,A=O/1.25+D,H=E/2+F+G*Math.cos(B),P=O-(O-A)+D-G*Math.sin(B),M=E/2+F+J*Math.cos(B),z=O-(O-A)+D-J*Math.sin(B),I;I+="M"+(C-J)+","+A+" ";I+="L"+(C-G)+","+A+" ";I+="A"+G+","+G+" 0 0,1 "+H+","+P+" ";I+="L"+M+","+z+" ";I+="A"+J+","+J+" 0 0,0 "+(C-J)+","+A+" ";I+="z ";return{path:I}};this.gauge=this.canvas.path().attr({stroke:"none",fill:this.config.gaugeColor,pki:[this.config.max,this.config.min,this.config.max,this.params.widgetW,this.params.widgetH,this.params.dx,this.params.dy,this.config.gaugeWidthScale]});this.gauge.id=this.config.id+"-gauge";this.level=this.canvas.path().attr({stroke:"none",fill:getColorForPercentage((this.config.value-this.config.min)/(this.config.max-this.config.min),this.config.levelColors,this.config.levelColorsGradient),pki:[this.config.min,this.config.min,this.config.max,this.params.widgetW,this.params.widgetH,this.params.dx,this.params.dy,this.config.gaugeWidthScale]});this.level.id=this.config.id+"-level";this.txtTitle=this.canvas.text(this.params.titleX,this.params.titleY,this.config.title);this.txtTitle.attr({"font-size":this.params.titleFontSize,"font-weight":"bold","font-family":"Arial",fill:this.config.titleFontColor,"fill-opacity":"1"});this.txtTitle.id=this.config.id+"-txttitle";this.txtValue=this.canvas.text(this.params.valueX,this.params.valueY,this.originalValue);this.txtValue.attr({"font-size":this.params.valueFontSize,"font-weight":"bold","font-family":"Arial",fill:this.config.valueFontColor,"fill-opacity":"0"});this.txtValue.id=this.config.id+"-txtvalue";this.txtLabel=this.canvas.text(this.params.labelX,this.params.labelY,this.config.label);this.txtLabel.attr({"font-size":this.params.labelFontSize,"font-weight":"normal","font-family":"Arial",fill:this.config.labelFontColor,"fill-opacity":"0"});this.txtLabel.id=this.config.id+"-txtlabel";this.txtMin=this.canvas.text(this.params.minX,this.params.minY,this.config.min);this.txtMin.attr({"font-size":this.params.minFontSize,"font-weight":"normal","font-family":"Arial",fill:this.config.labelFontColor,"fill-opacity":(this.config.showMinMax==true)?"1":"0"});this.txtMin.id=this.config.id+"-txtmin";this.txtMax=this.canvas.text(this.params.maxX,this.params.maxY,this.config.max);this.txtMax.attr({"font-size":this.params.maxFontSize,"font-weight":"normal","font-family":"Arial",fill:this.config.labelFontColor,"fill-opacity":(this.config.showMinMax==true)?"1":"0"});this.txtMax.id=this.config.id+"-txtmax";var a=this.canvas.canvas.childNodes[1];var o="http://www.w3.org/2000/svg";if(ie<9){onCreateElementNsReady(function(){this.generateShadow()})}else{this.generateShadow(o,a)}this.level.animate({pki:[this.config.value,this.config.min,this.config.max,this.params.widgetW,this.params.widgetH,this.params.dx,this.params.dy,this.config.gaugeWidthScale]},this.config.startAnimationTime,this.config.startAnimationType);this.txtValue.animate({"fill-opacity":"1"},this.config.startAnimationTime,this.config.startAnimationType);this.txtLabel.animate({"fill-opacity":"1"},this.config.startAnimationTime,this.config.startAnimationType)};JustGage.prototype.refresh=function(b){originalVal=b;if(b>this.config.max){b=this.config.max}if(b",b[0]){}return a>4?a:c}()); \ No newline at end of file +/** + * JustGage - a handy JavaScript plugin for generating and animating nice & clean dashboard gauges. + * Copyright (c) 2012 Bojan Djuricic - pindjur(at)gmail(dot)com | http://www.madcog.com + * Licensed under MIT. + * Date: 31/07/2012 + * @author Bojan Djuricic (@Toorshia) + * @version 1.0 + * + * http://www.justgage.com + */ + +JustGage=function(x){if(!x.id){alert("Missing id parameter for gauge!");return false}if(!document.getElementById(x.id)){alert('No element with id: "'+x.id+'" found!');return false}this.config={id:x.id,title:(x.title)?x.title:"Title",titleFontColor:(x.titleFontColor)?x.titleFontColor:"#999999",value:(x.value)?x.value:0,valueFontColor:(x.valueFontColor)?x.valueFontColor:"#010101",min:(x.min)?x.min:0,max:(x.max)?x.max:100,showMinMax:(x.showMinMax!=null)?x.showMinMax:true,gaugeWidthScale:(x.gaugeWidthScale)?x.gaugeWidthScale:1,gaugeColor:(x.gaugeColor)?x.gaugeColor:"#edebeb",label:(x.label)?x.label:"",showInnerShadow:(x.showInnerShadow!=null)?x.showInnerShadow:true,shadowOpacity:(x.shadowOpacity)?x.shadowOpacity:0.2,shadowSize:(x.shadowSize)?x.shadowSize:5,shadowVerticalOffset:(x.shadowVerticalOffset)?x.shadowVerticalOffset:3,levelColors:(x.levelColors)?x.levelColors:percentColors,levelColorsGradient:(x.levelColorsGradient!=null)?x.levelColorsGradient:true,labelFontColor:(x.labelFontColor)?x.labelFontColor:"#b3b3b3",startAnimationTime:(x.startAnimationTime)?x.startAnimationTime:700,startAnimationType:(x.startAnimationType)?x.startAnimationType:">",refreshAnimationTime:(x.refreshAnimationTime)?x.refreshAnimationTime:700,refreshAnimationType:(x.refreshAnimationType)?x.refreshAnimationType:">"};if(x.value>this.config.max){this.config.value=this.config.max}if(x.value1.25){c=1.25*q;l=q}else{c=d;l=d/1.25}var i=(d-c)/2;var g=(q-l)/2;var s=((l/8)>10)?(l/10):10;var n=i+c/2;var m=g+l/6.5;var k=((l/6.4)>16)?(l/6.4):16;var r=i+c/2;var p=g+l/1.4;var b=((l/16)>10)?(l/16):10;var f=i+c/2;var e=p+k/2+6;var j=((l/16)>10)?(l/16):10;var w=i+(c/10)+(c/6.666666666666667*this.config.gaugeWidthScale)/2;var v=g+l/1.126760563380282;var h=((l/16)>10)?(l/16):10;var u=i+c-(c/10)-(c/6.666666666666667*this.config.gaugeWidthScale)/2;var t=g+l/1.126760563380282;this.params={canvasW:d,canvasH:q,widgetW:c,widgetH:l,dx:i,dy:g,titleFontSize:s,titleX:n,titleY:m,valueFontSize:k,valueX:r,valueY:p,labelFontSize:b,labelX:f,labelY:e,minFontSize:j,minX:w,minY:v,maxFontSize:h,maxX:u,maxY:t};this.canvas.customAttributes.pki=function(K,L,N,E,O,F,D,y){var B=(1-(K-L)/(N-L))*Math.PI,G=E/2-E/10,J=G-E/6.666666666666667*y,C=E/2+F,A=O/1.25+D,H=E/2+F+G*Math.cos(B),P=O-(O-A)+D-G*Math.sin(B),M=E/2+F+J*Math.cos(B),z=O-(O-A)+D-J*Math.sin(B),I;I+="M"+(C-J)+","+A+" ";I+="L"+(C-G)+","+A+" ";I+="A"+G+","+G+" 0 0,1 "+H+","+P+" ";I+="L"+M+","+z+" ";I+="A"+J+","+J+" 0 0,0 "+(C-J)+","+A+" ";I+="z ";return{path:I}};this.gauge=this.canvas.path().attr({stroke:"none",fill:this.config.gaugeColor,pki:[this.config.max,this.config.min,this.config.max,this.params.widgetW,this.params.widgetH,this.params.dx,this.params.dy,this.config.gaugeWidthScale]});this.gauge.id=this.config.id+"-gauge";this.level=this.canvas.path().attr({stroke:"none",fill:getColorForPercentage((this.config.value-this.config.min)/(this.config.max-this.config.min),this.config.levelColors,this.config.levelColorsGradient),pki:[this.config.min,this.config.min,this.config.max,this.params.widgetW,this.params.widgetH,this.params.dx,this.params.dy,this.config.gaugeWidthScale]});this.level.id=this.config.id+"-level";this.txtTitle=this.canvas.text(this.params.titleX,this.params.titleY,this.config.title);this.txtTitle.attr({"font-size":this.params.titleFontSize,"font-weight":"bold","font-family":"Arial",fill:this.config.titleFontColor,"fill-opacity":"1"});this.txtTitle.id=this.config.id+"-txttitle";this.txtValue=this.canvas.text(this.params.valueX,this.params.valueY,this.originalValue);this.txtValue.attr({"font-size":this.params.valueFontSize,"font-weight":"bold","font-family":"Arial",fill:this.config.valueFontColor,"fill-opacity":"0"});this.txtValue.id=this.config.id+"-txtvalue";this.txtLabel=this.canvas.text(this.params.labelX,this.params.labelY,this.config.label);this.txtLabel.attr({"font-size":this.params.labelFontSize,"font-weight":"normal","font-family":"Arial",fill:this.config.labelFontColor,"fill-opacity":"0"});this.txtLabel.id=this.config.id+"-txtlabel";this.txtMin=this.canvas.text(this.params.minX,this.params.minY,this.config.min);this.txtMin.attr({"font-size":this.params.minFontSize,"font-weight":"normal","font-family":"Arial",fill:this.config.labelFontColor,"fill-opacity":(this.config.showMinMax==true)?"1":"0"});this.txtMin.id=this.config.id+"-txtmin";this.txtMax=this.canvas.text(this.params.maxX,this.params.maxY,this.config.max);this.txtMax.attr({"font-size":this.params.maxFontSize,"font-weight":"normal","font-family":"Arial",fill:this.config.labelFontColor,"fill-opacity":(this.config.showMinMax==true)?"1":"0"});this.txtMax.id=this.config.id+"-txtmax";var a=this.canvas.canvas.childNodes[1];var o="http://www.w3.org/2000/svg";if(ie<9){onCreateElementNsReady(function(){this.generateShadow()})}else{this.generateShadow(o,a)}this.level.animate({pki:[this.config.value,this.config.min,this.config.max,this.params.widgetW,this.params.widgetH,this.params.dx,this.params.dy,this.config.gaugeWidthScale]},this.config.startAnimationTime,this.config.startAnimationType);this.txtValue.animate({"fill-opacity":"1"},this.config.startAnimationTime,this.config.startAnimationType);this.txtLabel.animate({"fill-opacity":"1"},this.config.startAnimationTime,this.config.startAnimationType)};JustGage.prototype.refresh=function(b){originalVal=b;if(b>this.config.max){b=this.config.max}if(b",b[0]){}return a>4?a:c}()); diff --git a/rpimonitor/web/js/raphael.2.1.0.min.js b/rpimonitor/web/js/raphael.2.1.0.min.js index 4153bba..401c23c 100644 --- a/rpimonitor/web/js/raphael.2.1.0.min.js +++ b/rpimonitor/web/js/raphael.2.1.0.min.js @@ -1,10 +1,10 @@ -// +--------------------------------------------------------------------+ \\ -// ¦ Raphaël 2.1.0 - JavaScript Vector Library ¦ \\ -// +--------------------------------------------------------------------¦ \\ -// ¦ Copyright © 2008-2012 Dmitry Baranovskiy (http://raphaeljs.com) ¦ \\ -// ¦ Copyright © 2008-2012 Sencha Labs (http://sencha.com) ¦ \\ -// +--------------------------------------------------------------------¦ \\ -// ¦ Licensed under the MIT (http://raphaeljs.com/license.html) license.¦ \\ -// +--------------------------------------------------------------------+ \\ - -(function(a){var b="0.3.4",c="hasOwnProperty",d=/[\.\/]/,e="*",f=function(){},g=function(a,b){return a-b},h,i,j={n:{}},k=function(a,b){var c=j,d=i,e=Array.prototype.slice.call(arguments,2),f=k.listeners(a),l=0,m=!1,n,o=[],p={},q=[],r=h,s=[];h=a,i=0;for(var t=0,u=f.length;tf*b.top){e=b.percents[y],p=b.percents[y-1]||0,t=t/b.top*(e-p),o=b.percents[y+1],j=b.anim[e];break}f&&d.attr(b.anim[b.percents[y]])}if(!!j){if(!k){for(var A in j)if(j[g](A))if(U[g](A)||d.paper.customAttributes[g](A)){u[A]=d.attr(A),u[A]==null&&(u[A]=T[A]),v[A]=j[A];switch(U[A]){case C:w[A]=(v[A]-u[A])/t;break;case"colour":u[A]=a.getRGB(u[A]);var B=a.getRGB(v[A]);w[A]={r:(B.r-u[A].r)/t,g:(B.g-u[A].g)/t,b:(B.b-u[A].b)/t};break;case"path":var D=bR(u[A],v[A]),E=D[1];u[A]=D[0],w[A]=[];for(y=0,z=u[A].length;yd)return d;while(cf?c=e:d=e,e=(d-c)/2+c}return e}function n(a,b){var c=o(a,b);return((l*c+k)*c+j)*c}function m(a){return((i*a+h)*a+g)*a}var g=3*b,h=3*(d-b)-g,i=1-g-h,j=3*c,k=3*(e-c)-j,l=1-j-k;return n(a,1/(200*f))}function cq(){return this.x+q+this.y+q+this.width+" × "+this.height}function cp(){return this.x+q+this.y}function cb(a,b,c,d,e,f){a!=null?(this.a=+a,this.b=+b,this.c=+c,this.d=+d,this.e=+e,this.f=+f):(this.a=1,this.b=0,this.c=0,this.d=1,this.e=0,this.f=0)}function bH(b,c,d){b=a._path2curve(b),c=a._path2curve(c);var e,f,g,h,i,j,k,l,m,n,o=d?0:[];for(var p=0,q=b.length;p=0&&y<=1&&A>=0&&A<=1&&(d?n++:n.push({x:x.x,y:x.y,t1:y,t2:A}))}}return n}function bF(a,b){return bG(a,b,1)}function bE(a,b){return bG(a,b)}function bD(a,b,c,d,e,f,g,h){if(!(x(a,c)x(e,g)||x(b,d)x(f,h))){var i=(a*d-b*c)*(e-g)-(a-c)*(e*h-f*g),j=(a*d-b*c)*(f-h)-(b-d)*(e*h-f*g),k=(a-c)*(f-h)-(b-d)*(e-g);if(!k)return;var l=i/k,m=j/k,n=+l.toFixed(2),o=+m.toFixed(2);if(n<+y(a,c).toFixed(2)||n>+x(a,c).toFixed(2)||n<+y(e,g).toFixed(2)||n>+x(e,g).toFixed(2)||o<+y(b,d).toFixed(2)||o>+x(b,d).toFixed(2)||o<+y(f,h).toFixed(2)||o>+x(f,h).toFixed(2))return;return{x:l,y:m}}}function bC(a,b,c,d,e,f,g,h,i){if(!(i<0||bB(a,b,c,d,e,f,g,h)n)k/=2,l+=(m1?1:i<0?0:i;var j=i/2,k=12,l=[-0.1252,.1252,-0.3678,.3678,-0.5873,.5873,-0.7699,.7699,-0.9041,.9041,-0.9816,.9816],m=[.2491,.2491,.2335,.2335,.2032,.2032,.1601,.1601,.1069,.1069,.0472,.0472],n=0;for(var o=0;od;d+=2){var f=[{x:+a[d-2],y:+a[d-1]},{x:+a[d],y:+a[d+1]},{x:+a[d+2],y:+a[d+3]},{x:+a[d+4],y:+a[d+5]}];b?d?e-4==d?f[3]={x:+a[0],y:+a[1]}:e-2==d&&(f[2]={x:+a[0],y:+a[1]},f[3]={x:+a[2],y:+a[3]}):f[0]={x:+a[e-2],y:+a[e-1]}:e-4==d?f[3]=f[2]:d||(f[0]={x:+a[d],y:+a[d+1]}),c.push(["C",(-f[0].x+6*f[1].x+f[2].x)/6,(-f[0].y+6*f[1].y+f[2].y)/6,(f[1].x+6*f[2].x-f[3].x)/6,(f[1].y+6*f[2].y-f[3].y)/6,f[2].x,f[2].y])}return c}function bx(){return this.hex}function bv(a,b,c){function d(){var e=Array.prototype.slice.call(arguments,0),f=e.join("?"),h=d.cache=d.cache||{},i=d.count=d.count||[];if(h[g](f)){bu(i,f);return c?c(h[f]):h[f]}i.length>=1e3&&delete h[i.shift()],i.push(f),h[f]=a[m](b,e);return c?c(h[f]):h[f]}return d}function bu(a,b){for(var c=0,d=a.length;c',bl=bk.firstChild,bl.style.behavior="url(#default#VML)";if(!bl||typeof bl.adj!="object")return a.type=p;bk=null}a.svg=!(a.vml=a.type=="VML"),a._Paper=j,a.fn=k=j.prototype=a.prototype,a._id=0,a._oid=0,a.is=function(a,b){b=v.call(b);if(b=="finite")return!M[g](+a);if(b=="array")return a instanceof Array;return b=="null"&&a===null||b==typeof a&&a!==null||b=="object"&&a===Object(a)||b=="array"&&Array.isArray&&Array.isArray(a)||H.call(a).slice(8,-1).toLowerCase()==b},a.angle=function(b,c,d,e,f,g){if(f==null){var h=b-d,i=c-e;if(!h&&!i)return 0;return(180+w.atan2(-i,-h)*180/B+360)%360}return a.angle(b,c,f,g)-a.angle(d,e,f,g)},a.rad=function(a){return a%360*B/180},a.deg=function(a){return a*180/B%360},a.snapTo=function(b,c,d){d=a.is(d,"finite")?d:10;if(a.is(b,E)){var e=b.length;while(e--)if(z(b[e]-c)<=d)return b[e]}else{b=+b;var f=c%b;if(fb-d)return c-f+b}return c};var bn=a.createUUID=function(a,b){return function(){return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(a,b).toUpperCase()}}(/[xy]/g,function(a){var b=w.random()*16|0,c=a=="x"?b:b&3|8;return c.toString(16)});a.setWindow=function(b){eve("raphael.setWindow",a,h.win,b),h.win=b,h.doc=h.win.document,a._engine.initWin&&a._engine.initWin(h.win)};var bo=function(b){if(a.vml){var c=/^\s+|\s+$/g,d;try{var e=new ActiveXObject("htmlfile");e.write(""),e.close(),d=e.body}catch(f){d=createPopup().document.body}var g=d.createTextRange();bo=bv(function(a){try{d.style.color=r(a).replace(c,p);var b=g.queryCommandValue("ForeColor");b=(b&255)<<16|b&65280|(b&16711680)>>>16;return"#"+("000000"+b.toString(16)).slice(-6)}catch(e){return"none"}})}else{var i=h.doc.createElement("i");i.title="Raphaël Colour Picker",i.style.display="none",h.doc.body.appendChild(i),bo=bv(function(a){i.style.color=a;return h.doc.defaultView.getComputedStyle(i,p).getPropertyValue("color")})}return bo(b)},bp=function(){return"hsb("+[this.h,this.s,this.b]+")"},bq=function(){return"hsl("+[this.h,this.s,this.l]+")"},br=function(){return this.hex},bs=function(b,c,d){c==null&&a.is(b,"object")&&"r"in b&&"g"in b&&"b"in b&&(d=b.b,c=b.g,b=b.r);if(c==null&&a.is(b,D)){var e=a.getRGB(b);b=e.r,c=e.g,d=e.b}if(b>1||c>1||d>1)b/=255,c/=255,d/=255;return[b,c,d]},bt=function(b,c,d,e){b*=255,c*=255,d*=255;var f={r:b,g:c,b:d,hex:a.rgb(b,c,d),toString:br};a.is(e,"finite")&&(f.opacity=e);return f};a.color=function(b){var c;a.is(b,"object")&&"h"in b&&"s"in b&&"b"in b?(c=a.hsb2rgb(b),b.r=c.r,b.g=c.g,b.b=c.b,b.hex=c.hex):a.is(b,"object")&&"h"in b&&"s"in b&&"l"in b?(c=a.hsl2rgb(b),b.r=c.r,b.g=c.g,b.b=c.b,b.hex=c.hex):(a.is(b,"string")&&(b=a.getRGB(b)),a.is(b,"object")&&"r"in b&&"g"in b&&"b"in b?(c=a.rgb2hsl(b),b.h=c.h,b.s=c.s,b.l=c.l,c=a.rgb2hsb(b),b.v=c.b):(b={hex:"none"},b.r=b.g=b.b=b.h=b.s=b.v=b.l=-1)),b.toString=br;return b},a.hsb2rgb=function(a,b,c,d){this.is(a,"object")&&"h"in a&&"s"in a&&"b"in a&&(c=a.b,b=a.s,a=a.h,d=a.o),a*=360;var e,f,g,h,i;a=a%360/60,i=c*b,h=i*(1-z(a%2-1)),e=f=g=c-i,a=~~a,e+=[i,h,0,0,h,i][a],f+=[h,i,i,h,0,0][a],g+=[0,0,h,i,i,h][a];return bt(e,f,g,d)},a.hsl2rgb=function(a,b,c,d){this.is(a,"object")&&"h"in a&&"s"in a&&"l"in a&&(c=a.l,b=a.s,a=a.h);if(a>1||b>1||c>1)a/=360,b/=100,c/=100;a*=360;var e,f,g,h,i;a=a%360/60,i=2*b*(c<.5?c:1-c),h=i*(1-z(a%2-1)),e=f=g=c-i/2,a=~~a,e+=[i,h,0,0,h,i][a],f+=[h,i,i,h,0,0][a],g+=[0,0,h,i,i,h][a];return bt(e,f,g,d)},a.rgb2hsb=function(a,b,c){c=bs(a,b,c),a=c[0],b=c[1],c=c[2];var d,e,f,g;f=x(a,b,c),g=f-y(a,b,c),d=g==0?null:f==a?(b-c)/g:f==b?(c-a)/g+2:(a-b)/g+4,d=(d+360)%6*60/360,e=g==0?0:g/f;return{h:d,s:e,b:f,toString:bp}},a.rgb2hsl=function(a,b,c){c=bs(a,b,c),a=c[0],b=c[1],c=c[2];var d,e,f,g,h,i;g=x(a,b,c),h=y(a,b,c),i=g-h,d=i==0?null:g==a?(b-c)/i:g==b?(c-a)/i+2:(a-b)/i+4,d=(d+360)%6*60/360,f=(g+h)/2,e=i==0?0:f<.5?i/(2*f):i/(2-2*f);return{h:d,s:e,l:f,toString:bq}},a._path2string=function(){return this.join(",").replace(Y,"$1")};var bw=a._preload=function(a,b){var c=h.doc.createElement("img");c.style.cssText="position:absolute;left:-9999em;top:-9999em",c.onload=function(){b.call(this),this.onload=null,h.doc.body.removeChild(this)},c.onerror=function(){h.doc.body.removeChild(this)},h.doc.body.appendChild(c),c.src=a};a.getRGB=bv(function(b){if(!b||!!((b=r(b)).indexOf("-")+1))return{r:-1,g:-1,b:-1,hex:"none",error:1,toString:bx};if(b=="none")return{r:-1,g:-1,b:-1,hex:"none",toString:bx};!X[g](b.toLowerCase().substring(0,2))&&b.charAt()!="#"&&(b=bo(b));var c,d,e,f,h,i,j,k=b.match(L);if(k){k[2]&&(f=R(k[2].substring(5),16),e=R(k[2].substring(3,5),16),d=R(k[2].substring(1,3),16)),k[3]&&(f=R((i=k[3].charAt(3))+i,16),e=R((i=k[3].charAt(2))+i,16),d=R((i=k[3].charAt(1))+i,16)),k[4]&&(j=k[4][s](W),d=Q(j[0]),j[0].slice(-1)=="%"&&(d*=2.55),e=Q(j[1]),j[1].slice(-1)=="%"&&(e*=2.55),f=Q(j[2]),j[2].slice(-1)=="%"&&(f*=2.55),k[1].toLowerCase().slice(0,4)=="rgba"&&(h=Q(j[3])),j[3]&&j[3].slice(-1)=="%"&&(h/=100));if(k[5]){j=k[5][s](W),d=Q(j[0]),j[0].slice(-1)=="%"&&(d*=2.55),e=Q(j[1]),j[1].slice(-1)=="%"&&(e*=2.55),f=Q(j[2]),j[2].slice(-1)=="%"&&(f*=2.55),(j[0].slice(-3)=="deg"||j[0].slice(-1)=="°")&&(d/=360),k[1].toLowerCase().slice(0,4)=="hsba"&&(h=Q(j[3])),j[3]&&j[3].slice(-1)=="%"&&(h/=100);return a.hsb2rgb(d,e,f,h)}if(k[6]){j=k[6][s](W),d=Q(j[0]),j[0].slice(-1)=="%"&&(d*=2.55),e=Q(j[1]),j[1].slice(-1)=="%"&&(e*=2.55),f=Q(j[2]),j[2].slice(-1)=="%"&&(f*=2.55),(j[0].slice(-3)=="deg"||j[0].slice(-1)=="°")&&(d/=360),k[1].toLowerCase().slice(0,4)=="hsla"&&(h=Q(j[3])),j[3]&&j[3].slice(-1)=="%"&&(h/=100);return a.hsl2rgb(d,e,f,h)}k={r:d,g:e,b:f,toString:bx},k.hex="#"+(16777216|f|e<<8|d<<16).toString(16).slice(1),a.is(h,"finite")&&(k.opacity=h);return k}return{r:-1,g:-1,b:-1,hex:"none",error:1,toString:bx}},a),a.hsb=bv(function(b,c,d){return a.hsb2rgb(b,c,d).hex}),a.hsl=bv(function(b,c,d){return a.hsl2rgb(b,c,d).hex}),a.rgb=bv(function(a,b,c){return"#"+(16777216|c|b<<8|a<<16).toString(16).slice(1)}),a.getColor=function(a){var b=this.getColor.start=this.getColor.start||{h:0,s:1,b:a||.75},c=this.hsb2rgb(b.h,b.s,b.b);b.h+=.075,b.h>1&&(b.h=0,b.s-=.2,b.s<=0&&(this.getColor.start={h:0,s:1,b:b.b}));return c.hex},a.getColor.reset=function(){delete this.start},a.parsePathString=function(b){if(!b)return null;var c=bz(b);if(c.arr)return bJ(c.arr);var d={a:7,c:6,h:1,l:2,m:2,r:4,q:4,s:4,t:2,v:1,z:0},e=[];a.is(b,E)&&a.is(b[0],E)&&(e=bJ(b)),e.length||r(b).replace(Z,function(a,b,c){var f=[],g=b.toLowerCase();c.replace(_,function(a,b){b&&f.push(+b)}),g=="m"&&f.length>2&&(e.push([b][n](f.splice(0,2))),g="l",b=b=="m"?"l":"L");if(g=="r")e.push([b][n](f));else while(f.length>=d[g]){e.push([b][n](f.splice(0,d[g])));if(!d[g])break}}),e.toString=a._path2string,c.arr=bJ(e);return e},a.parseTransformString=bv(function(b){if(!b)return null;var c={r:3,s:4,t:2,m:6},d=[];a.is(b,E)&&a.is(b[0],E)&&(d=bJ(b)),d.length||r(b).replace($,function(a,b,c){var e=[],f=v.call(b);c.replace(_,function(a,b){b&&e.push(+b)}),d.push([b][n](e))}),d.toString=a._path2string;return d});var bz=function(a){var b=bz.ps=bz.ps||{};b[a]?b[a].sleep=100:b[a]={sleep:100},setTimeout(function(){for(var c in b)b[g](c)&&c!=a&&(b[c].sleep--,!b[c].sleep&&delete b[c])});return b[a]};a.findDotsAtSegment=function(a,b,c,d,e,f,g,h,i){var j=1-i,k=A(j,3),l=A(j,2),m=i*i,n=m*i,o=k*a+l*3*i*c+j*3*i*i*e+n*g,p=k*b+l*3*i*d+j*3*i*i*f+n*h,q=a+2*i*(c-a)+m*(e-2*c+a),r=b+2*i*(d-b)+m*(f-2*d+b),s=c+2*i*(e-c)+m*(g-2*e+c),t=d+2*i*(f-d)+m*(h-2*f+d),u=j*a+i*c,v=j*b+i*d,x=j*e+i*g,y=j*f+i*h,z=90-w.atan2(q-s,r-t)*180/B;(q>s||r=a.x&&b<=a.x2&&c>=a.y&&c<=a.y2},a.isBBoxIntersect=function(b,c){var d=a.isPointInsideBBox;return d(c,b.x,b.y)||d(c,b.x2,b.y)||d(c,b.x,b.y2)||d(c,b.x2,b.y2)||d(b,c.x,c.y)||d(b,c.x2,c.y)||d(b,c.x,c.y2)||d(b,c.x2,c.y2)||(b.xc.x||c.xb.x)&&(b.yc.y||c.yb.y)},a.pathIntersection=function(a,b){return bH(a,b)},a.pathIntersectionNumber=function(a,b){return bH(a,b,1)},a.isPointInsidePath=function(b,c,d){var e=a.pathBBox(b);return a.isPointInsideBBox(e,c,d)&&bH(b,[["M",c,d],["H",e.x2+10]],1)%2==1},a._removedFactory=function(a){return function(){eve("raphael.log",null,"Raphaël: you are calling to method “"+a+"” of removed object",a)}};var bI=a.pathBBox=function(a){var b=bz(a);if(b.bbox)return b.bbox;if(!a)return{x:0,y:0,width:0,height:0,x2:0,y2:0};a=bR(a);var c=0,d=0,e=[],f=[],g;for(var h=0,i=a.length;h1&&(v=w.sqrt(v),c=v*c,d=v*d);var x=c*c,y=d*d,A=(f==g?-1:1)*w.sqrt(z((x*y-x*u*u-y*t*t)/(x*u*u+y*t*t))),C=A*c*u/d+(a+h)/2,D=A*-d*t/c+(b+i)/2,E=w.asin(((b-D)/d).toFixed(9)),F=w.asin(((i-D)/d).toFixed(9));E=aF&&(E=E-B*2),!g&&F>E&&(F=F-B*2)}else E=j[0],F=j[1],C=j[2],D=j[3];var G=F-E;if(z(G)>k){var H=F,I=h,J=i;F=E+k*(g&&F>E?1:-1),h=C+c*w.cos(F),i=D+d*w.sin(F),m=bO(h,i,c,d,e,0,g,I,J,[F,H,C,D])}G=F-E;var K=w.cos(E),L=w.sin(E),M=w.cos(F),N=w.sin(F),O=w.tan(G/4),P=4/3*c*O,Q=4/3*d*O,R=[a,b],S=[a+P*L,b-Q*K],T=[h+P*N,i-Q*M],U=[h,i];S[0]=2*R[0]-S[0],S[1]=2*R[1]-S[1];if(j)return[S,T,U][n](m);m=[S,T,U][n](m).join()[s](",");var V=[];for(var W=0,X=m.length;W"1e12"&&(l=.5),z(n)>"1e12"&&(n=.5),l>0&&l<1&&(q=bP(a,b,c,d,e,f,g,h,l),p.push(q.x),o.push(q.y)),n>0&&n<1&&(q=bP(a,b,c,d,e,f,g,h,n),p.push(q.x),o.push(q.y)),i=f-2*d+b-(h-2*f+d),j=2*(d-b)-2*(f-d),k=b-d,l=(-j+w.sqrt(j*j-4*i*k))/2/i,n=(-j-w.sqrt(j*j-4*i*k))/2/i,z(l)>"1e12"&&(l=.5),z(n)>"1e12"&&(n=.5),l>0&&l<1&&(q=bP(a,b,c,d,e,f,g,h,l),p.push(q.x),o.push(q.y)),n>0&&n<1&&(q=bP(a,b,c,d,e,f,g,h,n),p.push(q.x),o.push(q.y));return{min:{x:y[m](0,p),y:y[m](0,o)},max:{x:x[m](0,p),y:x[m](0,o)}}}),bR=a._path2curve=bv(function(a,b){var c=!b&&bz(a);if(!b&&c.curve)return bJ(c.curve);var d=bL(a),e=b&&bL(b),f={x:0,y:0,bx:0,by:0,X:0,Y:0,qx:null,qy:null},g={x:0,y:0,bx:0,by:0,X:0,Y:0,qx:null,qy:null},h=function(a,b){var c,d;if(!a)return["C",b.x,b.y,b.x,b.y,b.x,b.y];!(a[0]in{T:1,Q:1})&&(b.qx=b.qy=null);switch(a[0]){case"M":b.X=a[1],b.Y=a[2];break;case"A":a=["C"][n](bO[m](0,[b.x,b.y][n](a.slice(1))));break;case"S":c=b.x+(b.x-(b.bx||b.x)),d=b.y+(b.y-(b.by||b.y)),a=["C",c,d][n](a.slice(1));break;case"T":b.qx=b.x+(b.x-(b.qx||b.x)),b.qy=b.y+(b.y-(b.qy||b.y)),a=["C"][n](bN(b.x,b.y,b.qx,b.qy,a[1],a[2]));break;case"Q":b.qx=a[1],b.qy=a[2],a=["C"][n](bN(b.x,b.y,a[1],a[2],a[3],a[4]));break;case"L":a=["C"][n](bM(b.x,b.y,a[1],a[2]));break;case"H":a=["C"][n](bM(b.x,b.y,a[1],b.y));break;case"V":a=["C"][n](bM(b.x,b.y,b.x,a[1]));break;case"Z":a=["C"][n](bM(b.x,b.y,b.X,b.Y))}return a},i=function(a,b){if(a[b].length>7){a[b].shift();var c=a[b];while(c.length)a.splice(b++,0,["C"][n](c.splice(0,6)));a.splice(b,1),l=x(d.length,e&&e.length||0)}},j=function(a,b,c,f,g){a&&b&&a[g][0]=="M"&&b[g][0]!="M"&&(b.splice(g,0,["M",f.x,f.y]),c.bx=0,c.by=0,c.x=a[g][1],c.y=a[g][2],l=x(d.length,e&&e.length||0))};for(var k=0,l=x(d.length,e&&e.length||0);ke){if(c&&!l.start){m=cs(g,h,i[1],i[2],i[3],i[4],i[5],i[6],e-n),k+=["C"+m.start.x,m.start.y,m.m.x,m.m.y,m.x,m.y];if(f)return k;l.start=k,k=["M"+m.x,m.y+"C"+m.n.x,m.n.y,m.end.x,m.end.y,i[5],i[6]].join(),n+=j,g=+i[5],h=+i[6];continue}if(!b&&!c){m=cs(g,h,i[1],i[2],i[3],i[4],i[5],i[6],e-n);return{x:m.x,y:m.y,alpha:m.alpha}}}n+=j,g=+i[5],h=+i[6]}k+=i.shift()+i}l.end=k,m=b?n:c?l:a.findDotsAtSegment(g,h,i[0],i[1],i[2],i[3],i[4],i[5],1),m.alpha&&(m={x:m.x,y:m.y,alpha:m.alpha});return m}},cu=ct(1),cv=ct(),cw=ct(0,1);a.getTotalLength=cu,a.getPointAtLength=cv,a.getSubpath=function(a,b,c){if(this.getTotalLength(a)-c<1e-6)return cw(a,b).end;var d=cw(a,c,1);return b?cw(d,b).end:d},cl.getTotalLength=function(){if(this.type=="path"){if(this.node.getTotalLength)return this.node.getTotalLength();return cu(this.attrs.path)}},cl.getPointAtLength=function(a){if(this.type=="path")return cv(this.attrs.path,a)},cl.getSubpath=function(b,c){if(this.type=="path")return a.getSubpath(this.attrs.path,b,c)};var cx=a.easing_formulas={linear:function(a){return a},"<":function(a){return A(a,1.7)},">":function(a){return A(a,.48)},"<>":function(a){var b=.48-a/1.04,c=w.sqrt(.1734+b*b),d=c-b,e=A(z(d),1/3)*(d<0?-1:1),f=-c-b,g=A(z(f),1/3)*(f<0?-1:1),h=e+g+.5;return(1-h)*3*h*h+h*h*h},backIn:function(a){var b=1.70158;return a*a*((b+1)*a-b)},backOut:function(a){a=a-1;var b=1.70158;return a*a*((b+1)*a+b)+1},elastic:function(a){if(a==!!a)return a;return A(2,-10*a)*w.sin((a-.075)*2*B/.3)+1},bounce:function(a){var b=7.5625,c=2.75,d;a<1/c?d=b*a*a:a<2/c?(a-=1.5/c,d=b*a*a+.75):a<2.5/c?(a-=2.25/c,d=b*a*a+.9375):(a-=2.625/c,d=b*a*a+.984375);return d}};cx.easeIn=cx["ease-in"]=cx["<"],cx.easeOut=cx["ease-out"]=cx[">"],cx.easeInOut=cx["ease-in-out"]=cx["<>"],cx["back-in"]=cx.backIn,cx["back-out"]=cx.backOut;var cy=[],cz=window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(a){setTimeout(a,16)},cA=function(){var b=+(new Date),c=0;for(;c1&&!d.next){for(s in k)k[g](s)&&(r[s]=d.totalOrigin[s]);d.el.attr(r),cE(d.anim,d.el,d.anim.percents[0],null,d.totalOrigin,d.repeat-1)}d.next&&!d.stop&&cE(d.anim,d.el,d.next,null,d.totalOrigin,d.repeat)}}a.svg&&m&&m.paper&&m.paper.safari(),cy.length&&cz(cA)},cB=function(a){return a>255?255:a<0?0:a};cl.animateWith=function(b,c,d,e,f,g){var h=this;if(h.removed){g&&g.call(h);return h}var i=d instanceof cD?d:a.animation(d,e,f,g),j,k;cE(i,h,i.percents[0],null,h.attr());for(var l=0,m=cy.length;l.5)*2-1;i(m-.5,2)+i(n-.5,2)>.25&&(n=f.sqrt(.25-i(m-.5,2))*e+.5)&&n!=.5&&(n=n.toFixed(5)-1e-5*e)}return l}),e=e.split(/\s*\-\s*/);if(j=="linear"){var t=e.shift();t=-d(t);if(isNaN(t))return null;var u=[0,0,f.cos(a.rad(t)),f.sin(a.rad(t))],v=1/(g(h(u[2]),h(u[3]))||1);u[2]*=v,u[3]*=v,u[2]<0&&(u[0]=-u[2],u[2]=0),u[3]<0&&(u[1]=-u[3],u[3]=0)}var w=a._parseDots(e);if(!w)return null;k=k.replace(/[\(\)\s,\xb0#]/g,"_"),b.gradient&&k!=b.gradient.id&&(p.defs.removeChild(b.gradient),delete b.gradient);if(!b.gradient){s=q(j+"Gradient",{id:k}),b.gradient=s,q(s,j=="radial"?{fx:m,fy:n}:{x1:u[0],y1:u[1],x2:u[2],y2:u[3],gradientTransform:b.matrix.invert()}),p.defs.appendChild(s);for(var x=0,y=w.length;x1?G.opacity/100:G.opacity});case"stroke":G=a.getRGB(p),i.setAttribute(o,G.hex),o=="stroke"&&G[b]("opacity")&&q(i,{"stroke-opacity":G.opacity>1?G.opacity/100:G.opacity}),o=="stroke"&&d._.arrows&&("startString"in d._.arrows&&t(d,d._.arrows.startString),"endString"in d._.arrows&&t(d,d._.arrows.endString,1));break;case"gradient":(d.type=="circle"||d.type=="ellipse"||c(p).charAt()!="r")&&r(d,p);break;case"opacity":k.gradient&&!k[b]("stroke-opacity")&&q(i,{"stroke-opacity":p>1?p/100:p});case"fill-opacity":if(k.gradient){H=a._g.doc.getElementById(i.getAttribute("fill").replace(/^url\(#|\)$/g,l)),H&&(I=H.getElementsByTagName("stop"),q(I[I.length-1],{"stop-opacity":p}));break};default:o=="font-size"&&(p=e(p,10)+"px");var J=o.replace(/(\-.)/g,function(a){return a.substring(1).toUpperCase()});i.style[J]=p,d._.dirty=1,i.setAttribute(o,p)}}y(d,f),i.style.visibility=m},x=1.2,y=function(d,f){if(d.type=="text"&&!!(f[b]("text")||f[b]("font")||f[b]("font-size")||f[b]("x")||f[b]("y"))){var g=d.attrs,h=d.node,i=h.firstChild?e(a._g.doc.defaultView.getComputedStyle(h.firstChild,l).getPropertyValue("font-size"),10):10;if(f[b]("text")){g.text=f.text;while(h.firstChild)h.removeChild(h.firstChild);var j=c(f.text).split("\n"),k=[],m;for(var n=0,o=j.length;n"));var $=X.getBoundingClientRect();t.W=m.w=($.right-$.left)/Y,t.H=m.h=($.bottom-$.top)/Y,t.X=m.x,t.Y=m.y+t.H/2,("x"in i||"y"in i)&&(t.path.v=a.format("m{0},{1}l{2},{1}",f(m.x*u),f(m.y*u),f(m.x*u)+1));var _=["x","y","text","font","font-family","font-weight","font-style","font-size"];for(var ba=0,bb=_.length;ba.25&&(c=e.sqrt(.25-i(b-.5,2))*((c>.5)*2-1)+.5),m=b+n+c);return o}),f=f.split(/\s*\-\s*/);if(l=="linear"){var p=f.shift();p=-d(p);if(isNaN(p))return null}var q=a._parseDots(f);if(!q)return null;b=b.shape||b.node;if(q.length){b.removeChild(g),g.on=!0,g.method="none",g.color=q[0].color,g.color2=q[q.length-1].color;var r=[];for(var s=0,t=q.length;s')}}catch(c){F=function(a){return b.createElement("<"+a+' xmlns="urn:schemas-microsoft.com:vml" class="rvml">')}}},a._engine.initWin(a._g.win),a._engine.create=function(){var b=a._getContainer.apply(0,arguments),c=b.container,d=b.height,e,f=b.width,g=b.x,h=b.y;if(!c)throw new Error("VML container not found.");var i=new a._Paper,j=i.canvas=a._g.doc.createElement("div"),k=j.style;g=g||0,h=h||0,f=f||512,d=d||342,i.width=f,i.height=d,f==+f&&(f+="px"),d==+d&&(d+="px"),i.coordsize=u*1e3+n+u*1e3,i.coordorigin="0 0",i.span=a._g.doc.createElement("span"),i.span.style.cssText="position:absolute;left:-9999em;top:-9999em;padding:0;margin:0;line-height:1;",j.appendChild(i.span),k.cssText=a.format("top:0;left:0;width:{0};height:{1};display:inline-block;position:relative;clip:rect(0 {0} {1} 0);overflow:hidden",f,d),c==1?(a._g.doc.body.appendChild(j),k.left=g+"px",k.top=h+"px",k.position="absolute"):c.firstChild?c.insertBefore(j,c.firstChild):c.appendChild(j),i.renderfix=function(){};return i},a.prototype.clear=function(){a.eve("raphael.clear",this),this.canvas.innerHTML=o,this.span=a._g.doc.createElement("span"),this.span.style.cssText="position:absolute;left:-9999em;top:-9999em;padding:0;margin:0;line-height:1;display:inline;",this.canvas.appendChild(this.span),this.bottom=this.top=null},a.prototype.remove=function(){a.eve("raphael.remove",this),this.canvas.parentNode.removeChild(this.canvas);for(var b in this)this[b]=typeof this[b]=="function"?a._removedFactory(b):null;return!0};var G=a.st;for(var H in E)E[b](H)&&!G[b](H)&&(G[H]=function(a){return function(){var b=arguments;return this.forEach(function(c){c[a].apply(c,b)})}}(H))}(window.Raphael) \ No newline at end of file +// +--------------------------------------------------------------------+ \\ +// ¦ Raphaël 2.1.0 - JavaScript Vector Library ¦ \\ +// +--------------------------------------------------------------------¦ \\ +// ¦ Copyright © 2008-2012 Dmitry Baranovskiy (http://raphaeljs.com) ¦ \\ +// ¦ Copyright © 2008-2012 Sencha Labs (http://sencha.com) ¦ \\ +// +--------------------------------------------------------------------¦ \\ +// ¦ Licensed under the MIT (http://raphaeljs.com/license.html) license.¦ \\ +// +--------------------------------------------------------------------+ \\ + +(function(a){var b="0.3.4",c="hasOwnProperty",d=/[\.\/]/,e="*",f=function(){},g=function(a,b){return a-b},h,i,j={n:{}},k=function(a,b){var c=j,d=i,e=Array.prototype.slice.call(arguments,2),f=k.listeners(a),l=0,m=!1,n,o=[],p={},q=[],r=h,s=[];h=a,i=0;for(var t=0,u=f.length;tf*b.top){e=b.percents[y],p=b.percents[y-1]||0,t=t/b.top*(e-p),o=b.percents[y+1],j=b.anim[e];break}f&&d.attr(b.anim[b.percents[y]])}if(!!j){if(!k){for(var A in j)if(j[g](A))if(U[g](A)||d.paper.customAttributes[g](A)){u[A]=d.attr(A),u[A]==null&&(u[A]=T[A]),v[A]=j[A];switch(U[A]){case C:w[A]=(v[A]-u[A])/t;break;case"colour":u[A]=a.getRGB(u[A]);var B=a.getRGB(v[A]);w[A]={r:(B.r-u[A].r)/t,g:(B.g-u[A].g)/t,b:(B.b-u[A].b)/t};break;case"path":var D=bR(u[A],v[A]),E=D[1];u[A]=D[0],w[A]=[];for(y=0,z=u[A].length;yd)return d;while(cf?c=e:d=e,e=(d-c)/2+c}return e}function n(a,b){var c=o(a,b);return((l*c+k)*c+j)*c}function m(a){return((i*a+h)*a+g)*a}var g=3*b,h=3*(d-b)-g,i=1-g-h,j=3*c,k=3*(e-c)-j,l=1-j-k;return n(a,1/(200*f))}function cq(){return this.x+q+this.y+q+this.width+" × "+this.height}function cp(){return this.x+q+this.y}function cb(a,b,c,d,e,f){a!=null?(this.a=+a,this.b=+b,this.c=+c,this.d=+d,this.e=+e,this.f=+f):(this.a=1,this.b=0,this.c=0,this.d=1,this.e=0,this.f=0)}function bH(b,c,d){b=a._path2curve(b),c=a._path2curve(c);var e,f,g,h,i,j,k,l,m,n,o=d?0:[];for(var p=0,q=b.length;p=0&&y<=1&&A>=0&&A<=1&&(d?n++:n.push({x:x.x,y:x.y,t1:y,t2:A}))}}return n}function bF(a,b){return bG(a,b,1)}function bE(a,b){return bG(a,b)}function bD(a,b,c,d,e,f,g,h){if(!(x(a,c)x(e,g)||x(b,d)x(f,h))){var i=(a*d-b*c)*(e-g)-(a-c)*(e*h-f*g),j=(a*d-b*c)*(f-h)-(b-d)*(e*h-f*g),k=(a-c)*(f-h)-(b-d)*(e-g);if(!k)return;var l=i/k,m=j/k,n=+l.toFixed(2),o=+m.toFixed(2);if(n<+y(a,c).toFixed(2)||n>+x(a,c).toFixed(2)||n<+y(e,g).toFixed(2)||n>+x(e,g).toFixed(2)||o<+y(b,d).toFixed(2)||o>+x(b,d).toFixed(2)||o<+y(f,h).toFixed(2)||o>+x(f,h).toFixed(2))return;return{x:l,y:m}}}function bC(a,b,c,d,e,f,g,h,i){if(!(i<0||bB(a,b,c,d,e,f,g,h)n)k/=2,l+=(m1?1:i<0?0:i;var j=i/2,k=12,l=[-0.1252,.1252,-0.3678,.3678,-0.5873,.5873,-0.7699,.7699,-0.9041,.9041,-0.9816,.9816],m=[.2491,.2491,.2335,.2335,.2032,.2032,.1601,.1601,.1069,.1069,.0472,.0472],n=0;for(var o=0;od;d+=2){var f=[{x:+a[d-2],y:+a[d-1]},{x:+a[d],y:+a[d+1]},{x:+a[d+2],y:+a[d+3]},{x:+a[d+4],y:+a[d+5]}];b?d?e-4==d?f[3]={x:+a[0],y:+a[1]}:e-2==d&&(f[2]={x:+a[0],y:+a[1]},f[3]={x:+a[2],y:+a[3]}):f[0]={x:+a[e-2],y:+a[e-1]}:e-4==d?f[3]=f[2]:d||(f[0]={x:+a[d],y:+a[d+1]}),c.push(["C",(-f[0].x+6*f[1].x+f[2].x)/6,(-f[0].y+6*f[1].y+f[2].y)/6,(f[1].x+6*f[2].x-f[3].x)/6,(f[1].y+6*f[2].y-f[3].y)/6,f[2].x,f[2].y])}return c}function bx(){return this.hex}function bv(a,b,c){function d(){var e=Array.prototype.slice.call(arguments,0),f=e.join("?"),h=d.cache=d.cache||{},i=d.count=d.count||[];if(h[g](f)){bu(i,f);return c?c(h[f]):h[f]}i.length>=1e3&&delete h[i.shift()],i.push(f),h[f]=a[m](b,e);return c?c(h[f]):h[f]}return d}function bu(a,b){for(var c=0,d=a.length;c',bl=bk.firstChild,bl.style.behavior="url(#default#VML)";if(!bl||typeof bl.adj!="object")return a.type=p;bk=null}a.svg=!(a.vml=a.type=="VML"),a._Paper=j,a.fn=k=j.prototype=a.prototype,a._id=0,a._oid=0,a.is=function(a,b){b=v.call(b);if(b=="finite")return!M[g](+a);if(b=="array")return a instanceof Array;return b=="null"&&a===null||b==typeof a&&a!==null||b=="object"&&a===Object(a)||b=="array"&&Array.isArray&&Array.isArray(a)||H.call(a).slice(8,-1).toLowerCase()==b},a.angle=function(b,c,d,e,f,g){if(f==null){var h=b-d,i=c-e;if(!h&&!i)return 0;return(180+w.atan2(-i,-h)*180/B+360)%360}return a.angle(b,c,f,g)-a.angle(d,e,f,g)},a.rad=function(a){return a%360*B/180},a.deg=function(a){return a*180/B%360},a.snapTo=function(b,c,d){d=a.is(d,"finite")?d:10;if(a.is(b,E)){var e=b.length;while(e--)if(z(b[e]-c)<=d)return b[e]}else{b=+b;var f=c%b;if(fb-d)return c-f+b}return c};var bn=a.createUUID=function(a,b){return function(){return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(a,b).toUpperCase()}}(/[xy]/g,function(a){var b=w.random()*16|0,c=a=="x"?b:b&3|8;return c.toString(16)});a.setWindow=function(b){eve("raphael.setWindow",a,h.win,b),h.win=b,h.doc=h.win.document,a._engine.initWin&&a._engine.initWin(h.win)};var bo=function(b){if(a.vml){var c=/^\s+|\s+$/g,d;try{var e=new ActiveXObject("htmlfile");e.write(""),e.close(),d=e.body}catch(f){d=createPopup().document.body}var g=d.createTextRange();bo=bv(function(a){try{d.style.color=r(a).replace(c,p);var b=g.queryCommandValue("ForeColor");b=(b&255)<<16|b&65280|(b&16711680)>>>16;return"#"+("000000"+b.toString(16)).slice(-6)}catch(e){return"none"}})}else{var i=h.doc.createElement("i");i.title="Raphaël Colour Picker",i.style.display="none",h.doc.body.appendChild(i),bo=bv(function(a){i.style.color=a;return h.doc.defaultView.getComputedStyle(i,p).getPropertyValue("color")})}return bo(b)},bp=function(){return"hsb("+[this.h,this.s,this.b]+")"},bq=function(){return"hsl("+[this.h,this.s,this.l]+")"},br=function(){return this.hex},bs=function(b,c,d){c==null&&a.is(b,"object")&&"r"in b&&"g"in b&&"b"in b&&(d=b.b,c=b.g,b=b.r);if(c==null&&a.is(b,D)){var e=a.getRGB(b);b=e.r,c=e.g,d=e.b}if(b>1||c>1||d>1)b/=255,c/=255,d/=255;return[b,c,d]},bt=function(b,c,d,e){b*=255,c*=255,d*=255;var f={r:b,g:c,b:d,hex:a.rgb(b,c,d),toString:br};a.is(e,"finite")&&(f.opacity=e);return f};a.color=function(b){var c;a.is(b,"object")&&"h"in b&&"s"in b&&"b"in b?(c=a.hsb2rgb(b),b.r=c.r,b.g=c.g,b.b=c.b,b.hex=c.hex):a.is(b,"object")&&"h"in b&&"s"in b&&"l"in b?(c=a.hsl2rgb(b),b.r=c.r,b.g=c.g,b.b=c.b,b.hex=c.hex):(a.is(b,"string")&&(b=a.getRGB(b)),a.is(b,"object")&&"r"in b&&"g"in b&&"b"in b?(c=a.rgb2hsl(b),b.h=c.h,b.s=c.s,b.l=c.l,c=a.rgb2hsb(b),b.v=c.b):(b={hex:"none"},b.r=b.g=b.b=b.h=b.s=b.v=b.l=-1)),b.toString=br;return b},a.hsb2rgb=function(a,b,c,d){this.is(a,"object")&&"h"in a&&"s"in a&&"b"in a&&(c=a.b,b=a.s,a=a.h,d=a.o),a*=360;var e,f,g,h,i;a=a%360/60,i=c*b,h=i*(1-z(a%2-1)),e=f=g=c-i,a=~~a,e+=[i,h,0,0,h,i][a],f+=[h,i,i,h,0,0][a],g+=[0,0,h,i,i,h][a];return bt(e,f,g,d)},a.hsl2rgb=function(a,b,c,d){this.is(a,"object")&&"h"in a&&"s"in a&&"l"in a&&(c=a.l,b=a.s,a=a.h);if(a>1||b>1||c>1)a/=360,b/=100,c/=100;a*=360;var e,f,g,h,i;a=a%360/60,i=2*b*(c<.5?c:1-c),h=i*(1-z(a%2-1)),e=f=g=c-i/2,a=~~a,e+=[i,h,0,0,h,i][a],f+=[h,i,i,h,0,0][a],g+=[0,0,h,i,i,h][a];return bt(e,f,g,d)},a.rgb2hsb=function(a,b,c){c=bs(a,b,c),a=c[0],b=c[1],c=c[2];var d,e,f,g;f=x(a,b,c),g=f-y(a,b,c),d=g==0?null:f==a?(b-c)/g:f==b?(c-a)/g+2:(a-b)/g+4,d=(d+360)%6*60/360,e=g==0?0:g/f;return{h:d,s:e,b:f,toString:bp}},a.rgb2hsl=function(a,b,c){c=bs(a,b,c),a=c[0],b=c[1],c=c[2];var d,e,f,g,h,i;g=x(a,b,c),h=y(a,b,c),i=g-h,d=i==0?null:g==a?(b-c)/i:g==b?(c-a)/i+2:(a-b)/i+4,d=(d+360)%6*60/360,f=(g+h)/2,e=i==0?0:f<.5?i/(2*f):i/(2-2*f);return{h:d,s:e,l:f,toString:bq}},a._path2string=function(){return this.join(",").replace(Y,"$1")};var bw=a._preload=function(a,b){var c=h.doc.createElement("img");c.style.cssText="position:absolute;left:-9999em;top:-9999em",c.onload=function(){b.call(this),this.onload=null,h.doc.body.removeChild(this)},c.onerror=function(){h.doc.body.removeChild(this)},h.doc.body.appendChild(c),c.src=a};a.getRGB=bv(function(b){if(!b||!!((b=r(b)).indexOf("-")+1))return{r:-1,g:-1,b:-1,hex:"none",error:1,toString:bx};if(b=="none")return{r:-1,g:-1,b:-1,hex:"none",toString:bx};!X[g](b.toLowerCase().substring(0,2))&&b.charAt()!="#"&&(b=bo(b));var c,d,e,f,h,i,j,k=b.match(L);if(k){k[2]&&(f=R(k[2].substring(5),16),e=R(k[2].substring(3,5),16),d=R(k[2].substring(1,3),16)),k[3]&&(f=R((i=k[3].charAt(3))+i,16),e=R((i=k[3].charAt(2))+i,16),d=R((i=k[3].charAt(1))+i,16)),k[4]&&(j=k[4][s](W),d=Q(j[0]),j[0].slice(-1)=="%"&&(d*=2.55),e=Q(j[1]),j[1].slice(-1)=="%"&&(e*=2.55),f=Q(j[2]),j[2].slice(-1)=="%"&&(f*=2.55),k[1].toLowerCase().slice(0,4)=="rgba"&&(h=Q(j[3])),j[3]&&j[3].slice(-1)=="%"&&(h/=100));if(k[5]){j=k[5][s](W),d=Q(j[0]),j[0].slice(-1)=="%"&&(d*=2.55),e=Q(j[1]),j[1].slice(-1)=="%"&&(e*=2.55),f=Q(j[2]),j[2].slice(-1)=="%"&&(f*=2.55),(j[0].slice(-3)=="deg"||j[0].slice(-1)=="°")&&(d/=360),k[1].toLowerCase().slice(0,4)=="hsba"&&(h=Q(j[3])),j[3]&&j[3].slice(-1)=="%"&&(h/=100);return a.hsb2rgb(d,e,f,h)}if(k[6]){j=k[6][s](W),d=Q(j[0]),j[0].slice(-1)=="%"&&(d*=2.55),e=Q(j[1]),j[1].slice(-1)=="%"&&(e*=2.55),f=Q(j[2]),j[2].slice(-1)=="%"&&(f*=2.55),(j[0].slice(-3)=="deg"||j[0].slice(-1)=="°")&&(d/=360),k[1].toLowerCase().slice(0,4)=="hsla"&&(h=Q(j[3])),j[3]&&j[3].slice(-1)=="%"&&(h/=100);return a.hsl2rgb(d,e,f,h)}k={r:d,g:e,b:f,toString:bx},k.hex="#"+(16777216|f|e<<8|d<<16).toString(16).slice(1),a.is(h,"finite")&&(k.opacity=h);return k}return{r:-1,g:-1,b:-1,hex:"none",error:1,toString:bx}},a),a.hsb=bv(function(b,c,d){return a.hsb2rgb(b,c,d).hex}),a.hsl=bv(function(b,c,d){return a.hsl2rgb(b,c,d).hex}),a.rgb=bv(function(a,b,c){return"#"+(16777216|c|b<<8|a<<16).toString(16).slice(1)}),a.getColor=function(a){var b=this.getColor.start=this.getColor.start||{h:0,s:1,b:a||.75},c=this.hsb2rgb(b.h,b.s,b.b);b.h+=.075,b.h>1&&(b.h=0,b.s-=.2,b.s<=0&&(this.getColor.start={h:0,s:1,b:b.b}));return c.hex},a.getColor.reset=function(){delete this.start},a.parsePathString=function(b){if(!b)return null;var c=bz(b);if(c.arr)return bJ(c.arr);var d={a:7,c:6,h:1,l:2,m:2,r:4,q:4,s:4,t:2,v:1,z:0},e=[];a.is(b,E)&&a.is(b[0],E)&&(e=bJ(b)),e.length||r(b).replace(Z,function(a,b,c){var f=[],g=b.toLowerCase();c.replace(_,function(a,b){b&&f.push(+b)}),g=="m"&&f.length>2&&(e.push([b][n](f.splice(0,2))),g="l",b=b=="m"?"l":"L");if(g=="r")e.push([b][n](f));else while(f.length>=d[g]){e.push([b][n](f.splice(0,d[g])));if(!d[g])break}}),e.toString=a._path2string,c.arr=bJ(e);return e},a.parseTransformString=bv(function(b){if(!b)return null;var c={r:3,s:4,t:2,m:6},d=[];a.is(b,E)&&a.is(b[0],E)&&(d=bJ(b)),d.length||r(b).replace($,function(a,b,c){var e=[],f=v.call(b);c.replace(_,function(a,b){b&&e.push(+b)}),d.push([b][n](e))}),d.toString=a._path2string;return d});var bz=function(a){var b=bz.ps=bz.ps||{};b[a]?b[a].sleep=100:b[a]={sleep:100},setTimeout(function(){for(var c in b)b[g](c)&&c!=a&&(b[c].sleep--,!b[c].sleep&&delete b[c])});return b[a]};a.findDotsAtSegment=function(a,b,c,d,e,f,g,h,i){var j=1-i,k=A(j,3),l=A(j,2),m=i*i,n=m*i,o=k*a+l*3*i*c+j*3*i*i*e+n*g,p=k*b+l*3*i*d+j*3*i*i*f+n*h,q=a+2*i*(c-a)+m*(e-2*c+a),r=b+2*i*(d-b)+m*(f-2*d+b),s=c+2*i*(e-c)+m*(g-2*e+c),t=d+2*i*(f-d)+m*(h-2*f+d),u=j*a+i*c,v=j*b+i*d,x=j*e+i*g,y=j*f+i*h,z=90-w.atan2(q-s,r-t)*180/B;(q>s||r=a.x&&b<=a.x2&&c>=a.y&&c<=a.y2},a.isBBoxIntersect=function(b,c){var d=a.isPointInsideBBox;return d(c,b.x,b.y)||d(c,b.x2,b.y)||d(c,b.x,b.y2)||d(c,b.x2,b.y2)||d(b,c.x,c.y)||d(b,c.x2,c.y)||d(b,c.x,c.y2)||d(b,c.x2,c.y2)||(b.xc.x||c.xb.x)&&(b.yc.y||c.yb.y)},a.pathIntersection=function(a,b){return bH(a,b)},a.pathIntersectionNumber=function(a,b){return bH(a,b,1)},a.isPointInsidePath=function(b,c,d){var e=a.pathBBox(b);return a.isPointInsideBBox(e,c,d)&&bH(b,[["M",c,d],["H",e.x2+10]],1)%2==1},a._removedFactory=function(a){return function(){eve("raphael.log",null,"Raphaël: you are calling to method “"+a+"” of removed object",a)}};var bI=a.pathBBox=function(a){var b=bz(a);if(b.bbox)return b.bbox;if(!a)return{x:0,y:0,width:0,height:0,x2:0,y2:0};a=bR(a);var c=0,d=0,e=[],f=[],g;for(var h=0,i=a.length;h1&&(v=w.sqrt(v),c=v*c,d=v*d);var x=c*c,y=d*d,A=(f==g?-1:1)*w.sqrt(z((x*y-x*u*u-y*t*t)/(x*u*u+y*t*t))),C=A*c*u/d+(a+h)/2,D=A*-d*t/c+(b+i)/2,E=w.asin(((b-D)/d).toFixed(9)),F=w.asin(((i-D)/d).toFixed(9));E=aF&&(E=E-B*2),!g&&F>E&&(F=F-B*2)}else E=j[0],F=j[1],C=j[2],D=j[3];var G=F-E;if(z(G)>k){var H=F,I=h,J=i;F=E+k*(g&&F>E?1:-1),h=C+c*w.cos(F),i=D+d*w.sin(F),m=bO(h,i,c,d,e,0,g,I,J,[F,H,C,D])}G=F-E;var K=w.cos(E),L=w.sin(E),M=w.cos(F),N=w.sin(F),O=w.tan(G/4),P=4/3*c*O,Q=4/3*d*O,R=[a,b],S=[a+P*L,b-Q*K],T=[h+P*N,i-Q*M],U=[h,i];S[0]=2*R[0]-S[0],S[1]=2*R[1]-S[1];if(j)return[S,T,U][n](m);m=[S,T,U][n](m).join()[s](",");var V=[];for(var W=0,X=m.length;W"1e12"&&(l=.5),z(n)>"1e12"&&(n=.5),l>0&&l<1&&(q=bP(a,b,c,d,e,f,g,h,l),p.push(q.x),o.push(q.y)),n>0&&n<1&&(q=bP(a,b,c,d,e,f,g,h,n),p.push(q.x),o.push(q.y)),i=f-2*d+b-(h-2*f+d),j=2*(d-b)-2*(f-d),k=b-d,l=(-j+w.sqrt(j*j-4*i*k))/2/i,n=(-j-w.sqrt(j*j-4*i*k))/2/i,z(l)>"1e12"&&(l=.5),z(n)>"1e12"&&(n=.5),l>0&&l<1&&(q=bP(a,b,c,d,e,f,g,h,l),p.push(q.x),o.push(q.y)),n>0&&n<1&&(q=bP(a,b,c,d,e,f,g,h,n),p.push(q.x),o.push(q.y));return{min:{x:y[m](0,p),y:y[m](0,o)},max:{x:x[m](0,p),y:x[m](0,o)}}}),bR=a._path2curve=bv(function(a,b){var c=!b&&bz(a);if(!b&&c.curve)return bJ(c.curve);var d=bL(a),e=b&&bL(b),f={x:0,y:0,bx:0,by:0,X:0,Y:0,qx:null,qy:null},g={x:0,y:0,bx:0,by:0,X:0,Y:0,qx:null,qy:null},h=function(a,b){var c,d;if(!a)return["C",b.x,b.y,b.x,b.y,b.x,b.y];!(a[0]in{T:1,Q:1})&&(b.qx=b.qy=null);switch(a[0]){case"M":b.X=a[1],b.Y=a[2];break;case"A":a=["C"][n](bO[m](0,[b.x,b.y][n](a.slice(1))));break;case"S":c=b.x+(b.x-(b.bx||b.x)),d=b.y+(b.y-(b.by||b.y)),a=["C",c,d][n](a.slice(1));break;case"T":b.qx=b.x+(b.x-(b.qx||b.x)),b.qy=b.y+(b.y-(b.qy||b.y)),a=["C"][n](bN(b.x,b.y,b.qx,b.qy,a[1],a[2]));break;case"Q":b.qx=a[1],b.qy=a[2],a=["C"][n](bN(b.x,b.y,a[1],a[2],a[3],a[4]));break;case"L":a=["C"][n](bM(b.x,b.y,a[1],a[2]));break;case"H":a=["C"][n](bM(b.x,b.y,a[1],b.y));break;case"V":a=["C"][n](bM(b.x,b.y,b.x,a[1]));break;case"Z":a=["C"][n](bM(b.x,b.y,b.X,b.Y))}return a},i=function(a,b){if(a[b].length>7){a[b].shift();var c=a[b];while(c.length)a.splice(b++,0,["C"][n](c.splice(0,6)));a.splice(b,1),l=x(d.length,e&&e.length||0)}},j=function(a,b,c,f,g){a&&b&&a[g][0]=="M"&&b[g][0]!="M"&&(b.splice(g,0,["M",f.x,f.y]),c.bx=0,c.by=0,c.x=a[g][1],c.y=a[g][2],l=x(d.length,e&&e.length||0))};for(var k=0,l=x(d.length,e&&e.length||0);ke){if(c&&!l.start){m=cs(g,h,i[1],i[2],i[3],i[4],i[5],i[6],e-n),k+=["C"+m.start.x,m.start.y,m.m.x,m.m.y,m.x,m.y];if(f)return k;l.start=k,k=["M"+m.x,m.y+"C"+m.n.x,m.n.y,m.end.x,m.end.y,i[5],i[6]].join(),n+=j,g=+i[5],h=+i[6];continue}if(!b&&!c){m=cs(g,h,i[1],i[2],i[3],i[4],i[5],i[6],e-n);return{x:m.x,y:m.y,alpha:m.alpha}}}n+=j,g=+i[5],h=+i[6]}k+=i.shift()+i}l.end=k,m=b?n:c?l:a.findDotsAtSegment(g,h,i[0],i[1],i[2],i[3],i[4],i[5],1),m.alpha&&(m={x:m.x,y:m.y,alpha:m.alpha});return m}},cu=ct(1),cv=ct(),cw=ct(0,1);a.getTotalLength=cu,a.getPointAtLength=cv,a.getSubpath=function(a,b,c){if(this.getTotalLength(a)-c<1e-6)return cw(a,b).end;var d=cw(a,c,1);return b?cw(d,b).end:d},cl.getTotalLength=function(){if(this.type=="path"){if(this.node.getTotalLength)return this.node.getTotalLength();return cu(this.attrs.path)}},cl.getPointAtLength=function(a){if(this.type=="path")return cv(this.attrs.path,a)},cl.getSubpath=function(b,c){if(this.type=="path")return a.getSubpath(this.attrs.path,b,c)};var cx=a.easing_formulas={linear:function(a){return a},"<":function(a){return A(a,1.7)},">":function(a){return A(a,.48)},"<>":function(a){var b=.48-a/1.04,c=w.sqrt(.1734+b*b),d=c-b,e=A(z(d),1/3)*(d<0?-1:1),f=-c-b,g=A(z(f),1/3)*(f<0?-1:1),h=e+g+.5;return(1-h)*3*h*h+h*h*h},backIn:function(a){var b=1.70158;return a*a*((b+1)*a-b)},backOut:function(a){a=a-1;var b=1.70158;return a*a*((b+1)*a+b)+1},elastic:function(a){if(a==!!a)return a;return A(2,-10*a)*w.sin((a-.075)*2*B/.3)+1},bounce:function(a){var b=7.5625,c=2.75,d;a<1/c?d=b*a*a:a<2/c?(a-=1.5/c,d=b*a*a+.75):a<2.5/c?(a-=2.25/c,d=b*a*a+.9375):(a-=2.625/c,d=b*a*a+.984375);return d}};cx.easeIn=cx["ease-in"]=cx["<"],cx.easeOut=cx["ease-out"]=cx[">"],cx.easeInOut=cx["ease-in-out"]=cx["<>"],cx["back-in"]=cx.backIn,cx["back-out"]=cx.backOut;var cy=[],cz=window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(a){setTimeout(a,16)},cA=function(){var b=+(new Date),c=0;for(;c1&&!d.next){for(s in k)k[g](s)&&(r[s]=d.totalOrigin[s]);d.el.attr(r),cE(d.anim,d.el,d.anim.percents[0],null,d.totalOrigin,d.repeat-1)}d.next&&!d.stop&&cE(d.anim,d.el,d.next,null,d.totalOrigin,d.repeat)}}a.svg&&m&&m.paper&&m.paper.safari(),cy.length&&cz(cA)},cB=function(a){return a>255?255:a<0?0:a};cl.animateWith=function(b,c,d,e,f,g){var h=this;if(h.removed){g&&g.call(h);return h}var i=d instanceof cD?d:a.animation(d,e,f,g),j,k;cE(i,h,i.percents[0],null,h.attr());for(var l=0,m=cy.length;l.5)*2-1;i(m-.5,2)+i(n-.5,2)>.25&&(n=f.sqrt(.25-i(m-.5,2))*e+.5)&&n!=.5&&(n=n.toFixed(5)-1e-5*e)}return l}),e=e.split(/\s*\-\s*/);if(j=="linear"){var t=e.shift();t=-d(t);if(isNaN(t))return null;var u=[0,0,f.cos(a.rad(t)),f.sin(a.rad(t))],v=1/(g(h(u[2]),h(u[3]))||1);u[2]*=v,u[3]*=v,u[2]<0&&(u[0]=-u[2],u[2]=0),u[3]<0&&(u[1]=-u[3],u[3]=0)}var w=a._parseDots(e);if(!w)return null;k=k.replace(/[\(\)\s,\xb0#]/g,"_"),b.gradient&&k!=b.gradient.id&&(p.defs.removeChild(b.gradient),delete b.gradient);if(!b.gradient){s=q(j+"Gradient",{id:k}),b.gradient=s,q(s,j=="radial"?{fx:m,fy:n}:{x1:u[0],y1:u[1],x2:u[2],y2:u[3],gradientTransform:b.matrix.invert()}),p.defs.appendChild(s);for(var x=0,y=w.length;x1?G.opacity/100:G.opacity});case"stroke":G=a.getRGB(p),i.setAttribute(o,G.hex),o=="stroke"&&G[b]("opacity")&&q(i,{"stroke-opacity":G.opacity>1?G.opacity/100:G.opacity}),o=="stroke"&&d._.arrows&&("startString"in d._.arrows&&t(d,d._.arrows.startString),"endString"in d._.arrows&&t(d,d._.arrows.endString,1));break;case"gradient":(d.type=="circle"||d.type=="ellipse"||c(p).charAt()!="r")&&r(d,p);break;case"opacity":k.gradient&&!k[b]("stroke-opacity")&&q(i,{"stroke-opacity":p>1?p/100:p});case"fill-opacity":if(k.gradient){H=a._g.doc.getElementById(i.getAttribute("fill").replace(/^url\(#|\)$/g,l)),H&&(I=H.getElementsByTagName("stop"),q(I[I.length-1],{"stop-opacity":p}));break};default:o=="font-size"&&(p=e(p,10)+"px");var J=o.replace(/(\-.)/g,function(a){return a.substring(1).toUpperCase()});i.style[J]=p,d._.dirty=1,i.setAttribute(o,p)}}y(d,f),i.style.visibility=m},x=1.2,y=function(d,f){if(d.type=="text"&&!!(f[b]("text")||f[b]("font")||f[b]("font-size")||f[b]("x")||f[b]("y"))){var g=d.attrs,h=d.node,i=h.firstChild?e(a._g.doc.defaultView.getComputedStyle(h.firstChild,l).getPropertyValue("font-size"),10):10;if(f[b]("text")){g.text=f.text;while(h.firstChild)h.removeChild(h.firstChild);var j=c(f.text).split("\n"),k=[],m;for(var n=0,o=j.length;n"));var $=X.getBoundingClientRect();t.W=m.w=($.right-$.left)/Y,t.H=m.h=($.bottom-$.top)/Y,t.X=m.x,t.Y=m.y+t.H/2,("x"in i||"y"in i)&&(t.path.v=a.format("m{0},{1}l{2},{1}",f(m.x*u),f(m.y*u),f(m.x*u)+1));var _=["x","y","text","font","font-family","font-weight","font-style","font-size"];for(var ba=0,bb=_.length;ba.25&&(c=e.sqrt(.25-i(b-.5,2))*((c>.5)*2-1)+.5),m=b+n+c);return o}),f=f.split(/\s*\-\s*/);if(l=="linear"){var p=f.shift();p=-d(p);if(isNaN(p))return null}var q=a._parseDots(f);if(!q)return null;b=b.shape||b.node;if(q.length){b.removeChild(g),g.on=!0,g.method="none",g.color=q[0].color,g.color2=q[q.length-1].color;var r=[];for(var s=0,t=q.length;s')}}catch(c){F=function(a){return b.createElement("<"+a+' xmlns="urn:schemas-microsoft.com:vml" class="rvml">')}}},a._engine.initWin(a._g.win),a._engine.create=function(){var b=a._getContainer.apply(0,arguments),c=b.container,d=b.height,e,f=b.width,g=b.x,h=b.y;if(!c)throw new Error("VML container not found.");var i=new a._Paper,j=i.canvas=a._g.doc.createElement("div"),k=j.style;g=g||0,h=h||0,f=f||512,d=d||342,i.width=f,i.height=d,f==+f&&(f+="px"),d==+d&&(d+="px"),i.coordsize=u*1e3+n+u*1e3,i.coordorigin="0 0",i.span=a._g.doc.createElement("span"),i.span.style.cssText="position:absolute;left:-9999em;top:-9999em;padding:0;margin:0;line-height:1;",j.appendChild(i.span),k.cssText=a.format("top:0;left:0;width:{0};height:{1};display:inline-block;position:relative;clip:rect(0 {0} {1} 0);overflow:hidden",f,d),c==1?(a._g.doc.body.appendChild(j),k.left=g+"px",k.top=h+"px",k.position="absolute"):c.firstChild?c.insertBefore(j,c.firstChild):c.appendChild(j),i.renderfix=function(){};return i},a.prototype.clear=function(){a.eve("raphael.clear",this),this.canvas.innerHTML=o,this.span=a._g.doc.createElement("span"),this.span.style.cssText="position:absolute;left:-9999em;top:-9999em;padding:0;margin:0;line-height:1;display:inline;",this.canvas.appendChild(this.span),this.bottom=this.top=null},a.prototype.remove=function(){a.eve("raphael.remove",this),this.canvas.parentNode.removeChild(this.canvas);for(var b in this)this[b]=typeof this[b]=="function"?a._removedFactory(b):null;return!0};var G=a.st;for(var H in E)E[b](H)&&!G[b](H)&&(G[H]=function(a){return function(){var b=arguments;return this.forEach(function(c){c[a].apply(c,b)})}}(H))}(window.Raphael) diff --git a/rpimonitor/web/js/rpimonitor.addons.js b/rpimonitor/web/js/rpimonitor.addons.js new file mode 100644 index 0000000..3a36e9c --- /dev/null +++ b/rpimonitor/web/js/rpimonitor.addons.js @@ -0,0 +1,61 @@ +// This file is part of RPi-Monitor project +// +// Copyright 2014 - Xavier Berger - http://rpi-experiences.blogspot.fr/ +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +function ConstructPage() +{ + var activePage = GetURLParameter('activePage'); + + if ( typeof activePage == 'undefined') { + activePage=localStorage.getItem('activePage', activePage); + if ( activePage == null ) { activePage = 0 } + } + data = getData('addons'); + if ( activePage >= data.length ){ + activePage=0; + } + localStorage.setItem('activePage', activePage); + if ( data[activePage].showTitle != 0 ) { + $('

'+data[activePage].name+'


').insertBefore("#insertionPoint"); + } + + $("#insertionPoint").load("addons/"+data[activePage].addons+"/"+data[activePage].addons+".html") + + $("head").append(""); + + jQuery.ajax({ + url: "addons/"+data[activePage].addons+"/"+data[activePage].addons+".js", + dataType: "script", + }).done(function() { + }); + +} + +$(function () { + /* Set no cache */ + $.ajaxSetup({ cache: false }); + + /* Show friends */ + ShowFriends(); + + /* Add qrcode shortcut*/ + setupqr(); + doqr(document.URL); + + /* Get static values once */ + ConstructPage(); +}); + + diff --git a/rpimonitor/web/js/rpimonitor.index.js b/rpimonitor/web/js/rpimonitor.index.js index ca5cdc0..ff8e3bd 100644 --- a/rpimonitor/web/js/rpimonitor.index.js +++ b/rpimonitor/web/js/rpimonitor.index.js @@ -1,3 +1,19 @@ +// This file is part of RPi-Monitor project +// +// Copyright 2013 - 2014 - Xavier Berger - http://rpi-experiences.blogspot.fr/ +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . $(function () { ShowFriends(); /* Add qrcode shortcut*/ diff --git a/rpimonitor/web/js/rpimonitor.js b/rpimonitor/web/js/rpimonitor.js index 4f11942..760fefb 100644 --- a/rpimonitor/web/js/rpimonitor.js +++ b/rpimonitor/web/js/rpimonitor.js @@ -1,6 +1,6 @@ // This file is part of RPi-Monitor project // -// Copyright 2013 - Xavier Berger - http://rpi-experiences.blogspot.fr/ +// Copyright 2013 - 2015 - Xavier Berger - http://rpi-experiences.blogspot.fr/ // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -15,13 +15,12 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . var animate; -var shellinabox; var shellinaboxuri; -var shellinaboxwarning; var statusautorefresh; var refreshTimerId; var clickId; var active_rra; +var current_path = window.location.pathname.split('/').pop(); function GetURLParameter(sParam) { @@ -37,25 +36,6 @@ function GetURLParameter(sParam) } } -function SetShellinaboxMenu(){ - $('#shellinabox').attr('checked', shellinabox ); - $('#shellinaboxwarning').attr('checked', shellinaboxwarning ); - if ( shellinabox ) { - $('#shellinaboxmenu').removeClass('hide'); - $('#shellinaboxuri').val(shellinaboxuri); - $('#shellinaboxuri').attr('disabled',false); - $('#shellinaboxwarning').attr('disabled',false); - $('#shellinaboxwarninglabel').removeClass('muted'); - } - else{ - $('#shellinaboxmenu').addClass('hide'); - $('#shellinaboxuri').val(''); - $('#shellinaboxuri').attr('disabled',true); - $('#shellinaboxwarning').attr('disabled',true); - $('#shellinaboxwarninglabel').addClass('muted'); - } -} - function getData( name ){ if ( localStorage.getItem(name+'Version') == localStorage.getItem('version') ) { return eval("(" + localStorage.getItem(name) + ')'); @@ -94,16 +74,13 @@ function ShowFriends(){ function AddFooter(){ $('#footer').html( '