Popup filters (no js, no bs)






html

<style>
body {
  color: #333333;
  font-family: 'Helvetica', arial;
}
.wrap {
  padding: 40px;
  text-align: center;
}
hr {
  clear: both;
  margin-top: 40px;
  margin-bottom: 40px;
  border: 0;
}
h1 {
  font-size: 30px;
  margin-bottom: 40px;
}
p {
  margin-bottom: 20px;
}
.btn {
  background: #e06666;
  border: #e06666 solid 1px;
  border-radius: 3px;
  color: #fff;
  display: inline-block;
  font-size: 14px;
  padding: 8px 15px;
  text-decoration: none;
  text-align: center;
  min-width: 60px;
  position: relative;
  transition: color .1s ease;
}
.btn:hover {
  background: #555;
  border: #555 solid 1px;
}
.btn.btn-big {
  font-size: 18px;
  padding: 15px 20px;
  min-width: 100px;
}
.btn-close {
  color: #aaaaaa;
  font-size: 30px;
  text-decoration: none;
  position: absolute;
  right: 5px;
  top: 0;
}
.btn-close:hover {
  color: #919191;
}
.modal:before {
  content: "";
  display: none;
  background: rgba(0, 0, 0, 0.6);
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 10;
}
.modal:target:before {
  display: block;
}
.modal:target .modal-dialog {
  -webkit-transform: translate(0, 0);
  -ms-transform: translate(0, 0);
  transform: translate(0, 0);
  top: 20%;
}
.modal-dialog {
  background: #fefefe;
  border: #333333 solid 1px;
  border-radius: 5px;
  margin-left: -200px;
  position: fixed;
  left: 50%;
  top: -100%;
  z-index: 11;
  width: 360px;
  -webkit-transform: translate(0, -500%);
  -ms-transform: translate(0, -500%);
  transform: translate(0, -500%);
  -webkit-transition: -webkit-transform 0.3s ease-out;
  -moz-transition: -moz-transform 0.3s ease-out;
  -o-transition: -o-transform 0.3s ease-out;
  transition: transform 0.3s ease-out;
}
.modal-body {
  padding: 20px;
}
.modal-header,
.modal-footer {
  padding: 10px 20px;
}
.modal-header {
}
.modal-header h2 {
  font-size: 20px;
}
.modal-footer {
  text-align: right;
}
</style>


<!-- Button -->
<h1><center><a href="#modal-one" ><SpotfireControl id="6b028e3cabee4567b4e4081cd2459d63" /></a><center></h1>

<!-- Modal -->
<div class="modal" id="modal-one" aria-hidden="true">
  <div class="modal-dialog">
    <div class="modal-header">
      <h2>Select Date</h2>
      <a href="#" class="btn-close" aria-hidden="true">x</a>
    </div>
    <div class="modal-body">
      <p><SpotfireControl id="369d6997db7249718618a82baa23023b" /></p>
    </div>
    <div class="modal-footer">
      <a href="#" class="btn">OK</a>
    </div>
  </div>
</div>


js
no javascrip required!

Send email with PDF

# This script sends a pdf via email
from System import IO, Net, DateTime
from System.Net import Mail, Mime
from System.Text import Encoding

#1. Configure email
#gmail settings    : smtp.gmail.com,  port:587 encription=True credentials=True)
#corporate settings: smtp.contoso.com,port:25  encription=False credentials=False)
SMTPClient = "smtp.gmail.com"
SMTPPort = 587
useEncription = True
useCredentials = True

fromEmail = "your@gmail.com"
toEmail ="someone@acompany.com"
fromEmailUsr = fromEmail
fromEmailPwd = "y0urP4$$w0rd!"
filename = "test.pdf"
myMailSubject = "PDF email from a spotfire geek"
myMailBody = "Attached find pdf"
PDFattachment= "C://temp//test.pdf"

#2. Prepare email
MyMailMessage = Mail.MailMessage()
MyMailMessage.From = Mail.MailAddress(fromEmail)
MyMailMessage.To.Add(toEmail)
MyMailMessage.Subject = myMailSubject
MyMailMessage.Body = myMailBody
ct = Mime.ContentType(Mime.MediaTypeNames.Application.Pdf)

