Customer Banners (Ads) - SpiceUp. AX and SpotfireX Disclaimer



Refer to TIBCO Javascript best apractices when implementing these scripts
Please share your feedback

Add calculated value tooltips to a calculated value

Calculated values lacks of showing additional information other than the expression it was used (unless the display name changes). A little javascript helps to pass a hidden calculated value to it.

html

<div id="myCalcValue">
   Selected letters: <SpotfireControl id="6cdd0461700b42c98f246b8d61ffb55b" />
</div>

<div id="myCalcValueTootlips" hidden>
Calculated value tooltips:
  <SpotfireControl id="f245f0a3515d476783fbb36063e6669c" />
</div>

js

tooltip_source = "myCalcValueTootlips"
tooltip_target = "myCalcValue"

window.clearInterval(window.sectorTooltipInterval)
window.sectorTooltipInterval=setInterval(function(){
   document.querySelector("#"+tooltip_target).title = document.querySelector("#"+tooltip_source).innerText
}, 500);

output 








bypass html sanitation

html

<input value="inputs not supported:"/>
<button onclick=msg('spotfire developer')>not supported</button>
<a title="not supported! really?" href="#">test</a>
<script>
  function msg(name){alert(`hello ${name}`)}
</script>

<hr>

<code>
<input value="inputs now supported:"/>
<button onclick="msg('spotfire developer')">now supported</button>
<a title="titles are now supported" href="#">check title</a>
<script>
function msg(name){alert(`good job ${name}`)}
</script>
</code>


js

$("#alert").html($($("#alert")[0].innerText))
$("#alert").replaceWith(a=> { return $(':first', this); });









Hidden visualization properties

Sometimes latest API is not published or some functionality is not exposed. 

script

from Spotfire.Dxp.Application.Visuals.Maps import MapChart
#map is a script parameter pointing to your map
m = map.As[MapChart]()

import inspect
for i in inspect.getmembers(m):
    # Ignores anything starting with underscore 
    # (that is, private and protected attributes)
    if not i[0].startswith('_'):
        # Ignores methods
        if not inspect.ismethod(i[1]):
            print(i)


output

('AddAutoTitleTrigger', )
('ApplyColumnNamesOnAxis', )
:
('SerializeReadOnlyProperty', )
('ShowDescription', False)
('ShowInteractionMode', True)
('ShowLayerHandler', True)
('ShowNavigationControls', True)
('ShowScale', True)
('ShowSearchField', True)
('SupportsChangeDataTableByCheckingAxesAndExpressionColumns', )
('SupportsTransparency', True)
('SupportsTryChangeDataTable', )
('Title', 'Map chart')
('Transactions', )
('Transform', )
('Transparency', 0.0)
('Trellis', )
('TrellisLayerReference', )
('TryGetFilterRules', )
('TypeId', )
('UseSeparateColorForMarkedItems', False)
('ValidateAttached', )
('ViewExtent', )
('Visual', )

use case

from Spotfire.Dxp.Application.Visuals.Maps import MapChart
#map is a script parameter pointing to your map
m = map.As[MapChart]()

m.ShowLayerHandler= not m.ShowLayerHandler
m.ShowDescription= not m.ShowDescription
m.ShowSearchField= not m.ShowSearchField
m.ShowScale= not m.ShowScale
m.ShowInteractionMode= not m.ShowInteractionMode
m.ShowNavigationControls = not m.ShowNavigationControls #hide
m.ShowNavigationControls = True #show

Flame Visualization (Spotfire Mod)

 This visualization can be used to visualize how "hot" something is by showing an animated flame. The intensity is determined by the measure per category. Designed for not too many categories

Feel free to download from here







Tetris (My first mod)

This is my first mod after a year of being away from my blog. 

Play Tetris on Spotfire X. If you can play tetris on Spotfire, you can do anything with the platform! 

This is a tetris ported in Spotfire. I am so exited of these mods it really unlocks Spotfire capabilites!

Works both, in the client and the web player.

Download the Tetris.mod here

To install, just download, drag and drop to your spotfire client. You might be able to check it out here

