Skip to main content

Frontend JS-SDK Capability Guide

Overview

The Guandata BI JS-SDK is one of the open capabilities provided to customers and partners. It helps other systems embed Guandata BI pages through JavaScript or communicate with the Guandata BI system.

This document introduces how web developers can use the Guandata JS-SDK and the related considerations.

Some JS-SDK capabilities overlap with the earlier Web SDK. For future development, the newer JS-SDK is recommended.

Sample code repository: https://github.com/GuandataOSS/sdk-demo

Note

This capability is available since version 6.3.

JS-SDK Usage Steps

1. Include the following JS file on the page where you need to call JS interfaces: [JS-SDK](https://guandata-libs.oss-cn-hangzhou.aliyuncs.com/js-sdk.js) 1. Use the JS-SDK capabilities according to your needs. The currently available capabilities are:
- Embed a Dashboard in `frame` mode
- Embed a Card in `iframe` mode
- Link filters for embedded Dashboards (mobile linkage is not supported before version 6.5.0)
- Link filters for embedded Cards (mobile linkage is not supported before version 6.5.0)
- Chart rendering

These open capabilities continue to expand. If you have related requirements, contact Guandata technical support or your customer success team.

Embedded Parameter Description

Common Parameters

PropertyDescriptionRequiredDefault
hostGuandata BI access address, for example https://demo.guandata.comYes-
domainDomain ID. The default is usually guanbiYes-
ssoTokenPassword-free login tokenYesRefer to SSO Integration
themeTheme: LIGHT or DARKLIGHT (light theme)
psEmbedding mode identifier, for example iframeRefer to this page
widthWidth100%
heightHeight300px
filtersFilter conditionsFilter array after JSON.stringify (currently supported only for PC-side Cards)
Custom access attributesAttributes prefixed with data-, for example data-name -> name or data-gy-app-id -> gyAppId-

Custom access attributes are converted into URL query string parameters and passed to the Guandata BI system. For security reasons, the SSO token should be generated on the backend.

Scenario-Specific Parameters

Card Embedding Scenario

PropertyDescriptionRequiredDefault
cdIdCard IDYes-

Dashboard Embedding Scenario

PropertyDescriptionRequiredDefault
dashboardIdDashboard IDYes-

Filter Parameter Description

Common Parameters

PropertyDescriptionRequiredDefault
nameFilter nameYes-
filterValue[ 'filter value' ]Yes-
filterTypeFilter typeYes

Filter Types and Values

filterType specifies the filter type. The following values are supported. filterValue specifies the filter value and must be a string array. Its meaning varies depending on the selected filterType.

  • IN: Selection filter. filterValue may contain multiple values.
  • EQ: Equal to. filterValue may contain only one value.
  • NE: Not equal to. filterValue may contain only one value.
  • CONTAINS: Contains. filterValue may contain only one value.
  • NOT_CONTAINS: Does not contain. filterValue may contain only one value.
  • STARTSWITH: Starts with. filterValue may contain only one value.
  • ENDSWITH: Ends with. filterValue may contain only one value.
  • NOT_STARTSWITH: Does not start with. filterValue may contain only one value.
  • NOT_ENDSWITH: Does not end with. filterValue may contain only one value.
  • BT: Between. filterValue must contain two values representing the range.
  • GT: Greater than. filterValue may contain only one value.
  • GE: Greater than or equal to. filterValue may contain only one value.
  • LT: Less than. filterValue may contain only one value.
  • LE: Less than or equal to. filterValue may contain only one value.
  • IS_NULL: Is null. filterValue contains no value.
  • NOT_NULL: Is not null. filterValue contains no value.
Note

The filterType and filterValue you pass in must match the internal filter being targeted. For example, if the internal filter is a selection filter, the incoming filterType must be IN.

Scenario-Specific Parameters

Card Filter Linkage Scenario

PropertyDescriptionRequiredDefault
fdIdField IDYes-

Dashboard Filter Linkage Scenario

PropertyDescriptionRequiredDefault
cdIdFilter Card IDYes-

Chart Rendering, Viz SDK

Basic Usage

<script src="path/to/js-sdk.x.y.z.js"></script>

// Create the SDK instance
const sdk = new GDVizSDK(document.getElementById('chart'), {
BIAddress: {
baseUrl: 'http://localhost:3001/',
publicPath: '' // Optional path prefix
},
});

// Set the event callback
sdk.setEventCallback((event) => {
console.log('Chart event:', event.detail);
});

// Render the chart using the message format
sdk.setMessage(messageData);

// Render the chart using the visualization config from report, namely GuanViz.json
sdk.setGuanViz(guanVizJson, id);

// Or use the option format
sdk.setOption(optionData);

Configuration Options

const config = {
BIAddress: {
baseUrl: 'http://your-bi-address.com', // BI address (required)
publicPath: '', // Optional path prefix; check __GD_PUBLIC_PATH__ in the BI console
staticHTML: false, // Uses static BI frontend assets instead of relying on the BI site (not recommended)
},
theme: 'LIGHT', // Theme: 'LIGHT' (default) or 'DARK'
renderType: 'card' // Render type: 'card' (default) or 'largeScreenCard' (large screen mode)
// Note: large screen mode should be used together with theme: DARK
};

API Methods

Instance Methods

  • setMessage(message, chartType?) - Sets chart data in Chat-BI message format
  • setGuanViz(vizConfig, id) - Sets chart data in GuanViz format for insight analysis
  • setOption(option) - Sets chart configuration in a generic option format
  • setEventCallback(callback) - Registers an event callback
  • setTheme(theme) - Sets the theme (LIGHT or DARK)
  • destroy() - Destroys the SDK instance

Static Methods

  • GDVizSDK.isMessageHasViz(message) - Checks whether a message contains visualization data
  • GDVizSDK.getAvailableChartTypes(optionOrMessage) - Returns the available chart types
  • GDVizSDK.convertNumberFormat(value, format) - Number formatting utility
  • GDVizSDK.version - Returns the SDK version

Event Handling

The SDK supports listening to chart events. The following event types are currently supported:

  • TableHeightChange - Table height change event
  • Error - Chart rendering error event
// Set the event callback
sdk.setEventCallback((event) => {
const { name, ...data } = event.detail;

switch (name) {
case 'TableHeightChange':
// Triggered when the table height changes
console.log('Table height changed:', data.tableHeight);
// You can resize the container based on the new height
break;
default:
console.log('Unknown event:', name, data);
}
});

Usage Examples

Embed a Dashboard

<!DOCTYPE html>
<html>
<head>
   <meta charset="UTF-8">
   <title>Title</title>
</head>
<body>
   <gd-dashboard
       host="https://demo.guandata.com"
       ssoToken="**********"
       domain="demo"
       dashboardId="k5ec1f12793f649c3960ec2a"
       ps="iframe2"
   ></gd-dashboard>
</body>
<script src="js-sdk.js"></script>
</html>

Embed a Card

<!DOCTYPE html>
<html>
<head>
   <meta charset="UTF-8">
   <title>Title</title>
</head>
<body>
   <gd-card
       host="https://demo.guandata.com"
       ssoToken="**********"
       domain="demo"
       cdId="lf0c8ab616a394559b180529"
       ps="embed"
   ></gd-card>
</body>
<script src="js-sdk.js"></script>
</html>
<!DOCTYPE html>
<html>
<head>
   <meta charset="UTF-8">
   <title>Title</title>
</head>
<body>
   <gd-dashboard
       id="custom-dashboard"
       host="https://demo.guandata.com"
       ssoToken="**********"
       domain="demo"
       dashboardId="k5ec1f12793f649c3960ec2a"
       ps="iframe2"
   ></gd-dashboard>
</body>  
<script>
   const $dashboard = document.getElementById('custom-dashboard');
   function setDashboardFilter () {
     $dashboard.setAttribute('filters', JSON.stringify([{
           cdId: 'q9db09a2e5b8b44a4a46b271',
           name: 'Test',
           filterType: 'GT',
           filterValue: ['2020-03-01']
     }]));
   }
</script>
<script src="js-sdk.js"></script>
</html>
<!DOCTYPE html>
<html>
<head>
   <meta charset="UTF-8">
   <title>Title</title>
</head>
<body>
   <gd-card
       id="custom-card"
       host="https://demo.guandata.com"
       ssoToken="**********"
       domain="demo"
       cdId="lf0c8ab616a394559b180529"
       ps="embed"
   ></gd-card>
</body>
<script>
   const $card = document.getElementById('custom-card');
   function setCardFilter () {
     $card.setAttribute('filters', JSON.stringify([{
         fdId: 'l72472c483a2742de81e9767',
         name: 'Date',
         filterType: 'EQ',
         filterValue: ['2020-01-01']
     }]));
   }
</script>
<script src="js-sdk.js"></script>
</html>

Chart Rendering

<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Viz SDK Example</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
margin: 0;
padding: 20px;
background-color: #f5f5f5;
}

