Skip to content

Commit

Permalink
Add circular treemap
Browse files Browse the repository at this point in the history
  • Loading branch information
objerke committed Nov 3, 2020
1 parent e96ae82 commit 0177cfb
Show file tree
Hide file tree
Showing 27 changed files with 2,502 additions and 46 deletions.
4 changes: 3 additions & 1 deletion examples/js-areachart-d3/spotfire/spotfire-api.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -741,7 +741,9 @@ declare interface GeneralStylingInfo {
* console.log("Mod API loaded.");
* });
* ```
* [[include:initialize.md]]
* The initialize method will invoke the provided callback with an instance of the mod API, to be used by the mod developer.
*
* The initialize method needs to be invoked in order for the mod to function. Even if the mod only displays static content, the API must be initialized in order for events and export to work.
* @public
* @param onLoaded - Callback that is called when the mod API is initialized and ready to be interacted with.
*/
Expand Down
2 changes: 1 addition & 1 deletion examples/js-areachart-d3/static/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
</head>
<body>
<div id="mod-container"></div>
<script id="spotfire-loader">var Spotfire=function(e){"use strict";return e.initialize=function(e){var t="sfTemp"+1e4*Math.random()+"Cb",a=window;a[t]=e;var r={subject:"GetUrl",callbackId:-1,options:{cbId:t}};a.addEventListener("message",function e(t){if(t.source===a.parent&&t.data.src){a.removeEventListener("message",e);var r=document,n=r.createElement("script");n.src=t.data.src,(r.head||r.body).appendChild(n)}}),a.parent.postMessage(r,"*")},e}({});</script>
<script id="spotfire-loader">var Spotfire=function(e){"use strict";return e.initialize=function(e){var t="sfTemp"+1e4*Math.random()+"Cb",r=window;r[t]=e;var a={subject:"GetUrl",callbackId:-1,options:{cbId:t}};r.addEventListener("message",(function e(t){if(t.source!==r.parent||!t.data.src)return;r.removeEventListener("message",e);var a=document,n=a.createElement("script");n.src=t.data.src,(a.head||a.body).appendChild(n)})),r.parent.postMessage(a,"*")},e}({});</script>
<script src="bundle.js"></script>
</body>
</html>
Original file line number Diff line number Diff line change
Expand Up @@ -741,7 +741,9 @@ declare interface GeneralStylingInfo {
* console.log("Mod API loaded.");
* });
* ```
* [[include:initialize.md]]
* The initialize method will invoke the provided callback with an instance of the mod API, to be used by the mod developer.
*
* The initialize method needs to be invoked in order for the mod to function. Even if the mod only displays static content, the API must be initialized in order for events and export to work.
* @public
* @param onLoaded - Callback that is called when the mod API is initialized and ready to be interacted with.
*/
Expand Down
2 changes: 1 addition & 1 deletion examples/js-dev-barchart-googlecharts/src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
</head>
<body>
<div id="mod-container"></div>
<script id="spotfire-loader">var Spotfire=function(e){"use strict";return e.initialize=function(e){var t="sfTemp"+1e4*Math.random()+"Cb",a=window;a[t]=e;var r={subject:"GetUrl",callbackId:-1,options:{cbId:t}};a.addEventListener("message",function e(t){if(t.source===a.parent&&t.data.src){a.removeEventListener("message",e);var r=document,n=r.createElement("script");n.src=t.data.src,(r.head||r.body).appendChild(n)}}),a.parent.postMessage(r,"*")},e}({});</script>
<script id="spotfire-loader">var Spotfire=function(e){"use strict";return e.initialize=function(e){var t="sfTemp"+1e4*Math.random()+"Cb",r=window;r[t]=e;var a={subject:"GetUrl",callbackId:-1,options:{cbId:t}};r.addEventListener("message",(function e(t){if(t.source!==r.parent||!t.data.src)return;r.removeEventListener("message",e);var a=document,n=a.createElement("script");n.src=t.data.src,(a.head||a.body).appendChild(n)})),r.parent.postMessage(a,"*")},e}({});</script>
<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
<script src="main.js"></script>
</body>
Expand Down
4 changes: 3 additions & 1 deletion examples/js-dev-barchart/spotfire/spotfire-api.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -741,7 +741,9 @@ declare interface GeneralStylingInfo {
* console.log("Mod API loaded.");
* });
* ```
* [[include:initialize.md]]
* The initialize method will invoke the provided callback with an instance of the mod API, to be used by the mod developer.
*
* The initialize method needs to be invoked in order for the mod to function. Even if the mod only displays static content, the API must be initialized in order for events and export to work.
* @public
* @param onLoaded - Callback that is called when the mod API is initialized and ready to be interacted with.
*/
Expand Down
2 changes: 1 addition & 1 deletion examples/js-dev-barchart/src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
<div id="x-scale"></div>
<div id="y-scale"></div>
</div>
<script id="spotfire-loader">var Spotfire=function(e){"use strict";return e.initialize=function(e){var t="sfTemp"+1e4*Math.random()+"Cb",a=window;a[t]=e;var r={subject:"GetUrl",callbackId:-1,options:{cbId:t}};a.addEventListener("message",function e(t){if(t.source===a.parent&&t.data.src){a.removeEventListener("message",e);var r=document,n=r.createElement("script");n.src=t.data.src,(r.head||r.body).appendChild(n)}}),a.parent.postMessage(r,"*")},e}({});</script>
<script id="spotfire-loader">var Spotfire=function(e){"use strict";return e.initialize=function(e){var t="sfTemp"+1e4*Math.random()+"Cb",r=window;r[t]=e;var a={subject:"GetUrl",callbackId:-1,options:{cbId:t}};r.addEventListener("message",(function e(t){if(t.source!==r.parent||!t.data.src)return;r.removeEventListener("message",e);var a=document,n=a.createElement("script");n.src=t.data.src,(a.head||a.body).appendChild(n)})),r.parent.postMessage(a,"*")},e}({});</script>
<script src="main.js"></script>
</body>
</html>
19 changes: 19 additions & 0 deletions examples/js-dev-circular-treemap/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Circular Treemap
This mod example is a simple circular treemap implemented with [d3](https://d3js.org/). It shows how hierarchies in mod data views can be mapped to d3 hierarchies.

It also illustrates some basic concepts, such as using a color axis, and implementing simple marking and tooltips.

All source code for the mod example can be found in the `src` folder.

## Prerequisites
These instructions assume that you have [Node.js](https://nodejs.org/en/) (which includes npm) installed.

## How to get started (with development server)
- Open a terminal at the location of this example.
- Run `npm install`. This will install necessary tools. Run this command only the first time you are building the mod and skip this step for any subsequent builds.
- Run `npm run server`. This will start a development server.
- Start editing, for example `src/circular-treemap.js`.
- In Spotfire, follow the steps of creating a new mod and connecting to the development server.

## Working without a development server
- In Spotfire, follow the steps of creating a new mod and then browse for, and point to, the _manifest_ in the `src` folder.
110 changes: 110 additions & 0 deletions examples/js-dev-circular-treemap/development-server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/*
* Copyright © 2020. TIBCO Software Inc.
* This file is subject to the license terms contained
* in the license file that is distributed with this file.
*/

//@ts-check

/**
* # Spotfire mods development server
* The purpose of the development server is to simplify the development of mods. The development server mimics the way the Spotfire runtime works in regards to cross origin requests and content security policies.
*/

const liveServer = require("live-server");
const path = require("path");
const fs = require("fs");
const { promisify } = require("util");
const readFile = promisify(fs.readFile);
const readdir = promisify(fs.readdir);

const manifestName = "mod-manifest.json";
const rootDirectory = process.argv[2] || "./src/";

// The development server tries to mimic the CSP policy used by the Spotfire runtime.
const allowedExternalResources = new Set();
let declaredExternalResourcesInManifest = [];

main();

async function main() {
await readExternalResourcesFromManifest();

liveServer.start({
port: 8090,
noCssInject: true,
cors: false,
// @ts-ignore
open: "/" + manifestName,
root: rootDirectory,
wait: 250, // Waits for all changes, before reloading. Defaults to 0 sec.
middleware: [cacheRedirect]
});
}

/**
* Read external resources from the mod manifest placed in the root directory.
*/
async function readExternalResourcesFromManifest() {
const rootDirectoryAbsolutePath = path.resolve(rootDirectory);
const files = await readdir(rootDirectoryAbsolutePath);

if (files.find((fileName) => fileName == manifestName)) {
const manifestPath = path.join(rootDirectoryAbsolutePath, manifestName);

await readExternalResources();
fs.watch(manifestPath, {}, readExternalResources);

async function readExternalResources() {
let content = await readFile(manifestPath, { encoding: "utf-8" });

try {
let json = JSON.parse(content);
declaredExternalResourcesInManifest = json.externalResources || [];
} catch (err) {}
}
} else {
console.warn("Could not find a mod-manifest.json in the root directory", rootDirectoryAbsolutePath);
}
}

/**
* Middleware to manage caching and CSP headers.
* @param {any} req - request object
* @param {any} res - response object
* @param {any} next - next callback to invoke the next middleware
*/
function cacheRedirect(req, res, next) {
const isCorsRequest = req.headers.origin != undefined;
const requestFromOutsideSandbox = req.headers.origin != "null";

// Prevent CORS requests from the sandboxed iframe. E.g module loading will not work in embedded mode.
if (isCorsRequest && requestFromOutsideSandbox) {
allowedExternalResources.add(req.headers.origin);

res.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
res.setHeader("Access-Control-Allow-Origin", "*");
}

// Turn off caching on everything to avoid stale CSP headers etc. in the browser.
// This also ensures that live server can inject its websocket snippet in .html pages it serves to the mod iframe.
res.setHeader("Cache-Control", "no-store");

if (req.method !== "GET") {
next();
return;
}

// Set same security headers in the development server as in the Spotfire runtime.
res.setHeader(
"content-security-policy",
`sandbox allow-scripts; default-src 'self' 'unsafe-eval' 'unsafe-hashes' 'unsafe-inline' blob: data: ${[
...allowedExternalResources.values()
, ...declaredExternalResourcesInManifest].join(" ")}`
);

// CSP header used by older browsers where the CSP policy is not fully supported.
res.setHeader("x-content-security-policy", "sandbox allow-scripts");

next();
}
16 changes: 16 additions & 0 deletions examples/js-dev-circular-treemap/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"name": "spotfire-mod-example",
"version": "1.0.0",
"description": "",
"scripts": {
"start": "npm install && npm run server",
"server": "node development-server.js src"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"live-server": "^1.2.1"
},
"dependencies": {}
}
19 changes: 19 additions & 0 deletions examples/js-dev-circular-treemap/prettier.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/*
* Copyright © 2020. TIBCO Software Inc.
* This file is subject to the license terms contained
* in the license file that is distributed with this file.
*/

/*global module*/
module.exports = {
// Fit code within this line limit
printWidth: 120,
// Number of spaces it should use per tab
tabWidth: 4,
// If true, will use single instead of double quotes
singleQuote: false,
// Controls the printing of trailing commas wherever possible
trailingComma: "none",
// Controls the printing of spaces inside array and objects
bracketSpacing: true
};
Loading

0 comments on commit 0177cfb

Please sign in to comment.