Web Designer Themes and Pluggable Objects: Introduction

Table of Contents

1. Environment

The Web Designer environment currently contains following web applications:

Table 1. Environment
Web App URL Main purpose

Web Designer

https://design-app-eu1.scripturaengage.com

Template design

Resource Browser

https://browse-app-eu1.scripturaengage.com

Management of Scriptura resources such as templates, themes and pluggable objects

2. Known limitations

  • Spaces and tabs inside of your Pluggable Object or theme are not removed from the output.

3. Templates

The Web Designer web application can be used to link the following to a template:

  • 1 or more themes to control the look and feel of the template.

  • 1 data model providing data fields and sample data to the template.

  • 1 or more languages for supporting multilanguage templates.

Web Designer templates support two classes of object types:

  • Built-in objects provided as part of the standard Web Designer product.

  • Customer specific pluggable objects that extend the Web Designer object collection.

4. Themes

Themes define the look and feel of a template but they can also include content. Content included in a template by applying a theme cannot be deleted and/or edited in Web Designer.

A theme can for example include a company logo and a standard header and footer as content. A template designer applying this theme to a template would not be able to change or delete these objects from the template using Web Designer.

no theme applied
Figure 1. A template with no theme applied
kodu theme applied
Figure 2. The same template with the Kodu theme applied

4.1. Manage Themes

The Manage Themes page lists all recognized themes in the current environment. Unrecognized themes are not included. For every theme its status, name, location and author are listed. The page is useful in case of theme errors. If a theme contains an error, the theme is marked with warning icon and an error message is shown inline. Note that in order to access this page, the user needs to have the webdesigner:themes:manage permission.

4.2. Creating themes

A theme is defined by an HTML document containing specific metadata tags. The metadata tags should be the first children of the <head> element.

Table 2. Theme metadata
Name Description

scriptura:wbd:type

Identifies the HTML file as a theme and should have value html-theme

scriptura:wbd:name

Name of the theme

scriptura:wbd:author

Author of the theme

Minimal theme example
<html>
  <head>
    <meta name="scriptura:wbd:type" content="html-theme">
    <meta name="scriptura:wbd:name" content="My theme">
    <meta name="scriptura:wbd:author" content="John Doe">
  </head>
  <body>
    <p>This is my first theme</p>
  </body>
</html>

When designing themes you have to take into account a number of restrictions:

  • Only use absolute URLs to point to the CSS file to use. Do not use relative URLs.

  • Only use absolute URLs to point to images. As with CSS files, do not use relative URLs.

  • Do not use the HTML style attribute as this attribute will be removed from the theme automatically. Use styles and the HTML class attribute instead.

When you are done designing your theme, you can upload it to the Design Studio through the Resource Browser. The theme will automatically be listed on the Manage Themes page and also be available in the template designer.

4.2.1. Adding designer content in your theme

In order to integrate designer content into your theme, you can mark insertion points using the {{designer-content}} handlebars instruction.

Using a theme with a single insertion point for designer content
<html>
  <head>
    <meta name="scriptura:wbd:type" content="html-theme">
    <meta name="scriptura:wbd:name" content="My theme">
    <meta name="scriptura:wbd:author" content="John Doe">
  </head>
  <body>
    {{designer-content}}
  </body>
</html>

It is possible to add multiple designer content insertion points in your theme, provided you specify a unique name attribute for each of them. When there is only one insertion point, the name attribute can be omitted.

Using a theme with multiple named insertion points for designer content
<html>
  <head>
    <meta name="scriptura:wbd:type" content="html-theme">
    <meta name="scriptura:wbd:name" content="My theme">
    <meta name="scriptura:wbd:author" content="John Doe">
  </head>
  <body>
    <div class="banner">
      {{designer-content name="banner"}}
    </div>
    <div class="body">
      {{designer-content name="body"}}
    </div>
    <div class="footer">
      {{designer-content name="footer"}}
    </div>
  </body>
</html>

4.2.2. Using data in your theme

Themes can also refer to data within the currently linked data model. To do this, simply add references to data fields between {{ and }}. As soon as the theme is loaded, the data will be resolved relative to the root of the chosen data model.

However, note that the Web Designer does not enforce a coupling between the theme and the chosen data model. It is possible that template designers choose an incompatible data model which will result in the data fields being resolved to empty data.

Using data in theme
<html>
  <head>
    <meta name="scriptura:wbd:type" content="html-theme">
    <meta name="scriptura:wbd:name" content="My theme">
    <meta name="scriptura:wbd:author" content="John Doe">
  </head>
  <body>
    <div class="banner">
      <img src="{{company.logo}}">
    </div>
    <div class="body">
      {{designer-content name="body"}}
    </div>
    <div class="footer">
      <p>{{company.address.street}}</p>
      <p>{{company.address.zip}} {{company.address.city}}</p>
    </div>
  </body>
</html>

4.2.3. Defining styles in your theme

A theme could define a set of styles. The template designers can then choose a style to change the look and feel of the template. If your theme does not provide any styles, the default list is used.

Defining custom styles

You define a style by adding a comment to your CSS in the following format:

Name Description

@scriptura:wbd:style:name

Human readable name for the style

@scriptura:wbd:style:requires (optional)

Comma separated list of valid objects types. Possible values: Text, Container, Image, ReusableComponent, <Pluggable object id from component definition>