.container {
max-width: 1000px;
margin: 0 auto;
background: white;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
padding: 20px;
}

h1 {
color: #333;
text-align: center;
margin-bottom: 30px;
}

.example-section {
margin-bottom: 40px;
padding: 20px;
border: 1px solid #e8e8e8;
border-radius: 6px;
}

.example-title {
font-size: 18px;
font-weight: 600;
color: #333;
margin-bottom: 15px;
}

.chart-container {
width: 100%;
height: 500px;
border-radius: 4px;
background: #fafafa;
margin-bottom: 15px;
}

.controls {
display: flex;
gap: 10px;
margin-bottom: 15px;
}

button {
padding: 8px 16px;
background: #1890ff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
}

button:hover {
background: #40a9ff;
}

button:disabled {
background: #ccc;
cursor: not-allowed;
}

.log-container {
background: #f6f8fa;
border: 1px solid #e1e4e8;
border-radius: 4px;
padding: 10px;
max-height: 200px;
overflow-y: auto;
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
font-size: 12px;
color: #333;
}

.log-entry {
margin-bottom: 5px;
padding: 2px 0;
}

.log-entry.error {
color: #d73a49;
}

.log-entry.success {
color: #28a745;
}

.editor-container {
display: flex;
gap: 20px;
margin-bottom: 15px;
}

