Initial commit
This commit is contained in:
509
references/scripting-language-fundamentals.md
Normal file
509
references/scripting-language-fundamentals.md
Normal file
@@ -0,0 +1,509 @@
|
||||
# SAC Scripting Language Fundamentals
|
||||
|
||||
Complete reference for the Analytics Designer scripting language based on official SAP documentation.
|
||||
|
||||
---
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Language Overview](#language-overview)
|
||||
2. [Type System](#type-system)
|
||||
3. [Variables and Scope](#variables-and-scope)
|
||||
4. [Control Flow](#control-flow)
|
||||
5. [Loops](#loops)
|
||||
6. [Operators](#operators)
|
||||
7. [Built-in Objects](#built-in-objects)
|
||||
8. [Arrays](#arrays)
|
||||
9. [Method Chaining](#method-chaining)
|
||||
10. [Script Runtime Security](#script-runtime-security)
|
||||
|
||||
---
|
||||
|
||||
## Language Overview
|
||||
|
||||
The Analytics Designer scripting language is a **limited subset of JavaScript** with the following characteristics:
|
||||
|
||||
- Extended with a logical type system enforcing **type safety at design time**
|
||||
- Executes natively in the browser as true JavaScript
|
||||
- All scripts run and validate against **strict mode**
|
||||
- Some advanced JavaScript features are hidden
|
||||
- Scripts are tied to **events** or **global script objects**
|
||||
|
||||
### What Makes It Different from Standard JavaScript
|
||||
|
||||
| Feature | Standard JavaScript | Analytics Designer |
|
||||
|---------|--------------------|--------------------|
|
||||
| Typing | Weak, dynamic | Strong, static |
|
||||
| Type conversion | Automatic | Must be explicit |
|
||||
| Variable reuse | Can change type | Type is fixed |
|
||||
| External libraries | Can import | Not supported |
|
||||
| DOM access | Full access | Blocked |
|
||||
| Global variables | Accessible | Isolated |
|
||||
|
||||
---
|
||||
|
||||
## Type System
|
||||
|
||||
### Strong and Static Typing
|
||||
|
||||
Analytics Designer enforces strict types to enable powerful tooling (code completion, validation).
|
||||
|
||||
**Key Rules**:
|
||||
1. Once a variable has a type, it **cannot change**
|
||||
2. Variable names **cannot be reused** for different types
|
||||
3. Type conversions must be **explicit**
|
||||
|
||||
### No Automatic Type Casting
|
||||
|
||||
Standard JavaScript allows implicit type coercion:
|
||||
|
||||
```javascript
|
||||
// Valid JavaScript, but ERROR in Analytics Designer
|
||||
var nth = 1;
|
||||
console.log("Hello World, " + nth); // Auto-converts nth to string
|
||||
```
|
||||
|
||||
In Analytics Designer, you must explicitly cast:
|
||||
|
||||
```javascript
|
||||
// Correct: Explicit type conversion
|
||||
var nth = 1;
|
||||
console.log("Hello World, " + nth.toString());
|
||||
```
|
||||
|
||||
### Type Declaration
|
||||
|
||||
Variables are declared with `var` and their type is inferred from the initial value:
|
||||
|
||||
```javascript
|
||||
var myString = "Hello"; // Type: string
|
||||
var myNumber = 42; // Type: integer
|
||||
var myDecimal = 3.14; // Type: number
|
||||
var myBoolean = true; // Type: boolean
|
||||
var myArray = ["a", "b"]; // Type: string[]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Variables and Scope
|
||||
|
||||
### Local Variables
|
||||
|
||||
Declared within event handlers or functions, scoped to that block:
|
||||
|
||||
```javascript
|
||||
// In onSelect event
|
||||
var selectedValue = Chart_1.getSelections()[0];
|
||||
var filterValue = selectedValue["Location"];
|
||||
```
|
||||
|
||||
### Global Variables
|
||||
|
||||
Created via Outline panel → Script Variables → (+). Available across entire application:
|
||||
|
||||
```javascript
|
||||
// Access global variable from any script
|
||||
GlobalVariable_1 = "new value";
|
||||
var currentValue = GlobalVariable_1;
|
||||
```
|
||||
|
||||
### Script Variables in Calculated Measures
|
||||
|
||||
Script variables can be referenced in calculated measures for what-if simulations:
|
||||
|
||||
```javascript
|
||||
// Formula: [Gross_Margin] * [@ScriptVariable_Rate]
|
||||
// Change ScriptVariable_Rate via script to see updated results
|
||||
```
|
||||
|
||||
### URL Parameters
|
||||
|
||||
Prefix global variable name with `p_` to pass values via URL:
|
||||
|
||||
```
|
||||
[https://your-sac-tenant.com/app?p_myVariable=value](https://your-sac-tenant.com/app?p_myVariable=value)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Control Flow
|
||||
|
||||
### if/else Statements
|
||||
|
||||
Standard syntax, but remember type safety rules:
|
||||
|
||||
```javascript
|
||||
if (nth === 1) {
|
||||
console.log("if...");
|
||||
} else if (nth < 3) {
|
||||
console.log("else if...");
|
||||
} else {
|
||||
console.log("else...");
|
||||
}
|
||||
```
|
||||
|
||||
### switch Statements
|
||||
|
||||
Standard JavaScript switch is supported:
|
||||
|
||||
```javascript
|
||||
switch (i) {
|
||||
case 0:
|
||||
day = "Zero";
|
||||
break;
|
||||
case 1:
|
||||
day = "One";
|
||||
break;
|
||||
case 2:
|
||||
day = "Two";
|
||||
break;
|
||||
default:
|
||||
day = "Unknown";
|
||||
}
|
||||
```
|
||||
|
||||
### break Statement
|
||||
|
||||
Use `break` to exit loops and switch statements.
|
||||
|
||||
---
|
||||
|
||||
## Loops
|
||||
|
||||
### for Loop
|
||||
|
||||
**Important**: You must explicitly declare the loop iterator.
|
||||
|
||||
```javascript
|
||||
// ERROR: Iterator not declared
|
||||
for (i = 0; i < 3; i++) {
|
||||
console.log(i.toString());
|
||||
}
|
||||
|
||||
// CORRECT: Iterator declared with var
|
||||
for (var i = 0; i < 3; i++) {
|
||||
console.log(i.toString());
|
||||
}
|
||||
```
|
||||
|
||||
### while Loop
|
||||
|
||||
Fully supported:
|
||||
|
||||
```javascript
|
||||
var nth = 1;
|
||||
while (nth < 3) {
|
||||
console.log("Hello while, " + nth.toString());
|
||||
nth++;
|
||||
}
|
||||
```
|
||||
|
||||
### for...in Loop
|
||||
|
||||
Iterate over object properties (useful for selections):
|
||||
|
||||
```javascript
|
||||
var selection = {
|
||||
"Color": "red",
|
||||
"Location": "GER"
|
||||
};
|
||||
|
||||
for (var propKey in selection) {
|
||||
var propValue = selection[propKey];
|
||||
console.log(propKey + ": " + propValue);
|
||||
}
|
||||
```
|
||||
|
||||
**Note**: `foreach` iterators are **not supported**.
|
||||
|
||||
---
|
||||
|
||||
## Operators
|
||||
|
||||
### Equality Operators
|
||||
|
||||
Analytics Designer supports two equality operators with important differences:
|
||||
|
||||
| Operator | Name | Behavior |
|
||||
|----------|------|----------|
|
||||
| `===` | Triple equals (strict) | Compares value AND type |
|
||||
| `==` | Double equals | Only valid if both sides have same static type |
|
||||
|
||||
**Triple Equals (Recommended)**:
|
||||
|
||||
```javascript
|
||||
var aNumber = 1;
|
||||
if (aNumber === "1") {
|
||||
// FALSE: Different types (number vs string)
|
||||
}
|
||||
```
|
||||
|
||||
**Double Equals**:
|
||||
|
||||
```javascript
|
||||
var aNumber = 1;
|
||||
if (aNumber == "1") {
|
||||
// TRUE in standard JS, but ERROR in Analytics Designer
|
||||
// unless both sides have same static type
|
||||
}
|
||||
```
|
||||
|
||||
**Best Practice**: Always use triple equals (`===`) for comparisons.
|
||||
|
||||
---
|
||||
|
||||
## Built-in Objects
|
||||
|
||||
Analytics Designer supports standard JavaScript built-in objects:
|
||||
|
||||
### Math
|
||||
|
||||
```javascript
|
||||
var max = Math.max(10, 20);
|
||||
var min = Math.min(10, 20);
|
||||
var rounded = Math.round(3.7);
|
||||
var random = Math.random();
|
||||
var power = Math.pow(2, 3); // 8
|
||||
var sqrt = Math.sqrt(16); // 4
|
||||
```
|
||||
|
||||
### Date
|
||||
|
||||
```javascript
|
||||
var now = new Date();
|
||||
var year = now.getFullYear();
|
||||
var month = now.getMonth(); // 0-11
|
||||
var day = now.getDate();
|
||||
var formatted = now.toISOString();
|
||||
```
|
||||
|
||||
### Number
|
||||
|
||||
```javascript
|
||||
var num = 42;
|
||||
var str = num.toString();
|
||||
var fixed = (3.14159).toFixed(2); // "3.14"
|
||||
```
|
||||
|
||||
### String
|
||||
|
||||
```javascript
|
||||
var str = "Hello World";
|
||||
var upper = str.toUpperCase();
|
||||
var lower = str.toLowerCase();
|
||||
var sub = str.substring(0, 5); // "Hello"
|
||||
var index = str.indexOf("World"); // 6
|
||||
var replaced = str.replace("World", "SAC");
|
||||
var split = str.split(" "); // ["Hello", "World"]
|
||||
var len = str.length; // 11
|
||||
```
|
||||
|
||||
### Array
|
||||
|
||||
```javascript
|
||||
var arr = [1, 2, 3];
|
||||
arr.push(4); // Add to end
|
||||
var last = arr.pop(); // Remove from end
|
||||
var len = arr.length;
|
||||
var joined = arr.join(", ");
|
||||
var sorted = arr.sort();
|
||||
```
|
||||
|
||||
**Note**: External JavaScript libraries **cannot be imported**.
|
||||
|
||||
---
|
||||
|
||||
## Arrays
|
||||
|
||||
### One-Dimensional Arrays
|
||||
|
||||
```javascript
|
||||
// Create typed array
|
||||
var arr1D = ArrayUtils.create(Type.number);
|
||||
|
||||
// Or use literal syntax
|
||||
var myArray = [1, 2, 3];
|
||||
```
|
||||
|
||||
### Two-Dimensional Arrays
|
||||
|
||||
Analytics Designer doesn't have a direct 2D array method, but you can create one using nested 1D arrays:
|
||||
|
||||
```javascript
|
||||
var numCols = 4;
|
||||
var numRows = 3;
|
||||
|
||||
// Create first row
|
||||
var arr1D = ArrayUtils.create(Type.number);
|
||||
|
||||
// Create 2D array containing the first row
|
||||
var arr2D = [arr1D];
|
||||
|
||||
// Add remaining rows
|
||||
for (var i = 1; i < numRows; i++) {
|
||||
arr2D.push(ArrayUtils.create(Type.number));
|
||||
}
|
||||
```
|
||||
|
||||
**Note**: You cannot use `var arr2D = []` because Analytics Designer cannot infer the content type from an empty array.
|
||||
|
||||
### Setting Values in 2D Array
|
||||
|
||||
```javascript
|
||||
arr2D[row][col] = value;
|
||||
|
||||
// Example: Fill with sequential numbers
|
||||
var count = 0;
|
||||
for (var row = 0; row < numRows; row++) {
|
||||
for (var col = 0; col < numCols; col++) {
|
||||
arr2D[row][col] = count;
|
||||
count = count + 1;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Getting Values from 2D Array
|
||||
|
||||
```javascript
|
||||
for (var row = 0; row < numRows; row++) {
|
||||
for (var col = 0; col < numCols; col++) {
|
||||
console.log(arr2D[row][col].toString());
|
||||
}
|
||||
console.log(""); // Row separator
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Method Chaining
|
||||
|
||||
Chain method calls for compact code:
|
||||
|
||||
```javascript
|
||||
// Without chaining (verbose)
|
||||
var theDataSource = Chart_1.getDataSource();
|
||||
var theVariables = theDataSource.getVariables();
|
||||
console.log(theVariables);
|
||||
|
||||
// With chaining (compact)
|
||||
console.log(Chart_1.getDataSource().getVariables());
|
||||
```
|
||||
|
||||
**Use Cases**:
|
||||
- Debug logging
|
||||
- One-time data retrieval
|
||||
- Reducing visual clutter
|
||||
|
||||
**When NOT to Chain**:
|
||||
- When you need to reuse intermediate results
|
||||
- When debugging complex operations
|
||||
|
||||
---
|
||||
|
||||
## Script Runtime Security
|
||||
|
||||
Analytics Designer validates scripts before execution to prevent security risks.
|
||||
|
||||
### What Is Blocked
|
||||
|
||||
The execution is isolated to prevent:
|
||||
|
||||
- **DOM access**: Cannot manipulate HTML elements
|
||||
- **Global variable access**: Cannot access browser globals
|
||||
- **Prototype modification**: Cannot alter built-in prototypes
|
||||
- **Network requests**: Cannot send arbitrary HTTP requests
|
||||
- **Script imports**: Cannot load external JavaScript
|
||||
- **ActiveX/plugins**: Cannot use browser plugins
|
||||
- **Web Workers**: Cannot spawn additional workers
|
||||
- **Cookies**: Cannot access browser cookies
|
||||
- **Cross-domain access**: Enforced same-origin policies
|
||||
|
||||
### Validation
|
||||
|
||||
- Same validation logic as script editor
|
||||
- Not all validations performed at runtime (e.g., analytic data validation)
|
||||
- Only allowed JavaScript subset can execute
|
||||
- Critical features use secured alternative APIs
|
||||
|
||||
---
|
||||
|
||||
## The `this` Keyword
|
||||
|
||||
Use `this` to reference the current widget or script object:
|
||||
|
||||
```javascript
|
||||
// In Chart_1 onSelect event
|
||||
var theDataSource = this.getDataSource();
|
||||
console.log(theDataSource.getVariables());
|
||||
|
||||
// Equivalent to:
|
||||
var theDataSource = Chart_1.getDataSource();
|
||||
console.log(theDataSource.getVariables());
|
||||
```
|
||||
|
||||
**Usage Contexts**:
|
||||
- Within widget scripts: `this` = the widget instance
|
||||
- Within script object functions: `this` = the script object
|
||||
- Explicitly reference parent: Use widget name (`Chart_1`)
|
||||
- Either approach is valid (stylistic choice)
|
||||
|
||||
---
|
||||
|
||||
## Code Completion and Value Help
|
||||
|
||||
The script editor provides intelligent assistance:
|
||||
|
||||
### Code Completion
|
||||
|
||||
- **Standard completion**: Complete local/global identifiers
|
||||
- **Semantic completion**: Suggest member functions
|
||||
- **Fuzzy matching**: Find widgets even with typos (e.g., "cose" → "console")
|
||||
|
||||
### Value Help
|
||||
|
||||
Context-aware value proposals:
|
||||
- Measures of a data source for function parameters
|
||||
- Dimension members for filter methods
|
||||
- Widget names in the application
|
||||
|
||||
### Keyboard Shortcut
|
||||
|
||||
Press `CTRL+Spacebar` to:
|
||||
- Auto-complete if only one valid option
|
||||
- Display value help list if multiple options
|
||||
|
||||
---
|
||||
|
||||
## Accessing Objects
|
||||
|
||||
Every object in the Outline (widgets, script variables, script objects) is a global object accessible by name:
|
||||
|
||||
```javascript
|
||||
// Access widget
|
||||
var isVisible = Chart_1.isVisible();
|
||||
|
||||
// Access script variable
|
||||
var currentValue = ScriptVariable_1;
|
||||
|
||||
// Access script object function
|
||||
ScriptObject_1.myFunction();
|
||||
```
|
||||
|
||||
### Finding Widgets with Fuzzy Matching
|
||||
|
||||
Type partial names and use CTRL+Spacebar:
|
||||
- Completes automatically if unique match
|
||||
- Shows list if multiple matches
|
||||
- Works even with typos
|
||||
|
||||
---
|
||||
|
||||
## External Resources
|
||||
|
||||
- **API Reference**: [https://help.sap.com/doc/958d4c11261f42e992e8d01a4c0dde25/release/en-US/index.html](https://help.sap.com/doc/958d4c11261f42e992e8d01a4c0dde25/release/en-US/index.html)
|
||||
- **Scripting Documentation**: [https://help.sap.com/docs/SAP_ANALYTICS_CLOUD/00f68c2e08b941f081002fd3691d86a7/6a4db9a9c8634bcb86cecbf1f1dbbf8e.html](https://help.sap.com/docs/SAP_ANALYTICS_CLOUD/00f68c2e08b941f081002fd3691d86a7/6a4db9a9c8634bcb86cecbf1f1dbbf8e.html)
|
||||
|
||||
---
|
||||
|
||||
**Source**: SAP Analytics Designer Development Guide - Chapter 4: Scripting in Analytics Designer
|
||||
**Last Updated**: 2025-11-23
|
||||
Reference in New Issue
Block a user