@scriptura:wbd:style:class (optional)

You can optionally define a class. When no class is provided we will extract it from the following rule provided it is a simple class selector.

example
<html>
  <head>
    <style>
      /*
        @scriptura:wbd:style:name = Header 1
        @scriptura:wbd:style:requires = Text
        @scriptura:wbd:style:class = header-1
        @scriptura:wbd:style:name = Header 2
        @scriptura:wbd:style:requires = Text
        @scriptura:wbd:style:class = header-2
        @scriptura:wbd:style:name = Header 3
        @scriptura:wbd:style:requires = Text
        @scriptura:wbd:style:class = header-3
      */

      /*
        @scriptura:wbd:style:name = Footer
        @scriptura:wbd:style:requires = Container, pluggable-object-section
      */
      .footer {
        color: blue;
      }
    </style>
  </head>
  <body>
    {{designer-content}}
  </body>
</html>

We only parse embedded styles, so this will not work with style sheets hosted on a remote server. You can work around this by hosting the style sheet on the remote server and adding a style tag with all your styles defined in one comment.

4.2.4. Using responsive images

HTML5 offers a number of features to support responsive images such as srcset, sizes and picture. See https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images for more information.

Using images with different DPIs in PDF output

The srcset feature of HTML5 and image-set CSS function can be leveraged to select images with the correct DPI when generating PDF output. This can be useful when for example you want to generate previews with low DPI images and the final output with high DPI images. To do this, first enable the required feature (HTML or CSS version) in the theme definition. Then define the set of images using the srcset attribute and set the Device pixel ratio property to the corresponding value when generating PDF output depending on which image you want.

This works for both the Xribe Output API and the Web Designer output step.

Example usage:

...
<head>
  <script id="theme-definition" type="application/json">
    {
      "profile": {
        "pdf": {
          "src-set": true,
          "image-set": true
        }
      }
    }
  </script>
  <meta charset="utf-8">
  <meta name="scriptura:wbd:type" content="html-theme">
  <meta name="scriptura:wbd:name" content="My theme">
  ...
  <style>
    body {
      background-image: image-set(
        url('http://example.com/img/lores.png') 1x,
        url('http://example.com/img/hires.png') 2x
      );
    }
  </style>
</head>
<body>
...
  <picture>
    <img
      srcset="http://example.com/img/lores.png 1x, http://example.com/img/hires.png 2x"
      src="http://example.com/img/lores.png"
      alt="My image" />
  </picture>
...

You can also use the browser specific versions -webkit-image-set() and -moz-image-set().

The sizes attribute, dpi-descriptors, w-descriptors and other absolute units are not supported. Only x-descriptors to indicate display resolution are supported.

For more information on how to set the Device pixel ratio property, refer to the Xribe Output API documentation or the Scriptura documentation that came with the Web Designer output step extension.

We recommend to only enable the option that you need as these can have a performance impact when generating PDF output for larger templates.

4.3. Styling built-in objects

4.3.1. Text Object

A Text object is created to support styling the text object. It is allowed to style the Text object by using the text-object class in a theme. For more information that how the text object work on the template designer, please check Text objects.

Example of styling a text object paragraph in legacy mode
.text-span {
  /*styling color and font-size of the paragraph*/
   color: purple;
   font-size: 60px;
}
Example of styling a text object paragraph in non-legacy mode
.text-object p {
  /*styling color and font-size of the paragraph*/
   color: red;
   font-size: 70px;
}
Example of styling a bold text object in legacy mode
.firepad-b {
  /*styling color and font-size of the bold text */
   color: blue;
   font-size: 20px;
}
Example of styling a bold text object in non-legacy mode
.text-object strong {
  /*styling color and font-size of the bold text */
   color: green;
   font-size: 90px;
}

4.3.2. Spacer

A Spacer is useful for creating space between objects. To provide support in email clients, the Spacer will be rendered using a table element when creating email output. It is allowed to style the Spacer object by using the spacer-object class in a theme, but styles relying on the corresponding element type and its possible child elements are not supported. Those change depending on output type. Additionally, this is implementation.

Example of styling a Spacer object
.spacer-object {
  /* providing extra margin */
  margin: 16px;
}

4.3.3. Divider

A Divider is useful for dividing several objects. The Divider object consists of a hr element wrapped in a div. It is supported to style both the div and the containing hr element using the divider-object class in a theme.

Example of styling a Divider object
.divider-object hr {
  /* making the Divider a red line */
  border-top: 1px solid red;
}

4.3.4. QR Code

A QR code object allows template designers to encode data in the form of a QR code. The actual QR Code is generated as an SVG image, wrapped with a div element. Because of the SVG format, a QR code will not be rendered when creating email output. Styling of the svg element is not supported, but the surrounding container can be styled by using the qr-code-object class name. As an example, making a QR code centered on the page:

Example of styling a QR Code object
.qr-code-object {
  /* centering the QR code */
  text-align: center;
}

4.3.5. Table

A table object is a built-in object that allows template designers to represent information in the form of a table. The table object does not contain any styling apart from a margin and a 100% width. Since we render the table object as a regular HTML table, you can use regular CSS rules to style the rendered table.

Example of styling a table object
table,
th,
td {
  border: 1px solid #c6c8cb;
}