.editor-panel {
flex: 1;
}

.editor-label {
font-weight: 600;
margin-bottom: 8px;
color: #333;
}

.editor-textarea {
width: 100%;
height: 300px;
border: 1px solid #ddd;
border-radius: 4px;
padding: 10px;
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
font-size: 13px;
line-height: 1.4;
resize: vertical;
background: #fafafa;
}

.editor-textarea:focus {
outline: none;
border-color: #1890ff;
box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
}

/* JSON editor styles */
.json-editor-container {
position: relative;
width: 100%;
height: 400px;
border: 1px solid #ddd;
border-radius: 4px;
background: #fafafa;
}

.json-editor-container.focused {
border-color: #1890ff;
box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
}

.json-editor-container.valid {
border-color: #52c41a;
background: #f6ffed;
}

.json-editor-container.invalid {
border-color: #ff4d4f;
background: #fff2f0;
}

.json-editor {
box-sizing: border-box;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
padding: 10px;
margin: 0;
border: none;
outline: none;
resize: none;
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
font-size: 13px;
line-height: 1.4;
background: transparent;
color: transparent;
caret-color: #333;
z-index: 2;
}

.json-highlight {
box-sizing: border-box;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
padding: 10px;
margin: 0;
border: none;
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
font-size: 13px;
line-height: 1.4;
background: transparent;
white-space: pre-wrap;
word-wrap: break-word;
overflow: auto;
z-index: 1;
pointer-events: none;
}

/* Custom highlight.js styles */
.json-highlight .hljs {
background: transparent;
padding: 0;
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
font-size: 13px;
line-height: 1.4;
}

.editor-actions {
display: flex;
gap: 10px;
margin-top: 10px;
}

.format-btn {
background: #52c41a;
}

.format-btn:hover {
background: #73d13d;
}

.reset-btn {
background: #faad14;
}

.reset-btn:hover {
background: #ffc53d;
}

#templateSelect,
#exampleSelector,
#chartTypeSelector1,
#chartTypeSelector2 {
padding: 8px 12px;
border: 1px solid #ddd;
border-radius: 4px;
background: white;
font-size: 14px;
cursor: pointer;
margin-right: 10px;
}

#templateSelect:focus,
#exampleSelector:focus,
#chartTypeSelector1:focus,
#chartTypeSelector2:focus {
outline: none;
border-color: #1890ff;
}

