Scriptor

From Morfikwiki.com

Jump to: navigation, search

This is an appendix of the Morfik 07 Developer’s Handbook. This text provides is a general description of how complex behavior can be independently executed on the browser side, using Morfik Scriptor.

Contents


[edit] What is Morfik Scriptor?

The first thing you might wonder is, exactly what is Morfik Scriptor? The Scriptor, despite any ideas that the name might suggest, is just a utility built into the AppsBuilder IDE. It is basically, a script generator with which you build complex browser side code, in high level languages.

You must write all of the code by hand, just as you would if you were using JavaScript and you still need to have some knowledge of HTML and the Document Object Model (DOM). All your complex logic code is written in the four high level syntaxes that are supported by AppsBuilder, just as with a common AppsBuilder project, however a base HTML file is created for the project and you can directly alter it.

[edit] When to Use Scriptor?

There are two main reasons you might wish to use the Scriptor to create a project: to create a page you are going to use in conjunction with some pre-existing web site and will not be deploying a full Morfik built application or when your objective is to create a complex browser side script such as when implementing a math algorithm or game logic, instead of endeavoring to create a fully fledged application.

Such scripts can be run locally, without the requirement of having access to the server. It is important to note that due to browser architecture limitations such applications are not able to save any information on the user’s computer, except of course through the use of cookies.

[edit] Getting Started

The first step to creating a script with Morfik Scriptor is to choose New Project and select the "Intermediate" option. When this option is selected, as its third step the New Project Wizard will ask which kind of project should be created and the user can choose Scriptor. When you create a Scriptor project, at least two files are created, the project file and a base HTML file. If you chose to create a project in mxp format you will have additional files as the application’s modules are not embedded into the project file.

Figure 1 – The third step in the New Project Wizard (intermediate mode) offers the choice of creating a Scriptor project.

Listing 1 shows the very basic Scriptor project which adds a button to an empty HTML page.

Listing 1 – Main code module for a Scriptor project.

Program Project1;

Uses
    SystemDOM,
    SystemDOMHTML,
    SystemInterface,
    SystemUtilities;

Type
    TScriptApp = Class(TScriptInterface)
    Private
        Procedure ButtonClick(Event : TDOMEvent);
    Public
        Constructor Create; Override;
        Procedure  Initialize; Override;
        Destructor Destroy; Override;
    End;

Constructor TScriptApp.Create;
Begin
    Inherited Create;
End;

Procedure SetButtonType(button : THTML_InputElement); Javascript;
(*!
    button.type = "button";
*)

Procedure CreateButton(ID, Caption, left, width : string; ClickHandler : Pointer);
Var
    Button  : THTML_InputElement;
Begin
    Button := THTML_InputElement(Document.CreateElement('input'));
    Button.ID := ID;
    SetButtonType(Button);
    Button.Style.Position := 'absolute';
    Button.Style.left := left;
    Button.Style.top := '10px';
    Button.Style.width := width;
    Button.Style.height := '24px';
    Button.Value := Caption;
    THTML_ElementExt(Button).onclick := ClickHandler;
    Document.Body.appendChild(Button);
End;

Procedure TScriptApp.ButtonClick(Event : TDOMEvent);
Begin
    ShowMessage('Hello!');
End;

Procedure  TScriptApp.Initialize;
Begin
    CreateButton('Button_Hello', 'Hello', '100px', '80px', 
                 GetMethodPointer(Self, ButtonClick));
End;

Destructor TScriptApp.Destroy;
Begin
    Inherited Destroy;
End;

Var
    ScriptApp : TScriptApp;
Begin
    ScriptApp := TScriptApp.Create;
End.

Listing 2, which follows, shows the simple code in the base HTML file for this same project.

Listing 2 – Base HTML code for sample project.

<html>
  <head>
  </head>
  <script language="JavaScript" src="Project1.js">
  </script>
  <body style="margin:0;padding:0;overflow:auto;">
  </body>
</html>


As you can see the actual HTML code is quite small. In many cases, depending on what you aim to create, you might not need to touch it more than once or twice, concentrating on the programmatic aspects of your application. The sample page which results from the code in listing 1 and listing 2 can be seen, in Internet Explorer 7, Figure 1.