More mods coming up. Stay tuned!



Format a column

from Spotfire.Dxp.Data import DataType
from Spotfire.Dxp.Data.Formatters import NumberFormatCategory 

formatter=DataType.Real.CreateLocalizedFormatter()
formatter.Category=NumberFormatCategory.Number
formatter.DecimalDigits=02

#table is a DataTable script parameter
table.Columns["temperaturemin"].Properties.Formatter=formatter

Change a join after inserting a column

from Spotfire.Dxp.Data import *
from Spotfire.Dxp.Data.DataOperations import *
from System import *

#Join Types
i   = 'InnerJoin'
lo = 'LeftOuterJoin'
ro = 'RightOuterJoin'
fo = 'FullOuterJoin'
ls = 'LeftSingleMatchJoin'
rs = 'RightSingleMatchJoin'

joinType = lo
matchOnNull = 0

op = table.GenerateSourceView().GetAllOperations[AddColumnsOperation]()[0]
old = op.AddColumnsSettings;
newSettings = AddColumnsSettings(old.Map, Enum.Parse(JoinType, joinType), old.IgnoredColumns, matchOnNull)
op.AddColumnsSettings = newSettings

source: https://community.tibco.com/wiki/how-change-join-type-add-columns-operation-tibco-spotfire-using-ironpython

Simple popup




html

<ul>
  <li><a  href="#popup1">Filters</a>
  <li><a  href="#popup2">Inputs</a>
  <li><a  href="#popup3">About</a>
</ul>

<div id="popup1" class="overlay">
  <div class="popup">
    <h2>Filters</h2>
    <a class="close" href="#">&times;</a>
    <div class="content">
      Region:<br>
      <SpotfireControl id="4e267da24bbb4c0caaec3a001ed70c93" />
      <br>Rank:<br>
      <SpotfireControl id="4a5d98d3faae45cfb14beabd3338aeb1" />
      <br>Promotions:<br>
      <SpotfireControl id="7cd9afca669640dba8ef856d5ce227e4" />
      <br>Store Type:<br>
      <SpotfireControl id="634158287ec847808d07dab327b04cf3" />
    </div>
  </div>
</div>

<div id="popup2" class="overlay">
  <div class="popup">
    <h2>Inputs</h2>
    <a class="close" href="#">&times;</a>
    <div class="content">
      Enter discount amount:<br> 
      <SpotfireControl id="7965aff6ff4541ca8bf89464411cbfdc" />
      <br>Select state:<br> 
      <SpotfireControl id="c01e7f23d4e04c2bbedf7dac3f16a6da" />
      <br><a href="#"><button style='float:right'>apply</button></a>
    </div>
  </div>
</div>

<div id="popup3" class="overlay">
  <div class="popup">
    <h2>About</h2>
    <a class="close" href="#">&times;</a>
    <div class="content">
      Save real state with smart popups!
    </div>
  </div>
</div>

<style>
  .overlay {
  position: fixed;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  background: rgba(0, 0, 0, 0.2);
  transition: opacity 500ms;
  visibility: hidden;
  opacity: 0;
  }
  .overlay:target {
  visibility: visible;
  opacity: 1;
  }
  .popup {
  margin: 70px auto;
  padding: 20px;
  background: whitesmoke;
  border-radius: 5px;
  width: 30%;
  position: relative;
  transition: all 5s ease-in-out;
  }
  .popup h2 {
  margin-top: 0;
  color: #333;
  font-family: Tahoma, Arial, sans-serif;
  }
  .popup .close {
  position: absolute;
  top: 20px;
  right: 30px;
  transition: all 100ms;
  font-size: 30px;
  font-weight: bold;
  text-decoration: none;
  color: #333;
  }
  .popup .close:hover {
  color: #06D85F;
  }
  .popup .content {
  max-height: 30%;
  overflow: auto;
  }
  @media screen and (max-width: 700px){
  .box{
  width: 70%;
  }
  .popup{
  width: 70%;
  }
  }
</style>

Rotate bullet graph vertically

Turn a bullet graph vertically


Example 1