/* Configuration input styles */
#baseUrlInput,
#publicPathInput,
#chart1Width,
#chart1Height,
#chart2Width,
#chart2Height {
transition: border-color 0.2s ease, box-shadow 0.2s ease;
}

#baseUrlInput:focus,
#publicPathInput:focus,
#chart1Width:focus,
#chart1Height:focus,
#chart2Width:focus,
#chart2Height:focus {
outline: none;
border-color: #1890ff;
box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
}

#updateConfig,
#updateChart1Size,
#updateChart2Size {
background: #52c41a;
transition: background-color 0.2s ease;
}

#updateConfig:hover,
#updateChart1Size:hover,
#updateChart2Size:hover {
background: #73d13d;
}

#updateChart1Size,
#updateChart2Size {
background: #1890ff;
}

#updateChart1Size:hover,
#updateChart2Size:hover {
background: #40a9ff;
}

#toggleTheme1,
#toggleTheme2 {
background: #722ed1;
}

#toggleTheme1:hover,
#toggleTheme2:hover {
background: #9254de;
}

#changeChartType1,
#changeChartType2 {
background: #fa8c16;
}

#changeChartType1:hover,
#changeChartType2:hover {
background: #ffa940;
}

/* Tooltip styles */
.tooltip-container {
position: relative;
display: inline-block;
cursor: help;
color: #666;
font-size: 14px;
margin-left: 5px;
}

.tooltip-content {
visibility: hidden;
width: 300px;
background-color: #333;
color: #fff;
text-align: left;
border-radius: 4px;
padding: 8px;
position: absolute;
z-index: 1000;
bottom: 125%;
left: 50%;
margin-left: -150px;
opacity: 0;
transition: opacity 0.3s;
font-size: 12px;
line-height: 1.4;
}

.tooltip-content::after {
content: "";
position: absolute;
top: 100%;
left: 50%;
margin-left: -5px;
border-width: 5px;
border-style: solid;
border-color: #333 transparent transparent transparent;
}

.tooltip-container:hover .tooltip-content {
visibility: visible;
opacity: 1;
}
</style>
</head>

<body>
<div class="container">
<h1>Viz SDK Example</h1>

<div class="example-section">
<div class="example-title">SDK Configuration</div>
<div class="controls">
<div style="display: flex; gap: 20px; align-items: center; margin-bottom: 10px;">
<div style="display: flex; align-items: center; gap: 8px;">
<label for="baseUrlInput" style="font-weight: 500; color: #333;">Base URL:</label>
<input type="text" id="baseUrlInput" value="http://localhost:3001/"
style="padding: 6px 12px; border: 1px solid #ddd; border-radius: 4px; width: 200px; font-size: 14px;">
</div>
<div style="display: flex; align-items: center; gap: 8px;">
<label for="publicPathInput" style="font-weight: 500; color: #333;">Public Path:</label>
<input type="text" id="publicPathInput" value=""
style="padding: 6px 12px; border: 1px solid #ddd; border-radius: 4px; width: 200px; font-size: 14px;">
<span class="tooltip-container">

<div class="tooltip-content">
Check `__GD_PUBLIC_PATH__` in the BI console to get this value.
</div>
</span>
</div>
<button id="updateConfig" style="background: #52c41a;">Update Configuration</button>
</div>
</div>
</div>