Figure 2 – Sample application displaying a "Hello" button.

If you look closely, in listing 1 you will find one procedure which is implemented as one line of JavaScript code, for the sake of expediency. If you do have knowledge of JavaScript programming and experience in developing web applications with that language, you can combine these skills with the power of a high level language to be even more productive.

[edit] Creating a Simple Calculator with Scriptor.

In this section we will see how to extend the sample shown previously in order to create a simple calculator.

A calculator is a fairly simple application which has several interface elements. Most elements of the interface are buttons but there is, also, a display. This should give the developer a nice idea of how to go about doing things in a Scriptor project as well provide some routines that might be reused in other projects.

Listing 3 shows the HTML source for this project. Notice that it is almost identical to the HTML code shown in Listing 2.

Listing 3 –HTML code for the Calculator project.

<html>
  <head>
  </head>
  <script language="JavaScript" src="Calculator.js">
  </script>
  <body style="margin:0;padding:0;overflow:auto;">
  </body>
</html>

This is, in essence, an empty HTML document. It is through the code of the calculator project that new elements will be added to this document, bringing to life a small calculator application.

Listing 4 shows the full source code for the calculator project in Morfik Pascal, including some comments to help identify the more relevant bits and pieces. We will go through the parts of the source code which are directly related to creating the interface elements in more detail, while just browsing over the calculator logic elements.

Listing 4 –Morfik Pascal source code for the Calculator project.

Program Calculator;

Uses
    SystemDOM,
    SystemDOMHTML,
    SystemInterface,
    SystemUtilities;

Type

    CalcOperations = (NoOp, AddOp, SubOp, MulOp, DivOp, EqOp);

    TScriptApp = Class(TScriptInterface)
    Private
        Value : Double;
        PreviousValue: Double;
        CurrentOp: CalcOperations;
        StartNew: boolean;
        DotEntered: boolean;
        procedure Reset;
        Procedure  ButtonClick(Event : TDOMEvent);
        procedure  InsertCharacter(Ch: String);
        Function   GetDisplayValue : String;
        Procedure  SetDisplayValue (s: String);
        procedure InvertSign;
        procedure StoreValue;
    Public
        Display : THTML_ElementExt;
        Constructor Create; Override;
        Procedure  Initialize; Override;
        Destructor Destroy; Override;
        Procedure ProcessOp;
    End;

Constructor TScriptApp.Create;
Begin
    Inherited Create;
    PreviousValue := 0;
End;

procedure TScriptApp.Reset;
begin
    Value := 0;
    DotEntered := false;
    StartNew := false;
    SetDisplayValue('0');
end;

Procedure SetButtonType(button : THTML_InputElement); Javascript;
(*!
    button.type = "button";
*)

(**
    Creates a button with the specified Id and caption with the specified
    Size, at the specified position assigning it an event handler for the
    OnClick event.
*)
Procedure CreateButton(ID, Caption, left, top,  width : string; 
                       ClickHandler : Pointer);
Var
    Button: THTML_InputElement;
Begin
    Button := THTML_InputElement(Document.CreateElement('input'));
    Button.ID := ID;
    SetButtonType(Button);
    Button.Style.Position := 'absolute';
    Button.Style.left := left;
    Button.Style.top := top;
    Button.Style.width := width;
    Button.Style.height := '24px';
    Button.Value := Caption;
    THTML_ElementExt(Button).onclick := ClickHandler;
    Document.Body.appendChild(Button);
End;

Function  Get_EventSource(Event : TDOMEvent) : THTML_ElementExt;
Begin
    If Assigned(Event) Then
        If Assigned(Event.target) Then
            Result := THTML_ElementExt(Event.target)
        Else
            Result := THTML_ElementExt(Event.srcElement);
End;


Procedure TScriptApp.InsertCharacter(Ch: String);
Var
    ValStr: String;
Begin
    If StartNew Then
        Reset;
    ValStr := GetDisplayValue;
    Value := StrToFloat(ValStr);
    If (Value <> 0) Then
        ValStr := ValStr + Ch
    Else
        ValStr := Ch;
    Value := StrToFloat(ValStr);
    SetDisplayValue(ValStr);