#3. Attach PDF file
attach = Mail.Attachment(PDFattachment, ct) 
attach.ContentDisposition.FileName = filename 
MyMailMessage.Attachments.Add(attach)
Mail.Attachment(PDFattachment, ct)

#4. Send email
emailSender = Mail.SmtpClient(SMTPClient)
emailSender.Port = SMTPPort
if useCredentials: emailSender.Credentials = Net.NetworkCredential(fromEmailUsr, fromEmailPwd)
emailSender.EnableSsl = useEncription
emailSender.Send(MyMailMessage)

print "Email sent!"

Render HTML from Data Table

html
<span id='dropDownDocProp' style='display:none'>
<SpotfireControl id="calculatedValueGoesHere123" />
calculated value expression: first([myHTMLColumn])
</span>

<div id="here">html renders here</div>

js
MutationObserver = window.MutationObserver || window.WebKitMutationObserver;

//this is the target element to monitor changes
//just put the span id here. You can remove next line and add a script param called targetDomId
var targetDomId = "dropDownDocProp"

//function when dropdown value changes
var myFunction = function(oldValue,newValue){
$("#here").html(newValue)
}


//no need to change after this line.
var target = document.getElementById(targetDomId)

//callback is the function to trigger when target changes
var oldVal = target.innerText.trim()
var callback = function(mutations) {
 newVal=$('#'+targetDomId+' ').text()
 if(newVal!=oldVal) myFunction(oldVal,newVal)
 oldVal = newVal;
}

//this is to glue these two together
var observer = new MutationObserver(callback);

var opts = {
    childList: true, 
    attributes: true, 
    characterData: true, 
    subtree: true
}


observer.observe(target,opts);




Sample Data
copy the text below and add a data table from clipboard in spotfire

ID Detail
2 "<div style=""position:relative;""> <div style=""opacity:0.5;position:absolute;left:50px;top:-30px;width:300px;height:150px;background-color:#40B3DF""></div> <div style=""opacity:0.3;position:absolute;left:120px;top:20px;width:100px;height:170px;background-color:#73AD21""></div> <div style=""margin-top:30px;width:360px;height:130px;padding:20px;border-radius:10px;border:10px solid #EE872A;font-size:120%;""> <h1>CSS = Styles and Colors</h1> <div style=""letter-spacing:12px;font-size:15px;position:relative;left:25px;top:25px;"">Manipulate Text</div> <div style=""color:#40B3DF;letter-spacing:12px;font-size:15px;position:relative;left:25px;top:30px;"">Colors, <span style=""background-color:#B4009E;color:#ffffff;""> Boxes</span></div> </div> </div>"
3 "<style> table { width:100%; } table, th, td { border: 1px solid black; border-collapse: collapse; } th, td { padding: 5px; text-align: left; } table.names tr:nth-child(even) { background-color: #eee; } table.names tr:nth-child(odd) { background-color:#fff; } table.names th { background-color: black; color: white } </style> </head> <body><table> <tr> <th>Firstname</th> <th>Lastname</th> <th>Age</th> </tr> <tr> <td>Jill</td> <td>Smith</td> <td>50</td> </tr> <tr> <td>Eve</td> <td>Jackson</td> <td>94</td> </tr> <tr> <td>John</td> <td>Doe</td> <td>80</td> </tr> </table><br><table class=""names""> <tr> <th>Firstname</th> <th>Lastname</th> <th>Age</th> </tr> <tr> <td>Jill</td> <td>Smith</td> <td>50</td> </tr> <tr> <td>Eve</td> <td>Jackson</td> <td>94</td> </tr> <tr> <td>John</td> <td>Doe</td> <td>80</td> </tr> </table>"
7 "<style> .flex-container { display: -webkit-flex; display: flex; -webkit-flex-flow: row wrap; flex-flow: row wrap; text-align: center; } .flex-container > * { padding: 15px; -webkit-flex: 1 100%; flex: 1 100%; } .article { text-align: left; } header {background: black;color:white;} footer {background: #aaa;color:white;} .nav {background:#eee;}.nav ul { list-style-type: none; padding: 0; } .nav ul a { text-decoration: none; }@media all and (min-width: 768px) { .nav {text-align:left;-webkit-flex: 1 auto;flex:1 auto;-webkit-order:1;order:1;} .article {-webkit-flex:5 0px;flex:5 0px;-webkit-order:2;order:2;} footer {-webkit-order:3;order:3;} } </style><div class=""flex-container""> <header> <h1>City Gallery</h1> </header><nav class=""nav""> <ul> <li><a href=""#"">London</a></li> <li><a href=""#"">Paris</a></li> <li><a href=""#"">Tokyo</a></li> </ul> </nav><article class=""article""> <h1>London</h1> <p>London is the capital city of England. It is the most populous city in the United Kingdom, with a metropolitan area of over 13 million inhabitants.</p> <p>Standing on the River Thames, London has been a major settlement for two millennia, its history going back to its founding by the Romans, who named it Londinium.</p> <p><strong>Resize this page to see what happens!</strong></p> </article></div>"
10 "<style> body {background-color: powderblue;} h1 {color: red;} p {color: blue;} </style> <h1>This is a Heading</h1> <p>This is a paragraph.</p> <script> function myFunction() { document.getElementById(""demo"").innerHTML = ""Hello JavaScript!""; } </script> </head> <body> <h1>My Web Page</h1> <p id=""demo"">A Paragraph</p> <button type=""button"" onclick=""myFunction()"">Try it</button><img src=""https://www.w3schools.com/images/html5.gif""> "