<div class="example-section">
<div class="example-title">Chart Example Selector</div>
<div class="controls">
<select id="exampleSelector">
<option value="">Select an example...</option>
</select>
<button id="loadSelectedExample">Load Chart</button>
<button id="destroySDK">Clear Chart</button>
<button id="toggleTheme1" style="background: #722ed1;">Switch Theme</button>
</div>
<div class="controls" style="margin-top: 10px;">
<div style="display: flex; gap: 20px; align-items: center;">
<div style="display: flex; align-items: center; gap: 8px;">
<label for="chartTypeSelector1" style="font-weight: 500; color: #333;">Chart Type:</label>
<select id="chartTypeSelector1"
style="padding: 6px 12px; border: 1px solid #ddd; border-radius: 4px; width: 200px; font-size: 14px;">
<option value="">Load a chart first...</option>
</select>
</div>
<button id="changeChartType1" style="background: #fa8c16;">Switch Type</button>
</div>
</div>
<div class="controls" style="margin-top: 10px;">
<div style="display: flex; gap: 20px; align-items: center;">
<div style="display: flex; align-items: center; gap: 8px;">
<label for="chart1Width" style="font-weight: 500; color: #333;">Width:</label>
<input type="number" id="chart1Width" value="100" min="200" max="2000"
style="padding: 6px 12px; border: 1px solid #ddd; border-radius: 4px; width: 80px; font-size: 14px;">
<span style="color: #666;">px</span>
</div>
<div style="display: flex; align-items: center; gap: 8px;">
<label for="chart1Height" style="font-weight: 500; color: #333;">Height:</label>
<input type="number" id="chart1Height" value="500" min="200" max="1000"
style="padding: 6px 12px; border: 1px solid #ddd; border-radius: 4px; width: 80px; font-size: 14px;">
<span style="color: #666;">px</span>
</div>
<button id="updateChart1Size" style="background: #1890ff;">Update Size</button>
</div>
</div>
<div id="chart1" class="chart-container"></div>
<div class="log-container">
<div id="log1"></div>
</div>
</div>
</div>
<script src="https://guandata-libs.oss-cn-hangzhou.aliyuncs.com/js-sdk.1.1.3.js"></script>
<!-- Load example data and configuration -->
<script type="module">
const EXAMPLES = {
messages: {
'basic_column': {
name: 'Basic Column Chart',
description: 'Basic column chart example using the message format',
data: {
"message_id": "e90bac12-7c0d-4efe-b93b-5e4d1ac885d6",
"card": {
"card_version": 2,
"card_meta": {
"chartType": "BASIC_COLUMN",
"chartName": "Column Chart",
"chartMeta": {
"chartMain": {
"zoneData": {
"row": [
{
"fdType": "STRING",
"metaType": "DIM",
"name": "Member Tier"
}
],
"column": [
{
"metaType": "MPH"
}
],
"tooltip": [],
"metric": [
{
"fdType": "DOUBLE",
"metaType": "METRIC",
"name": "Average Transaction Amount",
"fieldFormat": {
"numberFormat": {
"formatType": "NUMBER",
"decimalPlaces": 2,
"useThousandsSeparator": true,
"suffix": "CNY",
"divideDataBy": 1
}
}
}
],
"metric_additional": []
},
"props": {
"chartLegend": {
"showLegend": true
},
"dataLabels": {
"metric": {
"showNumber": true
},
"metricAdditional": {
"showNumber": true
}
},
"grandTotal": {
"onRow": {
"position": "TOP"
}
}
}
}
}
}
},
"query_result": {
"columns": [
{
"name": "Member Tier",
"fdType": "STRING",
"seqNo": 0
},
{
"name": "Average Transaction Amount",
"fdType": "DOUBLE",
"seqNo": 1
}
],
"preview": [
[
"L1",
"120.57382293762551"
],
[
"L2",
"123.99325147347734"
],
[
"L3",
"122.96379170879656"
],
[
"L4",
"117.10799580272817"
],
[
"L5",
"121.52193768257028"
]
],
"truncated": false,
"sql": ""
},
}
},
},
options: {}
};

// Get the complete list of examples
function getAllExamples() {
const examples = [];

// Add examples from messages
Object.keys(EXAMPLES.messages).forEach(key => {
const example = EXAMPLES.messages[key];
examples.push({
id: key,
name: example.name,
description: example.description,
type: 'message',
data: example.data
});
});

// Add examples from options
Object.keys(EXAMPLES.options).forEach(key => {
const example = EXAMPLES.options[key];
examples.push({
id: key,
name: example.name,
description: example.description,
type: 'option',
data: example.data
});
});

return examples;
}

// Get an example by ID
function getExampleById(id) {
const allExamples = getAllExamples();
return allExamples.find(example => example.id === id);
}

// Theme enum
const VizTheme = {
LIGHT: 'LIGHT',
DARK: 'DARK'
};

// Logging utility
function addLog(containerId, message, type = 'info') {
const logContainer = document.getElementById(containerId);
const logEntry = document.createElement('div');
logEntry.className = `log-entry ${type}`;
logEntry.textContent = `[${new Date().toLocaleTimeString()}] ${message}`;
logContainer.appendChild(logEntry);
logContainer.scrollTop = logContainer.scrollHeight;
}