End;

Function TScriptApp.GetDisplayValue : String;
Begin
    Result := Display.innerHTML;
End;

Procedure TScriptApp.SetDisplayValue (s: String);
Begin
    Display.innerHTML := s;
End;

procedure TScriptApp.StoreValue;
Begin
    PreviousValue := StrToFloat(GetDisplayValue);
End;


procedure TScriptApp.InvertSign;
Begin
    Value := StrToFloat(GetDisplayValue);
    Value := Value *-1;
    SetDisplayValue(FloatToStr(Value));
End;

(**
    Processes the requested operation.
*)
Procedure TScriptApp.ProcessOp;
Begin
    Value := StrToFloat(GetDisplayValue);
    Case CurrentOp of
        AddOp: PreviousValue := Value + PreviousValue;
        SubOp: PreviousValue := PreviousValue- value;
        MulOp: PreviousValue := Value * PreviousValue;
        DivOp: PreviousValue := PreviousValue / Value;
    End;
    SetDisplayValue(FloatToStr(PreviousValue));
End;

(**
    Handles mouse clicks on all the calculator buttons
*)
Procedure TScriptApp.ButtonClick(Event : TDOMEvent);
Var
    aButton    : THTML_InputElement;
    DisplayVal : String;
Begin
    aButton := THTML_InputElement(Get_EventSource(Event));

    Case aButton.value of
      '1', '2',
      '3', '4',
      '5', '6',
      '7', '8',
      '9', '0': Begin
                    InsertCharacter(aButton.value);
                    StartNew := false;
                End;
      '=': ProcessOp;
      'C': Begin
               SetDisplayValue('0');
               PreviousValue  := 0;
               CurrentOp     := NoOp;
           End;
      'CE': Begin
                SetDisplayValue('0');
                CurrentOp := NoOp;
            End;

       '.': Begin
              If StartNew Then
                  Reset;
              If not DotEntered Then
              Begin
                  InsertCharacter(aButton.value);
                  DotEntered := true;
              End;
            End;

       '+/-': InvertSign;

       '+': Begin
                CurrentOp := AddOp;
                StartNew := True;
                StoreValue;
            End;

       '-': Begin
                CurrentOp := SubOp;
                StartNew := True;
                StoreValue;
            End;

       '*': Begin
                CurrentOp := MulOp;
                StartNew := True;
                StoreValue;
            End;

       '/': Begin
                CurrentOp := DivOp;
                StartNew := True;
                StoreValue;
            End;

       '<-': Begin
                 DisplayVal := GetDisplayValue;
                 If DisplayVal <> '0' Then
                 Begin
                     Delete(DisplayVal, Length(DisplayVal), 1);
                     If (Length(DisplayVal) = 0) or (DisplayVal = '-') Then
                         DisplayVal := '0';
                     SetDisplayValue(DisplayVal);
                 End;
             End;
    End;
End;