Change sparkline color on Graphical Table

from Spotfire.Dxp.Application. Visuals import VisualTypeIdentifiers
from Spotfire.Dxp.Application. Visuals.Miniatures import GraphicalTable, SparklineMiniatureVisualization 
from System.Drawing import Color 

#graphPlot is a GraphicalTable visualization script parameter
#color is a string document property holding values like "red", "blue" or "papayawhip" 

for column in graphPlot.As[GraphicalTable]() .Columns: 
    if column.Visualization.TypeId == VisualTypeIdentifiers. SparklineMiniatureVisualization: 
print column.Title
if column.Title == "Trend": 
#column.Visualization.LineColor = Color.FromArgb(R,G,B)
column.Visualization.LineColor = Color.FromName(color)



Thanks to Mike Akister and Adam W!

nice little round button



html
<span id="btn"><SpotfireControl id="fe2511f37ad94401ae64dc4f59d1cf24" /></span>

js
$("#btn input").css({
    "color": "#26A2ED",
    "border-radius": "60%",
    "height": "30px",
    "width": "30px",
    "font-size": "8px",
    "padding": "0px",
    "position": "absolute"
})

slideshow

To loop through pages automatically we need a javascript timer on each page to trigger an iron python script that changes to the next page.




html
<FONT size=6><SPAN>the iron python script switches to the next page</SPAN></FONT> 

<DIV id=switchPageBtn><SpotfireControl id="ff8993c967e049f580fed8aa9b25ec45" /></DIV>


js
$(function(){
clearTimeout(document.body.timeout);
document.body.timeout=setTimeout(function(){$('#switchPageBtn input').click();}, 3000)
});

script 
#current page number
cp = Document.Pages.IndexOf(Document.ActivePageReference)

#total pages
tp = Document.Pages.Count

#calculate next page
np = cp+1 if cp+1<tp else 0

#go to next page
Document.ActivePageReference = Document.Pages[np]


Slide menu



html

<script src='//cdn.muicss.com/mui-0.4.6/js/mui.js'></script>

<style>

#sidedrawer {
  position: fixed;
  top: 0;
  bottom: 0;
  width: 300px;
  left: -300px;
  overflow: auto;
  z-index: 2;
  background-color: #fff;
  border:1px solid lightblue;
  top:27px;
  transition: transform 0.2s;
}

@media (min-width: 768px) {
  #sidedrawer {
    transform: translate(300px);
  }

  body.hide-sidedrawer #sidedrawer {
    transform: translate(0px);
  }
}

#sidedrawer ul {
  list-style: none;
}

#sidedrawer > ul {
  padding-left: 0px;
}

#sidedrawer > ul > li:first-child {
  padding-top: 15px;
}

#sidedrawer strong {
  display: block;
  padding: 15px 22px;
  cursor: pointer;
}

#sidedrawer strong:hover {
  background-color: #E0E0E0;
}

#sidedrawer strong + ul > li {
  padding: 6px 0px;
}

.closebtn{
  cursor: pointer;
}
</style>

<b  class="closebtn js-hide-sidedrawer"><font size=4>☰</font></b>  click the hamburger to to open the menu