function extractPathPrefix(location, pathSuffix) {
if (!location || !location.pathname) {
return '';
}

const pathname = location.pathname;

if (!pathSuffix) {
const lastSlashIndex = pathname.lastIndexOf('/');
return lastSlashIndex > 0 ? pathname.substring(0, lastSlashIndex) : '';
}

const normalizedSuffix = pathSuffix.startsWith('/') ? pathSuffix : '/' + pathSuffix;

// Check whether the path ends with the full suffix
if (pathname.endsWith(normalizedSuffix)) {
return pathname.substring(0, pathname.length - normalizedSuffix.length);
}

// Check whether the path contains the specified directory segment
const suffixIndex = pathname.indexOf(normalizedSuffix);
if (suffixIndex !== -1) {
return pathname.substring(0, suffixIndex);
}

// Fallback: return the directory path without the file name
const lastSlashIndex = pathname.lastIndexOf('/');
return lastSlashIndex > 0 ? pathname.substring(0, lastSlashIndex) : '';
}

// Default SDK configuration
let config = {
BIAddress: {
baseUrl: 'https://demo.guandata.com',
publicPath: '',
},
theme: VizTheme.LIGHT
};
addLog('log1', JSON.stringify(location, null, 2), 'info')
addLog('log1', `Using configuration: ${JSON.stringify(config)}`, 'info');

// Theme state tracking
let currentTheme = VizTheme.LIGHT;

// Update configuration
function updateSDKConfig() {
const baseUrl = document.getElementById('baseUrlInput').value.trim();
const publicPath = document.getElementById('publicPathInput').value.trim();

if (!baseUrl) {
addLog('log1', 'Base URL cannot be empty', 'error');
return;
}

// Update the configuration
config = {
BIAddress: {
baseUrl: baseUrl,
...(publicPath && { publicPath: publicPath })
}
};

addLog('log1', `Configuration updated: ${JSON.stringify(config)}`, 'success');

// Recreate the SDK instance if it already exists
if (sdk1) {
sdk1.destroy();
sdk1 = null;
addLog('log1', 'The previous SDK instance has been destroyed. Reload the chart to continue.', 'info');
}
}

// Bind the configuration update button event
document.getElementById('updateConfig').addEventListener('click', updateSDKConfig);

// Update chart size
function updateChartSize(chartId, widthInputId, heightInputId, sdk, logId) {
const width = document.getElementById(widthInputId).value;
const height = document.getElementById(heightInputId).value;
const chartContainer = document.getElementById(chartId);

if (width && height) {
// Update container styles
chartContainer.style.width = width + 'px';
chartContainer.style.height = height + 'px';

// Trigger a resize event if the SDK instance exists
if (sdk) {
// Wait for the DOM to update before triggering resize
setTimeout(() => {
// Trigger a window resize event so the chart adapts to the new size
window.dispatchEvent(new Event('resize'));
addLog(logId, `Chart size updated: ${width}px × ${height}px`, 'success');
}, 100);
} else {
addLog(logId, `Container size updated: ${width}px × ${height}px (takes effect after a chart is loaded)`, 'info');
}
} else {
addLog(logId, 'Enter valid width and height values', 'error');
}
}

// Bind the chart 1 size update button event
document.getElementById('updateChart1Size').addEventListener('click', () => {
updateChartSize('chart1', 'chart1Width', 'chart1Height', sdk1, 'log1');
});

// Example 1: chart example selector
let sdk1 = null;
let currentData1 = null; // Store the current chart data
let currentDataType1 = null; // Store the current data type
const exampleSelector = document.getElementById('exampleSelector');
const chartTypeSelector1 = document.getElementById('chartTypeSelector1');

// Initialize the example selector
function initializeExampleSelector() {
const examples = getAllExamples();

// Clear existing options
exampleSelector.innerHTML = '<option value="">Select an example...</option>';

// Add example options
examples.forEach(example => {
const option = document.createElement('option');
option.value = example.id;
option.textContent = `${example.name} (${example.type})`;
option.title = example.description;
exampleSelector.appendChild(option);
});

addLog('log1', `${examples.length} examples loaded`, 'info');
}