table {
  border-spacing: 0;
  border-collapse: collapse;
  width: 100%;
  table-layout: fixed;
}

table tr {
  margin: 0;
  padding: 0;
}

table th,
table td {
  border-right-width: 0;
  border-bottom-width: 0;
  vertical-align: top;
  padding: 8px;
}

4.3.6. Using headless content theme

Design Studio supports defining headless output properties which can be used to generate headless content that can be consumed by backend systems. In order to define headless output properties, add a new script tag to your existing HTML theme with the special marker ID output-properties-definition. It is best practice to mark the type of this script element’s contents as application/json. An example of the headless output properties definition can be found below.

Example of the headless theme (include some comments to explain the structure).

<html>
  <head>
      <meta name="scriptura:wbd:type" content="html-theme">
      <meta name="scriptura:wbd:name" content="headless_theme_1">
      <meta name="scriptura:wbd:author" content="John Doe">
      <script id="output-properties-definition" type="application/json">
          {
              "name": "Headless",
              "output": {
                  /* The target will be used to determine the ID of the final output containing the headless output properties. */
                  /* When generating output using the Compose Runtime, the target will be the name of the output in the deliverable. */
                  /* On the other hand, when generating output using the Scriptura Engage Document Flow step, */
                  /* the target will be available in the property data source of the output flow object containing the output properties. */
                  "target": "headless"
              },
              "definition":
              [
                {

                  /* A unique ID identifying the spec entry */
                  "id": "title",

                  /* Label supports multilingual labels or single string */
                  "label": {
                      "en": "Title (required)",
                      "nl": "Titel (gewenste)"
                  },

                  /* 1 or more targets specify the location(s) in the final output where the rendered*/
                  /* content for this entry will be injected*/
                  "targets": [
                      "message.title",
                      "push.title"
                  ],

                  /* The resulting JSON type. Possible values are “string”, “boolean”, “number” or “array”.*/
                  /* In the case of an “array” type, the item type must be specified as well. */
                  /* When omitted, the resulting type will fallback to string.*/
                  "resultType": "string",

                  /* Priority specifies whether the field is "required", "important" (shown by default) or*/
                  /* "optional" (hidden by default but shows when expanded)*/
                  "priority": "required",

                  /* Specifies whether the entry is language sensitive. The default value is true.*/
                  "languageSensitive": false,

                  /* Support multiline content. When omitted or false, the entry will be single line*/
                  "multiline": false,

                  /* Specifies how many text lines will be showing if "multiline": true else*/
                  /* default text object is showing with 1 text line*/
                  "rows": 6
                },
                {
                  "id": "remark",
                  "label": {
                      "en": "Remark (optional)",
                      "nl": "Opmerking (optioneel)"
                  },
                  "targets": [
                      "message.remark",
                      "push.remark"
                  ],
                  "resultType": {
                      "type": "array",
                      "items": {
                          "type": "string"
                      }
                  },
                  "priority": "optional",
                  "languageSensitive": false,
                  "multiline": true,
                  "rows": 6
              }
            ]
          }
      </script>
  </head>
<body>
    {{designer-content name="abc"}}
</body>
</html>

The actual example of the headless theme.

<html>
  <head>
    <meta name="scriptura:wbd:type" content="html-theme">
    <meta name="scriptura:wbd:name" content="headless_theme_1">
    <meta name="scriptura:wbd:author" content="John Doe">
    <script id="output-properties-definition" type="application/json">
      {
        "name": "Headless",
        "output": {
            "target": "headless"
        },
        "definition":
          [
            {
              "id": "title",
              "label": {
                  "en": "Title (required)",
                  "nl": "Titel (gewenste)"
              },
              "targets": [
                  "message.title",
                  "push.title"
              ],
              "resultType": "string",
              "priority": "required",
              "languageSensitive": false,
              "multiline": false,
              "rows": 6
            },
            {
              "id": "remark",
              "label": {
                  "en": "Remark (optional)",
                  "nl": "Opmerking (optioneel)"
              },
              "targets": [
                  "message.remark",
                  "push.remark"
              ],
              "resultType": {
                    "type": "array",
                    "items": {
                        "type": "string"
                    }
              },
              "priority": "optional",
              "languageSensitive": false,
              "multiline": true,
              "rows": 6
          }
        ]
      }
    </script>
  </head>
  <body>
      {{designer-content name="abc"}}
  </body>
</html>

After the theme is used in a template, the template designer will show additional text fields above the template allowing the user to specify the output properties content.

headless theme demo

Additional input fields are rendered based on the headless ouptut-properties-definition script tag.

When generating output with the Compose Runtime API, JSON output result are generated, for example:

{
        "message":{
              "title":"abc",
              "remark": ["xyz1"]
        },
        "push":{
              "title":"abc",
              "remark": ["xyz1"]
        }
}

5. Pluggable objects

5.1. Introduction

Web Designer includes four builtin object types:

  • Text objects.

  • Image objects.

  • Container objects.

  • Table objects

Other object types listed in the Add Object panel are customer developed pluggable objects, highlighted in the following figure.

pluggable objects
Figure 3. Builtin and pluggable object types

5.2. Manage Pluggable Objects

The Manage Pluggable Objects page lists all recognized pluggable objects in the current environment. Unrecognized pluggable objects are not included. For every pluggable objects its status, title, category and location are listed. The page is useful in case of pluggable object errors. If a pluggable object contains an error, the pluggable object is marked with warning icon and an error message is shown inline. Note that in order to access this page, the user needs to have the webdesigner:pluggableobjects:manage permission.

