Just uncheck the marker layer or remove the Positioning markers (geocoding or coordinate columns) from the marker layer, go back to the image layer and hit the reset button
Autocomplete
Add autocomplete to an existing Spotfire input control (webplayer and cliente )
html
<div id="autocomplete">
<SpotfireControl id="spotfire_Input" />
</div>
<div id="autocomplete-data" hidden>
John Doe,Jane Smith,Robert Johnson,Michael Brown,Emily Davis,Sarah Miller,James Wilson,Patricia Moore,Richard Taylor,Linda Anderson
</div>
Preventing zoom when scrolling with the mouse wheel on visuals with zoom sliders
Here is how to prevent zoom on visualization that have zoom sliders on to zoom when scrolling the page down using the mouse wheel
preventMouseScrollFromZoom.js
divsWithZoomSliders = [...document.querySelectorAll(".sf-element.sf-element-visual")].filter(div => div.querySelector(".sf-element-zoom-slider"));
divsToDisable = divsWithZoomSliders.map(div => div.querySelector(".sf-element.sf-element-canvas-visualization"));
divsToDisable.forEach(div => {div.firstChild.addEventListener('wheel', function(event) {event.preventDefault()}, { passive: true })});
Highlight Visual based on Document Property
When a document property changes, it highlights a visual. This can be useful for data analysis to pay close attention to visuals that require focus
html
<pre id=docPropValues xhidden>
{
"Indexed price charter":"<SpotfireControl id="5858b9bd6d344a98ba87c742af3d9f05" />",
"Top holders by percent held":"<SpotfireControl id="96f46c37e0ab4731a43124c827f3956f" />",
"Historical data":"<SpotfireControl id="5302059ba4724d1f8e45c6a1b95bcfe6" />",
"Calendar quarter estimates":"<SpotfireControl id="21331969168d4e2fb600d4ed1e0004be" />"
}
</pre>
JavaScript
//finds visuals in which title contains visualTitle (use *= for contains, ^= starts with, $= ends with or = exact match)
elements = Array.from(document.querySelectorAll(".sf-element.sf-element-visual"));
function highlighVisual(visualTitle){
//set background for those visuals found
elementWithChild = elements.filter(element => element.querySelector(
[title*='"+visualTitle+"']") !== null); //<-- change here for search operator
elementWithChild.forEach(x=>x.style.background="red")
}
element = document.querySelector('#docPropValues');
observer = new MutationObserver(_ => {
json = JSON.parse(element.innerText);
//reset visual backgrounds
elements.forEach(x=>{x.style.background=""})
Object.entries(json)
.filter(([key, value]) => value === "True")
.map(([key, value]) => key)
.forEach(visualTitle => {highlighVisual(visualTitle)});
});
observer.observe(element, {
childList: true,
characterData: true,
subtree: true
});
IronPython Show/Hide Items
from Spotfire.Dxp.Application.Visuals import BarChart
barChart = vis.As[BarChart]()
#get filter rules
gotFilerRules,filterRuleCollection = barChart.TryGetFilterRules()
#delete all filter rules
if gotFilerRules:
for filterRule in filterRuleCollection:
filterRuleCollection.Remove(filterRule)
#print filterRule.DisplayName, filterRule.Expression
#add a filter rule collection to show top 5 axis values
#filterRuleCollection.AddTopNRule("THEN [Y.FinalValue]",5,True)
Create trellised visualizations based on marking
from Spotfire.Dxp.Application.Visuals import BarChart, VisualContent, VisualTypeIdentifiers, LabelOrientation, BarChartOrientation
from Spotfire.Dxp.Application.Layout import LayoutDefinition
#script params
dataTable = Document.Data.Tables["Best predictors"]
#delete all barchart visuals
page = Document.ActivePageReference
for vis in page.Visuals:
if vis.TypeId == VisualTypeIdentifiers.BarChart:
page.Visuals.Remove(vis)
#The last visual left should be the treemap
tm = next(iter(page.Visuals))
#create a barchart template
bc = Application.Document.ActivePageReference.Visuals.AddNew[BarChart]()
bc.Data.DataTableReference = dataTable
bc.Title = "${DataTable.DisplayName}"
bc.Legend.Visible= False
bc.YAxis.Expression = "Sum([p-value])"
bc.XAxis.Expression = "<[Case Name]>"
bc.SortedBars=True
bc.Orientation = BarChartOrientation.Horizontal
#duplicate as many barcharts as selected sites from marking
siteNames = Document.Properties["markedSites"]
sites = [s.strip() for s in siteNames.split(',')]
#setup first barchart
firstSite = sites.pop()
bc.Title = firstSite
siteVisuals = [bc]
bc.Data.WhereClauseExpression = "[Site_No] = '"+firstSite+"'"
#create visuals
for site in sites:
vis = page.Visuals.AddDuplicate(bc.Visual)
vis.Title =site
bc = vis.As[BarChart]()
bc.Data.WhereClauseExpression = "[Site_No] = '"+site+"'"
siteVisuals.append(vis.As[BarChart]())
#arrange visuals
#tm is the existing treemap and will take 10% of the screen
ld = LayoutDefinition()
ld.BeginSideBySideSection()
ld.Add(tm, 10)
# Begin a stacked section for the second column at 70% of the screen
ld.BeginStackedSection(70)
i = 0
for bc in siteVisuals:
if i % 3 == 0:
if i > 0: ld.EndSection()
ld.BeginSideBySideSection()
ld.Add(bc.Visual)
i += 1
ld.EndSection()
ld.EndSection()
ld.EndSection()
page.ApplyLayout(ld)
To trigger this script when marking changes, create a bypass data function. The script definition is simply an 'x' and so is the input and output. Make sure it runs automatically. The script parameters for the 'x' input is "UniqueConcatenate([Explore_YieldData - Explore_YieldData].[Site])" limited by the blue Marking. The output is a document property called "markedSites" that must be setup to trigger the above script when its value changes.
ColorPicker
html
<span id="color">
<SpotfireControl id="Input Filed goes here" />
</span>
ColorPicker.js
Here is a use case on how to use it.
changeColor.py
# get the color from the color picker doc prop
hcolor = Document.Properties["color"] #hexadecimal color
Change Series on Combination Chart
Each document property triggers the corresponding iron python script when the value changes
for the category doc prop: reads the combination chart color and series type from the selected category
from Spotfire.Dxp.Application.Visuals import CombinationChart, CategoryKey
#cast visual to combo chart
combinationChart = vis.As[CombinationChart]()
category = CategoryKey(Document.Properties["category"])
#get type
Document.Properties["type"] = str(combinationChart.IndexedSeriesType[category])
#get color
import re
color = str(combinationChart.ColorAxis.Coloring.GetColorForCategory(category))
color = re.findall(r"\[([^\[\]]+)\]", color)[0]
Document.Properties["color"] = color.lower()
for the color doc prop: changes the color for the selected category
from Spotfire.Dxp.Application.Visuals import CombinationChart, CategoryKey
from System.Drawing import Color
#cast visual to combo chart
combinationChart = vis.As[CombinationChart]()
#get script parameters from doc props
category = CategoryKey(Document.Properties["category"]) #string representing a category from the series
color = Color.FromName(Document.Properties["color"]) #named color such as blue, green, magenta, beige...
#color = Color.FromArgb(255,0,0) #if you know the rgb values
#if hexadecimal color (hc) comes from a color picker (#FF0000)
#color = Color.FromArgb(int(hc[1:3], 16), int(hc[3:5], 16), int(hc[5:7], 16))
# change the color for the corresponding category
combinationChart.ColorAxis.Coloring.SetColorForCategory(category,color)
and for the type dropdown that changes the series type for the selected category:
from Spotfire.Dxp.Application.Visuals import CombinationChart, CombinationChartSeriesType, CategoryKey
#cast visual to combo chart
combinationChart = vis.As[CombinationChart]()
#get script parameters from doc props
category = CategoryKey(Document.Properties["category"])
#string representing a category from the series
type = CombinationChartSeriesType.Bar if Document.Properties["type"] == "Bar" else CombinationChartSeriesType.Line
# change series type as Bar or line
combinationChart.IndexedSeriesType[category] = type
Reset Visible Filters
#1. Take the filter panel from the desired page in filter panel
#filterPanel = Document.Pages[1].FilterPanel
filterPanel = Document.ActivePageReference.FilterPanel
#1.1 Select the filtering scheme to use (optional)
#filterringScheme = Document.FilteringSchemes[Document.Data.Filterings["Filtering scheme"]]
#filterPanel.FilteringSchemeReference = filteringScheme
#2. Reset only visible filters
for tableGroup in filterPanel.TableGroups:
for filterHandle in tableGroup.FilterHandles:
if filterHandle.Visible:
filterHandle.FilterReference.Reset()
Sync zoom sliders
sync the zoom sliders to the exact same range between different visuals.
The style attribute of the activeVisual tag hides the input visibility. It has an input property control that holds the visual title. This title is updated by the javascript that takes the last active visual on mouse over. This property control is then passed as a parameter for the sync.ip to take the last active visual as a reference.
html
<div style="position:fixed;left:-1000px" id="activeVisual">
<SpotfireControl id="input button" />
</div>
<br>
<span id="syncBtn">
<SpotfireControl id="replace with button to run sync.ip" />
</span>
<SpotfireControl id="optional reset button that runs reset.ip" />
sync.ip
from Spotfire.Dxp.Application.Visuals import AxisRange, ScatterPlot
# We detect which visual is "active"
sourceVisualTitle = Document.Properties["activeVisualTitle"]
visX = None
# Iterate through all visuals on the active page
for visual in Document.ActivePageReference.Visuals:
if visual.Title == sourceVisualTitle:
visX = visual
# We need to cast the visual script parameters visA, visB and visC to ScatterPlot object or whatever visual you are using in your analysis
scatterX = visX.As[ScatterPlot]()
scatterA = visA.As[ScatterPlot]()
scatterB = visB.As[ScatterPlot]()
scatterC = visC.As[ScatterPlot]()
# We create a reference to the Y axis ZoomRange from the first visual (A)
zoomXy = scatterX.YAxis.ZoomRange
# We need to create an AxisRange object based on visual X range settings for Y axis
axisRangeX = AxisRange(zoomXy.Low, zoomXy.High)
# Apply scatterA,B and C to the selected axisRange
scatterA.YAxis.ZoomRange = axisRangeX
scatterB.YAxis.ZoomRange = axisRangeX
scatterC.YAxis.ZoomRange = axisRangeX
reset.ip
from Spotfire.Dxp.Application.Visuals import AxisRange, ScatterPlot
# We need to cast the visual parameters visA, visB and visC to ScatterPlot object or whatever visual you are using in your analysis
scatterA = visA.As[ScatterPlot]()
scatterB = visB.As[ScatterPlot]()
scatterC = visC.As[ScatterPlot]()
#reset scatterA,B and C ranges
scatterA.YAxis.ZoomRange=AxisRange.DefaultRange
scatterB.YAxis.ZoomRange=AxisRange.DefaultRange
scatterC.YAxis.ZoomRange=AxisRange.DefaultRange
js
function getActiveVisual(){
vis = document.querySelector(".sfpc-active .sf-element-visual-title").innerText.trim();
inp = document.querySelector("#activeVisual input");
inp.value = vis;
inp.focus();
inp.blur();
}
document.getElementById("syncBtn").onmouseover = getActiveVisual
Loop through pages
html
<span id="startButton" style="cursor:default">[Start]</span>
<span id="stopButton" style="cursor:pointer">[Stop]</span>
JavaScript
//parameters
var pages = [0, 1, 3]; //◄ Select the pages you want to cycle through. 0 is the first one
var timeout = 10000; //◄ Select the time in ms to delay between pages. 10000 is 10 seconds
//maintain a registry of interval in case multiple are triggered to stop them all at once with the stop button
window["intervalIds"]??=[];
tmp=[...pages]
function startCycle() {
(function cycle(){
page = tmp.shift();
if(!tmp.length) tmp=[...pages]
goToPage(page);
window.intervalIds.push(setTimeout(cycle, timeout));
})();
}
function stopCycle() {
console.log("Slideshow Ended")
window.intervalIds.forEach(clearInterval);
}
function goToPage(x) {
document.querySelectorAll(".sf-element-page-tab")[x].click();
}
// Hook html buttons to start and stop functions
document.getElementById("startButton").onclick = startCycle;
document.getElementById("stopButton").onclick = stopCycle;
List table columns
# Replace with the actual table name
table_name = "Data Table"
# Get the table by name
table = Document.Data.Tables[table_name]
# Iterate through the columns and add their names to the list
for column in table.Columns:
print (column.Name)
Update a multiple select list box
The List box (multiple select) Property Control holds an array of values. Understanding this makes it easy to update its values
Filter Property Control
Create Searchable dropdowns / autocomplete with the help of Filters to act as Input Controls
This recipe allows you to leaverage the power of filters to drive property controls to enhance the user experience of Spotfire.
Ingredients
- New filtering scheme
- A filter from the new filtering scheme
- A calculated Value
- 1 Dropdown or Input property control
- 1 Input field property control
- html and javaScript
html
<div id="myInput" >
<span class="ddown">
◄spotfire 'selection' input property control goes here►
<span class='srchBtn'>⋯</span>
</span>
<span class="sfFltr"> ◄spotfire filter(s) goes here►
<span class='closeBtn'>✕</span>
</span>
<div class="sfCalcVal">◄spotfire calculated value from new filtering scheme goes heere►</div>
<div class="sfInput">◄spotfire 'selection' input fileld control goes here► </div>
</div>
javaScript
//script parameters
target = "myInput"
//node elements
container = document.getElementById(target);
dropdown = document.querySelector(".ddown");
filter = document.querySelector(".sfFltr");
searchButton= document.querySelector(".srchBtn");
closeButton = document.querySelector(".closeBtn");
selection = document.querySelector(".sfCalcVal");
sfInput = document.querySelector(".sfInput input");
//events
closeButton.addEventListener("click",()=>{
filter.hidden=true;
dropdown.hidden=false;
})
searchButton.addEventListener("click",()=>{
dropdown.hidden = true;
filter.hidden = false;
})
//monitor selection when its value changes
observer = new MutationObserver(()=>{
filter.hidden=true;
dropdown.hidden=false;
sfInput.value = selection.innerText;
sfInput.focus();
sfInput.blur();
})
observer.observe(selection, {childList: true,subtree: true});
//apply styling and attributes
filter.hidden = true;
selection.hidden = true;
css = `<style>
.closeBtn, .srchBtn{
vertical-align:top;
cursor:pointer;
}
.sfFltr {position:fixed;z-index:1;}
.sfInput{position:fixed;z-Index:-1;}
</style>`
container.insertAdjacentHTML('afterend',css)
Preparation
- Open the Sales and Marketing analysis from the library/samples folder
- Delete all but Sales performance and just keep the map
- Limit the SalesAndMarketing map layer with expression:
upper([Store Name]) ~= Upper("${selection}") - Create a new filtering scheme called "filtersForPropertyControls" or something like that
- you can do that by right clicking the filter panel and show the filtering scheme menu
- Change the [Store Name] filter type to use for the input property control as List Box Filter
- Make sure the "filtersForPropertyControls" filtering scheme IS NOT selected in the filters panel
- Add a Text area, edit in html and copy the html below
- Create an input or dropdown property control linked to a new document property called "selection" from a Text area. Place the control as the first child of the <span class="ddown"> element
- if using a dropdown, make sure to select unique values from [Store Name] column
- Add the filters from the "filtersForPropertyControls" filtering scheme inside the <span class="sfFltr"> element
- Create a calculated value inside the <div class="sfCalcVal"> to get the first element from the filter and limit the data only from the "filtersForPropertyControls" filtering scheme
- First([Store Name])
- very important not to leave any spaces. For example:
<div class="sfCalcVal"><SpotfireControl id="2d1..26cc" /></div> - Insert an Input field property control inside the <div class="sfInput"> element and link it to the "selection" document property
- Save the text area and Insert the JavaScript below
IronPython Terminal
script to run ironPython scripts
import sys
from io import StringIO
code = Document.Properties["input"]
capturer = StringIO()
sys.stdout = capturer
try:
exec(code)
sys.stdout = sys.__stdout__
output = capturer.getvalue()
except Exception as e:
output = str(e)
Document.Properties["output"]=output
script to run Python code via a data function
import sys
from io import StringIO
capturer = StringIO()
sys.stdout = capturer
try:
exec(code)
sys.stdout = sys.__stdout__
output = capturer.getvalue()
except Exception as e:
output = str(e)
html
<SpotfireControl id="input field multiple lines property control" />
<SpotfireControl id="action control that runs the above script" />
<pre style="height:50%;background:blue;color:yellow;overflow:auto;padding:10px;border:3px inset">
<SpotfireControl id="output document property (Label)" /></pre>
Example code to list python packages on TIBCO Cloud spotfire from this termnal
import pandas as pd
import pkg_resources
installed_packages = pkg_resources.working_set
installed_packages_list = sorted(["%s==%s" % (i.key, i.version)
for i in installed_packages])
package_list = [[r.split('==')[0],r.split('==')[1]] for r in installed_packages_list]
packages = pd.DataFrame(columns=["Package Name", "Package Version"])
idx = 0
for pkg in package_list:
packages.loc[idx] = [pkg[0], pkg[1]]
idx += 1
print(packages.to_string())
Note: if you put this code on a data function and you want to return a data table with the list of packages, then remove the last line and set the output parameter to point to the packages variable
Note: Please be careful running this script in production environments.
With great power comes great responsibiliy
Checkbox List Property Control
On a text area, create a multiple select list box property control and call it "myStringList" and another string input field property control and call it "transfer". The transfer document property should trigger the following script when it changes:
#update multiSelect
Document.Properties["myStringList"] = Document.Properties["transfer"].split(",")
Edit the text area html and wrap the controls with a tag element with id "myCheckboxList"
html
<div id="myChecboxList">
<SpotfireControl id="'myStringList' muiltiple selection list box" />
<SpotfireControl id="'transfer' input control " />
</div>
JS
id = "myChecboxList"
//get items from multiSelect
ph = document.getElementById(id);
items = ph.innerText.split("\n").filter(x=>x!="..."&&x.trim());
//get checked values from csv
target = ph.querySelector("input");
checked = target.value.split(",").filter(x=>x!="");
//create checkboxes
items.forEach((x,i)=>{
val = items[i];
cb = document.createElement("input");
cb.type ="checkbox";
cb.value = val;
tn = document.createTextNode(" "+val);
br = document.createElement("br");
[br,cb,tn].forEach(e => {ph.appendChild(e)});
//check if checked
cb.checked = checked.includes(val)
console.log(val,checked,val in checked)
//onclick
cb.onclick = () => {
vals = [...document.querySelectorAll(`#${id} input[type='checkbox']`)]
target.value = vals.filter(x => x.checked).map(x => x.checked ? x.value : null);
target.focus();
target.blur();
};
//hide input
target.style.position="fixed"
target.style.zIndex=-1"
//hide multiselect
ph.firstElementChild.style.display="none"
})
JavaScript Radial Icon Menu
itemsPerLayer = 4
html
<div class="iconMenu">
<SpotfireControl id="spotfire action control link 1" />
<SpotfireControl id="spotfire action control link 2" />
<SpotfireControl id="spotfire action control link 3" />
<SpotfireControl id="spotfire action control link 4" />
<SpotfireControl id="spotfire action control link 5" />
<SpotfireControl id="spotfire action control link 6" />
</div>
<img class="icons fa-solid fa-bolt,fa-solid fa-bolt-lightning,icon-graph,fa-solid fa-arrow-trend-up,fa-solid fa-check,fa-solid fa-house"/>
js
/*
description:converts spotfire links to an iconic menu. Supports font awesome and simple-line-icons
usage:
<div class="iconMenu">
<a>spotfire link 1</a>
<a>spotfire link 2</a>
<a>spotfire link 3</a>
</div>
<img class="icons icon-user,icon-fire,fa-solid fa-arrow-trend-up"
*/
//script parameters
let spaceBetweenLayers = 40;
let itemsPerLayer = 5; //◄ set to 1 for horizontal menu
style = `
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/simple-line-icons/2.4.1/css/simple-line-icons.css"></link>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.3.0/css/all.min.css"/>
<style>
.iconMenu {
position: fixed;
top:37px;left:53px;
z-index:1;
width: 50px;
height: 50px;
}
.iconMenu a {
position: fixed;
width: 20px;
height: 20px;
background-color: #ccc;
border-radius: 50%;
justify-content: center;
transition-timing-function: cubic-bezier(0,0,0,1);
transition-duration: .5s;
display: flex;
justify-content: center;
align-items: center;
text-decoration:none !important;
/*look and feel*/
background: #1f83f2;
box-shadow: 5px 5px 7px rgba(0, 0, 0, 0.25), inset 2px 2px 5px rgba(255, 255, 255, 0.5), inset -3px
3px 5px rgba(0, 0, 0, 0.5);
color:white !important;
height:23px;
width:23px;
}
.iconMenu a:last-child{opacity:1}
.iconMenu a:hover{
background: #8f18f8;
}
</style>
`
document.querySelector(".iconMenu").insertAdjacentHTML("afterend",style);
//script
let timeOutPID=0;
let boxShadow = document.querySelector(".iconMenu a").style.boxShadow;
function hover() {
let gap = spaceBetweenLayers;
let elements = document.querySelectorAll(".iconMenu a");
elements.forEach((e, i) => {
if(i==elements.length-1) return;
let angle = (100 / itemsPerLayer) * (i%itemsPerLayer);
i%itemsPerLayer||(gap+=spaceBetweenLayers)
e.style.transform = `rotate(${angle}deg) translate(${gap}px) rotate(-${angle}deg)`;
e.style.boxShadow=boxShadow;
e.onmouseover = resetDelayClose;
e.onmouseout = delayClose;
});
resetDelayClose();
}
function close(){
let elements = document.querySelectorAll(".iconMenu a");
elements.forEach((e, i) => {
if (i==elements.length-1) return;
e.style.transform = `translate(0px)`;
e.style.boxShadow="unset";
});
}
function delayClose(){
timeOutPID = setTimeout(close,1234)
}
function resetDelayClose(){
timeOutPID && clearTimeout(timeOutPID);
}
document.querySelector(".iconMenu a:last-child").onmouseover = hover;
document.querySelector(".iconMenu a:last-child").onmouseout = delayClose;
//setup icons on links
icons = document.querySelector(".icons").classList.value.split(",")
icons[0] = icons[0].replace("icons ","");
console.log(icons)
document.querySelectorAll(".iconMenu a").forEach((e,i)=>{
e.className = icons[i];
e.title = e.innerText;
e.innerText=""
})
hover();
delayClose();
Add Autocomplete to an existing Spotfire input control (only webplayer)
Step 1
. Create a calcualted colum with the input data. For example:
"<option value=""" & [Holders] & """>" as [options]
Step 2. Edit the html of a Text Area and Create an Input Property Control and wrap it with an identified tag. Example:
<a id="myTickers"><SpotfireControl id="60e360db89924916ab4790b20e85d339" /></a>
Step 3. Create a Calculated Value that concatenates the unique values of the calculated column in step 1:
UniqueConcatenate([options])
Step 4. Wrap the Calcualted Value with an iddentified hidden tag. The id is the same as the id from step2 + "-data" sufix. For example:
<a id="myTickers-data" hidden ><SpotfireControl Calculated Value Dynamic Item goes here /></a>
Step 5: Add the following javascript
//Note: only works on webplayer
//html:
//<a id="autocompleteElement"><input /></a>
//<a id="autocompleteElement-data" hidden >uno,dos,tres,cuatro</a>
function setupAutocomplete(id) {
const autocomplete = document.querySelector("#"+id+" input");
autocomplete.setAttribute("list",id+"-datalist");
const datalist = document.createElement("datalist");
datalist.id=id+"-datalist";
document.body.appendChild(datalist);
const data = document.getElementById(id+"-data");
const setData = () => {
datalist.innerHTML = '';
data.innerText.split(',').forEach(item => {
let option = document.createElement('option');
option.value = item;
datalist.appendChild(option);
});
}
//run setData as soon as the calculated value changes
const mutationObserver = new MutationObserver(setData);
mutationObserver.observe(data, {subtree: true, childList: true} );
setData(); // populate the datalist initially
}
setupAutocomplete("autocompleteElement");
Step 6: Save the analysis to the library and open it on web player because autocomplete does not work on the client.
Here is how everything looks together:
Spotfire Confirmation Dialogs
These funcitons are not officially supported by TIBCO and might change from one version to the other. Use of them at your own risk
js
okClick = function(x){alert("mee too!")}
noClick = function(x){alert("too bad")}
xClick = function(x){alert("why cancel?")}
Spotfire.ConfirmDialog.showYesNoCancelDialog("Hello","do you like this",okClick,noClick,xClick)//last two areguments are optional
myDialog=Spotfire.ConfirmDialog.showDialog("hello","there",[])
myDialog.close()
Spotifre.ConfirmDialog methods:
showDialog(title,message,emptyArray?)
showOkDialog(title,message,okCallbackFunction)
showOkCancelDialog(title,message,okFunction,CancelFunction)
showYesNoDialog(title,message,yesFunction,NoFunction,?,?)
showYesNoCancelDialog(title,message,yesFunction,NoFunction,CancelFunction,?)
? are optional unknown arguments
To explore other function, open the console on developer tools and type Spotfire
Here are some more Spotfire API snippets
progress = Spotfire.Progress.createProgressOverlay();
progress.setText("Loading, please wait");
setTimeout(function(){
progress.node().remove();
},3000);
This ones can be useful to detect the user agent:
Spotfire.isWebPlayer
Spotfire.isProfessional
Spotfire.isAuthorMode
Spotfire.isInitialized
Explore the JavaScript Spotfire object properties and methods by searching for keywords
function traversePropertiesAndFunctions(obj, keyword, path = []) {
for (let prop in obj) {
if (obj.hasOwnProperty(prop)) {
const currentPath = [...path, prop];
if (typeof obj[prop] === 'function') {
if (prop.toLowerCase().includes(keyword.toLowerCase())) {
console.log(`Found function: ${currentPath.join('.')}`);
}
} else if (typeof obj[prop] === 'object') {
traversePropertiesAndFunctions(obj[prop], keyword, currentPath);
} else if (typeof obj[prop] === 'string') {
if (prop.toLowerCase().includes(keyword.toLowerCase())) {
console.log(`Found property: ${currentPath.join('.')}`);
}
}
}
}
}
// Example usage
traversePropertiesAndFunctions(Spotfire, 'page');
Create a JavaScript programatically from IronPython
Create a Script
# Creates a JavasSript and adds it to the applicaiton document
from Spotfire.Dxp.Application.Scripting import ScriptDefinition, ScriptParameterCollection
from Spotfire.Dxp.Application.Scripting.ScriptLanguage import JavaScript
#this is the JavaScript code we want to create
jsCode = '''
function world(){
alert("world!")
}
'''
# 1. Creates the script
# 1.1 Define parameters (none in this example)
scriptParams = ScriptParameterCollection([])
# 1.2 Define the script with title, description, code, language, params and perform in transaction (not applies to JS)
scriptDef = ScriptDefinition.Create("myJavaScript","hello world",jsCode, JavaScript, scriptParams, False)
# 2. Adds the script to the application document
Application.Document.ScriptManager.AddScriptDefinition(scriptDef)
List available scripts
Keyboard shortcuts for custom expressions
You can use the following keyboard shortcuts in many expression or script editing fields in Spotfire:
Option |
Description |
Ctrl+C |
Copy |
Ctrl+X |
Cut |
Ctrl+V |
Paste |
Ctrl+Z |
Undo |
Ctrl+A |
Select all |
Ctrl+D |
Duplicates the current line. |
Ctrl+L |
Deletes the current line. |
Ctrl+T |
Switch current line position with the previous line position. |
Alt+Shift+Arrow |
Column mode select. |
Alt+Left Mouse Click |
Column mode select. |
Tab (selection of several lines) |
Indent |
Shift+Tab (selection of several lines) |
Remove indent. |
Ctrl+(Keypad-/Keypad+) or Ctrl+Mouse Wheel |
Zoom in/zoom out. |
Ctrl+Keypad division sign |
Restore the original size from zoom. |
Ctrl+BackSpace |
Delete to start of word. |
Ctrl+Shift+BackSpace |
Delete to end of word. |
Ctrl+Shift+Delete |
Delete everything after the current cursor position on this line. |
Ctrl+U |
Convert to lower case. |
Ctrl+Shift+U |
Convert to upper case. |
Ctrl+H |
Opens a Find/Replace dialog. |
F3 (in Find/Replace dialog) |
Find next. |
Shift+F3 (in Find/Replace dialog) |
Find previous. |
Ctrl+G |
Opens a GoToLine dialog. |