<div id="sidedrawer" class="mui--no-user-select">
click the top right button to close the menu 
 <div style='float:right;cursor:hand;font-size:20px' class="closebtn js-hide-sidedrawer">☒</div>
<div class="mui-divider"></div> 
<ul>
  <li class="active">
    <strong>Options</strong> 
    <ul> 
      <li>This is a List Box filter </li>
      <li><SpotfireControl id="8e46dc29e17746e38b69ed088933e1fc" /></li>
    </ul>
  </li>
  <li>
    <strong>More Options</strong>
    <ul> 
      <li>This is a Check Box filter</li>
      <li><SpotfireControl id="a5d461b193084a338c6b4c4d0cfb0cea" /></li>
    </ul>
  </li>
</ul> 
</div>
</div>
</div>


js

jQuery(function($) {
  var $bodyEl = $('body'),
      $sidedrawerEl = $('#sidedrawer');
  
  
  function showSidedrawer() {
    // show overlay
    var options = {
      onclose: function() {
        $sidedrawerEl
          .removeClass('active')
          .appendTo(document.body);
      }
    };
    
    var $overlayEl = $(mui.overlay('on', options));
    
    // show element
    $sidedrawerEl.appendTo($overlayEl);
    setTimeout(function() {
      $sidedrawerEl.addClass('active');
    }, 20);
  }
  
  
  function hideSidedrawer() {
    $bodyEl.toggleClass('hide-sidedrawer');
  }
  
  
  $('.js-show-sidedrawer').on('click', showSidedrawer);
  $('.js-hide-sidedrawer').on('click', hideSidedrawer);
  
  
  var $titleEls = $('strong', $sidedrawerEl);
  
  $titleEls
    .next()
    .hide();
  
  $titleEls.on('click', function() {
    $(this).next().slideToggle(200);
  });
});


Change Scatterplot Shape

from Spotfire.Dxp.Application.Visuals  import ScatterPlot,MarkerShape,MarkerType,CategoryKey

vis = v.As[ScatterPlot]()

#params
shape = Document.Properties["SHAPE"]
category = Document.Properties["CATEGO"]

markerType = getattr(MarkerType,shape)
vis.ShapeAxis.ShapeMap[category] = MarkerShape(markerType)

#list available shapes to copy and add data table from clipboard to populate shape dropdown:
shapes = [a for a in dir(MarkerType) if  not callable(getattr(MarkerType,a)) and not '__' in a ]
for x in shapes:print x



Trigger javascript from Dropdown-list property control (7.0+)

Trigger a javascript function after a dropdown property control changes. To trigger an ironPython, just associate a script on the document property.


html
<span id='dropDownDocProp'>
<SpotfireControl id="6d69ba781efa4b93ab8aaa1857a45d2d" />
</span>

javascript
MutationObserver = window.MutationObserver || window.WebKitMutationObserver;

//this is the target element to monitor changes
//just put the span id here. You can remove next line and add a script param called targetDomId
var targetDomId = "dropDownDocProp"

//function when dropdown value changes
var myFunction = function(oldValue,newValue){
alert("old value:["+oldValue+"]\nnew value:["+newValue+"]")
                //click on a actioncontrol with python script
}


//no need to change after this line.
var target = document.getElementById(targetDomId)

//callback is the function to trigger when target changes
var oldVal = target.innerText.trim()
var callback = function(mutations) {
newVal=$('#'+targetDomId+' .ComboBoxTextDivContainer').text()
if(newVal!=oldVal) myFunction(oldVal,newVal)
oldVal = newVal;
}

//this is to glue these two together
var observer = new MutationObserver(callback);

var opts = {
    childList: true, 
    attributes: true, 
    characterData: true, 
    subtree: true
}

observer.observe(target,opts);


Note: To trigger ironPython script, just associate the script with the document property

Checkbox property control


With this approach you can have a checkbox that can trigger javascript and python scripts:



The calculated value click action toggles a Boolean document property. When this Boolean doc property changes it triggers the iron python script.

The javascript is triggered by adding a click event handler