5.3. Creating a custom pluggable object

Pluggable objects behave in a way similar to the built-in objects. Like the built-in objects they are used to add content to the template that is being designed. They are created by combining HTML, CSS and javascript. In the following chapters we will go over de different steps needed to make one.

When designing pluggable objects you have to take into account a number of restrictions:

  • Only use absolute URLs within your pluggable object

  • Single root HTML element

5.3.1. Basic usage

A pluggable object contains 2 mandatory parts. It always starts with a script tag followed by the content of the pluggable object. The script tag needs to have the attribute id with the value of component-definition. Inside, you will need to specify the name, icon and category in a JSON format. See below for an example.

Following the script tag you can add as many style or script elements as you need. These will be hoisted to the head element of the HTML and are only included once per pluggable object so you don’t need to worry about using the same pluggable object multiple times within a template.

After that you need to add 1 HTML root element. All other root or text elements will be ignored.

<script type="text/json" id="component-definition">
  {
    "id": "2-columns", (1)
    "name": "2 Columns", (2)
    "icon": "columns", (3)
    "category": "Layout" (4)
  }
</script>

<style>
  & {
    color: blue;
  }
</style>

<script>
  console.log('Hello pluggable object');
</script>

<button>My first pluggable object</button>
<div>This won't be in the output</div> (5)
1 The id for the pluggable object (optionally)
2 The name
3 The icon based on fontawesome or an absolute url to the image.
4 Category of the pluggable object
5 Node won’t be rendered by the designer

5.3.2. Adding custom properties

In a lot of cases you will need to add dynamic data to the pluggable object. You can do this by defining properties. These properties will be visible in the right pane of the webdesigner.

You can add custom properties by implicitly using them or defining them inside of the component-definition. To use a property within the component add the curly braces around the name like so {{propertyName}}.

In the following example we implicitly define the variable color by placing it between curly braces. The default property type for variables will be string. If you want to have a different type, you will need to explicitly define the property which we will cover next.

<script type="text/json" id="component-definition">
  {
    "name": "Button",
    "icon": "square",
    "category": "Buttons"
  }
</script>

<style>
  & {
    color: blue;
  }
</style>

<button class="{{color}}">My first pluggable object</button>
Color property
Figure 4. Color property
1 Color property with a plain input field.

We can improve upon our last example by giving the template designer a pre-defined list of colors to use. This can be done by explicitly defining the properties in the component-definition.

<script type="text/json" id="component-definition">
  {
    "name": "Button",
    "icon": "square",
    "category": "Buttons",
    "properties": {
      "color": {
        "defaultValue": "red",
        "label": "Color",
        "supportedValues": ["red", "green"]
      }
    }
  }
</script>

<style>
  & {
    color: blue;
  }
</style>

<button class="{{color}}">My first pluggable object</button>
Color property
Figure 5. Color property as options
1 Color property with a list input field.

The table below lists all the different optional options you have for property definitions.

Item Description Remarks Default value

type

Used by Web Designer to select the user interface property editing component.

URL, boolean, string, bind

string

label

The label used in the Properties panel in Web Designer.

No default

scope

The scope property allows you to limit them to a certain fragment scope. The Value of the property should be the name of the fragment.

Only available on properties of type bind.

None (Pluggable Object scope)

supportedValues

When the type is string, specifying this property will change the control to edit this property from an input box to a dropdown list of which the display values are converted using the same rules as for labels.

None

defaultValue

The value the property gets when a new instance of the pluggable object is used in a template.

None

5.3.3. Requiring capabilities

Pluggable objects can define a set of required capabilities that should be injected into the document and be made available to the pluggable object at runtime. This can be done by extending the component-definition with the requiredCapabilities field:

<script type="text/json" id="component-definition">
{
  "name": "HighchartsDemo",
  "icon": "bar-chart",
  "category": "Charts",
  "requiredCapabilities": [{
      "highcharts": "9.3.2"
  }],
}
</script>

<script>
window.initializeChart = function(element) {
  // the on-mount handler function has access to the HighCharts object since it was specified in the requiredCapabilities.
  console.log(HighCharts);
}

</script>

<div onmount="window.initializeChart(this)"></div>

The current set of possible capabilities that can be injected can be found in the following table:

Capability Supported Versions

HighCharts

9.3.2

When having multiple pluggable objects requiring the same capabilities, it is recommended to make sure they all specify the same version. Specifying different versions across different pluggable objects will lead to undefined behaviour of which version you will end up receiving. Specifying an unsupported version will lead to the pluggable object not appearing in the objects pane.

5.4. Custom styling

The css for all pluggable objects is scoped so it cannot leak into other parts of the template. There is however one caveat for selectors on root elements. To understand this you need to know how scoping works within the webdesigner.

The Web Designer does not introduce additional HTML elements in between objects from the webdesigner and pluggable objects. In order to make sure that all pluggable objects are properly scoped we will add a unique class to the root element of your pluggable object. Afterwards we will rewrite all css selectors to contain this unique class. See the example below what would happen if we would create a class red which sets the background color to red.

input css
.red {
  background-color: red;
}
output css
.unique .red {
  background-color: red;
}

