Sciter. Declarative behavior assignment by CSS: ‘prototype’ and ‘aspect’ properties

From the very beginning Sciter supported declarative scripting class assignment to DOM elements.

If you want all div.some-widget elements in your document to behave in some special way (be subclassed) then you will need:

1. in script to declare

class SomeWidget : Behavior {
  function attached() { ... } // called with 'this' set to the DOM element 
                              // being subclassed on DOM construction

  function detached() { ... } // called when the element gets "declassed" - 
                              // removed from the DOM or get subclassed by other class.

  // ... SomeWidget specific methods here
}

2. and in CSS to declare

div.some-widget { prototype:SomeWidget; } 
/* or if SomeWidget id declared in other file: */
div.some-widget { prototype:SomeWidget url(some-widget.tis); }

After that all div elements having class="some-widget" will have SomeWidget class assigned to them. I have explained this mechanism 8 years ago (time is passing, yes) here.

This works reliably and is quite convenient.

The only problem with behaviors/prototypes – at any given moment of time particular DOM element can have one and only one scripting class. JavaScript and TIScript do not support multiple inheritance.

Imagine that you have multiple functions in script aimed to configure some DOM element to serve some specific functionality.
In other words each of such functions adds its own aspect (partial functionality) to the element it is called on. Like:

function addClickHandlerFor(element) {
  // adds onClick handler defined by "click" element attribute in html
  element.on("click", function() { 
    var attrClick = element.attributes["click"];  
    element.eval(attrClick); // evaluate the expression
  });
}

You can have set of such functions configuring different aspects of elements/behaviors.

This works in principle but you will need to call such functions explicitly for all elements for which you will need such configurations. And don’t forget to call them it for content created dynamically (non-trivial task by itself).

Considered all these I have introduced new ‘aspect’ Sciter specific CSS property.

The aspect CSS property

It is declared as

  aspect: function-name [ url(of-function-implementation-file) ];

Where function-name" is fully qualified name of the “aspect” function that is aimed to configure/setup some additional functionality on the element. And the url() is file name where it is defined.

Principles of aspect handling:

The “aspect” function is an ordinary tiscript function that gets called

  1. with this set to the DOM element satisfying the CSS rule.
  2. strictly once per life time of the DOM element.

And yet, the aspect CSS property uses non-standard inheritance – if the element has multiple matching rules with the aspect defined the used aspect is a list of all aspects. Thus if you have have these rules (example taken from the Plus engine):

[click] { aspect:Plus.Click; }
[dblclick] { aspect:Plus.DblClick; }

and the element defined in markup as

<b id="clickable" click="..." dblclick="...">text</b>

It will get two calls – Plus.Click() and Plus.DblClick() for it. As if you have the following in your CSS:

#clickable { aspect:"Plus.Click" "Plus.DblClick"; }

The aspect mechanism is actively used by Plus ( /samples/+plus/ ) and Lang ( /samples/+lang/ ) engines in Sciter SDK.
Plus provides declarative data binding facilities “a la” AngularJS and Lang is about i18n support.

Parametrized aspect definitions

As of Sciter 3.3.1.6 aspect assignment can contain parameters defined in CSS:

#chart { aspect: MicroChart.Donut(fill: #f00 #0f0 #00f, thickness:0.2 ); }

This will call MicroChart.Donut  function with single object parameter as if the following code is run:

const params = {
  fill: [ color(255,0,0), color(0,255,0), color(0,0,255) ],
  thickness: 0.2
};
MicroChart.Donut(params); // in fact as MicroChart.Donut.call(domElement,params);

If your aspect function supports parameters then it should have the following signature:

namespace MicroChart {
  function Donut(params = {}) {
    ...
  }
}

this way it can be assigned as with as without params in CSS.

Leave a Reply