// Listen for example selection changes
exampleSelector.addEventListener('change', (e) => {
const selectedId = e.target.value;
if (selectedId) {
const example = getExampleById(selectedId);
if (example) {
addLog('log1', `Selected: ${example.name} - ${example.description}`, 'info');
}
}
});

// Load the selected example
document.getElementById('loadSelectedExample').addEventListener('click', () => {
try {
const selectedId = exampleSelector.value;
if (!selectedId) {
addLog('log1', 'Select an example first', 'error');
return;
}

const example = getExampleById(selectedId);
if (!example) {
addLog('log1', `Example not found: ${selectedId}`, 'error');
return;
}

const chartContainer = document.getElementById('chart1');

if (!sdk1) {
sdk1 = new GDVizSDK(chartContainer, config);
window.sdk1 = sdk1;
addLog('log1', `SDK1 created with theme: ${currentTheme}`, 'info');
}

// Set the event callback
sdk1.setEventCallback((event) => {
addLog('log1', `Chart event: ${JSON.stringify(event.detail)}`, 'info');
});

// Load data based on the example type
if (example.type === 'message') {
sdk1.setMessage(example.data);
currentData1 = example.data;
currentDataType1 = 'message';
} else if (example.type === 'option') {
sdk1.setOption(example.data);
currentData1 = example.data;
currentDataType1 = 'option';
}

// Update the chart type selector
const currentType = currentDataType1 === 'message' ?
example.data.card.card_meta.chartType :
example.data.chartType;
const availableTypes = updateChartTypeSelector(chartTypeSelector1, currentData1, currentType);

addLog('log1', `${example.name} loaded successfully`, 'success');
addLog('log1', `Available chart types: ${availableTypes.map(t => chartTypeDisplayNames[t] || t).join(', ')}`, 'info');

} catch (error) {
addLog('log1', `Load failed: ${error.message}`, 'error');
console.error('Failed to load example:', error);
}
});

document.getElementById('destroySDK').addEventListener('click', () => {
if (sdk1) {
sdk1.destroy();
sdk1 = null;
currentData1 = null;
currentDataType1 = null;
// Reset the chart type selector
chartTypeSelector1.innerHTML = '<option value="">Load a chart first...</option>';
addLog('log1', 'SDK destroyed', 'info');
}
});

// Theme switching
function toggleTheme() {
currentTheme = currentTheme === VizTheme.LIGHT ? VizTheme.DARK : VizTheme.LIGHT;

// Update the configuration
config.theme = currentTheme;

// Update the existing SDK instance
if (sdk1) {
sdk1.setTheme(currentTheme);
addLog('log1', `Switched to ${currentTheme} theme`, 'success');
}

// Update button text
const themeText = currentTheme === VizTheme.LIGHT ? 'Light' : 'Dark';
document.getElementById('toggleTheme1').textContent = `Switch Theme (Current: ${themeText})`;

addLog('log1', `Global theme switched to: ${currentTheme}`, 'info');
}

// Bind the theme switch button event
document.getElementById('toggleTheme1').addEventListener('click', toggleTheme);

// Bind the chart type switch button event
document.getElementById('changeChartType1').addEventListener('click', () => {
const selectedType = chartTypeSelector1.value;
if (!selectedType) {
addLog('log1', 'Select a chart type to switch to', 'error');
return;
}

const newData = changeChartType(sdk1, currentData1, currentDataType1, selectedType, 'log1');
if (newData) {
currentData1 = newData;
// Update the selector to reflect the new current type
updateChartTypeSelector(chartTypeSelector1, currentData1, selectedType);
}
});