Since both unique and red are both classes of button the selector won’t match and the background color will not change. To solve this you should use the pseudo class :root. The following example shows how you could write selectors to change the background color of the button.

<script type="text/json" id="component-definition">
  {
    "name": "Button",
    "icon": "square",
    "category": "Buttons",
    "properties": {
      "color": {
        "supportedValues": ["red", "green"]
      }
    }
  }
</script>

<style>
  & {
    color: blue;
  }

  .red:root {
  	background-color: red;
  }

  .green:root {
  	background-color: green;
  }
</style>

<button class="{{color}}">Button</button>

5.5. Language sensitive User interface

You can optionally provide translations for the name, category and property labels in your Pluggable object. This is done by assigning a translation object to those properties instead of a string. A translation object will have ISO-3166 keys and the translation as the value like so.

{
  "nl": "Kleur",
  "en-GB": "Colour",
  "en-US": "Color"
}

In a Pluggable object it will look as followed:

<script type="text/json" id="component-definition">
  {
    "name": {"en": "Button"},
    "icon": "square",
    "category":  {"en": "Buttons"},
    "properties": {
      "color": {
        "label": {"en": "Color", "en-GB": "Colour"},
        "supportedValues": ["red", "green"]
      }
    }
  }
</script>

<button class="{{color}}">Button</button>

5.6. Output profiles (Experimental)

The designer supports generating output based on several output profiles, such as web, application or email. Depending on this profile, certain capabilities can be available or unavailable. For example, it is not possible to use form elements in the email output profile. You can use these output profiles to restrict the availabilitty of your pluggable object by specifying the required capabilities needed by the pluggable object. If the chosen output profile does not support these capabilities, the pluggable object will not be shown in the Objects Pane.

To specify the capabilities of the pluggable object, simply add a requiredCapabilities propoerty to the component definition as follows:

<script type="text/json" id="component-definition">
  {
    "name": {"en": "Button"},
    "icon": "square",
    "category":  {"en": "Buttons"},
    "requiredCapabilities": [{"html:form": true}],
    "properties": {
      "color": {
        "label": {"en": "Color", "en-GB": "Colour"},
        "supportedValues": ["red", "green"]
      }
    }
  }
</script>

<button class="{{color}}">Button</button>

The list of possible capabilities per output profile can be found in the table below.

Capability Web Email Application

html:iframe

yes

no

yes

html:internal-css

yes

yes

yes

html:style-element-css

yes

yes

yes

html:inline-css

yes

yes

yes

html:external-css

yes

yes

yes

css:margin

yes

yes

yes

css:padding

yes

yes

yes

html:video

yes

no

yes

html:img

yes

yes

yes

html:table > css:width:percent

yes

no

yes

html:form

yes

no

yes

5.6.1. Output profile application

The output profile application needs javascript in order to render your document dynamically. The officially supported browsers for this are:

Browser Minimum version

Chrome

57

Safari

10.1

Internet Explorer

11

Firefox

52

Documents created with output profile application may work on older browsers but this is not tested or maintained.

5.7. Designer provided variables

The designer provides useful variables by default which can be used in your pluggable object. All designer provided variables are under the object _scriptura.

Variable

Type

Description

id

string

The id of the object. This is useful when used in combination with the Javascript API

repeatID

string

The repeatID for an object. The id of an object is not unique when working with repeats. This id is needed if you want to use the selection api.

designMode

boolean

Indicates if the designer is in design modus or not. This can be used to render additional information while designing the template.

selected

boolean

Indicates if the object is selected or not

selectedChildrenInfo

string[]

The ids for the selected children. This is optional and if you want it, you need to specify it via the needsChildSelectionInfo property in the component definition. !! Only use this when needed since this has a considerable performance impact.

Example
<script type="text/json" id="component-definition">
  {
    "name": "Image",
    "icon": "object",
    "category":  "Object"
  }
</script>