Explanation


  1. Calculated value has a custom expression to show a checkbox character marked when document property is on or off:

      If(${booleanDocProp},"☒","☐")

  2. Calculated value has a script associated to run a python script when clicked. The script changes the state of the document property:

    Document.Properties['booleanDocProp'] = not Document.Properties['booleanDocProp']

  3. Another python script changes the visualization title when document property changes:

    //viz is a Visualization type script parameter
    viz.Title = "Checkbox is ON" if Document.Properties['booleanDocProp'] else "Checkbox is OFF"

  4. Optionally trigger a javascript when the calculated value changes.

    cb=document.getElementById('boxyCheckbox')
    cb.addEventListener('click',function(){
      status = cb.innerText=="☒"?"OFF":"ON"
      alert("Checkbox is " + status)
    })

Reverse sort order on filters

Unfortunately there is no way to reverse the sort order on non categorical data such as dates and numbers on columns or filters. A work around is to create a calculated column to rank and concatenate.

Repeat("0",Max(Len(String(DenseRank([Date],"desc")))) - Len(String(DenseRank([Date],"desc")))) & DenseRank([Date],"desc") & " - " & [Date] as [DDate]






Highlight selected spotfire action control buttons






html
<style>

.on{
  background-color: #ABFF00;
  box-shadow:   rgba(45, 163, 215, 0.5) 0 0px 0px 0px, 
           inset rgb(45,163, 215) 0 -2px 0px, 
                 rgb(45,163, 215) 0 0px 7px;
}

.container {
    display: flex;
    flex-flow: row wrap;
    justify-content: space-around;
    backgrond:yellow;
}
.container > div {
    margin: 0px;
    padding: 0px;
    xwidth:220px;
    xheight:50px;
    xbackground-color: rgb(14,145,172);
    xborder-radius:5px;
    text-align:center;
    xcolor:white;
}

.r3 {
 font-weight:bold;
 width:100%;
 border-bottom:1px solid black;
 margin-right:20px; 
 text-align:center 
}

</style>


<div class="container">

   <div>
 <div class='r3'> Last <SpotfireControl id="action_button_control_here" /> days</div>
<span id='dayBtns'>
 <SpotfireControl id="action_button_control_here" /> <SpotfireControl id="action_button_control_here" />  <SpotfireControl id="action_button_control_here" /></span>
   </div>
   <div>
   <div class='r3'> Options</div>
    <TABLE border=0  width="1px" >
      <TBODY>
 <TR id='helpBtns'>
  <TD title="Shows subtitle (help) for each visualization">Help</TD>
  <TD ><SpotfireControl id="action_button_control_here" /></TD>
  <td ><SpotfireControl id="action_button_control_here" /></TD>
 </TR>
 <TR id='filterBtns'>
  <TD title="Shows or hides the Details on Demand Panel">Filters</TD>
  <TD ><SpotfireControl id="action_button_control_here" /></TD>
  <td ><SpotfireControl id="action_button_control_here" /></TD>
 </TR>
 <TR id='detailsBtns'>
  <TD title="Shows or hides the Details on Demand Panel">Details</TD>
  <TD ><SpotfireControl id="action_button_control_here" /></TD>
  <td ><SpotfireControl id="88ed6f5873ad4fb0b4cb6542696f4e3e" /></TD>
 </TR>
      </tbody>
    </table>
   </div>
</div>



JS
function lightsOff(cssSel){$(cssSel).removeClass("on")}


//make day selection buttons highlight when clicked
$("#dayBtns input").click(function(){
    $("#dayBtns input").removeClass("on")
 $(this).addClass("on")
})

//make on/off help buttons highlightable
$("#helpBtns input").click(function(){
    $("#helpBtns input").removeClass("on")
 $(this).addClass("on")
})

//add magic to details on|off buttons 
$("#detailsBtns input").click(function(){
    $("#detailsBtns input").removeClass("on")
 $(this).addClass("on")
})

//make spotfire action control buttons to turn on|off filter panel to highlight
$("#filterBtns input").click(function(){
    $("#filterBtns input").removeClass("on")
 $(this).addClass("on")
})


//highlight default buttons 
$("#dayBtns input:first").addClass("on")
$("#helpBtns input:last").addClass("on")
$("#detailsBtns input:last").addClass("on")

$("#filterBtns input:last").addClass("on")


Delete first 10 rows

#dt is a data table script parameter
from Spotfire.Dxp.Data import RowSelection, IndexSet

#select all rows
selection   = IndexSet(dt.RowCount,True)