// Chart type display name mapping
const chartTypeDisplayNames = {
'BASIC_COLUMN': 'Column Chart',
'GROUPED_COLUMN': 'Grouped Column Chart',
'STACKED_COLUMN': 'Stacked Column Chart',
'STACKED_SPLIT_COLUMN': 'Grouped Stacked Column Chart',
'PERCENT_STACKED_COLUMN': '100% Stacked Column Chart',
'GROUPED_COLUMN_WITH_LINE': 'Grouped Column + Line Chart',
'STACKED_COLUMN_WITH_LINE': 'Stacked Column + Line Chart',
'GROUPED_COLUMN_WITH_SYMBOL': 'Grouped Column + Symbol Chart',
'STACKED_COLUMN_WITH_SYMBOL': 'Stacked Column + Symbol Chart',
'BASIC_BAR': 'Bar Chart',
'BASIC_LINE': 'Line Chart',
'MULTI_LINE': 'Multi-line Chart',
'PIE': 'Pie Chart',
'BASIC_BUBBLE': 'Bubble Chart',
'PROGRESS_BAR': 'Progress Bar',
'KPI_CARD': 'Comparison KPI Card',
'SINGLE_VALUE': 'KPI Card',
'PIVOT_TABLE': 'Table'
};

// Update the chart type selector
function updateChartTypeSelector(selector, currentData, currentType) {
// Clear existing options
selector.innerHTML = '';

if (!currentData) {
selector.innerHTML = '<option value="">Load a chart first...</option>';
return;
}

try {
// Get available chart types
const availableTypes = GDVizSDK.getAvailableChartTypes(currentData);

if (availableTypes.length === 0) {
selector.innerHTML = '<option value="">No chart types available</option>';
return;
}

// Add options
availableTypes.forEach(type => {
const option = document.createElement('option');
option.value = type;
option.textContent = chartTypeDisplayNames[type] || type;
if (type === currentType) {
option.selected = true;
}
selector.appendChild(option);
});

return availableTypes;
} catch (error) {
console.error('Failed to get available chart types:', error);
selector.innerHTML = '<option value="">Failed to get chart types</option>';
return [];
}
}

// Switch chart type
function changeChartType(sdk, currentData, currentDataType, newChartType, logId) {
if (!sdk || !currentData || !newChartType) {
addLog(logId, 'Load a chart and select a new chart type first', 'error');
return null;
}

try {
let resultData = currentData;

// Switch chart type using the new interface
if (currentDataType === 'message') {
// Use the new setMessage API and pass the chartType parameter
sdk.setMessage(currentData, newChartType);
} else if (currentDataType === 'option') {
// For option data, the data structure still needs to be updated
let newData = JSON.parse(JSON.stringify(currentData));
newData.chartType = newChartType;
sdk.setOption(newData);
resultData = newData;
}

addLog(logId, `Chart type switched to: ${chartTypeDisplayNames[newChartType] || newChartType}`, 'success');
return resultData;
} catch (error) {
addLog(logId, `Failed to switch chart type: ${error.message}`, 'error');
console.error('Failed to switch chart type:', error);
return null;
}
}


// Initialize configuration inputs
function initializeConfigInputs() {
const baseUrlInput = document.getElementById('baseUrlInput');
const publicPathInput = document.getElementById('publicPathInput');

// Initialize inputs using the current configuration
baseUrlInput.value = config.BIAddress.baseUrl;
publicPathInput.value = config.BIAddress.publicPath || '';

addLog('log1', 'Configuration inputs initialized', 'info');
}

// Initialize size inputs
function initializeSizeInputs() {
// Initialize chart 1 size inputs
document.getElementById('chart1Width').value = 960;
document.getElementById('chart1Height').value = 500;

addLog('log1', 'Size inputs initialized', 'info');
}

// Initialize chart type selectors
function initializeChartTypeSelectors() {
chartTypeSelector1.innerHTML = '<option value="">Load a chart first...</option>';

addLog('log1', 'Chart type selector initialized', 'info');
}

// Initialize after page load
window.addEventListener('load', () => {
addLog('log1', 'Viz SDK Example loaded', 'success');

// Initialize configuration inputs
initializeConfigInputs();

// Initialize size inputs
initializeSizeInputs();

// Initialize chart type selectors
initializeChartTypeSelectors();

// Initialize the theme button text
const themeText = currentTheme === VizTheme.LIGHT ? 'Light' : 'Dark';
document.getElementById('toggleTheme1').textContent = `Switch Theme (Current: ${themeText})`;

initializeExampleSelector();
});

// Global error handling
window.addEventListener('error', (event) => {
addLog('log1', `Global error: ${event.error?.message || event.message}`, 'error');
addLog('log2', `Global error: ${event.error?.message || event.message}`, 'error');
});
</script>
</body>

</html>