(**
  Initializes the HTML elements used in the calculator.
*)
Procedure  TScriptApp.Initialize;
Begin
    // Display rectangle
    Display := THTML_ElementExt(document.CreateElement('div'));
    Display.ID := 'Display';
    Display.style.top := '20px';
    Display.style.Left := '50px';
    Display.style.width := '240px';
    Display.style.height := '10px';
    Display.style.position := 'absolute';
    Display.style.textAlign := 'right';
    Display.style.backgroundColor := '#AAFFFF';
    Document.Body.appendChild(Display);
    Reset;
    // First row of buttons
    CreateButton('Button_1', '1', '50px', '54px', '40px',
                 GetMethodPointer(Self, ButtonClick));
    CreateButton('Button_2', '2', '100px', '54px', '40px',
                 GetMethodPointer(Self, ButtonClick));
    CreateButton('Button_3', '3', '150px', '54px', '40px',
                 GetMethodPointer(Self, ButtonClick));
    CreateButton('Button_Div', '/', '200px', '54px', '40px',
                 GetMethodPointer(Self, ButtonClick));
    CreateButton('Button_BkSpc', '<-', '250px', '54px', '40px',
                 GetMethodPointer(Self, ButtonClick));
    // Second row of buttons
    CreateButton('Button_4', '4', '50px', '90px', '40px',
                 GetMethodPointer(Self, ButtonClick));
    CreateButton('Button_5', '5', '100px', '90px', '40px',
                 GetMethodPointer(Self, ButtonClick));
    CreateButton('Button_6', '6', '150px', '90px', '40px',
                 GetMethodPointer(Self, ButtonClick));
    CreateButton('Button_Mul', '*', '200px', '90px', '40px',
                 GetMethodPointer(Self, ButtonClick));
    CreateButton('Button_CE', 'CE', '250px', '90px', '40px',
                 GetMethodPointer(Self, ButtonClick));
    // Third row of buttons
    CreateButton('Button_7', '7', '50px', '126px', '40px',
                 GetMethodPointer(Self, ButtonClick));
    CreateButton('Button_8', '8', '100px', '126px', '40px',
                 GetMethodPointer(Self, ButtonClick));
    CreateButton('Button_9', '9', '150px', '126px', '40px',
                 GetMethodPointer(Self, ButtonClick));
    CreateButton('Button_Sub', '-', '200px', '126px', '40px',
                 GetMethodPointer(Self, ButtonClick));
    CreateButton('Button_C', 'C', '250px', '126px', '40px',
                 GetMethodPointer(Self, ButtonClick));
    // Fourth Row of buttons
    CreateButton('Button_0', '0', '50px', '162px', '40px',
                 GetMethodPointer(Self, ButtonClick));
    CreateButton('Button_Sign', '+/-', '100px', '162px', '40px',
                 GetMethodPointer(Self, ButtonClick));
    CreateButton('Button_dot', '.', '150px', '162px', '40px',
                 GetMethodPointer(Self, ButtonClick));
    CreateButton('Button_Add', '+', '200px', '162px', '40px',
                 GetMethodPointer(Self, ButtonClick));
    CreateButton('Button_Equal', '=', '250px', '162px', '40px',
                 GetMethodPointer(Self, ButtonClick));
End;

Destructor TScriptApp.Destroy;
Begin
    Inherited Destroy;
End;


Var
    ScriptApp : TScriptApp;
Begin
    ScriptApp := TScriptApp.Create;
End.

[edit] Creating the Calculator Interface

Two routines from Listing 4 deserve our special attention as they are responsible for setting up the interface for the calculator. These routines are the method Initialize and the procedure CreateButton.

The Initialize method of the TScriptApp class sets up the display and all the buttons for the Calculator. Since there are many buttons to create in a calculator a special helper function called CreateButton was created.

If we look at the Initialize method, it starts by creating an element in the document’s DOM structure and assigning it to the Display member variable of the ScriptApp object. This element is the calculator’s display and the Display variable will be used by the GetDisplayValue and SetDisplayValue methods to read and update the display.

The following is a snippet from the start of the Initialize method. Note that the element is created as a "Div" and the resulting object is typecast into a THTML_ElementExt for storing in the Display variable.

    // Display rectangle
    Display := THTML_ElementExt(document.CreateElement('div'));
    Display.ID := 'Display';
    Display.style.top := '20px';
    Display.style.Left := '50px';
    Display.style.width := '240px';
    Display.style.height := '10px';
    Display.style.position := 'absolute';
    Display.style.textAlign := 'right';
    Display.style.backgroundColor := '#AAFFFF';
    Document.Body.appendChild(Display);

Several properties of the Display object are set in order to correctly size and position it previously to inserting it into the DOM tree of the document, which is done by the last line in this snippet.

Once the Display has been created the buttons are next and they are created through a call to the CreateButton helper function. The following snippet is the full declaration of the CreateButton procedure. Note that it is very similar in composition to the previous snippet.

Procedure CreateButton(ID, Caption, left, top,  width : string; 
                       ClickHandler : Pointer);
Var
    Button: THTML_InputElement;
