JavaScript. Private members (instance variables).

March 13, 2008

Filed under: Sciter,Script,Source code,Web Application Techologies — Andrew @ 1:28 pm

As you know JavaScript has no concept of private members in objects. Objects there are “racks of properties” that anyone can change.

Here is a simple way of making “objects” in JS that have private instance variables. Such variables can be changed only by [public] methods you provide.

Consider following code:

  function CreateMyObject()
  {
    var m_first = 0;
    var m_second = 0;

    var envelope = // our public interface
    {
        getFirst: function() { return m_first; },
        setFirst: function(v) { m_first = v; },
        getSecond: function() { return m_second; },
        setSecond: function(v) { m_second = v; }
    };
    return envelope;
  }  

  var f = CreateMyObject();

  var n = f.getFirst(); // calling our methods

Here we have CreateMyObject() that behaves as a constructor of our object. It returns envelope object that has four methods. Only these methods can update or retrieve state of m_first and m_second variables. There are literally no other ways to access them from anywhere else other than set of methods provided.

Under the hood: In this example each interface method that we’ve created is a closure in fact: such methods have access to environment of outer function (CreateMyObject in this case) so they can access variables in this environment. Making a closure can be a memory expensive operation so avoid their use when you expect many instances of such objects.

Sciter. Working with persistent data (database)

March 2, 2008

Filed under: Sciter,Script,Source code,Web Application Techologies — Andrew @ 9:08 pm

Here is an example of minimalistic application working with DB (persistent data) in Sciter.

Screenshot of DB form.

This sample is using three files:

  1. simple-db-form.htm – main file of our application;
  2. db.tis – open or create database;
  3. form.tis – behavior that handles showing/saving data from list of input elements on the form.

db.tis – open or create database

This file contains single procedure that will open or create new storage if does not exist:

function openDatabase(pathname = "records.db")
{
  var ndb = Storage.open(pathname);
  if(!ndb.root)
  {
    // new db, initialize structure
    ndb.root =
      { // root of our db is an object having single field 'records'
        records: ndb.createIndex(#integer) // main index, int -> item
      };
  }
  return ndb;
}

Structure of our storage is simple – its root node contains single field records of type Index. In our example this index maps integer – number of the record to the record itself – object that may have arbitrary structure.

form.tis – showing/saving data from input elements on a form

// Form handler class
type Form: Behavior
{
  function show(rec)
  {
    if(this.shown) this.save(); // save previously shown record.
    this.shown = rec;
    function load_value(el)
    {
      var name = el.attributes#name;
      el.value = rec[symbol(name)];
    }
    this.select(load_value, "[name]");
    // call load_value for each element having "name" defined.
    // De facto this means that form content defines structure of the record.
  }
  function save()
  {
    var shown = this.shown;
    function store_value(el)
    {
      var name = el.attributes#name;
      shown[symbol(name)] = el.value;
    }
    this.select(store_value, "[name]");
    // call store_value for each element having "name" defined.
  }
}

This behavior class has two methods:

  • show(rec) – show fields of object rec in fields of the form and
  • save() – stores content of input elements of the form in fields of last shown rec object

simple-db-form.htm – main file of our application

And last part is our main file that defines UI layout and assembles all parts together:

<html>
<head>
  <style>
    form#record
    {
      prototype:Form;
    }
    ...
  </style>
  <script type="text/tiscript">
    include "form.tis";
    include "db.tis";

    var form = self.select("form#record");
    var rec_no = self.select("#rec-no");

    var db = openDatabase();
    var no = 0;

    function gotoRecNo( n )
    {
      no = n;
      if( no < 0 ) no = 0;
      else if( no >= db.root.records.length ) no = db.root.records.length - 1;
      form.show( db.root.records[no] );
      rec_no.text = String.printf("%d of %d", no, db.root.records.length); rec_no.update();
    }

    function gotoNewRecord()
    {
      // create new record
      var newidx = db.root.records.length;
      db.root.records[ newidx ] = { first:0, second:"", third:"<html><\/html>" };
      gotoRecNo(newidx);
    }

    // handlers of navigatonal buttons
    function self #first .onClick() { gotoRecNo(0); }
    function self #prev  .onClick() { gotoRecNo(no - 1); }
    function self #next  .onClick() { gotoRecNo(no + 1); }
    function self #last  .onClick() { gotoRecNo(db.root.records.length - 1); }
    function self #new   .onClick() { gotoNewRecord(); }

    function view.onLoad()   { if(db.root.records.length) gotoRecNo(0);
                                        else gotoNewRecord(); }
    function view.onUnload() { form.save(); db.close(); }

  </script>
<head>
<body>
  <h1>Simple DB form</h1>
  <form #record>
    <table>
      <tr><td>First (number)</td><td><input name="first" type="number"/></td></tr>
      <tr><td>Second (text)</td><td><input name="second" type="text"/></td></tr>
      <tr><td>Third (richtext(html))</td><td><richtext name="third" /></td></tr>
    </table>
  </form>
  <div #nav>
    <button #first title="to first record">|<</button>
    <button #prev title="to prev record"><</button>
    <button #next title="to next record">></button>
    <button #last title="to last record">>|</button>
    <button #new title="create new record">new</button>
    record: <span #rec-no>?</span>
  </div>
</body>
</html>

Done. We have simple form that allows us to create records with text, numeric and richtext (html) fields.


Code above is not the best one from architectural point of view – its goal is to show basic principles, not more.
You can find this sample in [Sciter SDK]/samples/ideas/db/ folder.

Generator functions in Sciter and tiscript.

February 16, 2008

Filed under: Sciter,Script — Andrew @ 11:47 pm

Generator function is a function that produce sequence of values. Each call of such function returns next value of some sequence.

Here is one possible implementation of generator function that can be used in Sciter. Idea of this implementation is borrowed from LUA programming language.

Let’s say we would like to enumerate some array in backward direction using for( .. in .. ) statement:

var a = [0,1,2,3,4,5,6,7,8,9];

for(var n in backward(a))
    stdout.printf("%d\n", n);

Here is an implementation of such backward() function:

    function backward(a)
    {
      var n = a.length;
      return function() { if(--n >= 0) return a[n]; }
    }

As you may see this function initializes n variable and returns reference to the inner function.
This inner function will supply next value on each its invocation. In this example it will return a[--n] (previous element of the array) or nothing.
This implementation employs the fact that inner function has access to local variables of outer function – closure in other words.

To be able to support this in the language I have extended for(var el in collection) statement.
So collection object here can be function, array and object.

JavaScript functions, named parameters.

November 20, 2007

Filed under: Sciter,Script — Andrew @ 5:22 pm

JavaScript does not support named parameters. For example following will not work in JS:

  var something = foo( one: 1, two: "2" );

As a workaround people use functions that accept single parameter – object reference:

  var something = foo( { one: 1, two: "2" } );

but I think such notation is not good aesthetically speaking …

Problem is that classic JS supports only one form of function call :

  ref '(' list-of-values ')'

In TIScript/Sciter I decided to “fix” this by introducing second form of function call – call of function with passing object literal:

  ref object-literal

Where object-literal is a standard (for JS) object literal: '{' list-of-name-value-pairs '}'.

Thus example above can be rewritten as

  var something = foo { one: 1, two: 2 };

foo is a function here that accepts single parameter – parameter-object:

  function foo( prm ) { return prm.one + prm.two; }

Practical example:
When I need to create DOM object dynamically I can write now something like this:

          tab = Element.create { tag: "option", text: label, value: filename };

that is a bit shorter than:

          tab = new Element("option");
          tab.text = label;
          tab.attributes["value"] = filename;

To be able to create DOM elements this way I’ve extended Element class (DOM element in Sciter) by following function:

/*
  create element by definition (def)
  Example:

  var el = Element.create {
    tag:"p",
    text:"hello", class:"info", id:"id1"
  };
*/
function Element.create(def)
{
  var tag = def.tag; if(!tag) throw "No tag given!";
  var text = def.text || "";
  var el = new Element(tag, text);
  for (var k in def)
  {
    if( k == #tag || k == #text ) continue;
    if( k == #children )
    {
      var children = def.children;
      if( typeof children != #array) throw "'children' is not an array!";
      for (var c in children )
        el.insert( Element.create(c) );
      continue;
    }
    // others are just attributes
    el.attributes[k] = def[k];
  }
  return el;
}

and this is it.

Aesthetically scripting.

September 28, 2007

Filed under: Sciter,Script — Andrew @ 11:39 pm

Say you have some html document that has some elements with assigned IDs. I would like to find better notation for functions – event handlers in TIScript.

For example we have following html fragment:

<button id="foo">Click me!</button>

and we know that such button can generate onClick event (whatever it means).

There are multiple possible ways of how to do this in JavaScript. First of all standard way (that uses function $() – get element by selector)

  $("#foo").onClick = function() { ... }

This means: assign anonymous function (handler) to onClick property (event name) so system will call back that function when event will happen.

Internet Explorer (JScript) allows to do the same definition as:

  function foo::onClick() { ... }
  // or probably this way:
  function foo.onClick() { ... }
  // and in VBScipt
  sub foo_onClick() ... end sub

This is possible in IE because all elements that have IDs are seen as variables of global name space. That approach has some limitations: IDs in HTML can contain characters that are not valid in variable names, e.g. ‘-’ in #foo-bar.

Sciter/TIScript has symbols that allowed to use ‘-’ and all other characters allowed in IDs (html/xml):

  // for element with id="foo"
  function self#foo.onClick() { ... }
  // for element with id="foo-bar"
  function self#foo-bar.onClick() { ... }

self#foo-bar is a syntax sugar – equivalent of self["#foo-bar"]
Syntax is a bit “noisy” but works. Other variants that I’ve considered:


  function onClick of self#foo (...) { ... }

  when onClick on self#foo-bar  (...) { ... }

and other variations. I don’t really know what is better… Any ideas?

JavaScript closures

August 1, 2007

Filed under: Philosophy,Script,Web Application Techologies — Andrew @ 10:35 am

Found excellent article of Richard Cornford:
Javascript Closures

BTW: All principles there apply to TIScript too.

« Previous PageNext Page »