<div>
  {{#if _scriptura.designMode}}
    Image source: {{imageURL}}
  {{/if}}
  <img src="{{imageURL}}"/>
</div>

5.8. Designer content

You can optionally add one or more designer managed areas to your pluggable object. This will allow the template designer to add other objects to that section. These areas are specified by adding the {{designer-content}} variable in the HTML.

You can specify following properties on Designer content.

Table 3. Designer content properties
Property Type Description

Name

string

Name for the designer content. This will also be in the content outline.

objectInsertionMode

'blank' or '' (default: '')

Determines the way of inserting objects into the Designer content.

If we take the button from the previous examples and make the text of the button a designer managed content, it will look like this.

<script type="text/json" id="component-definition">
  {
    "name": "Button",
    "icon": "square",
    "category": "Buttons",
    "properties": {
      "color": {
        "supportedValues": ["red", "green"]
      }
    }
  }
</script>

<style>
  & {
    color: blue;
  }
</style>

<button class="{{color}}">{{designer-content name="Text"}}</button>

5.8.1. Default content (Experimental)

It is possible to specify a default object for designer-content blocks. The default object will be inserted into the designer-content when the Pluggable Object is added to the template. The default object can either be a native object or reference another Pluggable Object. Native objects are referenced with the native: prefix. Supported objects are native:text, native:image, native:container, native:spacer, native:divider and native:qrcode. Pluggable objects can be specified with a path relative to the location of the current file. To prevent the universe from collapsing, avoid circular references in default objects, as they would lead to infinite loops. An optional attribute defaultObjectLocked can be set to true to prevent users from deleting the default object.

<script type="text/json" id="component-definition">
  {
    "name": "DefaultObject",
    "icon": "square",
  }
</script>

<div>
    <p>There is a wrapper component with default pluggable object:</p>
    <!-- Built-in objects are referenced by with the "native:" prefix. -->
    {{designer-content name="Head" defaultObject="native:text" }}

    <!-- Pluggable objects are referenced with a relative path to this document -->
    {{designer-content name="Body" defaultObject="../bodyObject.html" defaultObjectLocked=true }}
</div>

5.9. Designer fragments (Experimental)

Designer fragments are block helpers that allow you to specify a section of a pluggable object which can occur multiple times and specify custom properties.

You specify fragments by adding the block helper {{#designer-fragment}}{{/designer-fragment}}. Fragments can be nested and have support for Designer content. It is however not possible to add additional fragments by default via the Web Designer UI. It is up to you to provide a convenient way to create new fragments via the Javascript API.

Following properties are available on designer fragments.

Table 4. Designer fragment properties
Property Type Description

Name

string

Name for the designer fragment. This will also be in the content outline.

icon

string

Icon for the object. This is shown in the content outline and in the selection outline.

initialCount

number (default: 1)

Specify the amount of objects instantiated when inserting the pluggable object.

locked

boolean (default: false)

When set to true, the user will not be able to delete this object. You can only delete this object via the javascript API once created.

repeat

boolean (default: false)

Allows this fragment to be repeated. This will change the structure of Designer provided variables for fragments

Designer fragments need to contain at least 1 HTML element

5.9.1. Custom Properties

You can specify properties on fragments in the same way as you do for normal pluggable objects. The only difference is that you change scope underneath a fragment to that of the fragment. It is possible to access the parent scope via ../.

example
<ul>
  <li>Category: {{category}}</li>
  {{#designer-fragment name="Product" icon="product" initialCount=1}}
    <li>
      <span>Name: {{name}} - {{../category}}</span>
      <span>Price: {{price}}</span>
    </li>
  {{/designer-fragment}}
</ul>
  • Properties Pluggable object: category

  • Properties Product fragment: name, price

5.9.2. Designer provided variables for fragments

You can find the general Designer provided variables underneath each fragment plus one additional variable. This variable contains the child fragments, and it’s properties. Designer fragment variables follow the same rule as other properties and changes scope underneath each fragment.

example
<ul>
  <li>Category: {{category}}</li>
  {{#designer-fragment name="Product" icon="product" initialCount=1}}
    <li>
      <span>Name: {{name}} - {{../category}}</span>
      <span>Price: {{price}}</span>
    </li>
  {{/designer-fragment}}
</ul>
example structure
{
  category: "Clothing"
  _scriptura: {
    designerFragments: {
      Product: [
        {name: "Pants", price: 100},
        {name: "Shirt", price: 50}
      ]
    }
  }
}
Designer provided variables for fragments with repeats

If you allow repeats on fragments, the internal structure changes from a 1-dimensional array to a 2-dimensional array. Following example based on the previous one will highlight the key difference in the structure.

example
<ul>
  {{#designer-fragment name="Product" icon="product" initialCount=1 repeat=true}}
    <li>
      <span>Name: {{name}}</span>
      <span>Price: {{price}}</span>
    </li>
  {{/designer-fragment}}
</ul>
example structure
{
  _scriptura: {
    designerFragments: {
      Product: [
        [
          {name: "Pants", price: 100},
          {name: "Shirt", price: 50}
        ],
        [
          {name: "Socks", price: 10},
          {name: "Sweater", price: 100}
        ]
      ]
    }
  }
}

5.10. Javascript API

The Javascript API is in an experimental state. Breaking changes may occur, and backward compatibility is not guaranteed. The Javascript API is only available in the Web Designer unless otherwise specified.

5.10.1. Context menu (Experimental)

The context menu only works in design mode. You can add a context menu by placing a data-id-context-menu attribute on an HTML element. The recommended way of working with the context menu is via a function you place on the window object. An example of how you could do this is below.

example
<script type="text/json" id="component-definition">
  {
    "name": "Table",
    "icon": "table",
    "category":  "Tables"
  }
</script>

<script>
  window.createContextMenu = () => {
    return [
      {
        label: 'Insert Left',
        onClick: () => console.log('Insert Left'),
        disabled: true,
      },
      {
        label: 'Insert Right',
        onClick: () => console.log('Insert Right'),
      },
    ];
  }
</script>
<div>
  <table>
      <tr>
        <td data-id-context-menu="this.ownerDocument.defaultView.createContextMenu()">
          You can insert rows by clicking the row Pluggable object.
        </td>
      </tr>
  </table>
</div>

The result of the function is an array of context menu items. Each item has the following properties.

Property Type Description

label

String Or { [langCode: string]: string }

The label of the context menu item. This will be visible to the user. You can add translation support by creating a map of translations. The keys should be in ISO-3166, and the values are the translations.

onClick

Function

This function will be called when the user clicks the context menu item.

disabled

boolean

Optional parameter to mark certain items as optional.

5.10.2. OnLoad, OnUpdate

Many libraries wait for the DOM content to be loaded to execute a piece of javascript. Pluggable objects are loaded in dynamically as a result of this many of these libraries will not work out of the box. To resolve this issue, we provide three different attributes which are available on the root element. The only one of the 3 which will work on the actual output is onmount. The remaining 2 will never be called after generating output.

Attribute Type Description

onmount

Function

This will be evaluated each time a pluggable object is loaded into the document.

onupdate

Function

This will be evaluated each time the pluggable object is updated. E.g. change sample, update property,…​

onunmount

Function

This will be evaluated just before the item is removed. This is usually the place to do some clean-up.

<script type="text/json" id="component-definition">
  {
    "name": "Table",
    "icon": "table",
    "category":  "Tables"
  }
</script>

<script>
  let elements = [];
  window.onmount = (element) => elements.push(element);
  window.onupdate = (element) => console.log('Updated ', element);
  window.onunmount = (element) => elements.filter(e => e !== element)
</script>

<div onmount="this.ownerDocument.defaultView.onmount(this)"
  onupdate="this.ownerDocument.defaultView.onupdate(this)"
  onunmount="this.ownerDocument.defaultView.onunmount(this)"
  >
  Hello world
</div>

5.10.3. Editor (Experimental)

select(objectID, repeatID, omitCrumb) ⇒ void

Allows you to programmatically select an object based on the objectID and the repeatID.

Table 5. parameters
Parameter Type Description

objectID

string

The id of the object

repeatID

string

The repeatID of the object

omitCrumb

boolean (default: false)

Optionally omit the crumb of the selection outline.

example
window.id.wbd.api.editor.select(objectID, repeatID);
window.id.wbd.api.editor.select(objectID, repeatID, true);
setProperty(objectID, propertyName, newValue) ⇒ Delta

This method returns a delta object on which you can chain any of these methods. This method does not do anything until you call apply() on the delta. The delta is an atomic operation which enables the template designer to undo this with one undo action.

Table 6. parameters
Parameter Type Description

objectID

string

The id of the object where you want to change the property

propertyName

string

The name of the property you want to change

newValue

any

The new value for the property

example
window.id.wbd.api.editor
  .setProperty(objectID, 'show', true)
  .setProperty(objectID, 'price', 50)
  .setProperty(objectID, 'name', 'Shirt')
  .apply()

5.10.4. Fragments API (Experimental)

All these methods return a delta object on which you can chain any of these methods. The methods themselves do not do anything until you call apply() on the delta. The delta is an atomic operation which enables the template designer to undo this with a single undo action.

add(objectID, position) ⇒ Delta

Add a new designer fragment before or after the current one. As a result, you always need to have one designer fragment.

Table 7. parameters
Parameter Type Description

objectID

string

The id of the fragment where you want to add an additional one.

position

string (default: 'after')

Optionally define where the fragment needs to be added. Possible values are before or after.

example
window.id.wbd.api.designerFragments
  .add(fragmentID, 'after')
  .add(fragmentID, 'before')
  .apply()
Delete(ObjectID) ⇒ delta

Delete a designer fragment based on the objectID.

Table 8. parameters
Parameter Type Description

objectID

string

The id of the fragment you want to delete.

example
window.id.wbd.api.designerFragments
  .delete(fragmentID)
  .apply()

5.10.5. Data API (Experimental)

The Data API allows you to retrieve and modify data within your pluggable object. The functions contributed by this api all take a property of type bind as parameter. This bind property will reference a datafield within the data model.

Properties of type bind also take the current repeat context and fragment scope into account.

The following example creates a counter object which will provide a bind property allowing the user to choose a data field in the data model. When the user presses the button, the onclick event handler is fired. The event handler is executed with the following arguments in scope:

Table 9. Scope
Parameter Description

this

The element receiving the event.

_scriptura

The Scriptura object providing the api.

event

The event that triggered the event handler.

example
<script type="text/json" id="component-definition">
  {
    "name": "counter",
    "category": "Counter",
    "properties": {
      "amount": {
        "type": "bind",
        "label": "Amount"
      }
    }
  }
</script>

<script>
	window.button = {
		increment: (element, api, event) => {
			const current = api.po.getDataValue('amount') || 0;
			api.po.setDataValue('amount', current + 1);
		}
	}
</script>

<div>
	<button onclick="button.increment(this, _scriptura.api, event)">+</button>
	{{amount}}
</div>
_scriptura.api.po.getDataValue(bindPropertyName) ⇒ string | number

Retrieves a value from the data model, corresponding to the datafield associated with the bind property.

Table 10. parameters
Parameter Type Description

bindPropertyName

string

The name of the bind property.

example
_scriptura.api.po.getDataValue('amount')
_scriptura.api.po.setDataValue(bindPropertyName, newValue) ⇒ void

Sets the value in the datamodel associated with the bind property to the new value.

Table 11. parameters
Parameter Type Description

bindPropertyName

string

The name of the bind property.

newValue

string, number

The new value to set in the data model.

example
_scriptura.api.po.setDataValue('amount', newAmount)
_scriptura.api.po.submitData(url, options?) ⇒ void

Submits the data to the given url and optionally replaces either the entire document, the data model or nothing.

Table 12. parameters
Parameter Type Description

url

string

The URL to which to submit the data.

options

object

The optional options for the submission request.

Table 13. options
Property Values Default Value Description

replace

'all', 'data', 'none'

'none'

Indicates what to do with the response data.

method

'GET', 'POST', 'PUT', 'DELETE'

'POST'

The HTTP method for the request.

Table 14. replacement
Type Description

'all'

Replaces the entire document with the response contents

'data'

Replaces the instance data of the document with the response contents. This assumes the response data is of type application/json.

'none'

Does not replace anything.

example
_scriptura.api.po.submitData('https://example.com');
_scriptura.api.po.submitData('https://example.com', { method: 'PUT', replace: 'data' });
_scriptura.api.po.submitData('https://example.com')
  .then(data => console.log('Received: ', data))
  .catch(error => console.error('Received error: ', error.message));
_scriptura.api.po.validate() ⇒ boolean

Validates the form and returns the result of the validation.

Form validation is only supported in pluggable objects specifying the required capability html:form as true. Refer to Output profiles (Experimental) for more information on required capabilities.

example
_scriptura.api.po.validate();

5.11. Helpers

5.11.1. Comparison

{{if}}, {{unless}}

Variables in these expressions are recognized by the Web Designer as boolean properties.

Table 15. parameters
Property type

a

any

example
{{#if hasButton}}
  <button>Click me</button>
{{else}}
  <span>Text</span>
{{/if}}
{{and}}

Helper that renders the block if both of the given values are truthy. If an inverse block is specified it will be rendered when falsy. Works as a block helper, inline helper or subexpression.

Table 16. parameters
Property type

a

any

b

any

returns

string

example
<!-- {great: true, magnificent: true} -->
{{#and great magnificent}}A{{else}}B{{/and}}
<!-- results in: 'A' -->
{{or}}

Block helper that renders a block if any of the given values is truthy. If an inverse block is specified it will be rendered when falsy.

Table 17. parameters
Property type description

arguments

…​any

variable number of arguments

returns

string

example
{{#or a b c}}
  If any value is true this will be rendered.
{{/or}}
{{compare}}

Render a block when a comparison of the first and third arguments returns true. The second argument is the arithemetic operator to use. You may also optionally specify an inverse block to render when falsy.

Table 18. parameters
Property type description

a

any

operator

string

The operator to use. Operators must be enclosed in quotes: ">", "=", "<=", and so on.

be

any

example
{{#compare productCode '===' 1}}
  <button>Click me</button>
{{else}}
  <span>Text</span>
{{/if}}

5.11.2. Array

{{each}}

Iterates over each item in an array and exposes the current item in the array as context to the inner block.

Table 19. parameters
Property type description

array

Array

The array to iterator over

example
<ul class="people_list">
  {{#each people}}
    <li>{{this}}</li>
  {{/each}}
</ul>
{{withLast}}

se the last item or n items in an array as context inside a block. Opposite of [withFirst].

Table 20. parameters
Property type description

array

Array

idx

number (default: 0)

The starting index

example
<!-- array: ['a', 'b', 'c'] -->
{{#withLast array}}
  {{this}}
{{/withLast}}
<!-- results in: 'c' -->
{{withFirst}}

Use the first item in a collection inside a handlebars block expression. Opposite of [withLast].

Table 21. parameters
Property type description

array

Array

idx

number (default: 0)

The starting index

example
<!-- array: ['a', 'b', 'c'] -->
{{#withFirst array}}
  {{this}}
{{/withFirst}}
<!-- results in: 'a' -->

5.11.3. Math

{{min}}

Returns the smallest number of the two.

Table 22. parameters
Property type

a

number

b

number

returns

number

example
<!-- a: 1, b: 2 -->
{{min a b}}
<!-- results in: 1 -->
{{max}}

Returns the largest number of the two.

Table 23. parameters
Property type

a

number

b

number

returns

number

example
<!-- a: 1, b: 2 -->
{{max a b}}
<!-- results in: 2 -->
{{add}}

Return the sum of a plus b.

Table 24. parameters
Property type

a

number

b

number

returns

number

example
<!-- a: 1, b: 2 -->
{{add a b}}
<!-- results in: 3 -->
{{subtract}}

Return the product of a minus b.

Table 25. parameters
Property type

a

number

b

number

returns

number

example
<!-- a: 1, b: 2 -->
{{subtract a b}}
<!-- results in: -1 -->
{{sum}}

Returns the sum of all numbers in the given array.

Table 26. parameters
Property type

array

Array

returns

number

example
{{sum "[1, 2, 3, 4, 5]"}}
<!-- results in: '15' -->
{{avg}}

Returns the average of all numbers in the given array.

Table 27. parameters
Property type

array

Array

returns

number

example
{{avg "[1, 2, 3, 4, 5]"}}
<!-- results in: '3' -->

5.12. Focusable outline (Experimental)

It can be beneficial for the end user to hide the pluggable object tree in the content outline when he/she is not working on it. To do this add the option collapseOutline to the component-definition section as shown below. The default value is false.

<script type="text/json" id="component-definition">
  {
    "name": "Table",
    "icon": "table",
    "category":  "Tables",
    "collapseOutline": true
  }
</script>
po collapse outline global
Figure 6. Global outline
po collapse outline pluggable object
Figure 7. Pluggable object outline
1 Go back to global outline

Comments or suggestions?
Tell us here.

If you have any suggestions or comments about this guide, please send us an email using this form.