#mark first 10 rows row as true and the rest false
for r in selection:
   selection[r] = (r<=10)

#delete rows based on selection (rows from indexset marked as true)
dt.RemoveRows(RowSelection(selection))

Gauge.js in Spotfire

Gauge.js implementation in Spotfire



Ingredients:
  • 1 text area
  • calculated value control or label property control
  • two javascripts: gauge.js and yourGauge.js in textarea

Example 1





html
<canvas id="gauge1"></canvas>
<span id="calcValue1" style='display:none'>
   <SpotfireControl id="SpotfireCalculatedValueControlHere" />
</span>   

note: the spotire calculated value must not contain any formatting

gauge.js
copy and paste the javascript contents of gauge.js from this link:
http://bernii.github.io/gauge.js/dist/gauge.min.js 


Note: If using more than one gauge in a tab, only one gauge.js is needed in one text area.

yourGauge.js
var opts = {
  lines: 12,
  angle: 0.054,
  lineWidth: 0.54,
  pointer: {
    length: 0.8,
    strokeWidth: 0.035,
    color: '#000000'
  },
  staticZones: [
   {strokeStyle: "#F03E3E", min: 0, max: 130}, // Red from 0 to 130
   {strokeStyle: "#FFDD00", min: 130, max: 250}, // Yellow
   {strokeStyle: "#30B32D", min: 250, max: 320}, // Green
   {strokeStyle: "#FFDD00", min: 320, max: 460}, // Yellow
   {strokeStyle: "#F03E3E", min: 460, max: 1000}  // Red
],
staticLabels: {
  font: "10px sans-serif",  
  labels: [000, 250, 350, 450, 550, 1000],  
  color: "#000000",  
  fractionDigits: 0  
},
  limitMax: 'false', 
  percentColors: [[0.0, "#a9d70b" ], [0.50, "#f9c802"], [1.0, "#ff0000"]], 
  strokeColor: '#E0E0E0',
  generateGradient: true
};

var target = document.getElementById('gauge1');
var gauge = new Gauge(target).setOptions(opts);
gauge.maxValue = 1000;
gauge.animationSpeed = 12;
//gauge.set(250);

function refreshGauge1(){
  val = parseInt($("#calcValue1").text())
  if(isNaN(val)) return

  gauge.set(val)
}

$("#calcValue1").one('DOMSubtreeModified',refreshGauge1)

refreshGauge1();

To show the actual calculated value with specific formatting, add another calculated value wrapped in a div with proper positioning:


191 lb

html
<canvas id="gauge1"></canvas>
<span id="calcValue1" style='display:none'>
   <SpotfireControl id="SpotfireCalculatedValueControlHere" />
</span>   

<div style='position:absolute;top:150px;left:125px;font-size:30px'>
   <SpotfireControl id="VisibleSpotfireCalculatedValueControlHere" /></div>


note: The visible Spotfire calculated value can contain any formatting, but it will not animate the same was as the next example
Example 2



html
<div style="text-align:center">
   <canvas width="200" height="170" id="gauge2"></canvas>
<div id="measure2
   style="font-size:45px;
        position:absolute;
        top:55px;left:0;right:0;
        text-align:center;">
   </div>
</div>

<span id="calcValue2" style='display:none'>
   <SpotfireControl id="SpotfireCalculatedValueControlHere" 
</span>    

js
var opts = {
  lines: 12, 
  angle: 0.22,
  lineWidth: 0.1, 
  pointer: {
    length: 0.5, 
    strokeWidth: 0.035, 
    color: '#afafaf' 
  },
  limitMax: 'false', 
  colorStart: '#2DA3DC', 
  colorStop: '#C0C0DB',
  strokeColor: '#EEEEEE', 
  generateGradient: true
};
var target = document.getElementById('gauge2'); 
var gauge = new Donut(target).setOptions(opts); 

gauge.maxValue = 100; 
gauge.animationSpeed = 32; 
gauge.setTextField(document.getElementById("measure2"));

function refreshGauge2(){
  val = parseInt($("#calcValue2").text())
  if(isNaN(val)) return

  gauge.set(val)
}

$("#calcValue2").one('DOMSubtreeModified',refreshGauge2)

refreshGauge2();




Note: When editing the html or javascript from the text area, the gauge might seem to disappear. Just click another tab and come back to re-render the gauge. It also works by making the calc value change.