html
<div class='thermometer1'> <SpotfireControl id="4e93c6fb1408420cbdf20066dceaa8ed" /></div>

<style>
.thermometer1{
    width: 200px; 
    height: 50px; 
    margin-top: 123px;
    -webkit-transform: rotate(-90deg);
    -moz-transform: rotate(90deg);
    -o-transform: rotate(90deg);
    -ms-transform: rotate(90deg);
    /* transform: rotate(90deg); */
    /* text-align: center; */
}
.thermometer1 img{
    padding:10px;
width:20px;
    background-color: #fff;
    box-shadow: -5px 2px 13px 3px #8a6b6b;
    border-radius: 20px;
}
</style>




Example 2
No labels, no shadow




html

<div class='thermometer2 shadow'> <SpotfireControl id="4e93c6fb1408420cbdf20066dceaa8ed" /></div>


<style>
.thermometer2{
    -webkit-transform: rotate(-90deg);
    -moz-transform: rotate(90deg);
    -o-transform: rotate(90deg);
    -ms-transform: rotate(90deg);

    /*position*/
    width: 100px; 
    margin-top: 100px;
}

/*height, width*/
.thermometer2 img{
    width: 140px !important; 
    height: 30px !important; 
    border-radius: 100px;
    /*box-shadow: -5px 5px 25px 2px #8a6b6b;*/  /*optional*/

}
</style>

Get Scripts

for s in Document.ScriptManager.GetScripts():
print "Script Name: \t"+s.Name
print "Description: \t"+s.Description
print "Parameters:"
for p in s.Parameters: print "\t\t"+p.Name
print "_"*50

Hide empty checkbox filter value on text area

A Quick and dirty way to hide the annoying but useful [] (empty) checkbox on a checkbox filter in a text area:


html
<div id=filter onmouseover=hideEmpty()>
<SpotfireControl id="7daf87b0152b448a8fa82353af350875" />
</div>


js
var hideEmpty = function(){
document.querySelectorAll("#filter .sf-element.sf-element-filter-item:last-child")[0].style.display='none'
}

if(this['ps']) clearInterval(ps)
ps = setInterval(hideEmpty,100)




Spinning refresh data table button



METHOD 1

How it works
Spotfire link control that refreshes data table through ironpython. The button starts to spin when clicked. When the data table finishes loading, a document property gets updated with current timestamp, which is rendered as a label in the text area. When this label changes, the button stop spinning.

html
The first Spotfire control is a link that runs a script that refresh the data table and updates a document property. The second control is a label from a document property of data type Date Time

<center>
<div size=3  id=spinner><SpotfireControl id="add6c340686046cb92e10c16ac96680c" /></div>
<br/>
<font id ='updateLabel' size=1 >
Last Reload : <SpotfireControl id="8d5975f94c9442ae8badec2f462aa8f2" />
</font>
</center>

<style>

.spinner {

  width:20px;

  animation-name: spin;

  animation-duration: 2000ms;
  animation-iteration-count: 20;
  animation-timing-function: linear; 
}

@keyframes spin {

    from {
        transform:rotate(0deg);
    }
    to {
        transform:rotate(720deg);
    }
}
</style>


script
from System import DateTime
dt.Refresh()
Document.Properties[dp]=DateTime.Now


js
 $("#spinner").on('click',function(){
var $this = $(this).addClass('spinner');
});
  
  
//function when label changes
var myFunction = function(oldValue,newValue){
$("#spinner").removeClass('spinner')
}
targetDomId = "updateLabel"

//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);



METHOD 2
ActiveVisual is a text area holding the animation. In this case is just a label. Thanks to Jolene Roberson!

script
from Spotfire.Dxp.Application.Visuals import *
from System.Collections.Generic import List, Dictionary
from Spotfire.Dxp.Data import DataTable
from Spotfire.Dxp.Application.Scripting import ScriptDefinition
from Spotfire.Dxp.Framework.ApplicationModel import NotificationService
import clr
import time

activeVisual.As[HtmlTextArea]().HtmlContent = "Please wait.."

activeVisual = Document.ActivePageReference.ActiveVisualReference

