TIScript: classes, decorators and events.

June 25, 2010

Filed under: Sciter, Script — Andrew @ 7:13 pm

One of differences of TIScript from JavaScript is that it has classes and namespaces.

Declaration of a class in source code looks like this:

class Baz
{
  function foo() { ... }
  property bar(v) { ... }
  // ... other functions, properties, variables,
  // constants and decorator calls ...
}

As you see class declartion in TIScript is pretty similar to the ones in Java or in C++. The only difference is that in TIScript class declaration is an executable (in runtime) entity. While parsing the class declaration above the compiler produces bytecode that is equivalent of this:

const Baz = new Class();
      Baz.foo = function() { .... }
      Baz.bar = property(v) { .... }
...

So while loading bytecodes produced by the compiler TIScript VM will execute sequence of commands to build such class object.

As class construction is an executable chunk of code then in principle we can modify/synthesize class declarations in runtime. And decorators in TIScript are just about this – they allow to execute decorator functions in context of class that is being declared.

Decorator is an ordinary function with the name starting from ‘@’ character. It should have at least one parameter that will receive reference to the object being decorated.

Example of the decorator function, the @returns decorator creates proxy function around call of declarating function and verifies its return value:

function @returns(func, returnType)
{
   return function(params..)
   {
      var r = func.apply(this,params);
      assert typeof r == returnType;
      return r;
   }
}

Having such decorator in place we can declare methods of classes with checks of their return values:

class Zum
{
   @returns #integer
     function Add(i1,i2) {...}
}

The @returns #int function()...  here is a decorator function invocation. Under the hood all this will produce following code:

const Zum = new Class();
      Zum.Add = @returns( function(i1,i2) {...}, #integer );

Call of the @returns() creates proxy for its parameter function. The result (the proxy function) will be assigned to the Add field of the class object. As you may see decorators are pretty much syntax sugar that if properly used may increase expressiveness of your code a lot.

The this environment variable inside the decorator function is getting reference to the context object of the decorator call. E.g. inside class declaration this inside decorator function body will contain reference to the class object.

TIScript supports three types of decorators in total:

  1. Function decorators:  @decorator [...params..] function() { ... }
  2. Class (or namespace) decorators: @decorator [...params..] class { ... }
  3. Empty decorators: @decorator [...params..] ;

Last one – empty decorator – is used in cases when it is a need to synthesize some properties or functions.

Here is practical example of empty decorators used to declare events in class VGrid (Sciter SDK/samples/ideas/virtual-grid/. Virtual Grid is created by Dmitrii Yakimov from http://activekitten.com).

class VGrid: Behavior
{
    const BUF_ROWS = 64;
    ...

   // Events supported by VGrid instances:

   // onCurrentRowChanged( this_grid, row )
   @DECLARE_EVENT #onCurrentRowChanged;

   // onHeaderClick( this_grid, evt, colNo, th )
   @DECLARE_EVENT #onHeaderClick;

   // onCellClick( this_grid, evt, colNo, rowNo, td)
   @DECLARE_EVENT #onCellClick;

   // onRowClick( this_grid, evt, rowNo, tr)
   @DECLARE_EVENT #onRowClick;
}

Each @DECLARE_EVENT eventName; decorator call here creates in the class these three fields:

  • property eventName(v) – that represents list of event handlers assigned to the grid instance by users of the class,
  • function notify_eventName() – function used internally by the class to "raise the event" and
  • var _eventName = [] – variable that holds a list (array) of event handlers (functions) attached to the event by users of the grid.

And here is the implementation of the @DECLARE_EVENT decorator function:

function @DECLARE_EVENT(dummy, eventName)
{
  // create symbols for the fields:
  var notify_sym = symbol( "notify_" + eventName.toString());
  var list_sym = symbol( "_" + eventName.toString());
  var prop_sym = eventName;

  // generate 'notify_' function:
  this[ notify_sym ] = // 'this' here is a class being decorated
    function(params..)
    {
      // 'this' here is VGrid *instance*
      var list = this[list_sym];
      if(typeof list == #array)
      {
        // invoke each function in subscriptions list:
        for( var f in list )
          f.apply(this, this, params); // always pass 'this' in the
                                       // first param - source element
      }
    }
  // adding property  that allows to push
  this[prop_sym] = // 'this' here is a class being decorated,
                   // generating computable property for it.
    property(v)
    {
      get { return this[list_sym] || (this[list_sym] = []); }
      set { throw String.$(Use element.{ list_sym }.push(func)
                                             to subscribe to this event); }
    }
}

The decorator function above is a bit cryptic but defintions like:

@DECLARE_EVENT #onCurrentRowChanged;
@DECLARE_EVENT #onHeaderClick;
...

allow to declare and see the whole event set in compact and convenient form.

Printing support in Sciter.

February 19, 2010

Filed under: Sciter, Web Application Techologies — Andrew @ 11:51 pm

Sciter is getting print and print preview support. At the moment architecture and core functionality is established. Here is a screenshot of one of test pages demonstrating print preview:

Print preview widget

Print preview widget

Print preview is implemented as a native behavior that by default is assigned to any <frame type="pager"> element. Print Preview by its nature is a frame containing other document, that is why <frame> is used. The <frame> element may contain page-template="page.htm" DOM attribute that contains URL of so called page template document.

Here is an example of such page template.

<html>
  <head>
    <title></title>
    <style>
          frame#content-box  { size:*; }
          text#header { border-bottom: 1px solid gray; }
          text#footer { border-top: 1px solid gray; }

          :root[page-parity=odd] text#footer  { text-align:left; }
          :root[page-parity=even] text#footer { text-align:right; }
          :root[page-parity=odd] frame#content-box
          {
            margin-left:10pt;
                margin-right:20pt;
          }
          :root[page-parity=even] frame#content-box
          {
            margin-left:20pt;
                margin-right:10pt;
          }
        </style>
  </head>
<body>
  <text #header>Header....</text>
  <frame #content-box />
  <text #footer>Page <span #page-no /></text>
</body>
</html>

<frame #content-box /> here defines area where source document will be printed. Such page template may contain arbitrary markup and styles and is accessible/modifiable by script on the host page.
It is possible to style the template for even/odd pages, even for the page with particular number.

Print Preview Schema

Print Preview Schema

Aero, Windows V/7

December 2, 2009

Filed under: HTMLayout, Sciter — Andrew @ 9:00 pm

In the middle of adding Windows Aero DWM support to HTMLayout and Sciter:
htmlayout-aero-w7

This is standard sample htmlayoutsdk/html_samples/border-radius/rounded-tabs.htm from the SDK

Seems like I need to add “aero” as an additional value to CSS @media selector in order to support such screens. This document is using simple html { background-color:transparent; } declaration to make it transparent.

And w7aero sample application is calling this function

void ExtendFrameIntoClient(HWND hwnd)
{
   // Negative margins have special meaning to DwmExtendFrameIntoClientArea.
   // Negative margins create the "sheet of glass" effect, where the client area
   //  is rendered as a solid surface with no window border.
   MARGINS margins = {-1};
   HRESULT hr = S_OK;
   // Extend frame across entire window.
   hr = DwmExtendFrameIntoClientArea(hwnd,&margins);
   assert (SUCCEEDED(hr)); hr;
}

Pretty easy I would say…

Multi-return and multi-assignment in TIScript.

October 31, 2009

Filed under: Sciter, Script, Web Application Techologies — Andrew @ 11:42 pm

As we know parameters of functions are passed by value in languages like TIScript and JavaScript. Inside the function we can modify paramaters and their new values will not be seen outside the function.

Let’s say we need to implement function expand(rect, dx, dy) : rect that should increase dimensions of the rectangle.

If the rect is an object then we will write something like this:

function expand(rect, dx, dy)
{
   return {
     x: rect.x - dx,
     y: rect.y - dy,
     w: rect.w + 2*dx,
     h: rect.h + 2*dy };
}

Problem with this implementation: it allocates brand new object on each call. That is not desirable if the function is used frequently.

If TIScript would support pass-by-reference for parameters then this function will have signature like this: inset(&x, &y, &w, &h, dx, dy) but no such thing as pass-by-reference in JS and TIScript for many good reasons.

As a solution: in TIScript we can use so called multi-return and multi-assignment feature – function is allowed to return multiple values.

Using this feature we can rewrite our function as:

function expand( x, y, w, h , dx, dy)
{
   return ( rect.x - dx, rect.y - dy, rect.w + 2*dx, rect.h + 2*dy );
          // returns "list" or tuple if you wish of four values.
}

To call such function and to get multiple values from it we will use list of values as an l-value expression:

var x = 10, y = 10, w = 100, h = 100;
(x,y,w,h) = expand(x,y,w,h, 20, 20 );

Parameters and return values are passed using stack of the VM so this will not create additional payload for the heap manager.

And yet it is pretty convenient for other cases too. For example this:

var a = 10, b = 5;
(a,b) = (b,a);

is how you can swap values of two variables.

Small feature but quite usefull.

Main challenge was to plug value lists into existing syntax of JavaScript. Implementation is quite simple.

Stringizer functions in TIScript

October 24, 2009

Filed under: Sciter, Script — Andrew @ 4:28 pm

Motivation.

Tasks of defining string expressions and textual fragments in script code are pretty common.

Typical example in JS/browser is:

someelement.innerHTML = 
"<span style=\"color:#ffffff\">" + somestuff + "</span>";

As you see this could be quite noisy and complex to be comprehensible by human.

Various scripting languages are trying to reduce syntax noise in various ways. Scala for example allows to define XML literals http://programming-scala.labs.oreilly.com/ch10.html inline without need of "mangling" it by quotes. This feature appears as quite neat but is limited by XML only. E.g. it will not work for HTML ("HTML is not XML"™) or for things like CSS selectors, etc.

Another example is string templates in Ruby. They allow to define string literals with embedded code:

template = 'Dear #{customer},\nPlease pay us #{amount} now.\n'

but this still needs string literal. One more problem with such approach: various types of strings need different escapement rules applied to values being embedded in the string. E.g. for HTML it should be String.htmlEscape() call, for URLs – String.urlEscape(), etc.

So there is a need to have some syntax construction that will allow to define various ways to define string compostions.

Solution.

As a solution I’ve implemented in TIScript so called stringizer functions.

Stringizer function is a function (or method) with the name starting from ‘$’ character and with variable number of parameters.

Call of such function is written in the form:

$name( ... some text here... )

Where the text inside ‘(’ and ‘)’ is not required to be enclosed in quotes. Characters ‘(’ and ‘)’ are serving role of quotes by themselves.

Example of such stringizer call:

var firstDiv = self.$( #content > div:first-child );

$ here a stringizer method that accepts CSS selector expressions. The sample above is a direct equivalent of this:

var firstDiv = self.select( "#content > div:first-child" );

Text inside ‘(’ and ‘)’ may contain expressions enclosed in curly brackets ‘{’ and ‘}’. Result of the expression in such brackets will be passed into the stringizer function as is.

Having all this in place the very first example of the article can be written in Sciter simply as:

someelement.html
    = $html( <span style="color:#ffffff">{ somestuff }</span> );

TIScript will de facto compile this call as:

someelement.html
    = $html( "<span style="color:#ffffff">",
               somestuff,
               "</span>" );

The only thing left is an implementation of the $html() function that we can define like this:

function $html(params..)
{
  for( var (n,v) in params )
    if( n & 1 ) // each odd paramter is a
                    // value of expression inside '{' and '}'
      params[n] = v.toHtmlString(); // calling method
                    // that will do HTML escapement
  return params.join(""); //return composed string
}

Some ideas

Here are some ideas for such stringizer functions we can use in the Sciter:

  • function Element.$append( ... inline html ... ) – method of the DOM element class – appends the html after last child of the element.
  • function Element.$prepend( ... inline html ... ) – prepends the html before first child of the element.
  • function $text( ... some text ... ) – simple way of composing texts.
  • function Stream.$print(... some text ... ) – simply prints text with possible { } inclusions.
  • function $xml( ... some xml... ) – to compose XML string and create XML tree using built-in XML parser.
  • you name it…

TIScript vs. JavaScript

February 25, 2009

Filed under: Sciter, Script — Andrew @ 7:12 pm

I have published article comparing TIScript and JavaScript on CodeProject. That is a “big picture” if you wish.

I have written this article completely in the ScApp (Sciter Application) named BlockNote for the Net:

Screenshot of BlockNote ScApp

Screenshot of BlockNote ScApp

It is a part of Sciter SDK and reside in sdk/scapps/blocknote.net/ folder.

Eventually this will replace outdated BlockNote v.1.8 application. Plan is to create platform for comfortable content editing in Internet. E.g. as an editor it is already more convenient than built-in editor of the WordPress application I am using here.

Next Page »