Begin
    Button := THTML_InputElement(Document.CreateElement('input'));
    Button.ID := ID;
    SetButtonType(Button);
    Button.Style.Position := 'absolute';
    Button.Style.left := left;
    Button.Style.top := top;
    Button.Style.width := width;
    Button.Style.height := '24px';
    Button.Value := Caption;
    THTML_ElementExt(Button).onclick := ClickHandler;
    Document.Body.appendChild(Button);
End;

As you can see, here also, an element is created, has several of its properties set and is then inserted into the DOM tree of the document. The difference here is in the type of element which is created and the corresponding type with which it is typecast for manipulation in the code. In this case the element is typecast into a THTML_InputElement initially and then to THTML_ElementExt.

Notice that the CreateButton helper procedure is assigning to each button an event handler routine. In this case all buttons are being assigned the same event handler since a simple implementation allowed us to handle all buttons at once instead of having to write code for each one individually.

The following code snippet is the start of the ButtonClick method of the TScriptApp class, which handles the OnClick event for all the buttons in the calculator.

Procedure TScriptApp.ButtonClick(Event : TDOMEvent);
Var
    aButton    : THTML_InputElement;
    DisplayVal : String;
Begin
    aButton := THTML_InputElement(Get_EventSource(Event));

    Case aButton.value of
      '1', '2',
      '3', '4',
      '5', '6',
      '7', '8',
      '9', '0': Begin
                    InsertCharacter(aButton.value);
                    StartNew := false;
                End;
      '=': ProcessOp;

Note that in this snippet the first thing done is to obtain the object which originated the event. This is done through the use of a small helper function called Get_EventSource. Once the source of the event is obtained, and stored in a variable called aButton, it can be used to indentify which button was actually clicked. In this sample this is done through the use of the button’s caption as each button has a short caption which identifies its purpose/functionality. The rest of this method is basically a large case statement, the start of which can be seen in the snippet, which directs the appropriate handling of each button.

The Get_EventSource helper function is quite useful and can be used in other user created projects, just as the CreateButton procedure. The following code snippet shows the code for this function.

Function  Get_EventSource(Event : TDOMEvent) : THTML_ElementExt;
Begin
    If Assigned(Event) Then
        If Assigned(Event.target) Then
            Result := THTML_ElementExt(Event.target)
        Else
            Result := THTML_ElementExt(Event.srcElement);
End;

The rest of this small application, apart from two very small methods are related to the calculator’s logic and will not be discussed in detail here. The two other methods of interest are called GetDisplayValue and SetDisplayValue and are both one-liners. The following code snippet shows their implementation which is just a more convenient form of accessing the InnerHTML property of the Display member variable of the ScriptApp object.

Function TScriptApp.GetDisplayValue : String;
Begin
    Result := Display.innerHTML;
End;

Procedure TScriptApp.SetDisplayValue (s: String);
Begin
    Display.innerHTML := s;
End;

Figure 3 shows the calculator application running in Internet Explorer, after a few calculations have been performed.

Figure 3 – The Calculator application running within the browser.

The entire logic and interface definition of this application was created in a single file and except for a one line routine (which is being addressed in the next release of the AppsBuilder) all of the work has been done exclusively in Pascal. The same example can be implemented, with ease, in the other language syntaxes supported by the Morfik AppsBuilder.

[edit] Much More Complex Applications

Scripts a lot more complex than the simple calculator presented in this appendix can be created with Scriptor projects. Using the Scriptor, one can create extensions to existing web services provided by third parties, such as Salesforce.com S-Control Objects, to what are almost full fledged applications such as the Chess game for Apple’s iPhone, seen in Figure 4.

Figure 4 – A fully functional Chess game, totally created with Scriptor, by Morfik for iPhone users.

This game is a version of the original Morfik Chess game with some of the functionality removed (saving game statistics and other operations which require data storage). This should clearly illustrate the power and flexibility that is available through the use of Scriptor projects.

[edit] Wrapping it up

As seen in this appendix the Scriptor offers a powerful way of creating complex scripts for implementing sophisticated browser based algorithms. Code created with the Scriptor runs totally within the browser allowing you to create complex logic with the benefit of a high level, object oriented, language.

Personal tools