Document.Properties['refreshStatus'] = 'Loadng'

if Document.Properties['refreshStatus'] != 'Ready':

    notify = Application.GetService[NotificationService]()

    # Refresh Callback function
    def execCallBack(exception, Document=Document, notify=notify):
        if not exception:
            Document.Properties['refreshStatus'] = 'Ready'
            activeVisual.As[HtmlTextArea]().HtmlContent = 'Done!'
        else:
            activeVisual.As[HtmlTextArea]().HtmlContent = 'Error!'
            notify.AddErrorNotification('Error refreshing table(s)','Error details', str(exception))

    # Note that more than one table can be added to the List object. Repeat the Add() method if needed for multiple tables to refresh.
    dataTables = List[DataTable]()
    dataTables.Add(Document.Data.Tables['Table1'])
    dataTables.Add(Document.Data.Tables['Table2'])

    Document.Data.Tables.RefreshAsync(dataTables, execCallBack)

Remove (Empty) from cross table without messing number formatting

Normally I use a sn([column],"") to hide "(Empty)" but if my column data type is other than string, it affects the formatting (i.e. currency).

For that reason, Jquery to the rescue. This will replace all divs with class cell-text that contains the "(Empty)" string with "". Here is how:

$('div.cell-text:contains("(Empty)")').text("")

Thanks Jolene Robertson!

Hide crosstable header columns or rowheaders

from Spotfire.Dxp.Application.Visuals import *
from Spotfire.Dxp.Application.Visuals.Miniatures import *

#gt = VisualTypeIdentifiers.GraphicalTable
ct = VisualTypeIdentifiers.CrossTable


def hideColums(x,n,w):
   for vis in Document.ActivePageReference.Visuals:
     if vis.TypeId==x:
         vis = vis.As[Visualization]()
                          for i in range(0,n): 
                              vis.RowHeaderWidths[i] = w


hideColums(ct, 2, 0) #vis type, n=number of headers, width 




Thanks Jolene Robertson!

Comments in custom expressions

Currently is not possible to add comments or comment a line in custom expressions. The only way I can think of is to wrap the expression with a case statement:

syntax:
case  "the ignored expression must be valid" > "both expressions must return the same data type" then   valid data type result, but ignored 
else 
  your expression
end

Example:
case when "Returns the year and week for today's date as an integer (Year*100 + Week number). For example, the date 2005-10-13 will return 200541.">"" then 0 
else 
YearAndWeek(DateTimeNow())
end


Example 1: Expression that returns a string

expression without comment:

First(If(UniqueCount([ProjectName])=1,[ProjectName],Concatenate(UniqueCount([ProjectName])," projects selected")))

expression with comment:

case "This expression shows the full name when only one item is marked. Else it shows the item count" > "Author: Jose Leviaguirre"
else
  First(If(UniqueCount([ProjectName])=1,[ProjectName],Concatenate(UniqueCount([ProjectName])," projects selected")))
end

Example 2: Expression that returns a Date 

without comment:

Date(RXReplace([Origin of data],"(\\D*)(\\d{4})(\\d{2})(\\d{2})(.*)","$2-$3-$4","ig"))

with comments:

case 
when "extracts the date from a string of the form 'my_file_20020604.xls'" > "Date('2002-06-04')" then Date("never reached")
else 
Date(RXReplace([Origin of data],"(\\D*)(\\d{4})(\\d{2})(\\d{2})(.*)","$2-$3-$4","ig"))
end

Example 3: Expression that returns a number

without comment:

Sum([Oil Production]) OVER Intersect([Well ID], AllPrevious([Year]))

with comments:

case when "Cumulative oil production per well">"" then 0
else
  Sum([Oil Production]) OVER Intersect([Well ID], AllPrevious([Year]))
end




Show / Hide Panels

script
for a docked Filter Panel

#show hide FilterPanel across all tabs
#pages to ignore toogling filter panel
xTabs = ['HOME','css']

thisPagePanelState = Document.ActivePageReference.FilterPanel.Visible

for p in Document.Pages:
if p.Title not in xTabs:
p.FilterPanel.Visible = not thisPagePanelState 


script
for undocked Filter Panel (popup)
from Spotfire.Dxp.Application.Layout import PanelState 

filterPanel = Document.ActivePageReference.FilterPanel

#Hide undocked filter panel if opened 
if filterPanel.PanelState == PanelState.Popover:
filterPanel.PanelState = PanelState.Docked 
filterPanel.Visible=False
else: #show udocked filterpanel
filterPanel.PanelState = PanelState.Popover




script
for DoD Panel

#show hide DetailsOnDemandPanel across all tabs
#pages to ignore toogling filter panel
xTabs = ['HOME','css']

thisPagePanelState = Document.ActivePageReference.DetailsOnDemandPanel.Visible

for p in Document.Pages:
if p.Title not in xTabs:
p.DetailsOnDemandPanel.Visible = not thisPagePanelState  





css tabs

No jquery, no js. Just edit html on text area and paste the code below. It can handle spotfire conttrols too



html
<div class="tab_container"> <input id="tab1" type="radio" name="tabs" checked> <label for="tab1"><i class="far fa-arrow-alt-circle-right"></i> <span>Highlights & Forward Look</span></label> <input id="tab2" type="radio" name="tabs"> <label for="tab2"><i class="fas fa-bullseye"></i><span> Scope</span></label> <input id="tab3" type="radio" name="tabs"> <label for="tab3"><i class="fas fa-user-tie"></i><span> Ejecutive Summary</span></label> <section id="content1" class="tab-content"> <h3>Highlights</h3> <p> <ul> <li>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. </li> <li>Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, </li> <li> sunt in culpa qui officia deserunt mollit anim id est laborum. </li> </ul> </p> <h3>Forward Look</h3> <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. </p> <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. </p> </section> <section id="content2" class="tab-content"> <h3>Scope</h3> <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo. </p> <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. </p> </section> <section id="content3" class="tab-content"> <h3>Ejecutive Summary</h3> <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. </p> <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. </p> </section> <section id="content4" class="tab-content"> <h3>Headline 4</h3> <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. </p> <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. </p> <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. </p> </section> <section id="content5" class="tab-content"> <h3>Headline 5</h3> <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo. </p> <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. </p> </section> </div> <p class="no_wrap"> something static here </p> <style> @import url('https://use.fontawesome.com/releases/v5.7.0/css/all.css'); .clearfix:before, .clearfix:after { content: " "; display: table; } .clearfix:after {clear: both;} .tab_container { width: 90%; margin: 0 auto; padding-top: 10px; position: relative; } .tab_container input, .tab_container section { clear: both; padding-top: 10px; display: none; } .tab_container label { font-size: 12px; display: block; float: left; xwidth: 100%; padding: 1.0em; color: #757575; cursor: pointer; text-decoration: none; text-align: center; background: #f0f0f0; } #tab1:checked ~ #content1, #tab2:checked ~ #content2, #tab3:checked ~ #content3, #tab4:checked ~ #content4, #tab5:checked ~ #content5 { display: block; padding: 20px; background: #fff; color: #999; border-bottom: 2px solid #f0f0f0; } .tab_container .tab-content p, .tab_container .tab-content h3 { -webkit-animation: fadeInScale 0.7s ease-in-out; -moz-animation: fadeInScale 0.7s ease-in-out; animation: fadeInScale 0.7s ease-in-out; } .tab_container .tab-content h3 { text-align: center; } .tab_container [id^="tab"]:checked + label { background: #fff; box-shadow: inset 0 3px #0CE; } .tab_container [id^="tab"]:checked + label .fa { color: #0CE; } .tab_container label .fa { font-size: 1.3em; margin: 0 0.4em 0 0; } /*Media query*/ @media only screen and (max-width: 900px) { .tab_container label span { display: none; } .tab_container { width: 98%; } } /*Content Animation*/ @keyframes fadeInScale { 0% { transform: scale(0.9); opacity: 0; } 100% { transform: scale(1); opacity: 1; } } .no_wrap { text-align:center; color: gray; } .link { text-align:center; } </style>