General Interface is an open source project hosted by the Dojo Foundation

GI Contributor Blog Blog

  2012/02/08
Creating Custom Component by extending existing class
Last Changed by Darren Hwang, Jul 13, 2012 09:58
Labels: custom, addin, component

Introduction

Creating a completely new component for GI can be a big task. However most of the difficulties lies in the need to provide GI Builder integration. User want to be able to drag and drop the new component just like any other existing ones. Without providing GI Builder integration, it's actually not that hard to create a new component as a GI Addin that can be reused across projects.

Custom Button

Here we have an example of a Round corner button that makes use of round corner background images. This "button" was created using JQuery and was meant to convert Anchor element with a given class (fvbutton) into a button.

The following function inserts 3 span elements (one for a left side round corner image, one for the middle background image x-repeated, and one for the right side round corner) for the given anchor elm JQuery element.


function buttonify(elm) {
  var label = elm.text();
  elm.html('<span class="left"></span><span class="center"><span>' + label + '</span></span><span class="right"></span>');
  var a = elm.filter("a");
  if (a.attr("href") == "" || a.attr("href") == "#")
    a.attr("href", "#" + label.replace(/[^w]/g, ""));

  var fct = elm[0].onclick;
  if (fct)
    elm[0].onclick = function(event) {
      if ($(this).hasClass("disabled"))
        return false;
      else
        return fct.call(this, event);
    };
  else 
    elm.click(function(e) {
      if ($(this).hasClass("disabled")) {
        e.stopPropagation();
        return false;
      }
    });

  // allow space key to invoke onclick action (like a submit button would do by default)
  elm.keydown(function(e) {
    if (e.keyCode == 32 && !e.shiftKey && !e.altKey && !e.ctrlKey)
      elm.click();
  });
}

Wrapping the custom button for GI

Since we have a basic button type in GI already it makes sense to extend this class and provide our own paint method, which is the function that generates the HTML to be inserted into the DOM for all jsx3.gui classes.

jsx3.require( "jsx3.gui.Block", "jsx3.gui.Form", "jsx3.gui.Button");

jsx3.Class.defineClass("pf.Button", jsx3.gui.Button, [jsx3.gui.Form], function(Button, Button_prototype) {
  
  var Block = jsx3.gui.Block,
      Form = jsx3.gui.Form,
      Interactive = jsx3.gui.Interactive;

  Button.DEFAULTCLASSNAME = "fvbutton";
  
  Button_prototype.paint = function() {
    this.applyDynamicProperties();

    var bEnabled = this.getEnabled() == Form.STATEENABLED;

    var strEvents = bEnabled ? this._renderHandler("click", "_ebClick") : "";
    //render custom attributes
    var strAttributes = this.renderAttributes(null, true);

    var attr = 'id="' + this.getId() + '"' + this.paintIndex() + (this.jsxtip ? ' title="' + this.jsxtip + '"' : '') +
        ' class="fvbutton' + (bEnabled ? "" : " disabled") + (this.jsxdefault ? " default" : "") + '"' + strEvents + strAttributes;
    var styles = this.paintDisplay() + this.paintMargin();
  
    return '<a href="#" ' + attr + ' style="' + styles + '">' + this.getText() + '</a>';
  };
      
  Button_prototype.onAfterPaint = function(objGUI) {
    buttonify($(objGUI));
  };
  
  Button_prototype.paintMargin = function() {
    var m = this.getMargin();
    return m ? "margin:" + m.replace(/\b(\d+)\b/g, "$1px") + ";" : "";
  };
  
  Button_prototype.paintDisplay = function() {
    //TO DO: the following logic is wrong...change force explicit none???
    var d = this.getDisplay();
    return (jsx3.util.strEmpty(d) || d == Block.DISPLAYBLOCK) ? "" : "display:none;";
  };
  
  Button_prototype.paintIndex = function(intIndex) {
    if (intIndex == null) intIndex = this.getIndex();
    // HACK: see IE's jsx3.html.isFocusable()
    return (intIndex != null && this.jsxenabled != 0) ? ' tabindex="' + intIndex + '" jsxtabindex="' + intIndex + '"' : '';    
  };

 Button_prototype._ebClick = function(objEvent, objGUI) {
    if (objEvent.leftButton() && objEvent.isMouseEvent())
      this.doEvent(Interactive.EXECUTE, {objEVENT:objEvent});
  };
  
  Button_prototype.doExecute = function(objEvent) {
    this._ebClick(objEvent);
  };
   
  Button_prototype._renderHandler = function(eventType, strMethod) {
    var eventHandler = "on" + eventType;
    var bridgeEvent = "jsx3." + Interactive._EB + "(event,this,'" + strMethod + "',0);";
    return " " + eventHandler + '="' + bridgeEvent + '"';
  };

});

Since the JQuery version of the button uses a custom CSS and image files, we will need to provide them in our project.

Making it into a shareable Addin

To make it into an Addin for GI we need to change the project config.xml file slightly to give it the Addin properties including : name, description, long_description, namespace, and addin.

More information here:
Deploying the custom Add-in

<?xml version="1.0" encoding="utf-8"?>
<data>
  <record jsxid="version" type="string">1.0</record>
  <record jsxid="jsxversion" type="string">3.9</record>
  <record jsxid="id" type="string">pf.Button</record>
  <record jsxid="name" type="string">pf.Button</record>
  <record jsxid="description" type="string">Round corner button</record>
  <record jsxid="long_description" type="string">This is an addin with round corner button</record>
  <record jsxid="namespace" type="string">pf.APP</record>
  <record jsxid="addin" type="string">pf.APP</record>
  <record jsxid="includes" type="array">
    <record jsxid="1" type="map">
      <record jsxid="id" type="string">logic_js</record>
      <record jsxid="type" type="string">script</record>
      <record jsxid="load" type="number">1</record>
      <record jsxid="src" type="string">js/jquery-1.7.1-min.js</record>
    </record>
    <record jsxid="2" type="map">
      <record jsxid="id" type="string">pfbutton_js</record>
      <record jsxid="type" type="string">script</record>
      <record jsxid="load" type="number">1</record>
      <record jsxid="src" type="string">js/pfbutton.js</record>
    </record>
    <record jsxid="3" type="map">
      <record jsxid="id" type="string">button_css</record>
      <record jsxid="type" type="string">css</record>
      <record jsxid="load" type="number">1</record>
      <record jsxid="src" type="string">jss/button.css</record>
    </record>
  </record>
  <record jsxid="bodyhotkeys" type="boolean">false</record>
  <record jsxid="onunload" type="string"/>
</data>

Adding the component

Since we are sub-classing jsx3.gui.Button, we save some effort in creating the palette model and event palette model that would be needed to drag and drop the custom component. Instead we can drop in a regular jsx3.gui.Button, then go to the component XML editor and change the object type to "pf.Button".

Download the Addin

Download it here

Unzip the content to your "workspace/Addins" directory

Posted at 08 Feb @ 12:17 PM by Darren Hwang | 0 Comments
  2011/06/27
Running GI Builder on Firefox 5
Last Changed by Darren Hwang, Oct 04, 2011 11:06
Labels: firefox, builder

GI 3.9.1 does not officially support Firefox 5. [*Edit* _This applies to Firefox 5+ (5,6,7, and probably 8+_)] However...

As far as we know GI 3.9.1 does run on Firefox 5. However there's been report of issue with GI Builder that turn out to be related to the old same origin policy issue. To run GI Builder on Firefox 5, you must make sure that the security.fileuri.strict_origin_policy flag is set to false in the about:config tab.

NOTE
Only change this flag on your GI Firefox profile and not your web-browsing default Firefox profile. For instruction on how to create your custom GI Builder profile, See http://support.mozilla.com/en-US/kb/Profiles

You will no longer be able to run webservice call directly from inside Builder due to this change in Firefox 5.

See the security note https://developer.mozilla.org/En/Using_XMLHttpRequest#Security

Firefox 5 note
Versions of Firefox prior to Firefox 5 could use netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead"); to request cross-site access. This is no longer supported, even though it produces no warning and permission dialog is still presented.

The recommended way to enable cross-site scripting is to use the Access-Control-Allow-Origin HTTP header in the response to the XMLHttpRequest.

Posted at 27 Jun @ 11:22 AM by Darren Hwang | 0 Comments
  2011/04/01
Running GI 3.9.0 on Firefox 4
Last Changed by Darren Hwang, Apr 27, 2011 18:27
Labels: firefox

While you can run GI 3.9 on Firefox 4, you must get pass the browser support warning dialog. As far as we can tell, there is no compatibility issue for GI 3.9 and Firefox 4. So, you can get pass this warning dialog by adding a deployment parameter

Deployment Parameters

jsx_browsers. Overrides the default set of supported browsers. The format of this parameter is

bt={allow,warn}[,...]

where bt is the browser type returned by the getType() method in the jsx3.lang.ClassLoader class. See jsx3.lang.ClassLoader in General Interface API Reference.

allow launches the application in the browser.
warn provides a warning for unsupported browsers and allows the user to proceed

For example, to show a warning in Internet Explorer 6 and allow Firefox 4, modify the application launch page as follows:

jsx_browsers="ie6=warn,fx4=allow"
 document.write('<script type="text/javascript" src="JSX/js/JSX30.js"
 jsxapppath="' + appPath + '" jsx_browsers="ie6=warn,fx4=allow" ><'+'/script> ');

The next version of GI should have Firefox 4 support enabled by default, so you will not need to do this. However, if you can't wait this is how you do it.

Posted at 01 Apr @ 11:43 AM by Darren Hwang | 1 Comment
  2011/02/16
Running GI App from file url on Google Chrome
Last Changed by Darren Hwang, Apr 01, 2011 11:42
Labels: crossdomain, cross-origin, google-chrome, file-url

You get a strange error on Google chrome when launching GI application from file URL (when you open the launch.html directly from filesystem)
that says something like

"Invalid character at column 1 character 1 of logger.xml"

If you open the Developer Tool and look at the Console output you will see that it is throwing all these Error for each XML file resource request by GI

"Cross origin requests are only supported for HTTP."

Turns out that similar to Firefox/Mozilla strict origin policy Google Chrome doesn't allow you to load resource from different paths

So for test purpose you can launch Chrome with this flag to get around this restriction

google-chrome --allow-file-access-from-files

or

chrome.exe --allow-file-access-from-files

Posted at 16 Feb @ 5:02 PM by Darren Hwang | 3 Comments
  2011/01/19
Displaying the select edit mask with drop down indicator on Matrix column
Last Changed by Darren Hwang, Mar 21, 2011 18:52
Labels: matrix, editmask

This format handler will style a Select mask column with the down arrow indicator. This replaces the default @lookup format handler for a select mask column

    var selectFormat = function(objDiv, strCDFKey, objMatrix, objMatrixColumn, intRowNumber, objServer) {
        var objMask = objMatrixColumn.getEditMask();
        if (objMask != null && typeof(objMask.getRecordNode) == "function") {
          var objNode = objMatrix.getRecordNode(strCDFKey);
          if (objNode) {
            var strValue = objMatrixColumn.getValueForRecord(strCDFKey);
            var lookupNode = objMask.getRecordNode(strValue);
            objDiv.innerHTML = jsx3.util.strEscapeHTML(
                lookupNode ? lookupNode.getAttribute("jsxtext") : (strValue != null ? strValue : ""));
          }
          objDiv.style.backgroundImage = "URL(JSX/images/select/arrowmask.gif)";
          objDiv.style.backgroundRepeat= "no-repeat";
          objDiv.style.backgroundPosition = "top right";
        }
      };

Note:

Using Format Handler is very Javascript intensive, especially for large number of rows(and or columns). Do not use this if you need to support IE6/7. You've been warned.

http://www.generalinterface.org/docs/display/GIDOCS/Content+Formatting+and+Localization#ContentFormattingandLocalization-ww1852740

Posted at 19 Jan @ 11:24 AM by Darren Hwang | 0 Comments
  2011/01/18
Using onBeforeDeserialize and onAfterDeserialize
Last Changed by Darren Hwang, Feb 02, 2011 23:24

Question came up on the forum regarding how to auto-populate a selection/combo control. The idea is to trigger a service request that will fetch the content for the select control.

To make use of onAfterDeserialize more "granular"

  1. ctrl+n to create a new canvas
  2. go back to the original canvas with your component
  3. open the Component Hiearchy palette and locate your component
  4. drag your component to the new canvas tab (the [ unsaved ] tab)
    The [ unsaved ] tab will show at this point
  5. drop your component into the new canvas
  6. save this new cavas containing only a single component as its own autoPopulateSelect.xml file
  7. enter your custom onBeforeDeserialize/onAfterDeserialize code, save and reload.

You've just created a single component serialization file with its own onBeforeDeserialize / onAfterDeserialize logic
To use this new custom component file

  1. Select the container block in original canvas in the Component Hiearchy palette and right click
  2. Choose Import Asynch to load the autoPopulateSelect.xml
About onAfterDeserialize and onBeforeDeserialize
Edit added before/after code. onAfterDeserialize is safer, but on onBeforeDeserialize is useful for things like manipulating the serialization file content before passing it to be rendered which is faster than modifying things on screen after the fact.
On Component Ready
Sometimes the component you are trying to access may not be ready to be manipulated inside onAfterDeserialize. Component such as Matrix control uses the GI asynch queue to provide better responsiveness in its operations. This can cause the component to be unavailable. In such case, you can place your code inside a jsx3.sleep() call to add it to the asynch queue and be called when it is ready.

Configuring the initial state of components is one of the use case for using onBeforeDesrialize. By modifying the serialization file content before the compoents are actually rendered. This helps optimize load performance by avoiding a double "paint" that can happen when using onAfterDeserialize, which re-render(repaint) the component after it's already rendered.

The following sample code will prompt you to choose an initial state of enabled=1 or disabled=0 for a Select/Combo control

<serialization jsxversion="3.9">
<name>
Layout - Top/Over
</name>
<icon>
images/prototypes/layout-over.gif
</icon>
<description>
2 top-over-bottom panes in an adaptive layout grid.
</description>
<onBeforeDeserialize>
var t = prompt(&quot;enable = 1, disable = 0&quot;); jsx3.log(t); var node = objXML.selectSingleNode(&quot;//jsx1:object[@type='jsx3.gui.Select']/jsx1:variants&quot; ); node.setAttribute(&quot;jsxenabled&quot;, &quot;&quot;+t);
</onBeforeDeserialize>
<onAfterDeserialize>
</onAfterDeserialize>
<object type="jsx3.gui.LayoutGrid">
<variants jsxrelativeposition="0" jsxleft="0" jsxtop="0" jsxoverflow="2"/>
<strings jsxname="layoutRows" jsxrows="100,*" jsxwidth="100%" jsxheight="100%"/>
<object type="jsx3.gui.Block">
<variants jsxoverflow="2"/>
<strings jsxname="pane1" jsxwidth="100%" jsxheight="100%"/>
<dynamics jsxborder="@Outset"/>
<object type="jsx3.gui.Select">
<variants jsxenabled="1" jsxwidth="150" jsxheight="18" jsxtype="1" jsxxmlasync="1"/>
<strings jsxname="combo" jsxxmlurl="jsx:///xml/sample.xml" jsxmargin="0 4 0 0" jsxdefaulttext="Select" jsxvalue="" jsxdisable=""/>
<events jsxchange="var x = this.getXML(); "/>
</object>
</object>
<object type="jsx3.gui.Block">
<variants jsxoverflow="1"/>
<strings jsxname="pane2" jsxwidth="100%" jsxheight="100%"/>
<dynamics jsxborder="@Outset"/>
</object>
</object>
</serialization>
Posted at 18 Jan @ 4:56 PM by Darren Hwang | 0 Comments
Custom Matrix column using column formatter
Last Changed by Darren Hwang, Jan 18, 2011 17:28
Labels: matrix

Usage scenario

We have a requirement to display 3 different type of image buttons under a single column. Normally, you can only use one edit mask control under a column. So you cannot specify 3 different image button under a single column. This is one of the use case where custom column formatter comes in handy.

By using a custom column formatter we can generate 3 different type of image button depending on a record attribute (status).

Matrix Custom Format Handler
  instance.formatScheduleButton = function (objDiv, strCDFKey, objMatrix, objMatrixColumn, intRowNumber, objServer) {
    var status = objMatrix.getRecordNode(strCDFKey).getAttribute("status");

    var action = "Publish";
    if (status) {
      if (status == "started") {
        action = "Stop";
      } else if (status == "stopped") {
        action = "Start";
      }
    }
    var objPL = objMatrix.getAncestorOfType(pf.ProjectList);
    var buttonImage = KLASS.IMAGE_BUTTON_PATH + "Button_" + action + KLASS.IMAGE_EXT ;
    var sGetter = "jsx3.GO('" + objPL.getId() + "')";
    var handlers = " onmouseover=\"" + sGetter + ".onImageMouseover(event, this, '" + action + "')\" " +
                   " onmouseout=\"" + sGetter + ".onImageMouseout(event, this, '" + action + "')\" " +
                   " onmousedown=\"" + sGetter + ".onImageMousedown(event, this, '" + action + "')\" " +
                   " onclick=\"" + sGetter + ".onImageClick(event, this, '" + strCDFKey + "', '" + action + "')\" ";

    var strImageButton = "<span id='pfib_" + strCDFKey + "' " + " class='jsx30imagebutton' " + handlers + ">"
        + "<img width='80' height='22' src='" + objPL.getPlugIn().resolveURI(buttonImage) + "'/>" + "</span>";

    //jsx3.log(strImageButton);
    objDiv.innerHTML = strImageButton;

  };

The onmouseover, onmouseout and onmousedown DOM event handler are implemented to display the HOVER, Normal and DOWN images for each 3 types of buttons. onclick is the click event handler and there we decouple the view component from the model by using custom event publishing

Image button event handlers
  instance.onImageMouseover = function(evt, elm, action, imgExt) {
    var ext = (!imgExt) ? KLASS.IMAGE_EXT : imgExt;
    var imagePath = KLASS.IMAGE_BUTTON_PATH + "Button_" + action + "_rollover" + ext;
    elm.childNodes[0].src = this.getPlugIn().resolveURI(imagePath);
  };


  instance.onImageMouseout = function(evt, elm, action, imgExt) {
    var ext = (!imgExt) ? KLASS.IMAGE_EXT : imgExt;
    var imagePath = KLASS.IMAGE_BUTTON_PATH + "Button_" + action + ext;
    elm.childNodes[0].src = this.getPlugIn().resolveURI(imagePath);
  };


  instance.onImageMousedown = function(evt, elm, action, imgExt) {
    var ext = (!imgExt) ? KLASS.IMAGE_EXT : imgExt;
    var imagePath = KLASS.IMAGE_BUTTON_PATH + "Button_" + action + "_down" + ext;
    elm.childNodes[0].src = this.getPlugIn().resolveURI(imagePath);
  };


  instance.onImageClick = function(evt, elm, project, action) {
    LOG.debug("action=" + action + ", project=" + project);
    if (action == "Publish")
      this.getServer().publish({subject:"project.list.publish", name: project, status:action});
    else
      this.getServer().publish({subject:"project.list.schedule", name: project, status:action});
  };
Posted at 18 Jan @ 4:54 PM by Darren Hwang | 0 Comments
  2010/04/26
GI Build Tools - Creating a Build Optimized for Your App
Last Changed by Jesse Costello-Good, Apr 27, 2010 15:23

Following up on my earlier post I want to go into much more detail on creating a build of GI that is optimized for a particular GI application. We highly recommend that you create an optimized build of GI for your critical applications. Creating an optimized build can dramatically shorten the initial load time of your application. I've seen 20 second load times reduced to 6 seconds just by deploying an optimized build.

Background

In the directory on your computer where you installed GI you have several things. The "GI runtime" consists of JSX/, jsx3.gui.window.* and logger.xml. The GI runtime is the platform that a GI application runs on. GI Builder is one such application that runs on the GI runtime. GI Builder consists of the files GI_Builder/, GI_Builder.* and shell.* from the GI installation directory.

When you deploy an application you copy the GI runtime and your application resources to a server. Your application runs on this copy of the GI runtime. If you copy the runtime from the GI installation directory then your application is running on the same runtime as is GI Builder when you run GI Builder on your computer.

There's no reason that GI Builder and your application have to run on the exact same runtime. In fact there are many reasons why you would want to have them running on different runtimes. For example, you probably want to run GI Builder on the "debug"
runtime so that you can see more warnings while you are developing your application. But when you deploy your application you want it to run on a runtime that is as small and fast as possible.

This article will show you how to compile the best runtime for your deployed application. This is the runtime that you will want to upload with your application when you deploy it on a public HTTP server.

Load Performance

Load performance in a web application running over HTTP comes down to a few simple principles:

  1. Load as little as possible
  2. Load what you need in as few HTTP requests as possible
  3. If requesting multiple resources, load them in parallel as much as possible

Recent versions of GI are good at loading runtime and application resources in parallel so you don't have to worry too much about the third point. Creating an optimized runtime build will take care of the first two points.

Sometimes points one and two conflict with each other. For example, part of your application might use jsx3.gui.Matrix but that part is not commonly used. Should you reduce the number of requests by merging jsx3.gui.Matrix with the set of required classes? Or should you load as little as possible up front and load Matrix as needed? There is some art as well as science to resolving these decisions. My usual suggestion is to merge all resources that must load for your application to reach its first interactive state, and nothing else.

Determining What Classes to Load

The GI runtime consists of a number of required classes (e.g. jsx3.lang.*, jsx3.app.*) and many optional classes (e.g. most of jsx3.gui.*). To create the optimal build for your application you need to determine which optional classes are used by your application. To do this, follow these steps:

  1. Make sure you are running Builder on either the source distribution of GI or the standard or debug packages. Running on the max build won't work for these instructions.
  2. Open your application in Builder running on Firefox with the Firebug extension installed, IE8 or Safari 4.
  3. Make sure that your logger.xml contains the following lines:
    <handler name="console" class="jsx3.util.Logger.ConsoleHandler"/>
    ...
    <logger name="bench.jsx3.lang.ClassLoader" level="TRACE"/>
  4. Launch your application from Builder
  5. Open your browser's JavaScript console for the window that contains your running application. You may have to reload your application after you open the console.
  6. Wait for your application to reach its first interactive state, such as the home page or login screen.

At this point you should see a bunch of log messages in the JavaScript console like

2010-04-27 14:18:11.778 bench.jsx3.lang.ClassLoader (INFO) - JSX/js/jsx3/gui/Button.js : js.eval : 10ms

These are the optional GI classes that your application is loading before it reaches its first interactive state. All of these classes should be included in the build of GI optimized for your application. Make a note of the full list of classes. We'll use this list in the next step.

Configuring and Running the Build

Make sure you are set up to make custom builds of GI as described in my earlier post. Create a new properties file in GIHOME/build and call it something like app1-build.properties. This file will contain the parameters for the build optimized for your "app1" application.

Into this properties file add the list of classes you saw in the JavaScript console. So if the entire list of classes was jsx3.gui.Button and jsx3.gui.CheckBox your properties file would like like:

build.gi.includes.jsx = default,jsx3.gui.Button,jsx3.gui.CheckBox

Since you don't need to build Builder or the documentation you can add these lines to speed up the build.

build.gi.includes.jsx = default,jsx3.gui.Button,jsx3.gui.CheckBox

build.gi.docs = false
build.gi.docs.html = false
build.gi.ide = false

To create the build run the following commands.

$ cd GIHOME/build
$ ant clean; ant -propertyfile app1-build.properties

Once you have the build you should run your application on this build. To do this create a launch page with Builder and then hand edit it so that it points to the JSX30.js from your custom build. Make sure to copy the logger.xml file from your Builder installation to the new runtime.

This time when you run your application you should not see any logging messages with subject bench.jsx3.lang.ClassLoader. Repeat the steps in Determining What Classes to Load to make sure that all optional classes that your application uses are included in the optimized build. It may take your several iterations to finalize the list.

Once you have a fully optimized build simply deploy it along with your application to your web host. All the optional classes that your application uses will be baked into jsx.js, meaning that many fewer JavaScript resources must load when your application loads.

Congratulations, you have created an optimized runtime build for your GI application. Remember that every GI application has a unique load profile. You may want to create an optimized build for each of your applications or, if they are similar enough, create just one optimized build. The advantage of having just one build for multiple GI applications is that the runtime will be in the browser cache when the user visits the second and subsequent applications.

Additional Deployment Parameters Affecting Load Time

Here are a few more things you can do to speed up the loading of your application.

Don't load logger.xml

If you are not going to be monitoring the runtime logs of your application you can opt not to load logger.xml. It's just one small file but when your application is loading over HTTP every resource counts. Modify your launch page as follows.

<script src="gi/JSX/js/JSX30.js" jsx_logger_config=""></script>

Don't load messages.xml

The file messages.xml contains the English translations of all system error messages. If you do not load messages.xml you still see errors in the logs but instead of a nice English message you just see a string key. Again, it saves you just one file.

<script src="gi/JSX/js/JSX30.js" jsx_no_messages="true"></script>

Don't load locale.xml

The file locale.xml is required if you are using DateFormat, NumberFormat, DatePicker, TimePicker, Select or any of the Cacheable controls with XML Async = true. If you use none of these you can safely prevent locale.xml from being loaded.

<script src="gi/JSX/js/JSX30.js" jsx_no_locale="true"></script>
Posted at 26 Apr @ 3:42 PM by Jesse Costello-Good | 0 Comments
GI Build Tools - Introduction and Set Up
Last Changed by Jesse Costello-Good, Apr 26, 2010 15:31

Introduction

GI developers who want to create high performance GI deployments should take the time to understand and use the suite of build tools that are part of the open source GI project. Only by leveraging the GI build tools will you be able to create applications with the shortest load times and fastest code.

With the source distribution and the build tools you can:

  • Create a build of GI with a load profile optimized for a specific GI application
  • Compile your own API documentation
  • Create an un-obfuscated build of GI for easier debugging
  • Obfuscate, merge and optimize the JavaScript code in your own application
  • Create a build of GI or your own application with a default locale other than English
  • Conditionally include or exclude blocks of JavaScript code
  • Optimize your AMP based application
  • Create a completely automated build and deployment work flow

I'll try to cover most of these use cases in subsequent posts. First, we need to get set up with the GI source code and build tools.

Set up

GI has an Ant-based build process so you need to have a few things installed on your computer to use the build tools:

Ant works on pretty much any operating system. I use Mac OS X Terminal so my examples will look like a Linux prompt. If you are using Windows I recommend installing Cygwin.

Once you have Java and Ant installed you need to get a source distribution of GI. You can download the source distribution of the latest release or grab the code from SVN.

If you are using the latest release ZIP, unzip the contents of the archive. I'll refer to this directory as GIHOME (GIHOME contains logger.xml, JSX/ and several other files).

Your first GI build

The build tools, scripts and configuration files are all contained in the build/ directory. To make your first build, go to that directory.

$ cd GIHOME/build

To make a build of GI with all of the default settings simply invoke Ant.

$ ant
If you see many warnings like "Warning: The encoding 'UTF-8' is not supported by the Java runtime" your Java set up has a XML library that conflicts with the library that GI uses. As a work around always invoke Ant as ant -lib tools/lib/xml/xalan-serializer-2.7.jar.
You may get an out of memory error with Ant's default Java settings. You will probably need to increase Ant's maximum heap size to 512 MB. On most systems you can do this by setting the ANT_OPTS environment variable to -Xmx512m.

The full build process takes about 90 seconds on my machine with the default settings. Later, I'll describe some ways of making the build even faster.

Now, if you look in GIHome/dist/gi you'll see the build of GI that you just created. Open GI_Builder.html in a browser to verify that the build was successful.

Modifying the default build settings

All build settings are contained in the file GIHome/build/build.properties. There are two recommended ways of overriding these default settings. The first is on the command line and the second is with the user.properties file.

To modify a property on the command line when you invoke Ant, add -DpropName=propValue to the command invocation. See the Ant documentation for more information.

To modify a property semi-permanently so that you don't have to add it to the command line each time you invoke Ant, create a file GIHome/build/user.properties. Any properties defined in this file take precedence over those in build.properties.

Don't modify build.properties yourself as this file is owned and maintained by the GI distribution and often changes between GI releases.

As a first example, let's make a build of GI where the jsx3.gui.Matrix class is included in the load profile. Including Matrix in the load profile will speed up the load time of applications that use Matrix. First, invoke the clean target in Ant. This step is usually necessary to prevent errors.

$ ant clean

Next, invoke the default target while overriding the build.gi.includes.jsx build property.

$ ant -Dbuild.gi.includes.jsx=jsx3.gui.Matrix

(To include multiple classes format them as a comma-delimited list.) If you check the build you will see that the Matrix.js file does not exist, meaning that it has been baked into jsx.js, which defines the load profile.

$ ls -la ../dist/gi/JSX/js/jsx3/gui
...
-rw-r--r--   1 jesse  staff  28495 Apr 26 13:55 List.js
-rw-r--r--   1 jesse  staff  28144 Apr 26 13:55 Menu.js
...

Making the build process faster

You can make the build process faster by having it do less. Often when creating a custom build of GI you do not need to compile the API documentation or Builder documentation. To remove these steps from the build process add the following to your user.properties.

build.gi.docs = false
build.gi.docs.html = false

If you don't need to create an internationalized version of GI add the following, which excludes all locales other than en_US.

build.locales.jsx = en, en_US

If you are creating a build of GI for deployment only and not for running Builder, you can exclude Builder from the build by adding the following.

build.gi.ide = false

If I include all these overrides I can get the build time down to about 20 seconds.

More to come

I plan to go into much more detail describing all the advanced things you can accomplish with the GI build tools. Watch this blog for updates.

Posted at 26 Apr @ 1:06 PM by Jesse Costello-Good | 0 Comments
  2010/04/15
Stopping the repetitious security prompt on Firefox GI Builder
Last Changed by Darren Hwang, Jul 16, 2010 18:10
Labels: firefox, builder, strict, origin, policy, security, prompt
Why do I keep getting prompt for "Script from file:// is requesting enhanced abilities that are UNSAFE and could be used to compromise your machine or data."?

GI Builder requires access to the file system, enhanced abilities, to save the files created in a project.

The reason you are repeatly asked even thought you selected the "Remember this decision" checkbox is because there are multiple javascript file that requires enhanced abilities. In Firefox 3, files residing in different folders are consider to be of different origin by the strict origin policy. However the security prompt applies to the entire "file://" URL, which is not accurate. So you get prompted every time you start builder.

Solution

For the security minded, you should create a new developer-only Firefox profile and make the following configurations in this new profile. Security minded users should not use your default profile for this.

First you must have the security.fileuri.strict_origin_policy property disabled by setting it to false. You can do this by going to about:config and add/edit this property.

Next, by selecting "Allow" in the security prompt you create the following settings in the pref.js file

Alternate method
You can also edit Pref.js (or create a User.js, which is the Mozilla recommended way) found under "<user_home>/Application Data/Mozilla/Firefox/Profiles" as shown below.

Add these settings if they do not exist; if these settings exist, edit them as follows:

user_pref("security.fileuri.strict_origin_policy", false);
user_pref("capability.principal.codebase.p0.granted", "UniversalXPConnect UniversalBrowserRead");
user_pref("capability.principal.codebase.p0.id", "file://");
user_pref("capability.principal.codebase.p0.subjectName","");
Posted at 15 Apr @ 1:24 PM by Darren Hwang | 0 Comments
  2010/03/18
Disabling edit mask controls in Matrix
Last Changed by Darren Hwang, Apr 15, 2010 13:23
Labels: matrix, disable, edit, mask

First there are two types of edit masks in Matrix (editable grid Matrix control).

The first type is the always-on edit mask such as : button, checkbox, radio button, etc. An always-on edit mask is always displayed without user action.

The second type is the non-always-on edit mask such as : textbox, select, datepicker, etc. These editmask are display only when user focus on the cell.

Always on edit masks

Always on edit mask, such as checkbox and button, are disabled by the jsxdisabled="1" in the CDF record E.g.

  <data jsxid="jsxroot">
  <record jsxid="WEBM" jsxtext="WEBM" open="10" close="1" jsxdisabled="1"/>
  </data>
Non-always on edit masks

Disabling the Non-always-on edit masks means preventing the cell focus from displaying the mask control. You can do so by adding code logic to Before Edit event handler to evaluate to false. For example, set the event property "Before Edit" with following code fragment (follwing sample disables the non-always-on edit mask ,such select/combo.

 if (objCOLUMN.child(0).instanceOf(jsx3.gui.Select)) false;

To disable both the non-always-on and always-on edit mask on the same row and thus disabling the entire row, you can take advantage of the jsxdisalbed record property and use that as the condition. Add the following to the Before Edit event

 if (this.getRecord(strRECORDID).jsxdisabled == 1) false;

Non-always-on edit mask of an entire column can be disabled by setting event property Before Edit (Following code disables the "comboColumn" by evaluating to false in Before Edit event handler).

These sample code fragment goes into the event handler it self, but the code fragment is not part of a function, so should not use return statement. It should simply evaluate to true or false.
if (objCOLUMN.getName() == "comboColumn") 
  false;
else {
  jsx3.log('EVENT: (jsxbeforeedit). Record: ' + strRECORDID);
 }

Follow similar logic to disable edit mask of a cell based on different factors such as the value of the another column. In "Befor Edit" event property, this code

 if (this.getRecord(strRECORDID).status == "Deployed" 
    && objCOLUMN.getName() == "selectMachine" ) 
  false;

will disable the "selectMachine" cell (column) if status cell (column) has the value "Deployed".

Disabling individual always on control

That's nice but what about displaying the always on edit mask, like button and checkbox, as disabled?
For example, you like to display one checkbox in a checkbox column as disabled.

There's only one checkbox mask control object under a checkbox column, so you can't disable just one checkbox in an entire column of checkbox by simply calling checkboxMask.setEnabled(0); after the Matrix is painted. That won't work.

To display (paint) disabled always-on-edit-mask you will have to provide your own format handler. Remember that the format handler allows developer to customize the display of each cell. So here's an example

// Setting this format handler to a Checkbox column will disable the checkbox 
// of the record with jsxid="WEBM" (as an example).

var checkFormat = function(objDiv, strCDFKey, objMatrix, objColumn, rowIndex, objServer) { 
  if (objMatrix.getRecord(strCDFKey).jsxid == "WEBM") {
    var checkmask = objColumn.getChild(0); 
    checkmask.setEnabled(0);
    var html = checkmask.paint();
// override the cell's innerHTML with a disabled checkbox.
    objDiv.innerHTML = html;
    checkmask.setEnabled(1);
  }
}

The Format Handler property of the checkbox column will be set to checkFormat

Posted at 18 Mar @ 2:45 PM by Darren Hwang | 0 Comments
  2009/11/22
Create custom bullet chart based on GI Template Language
Last Changed by Eric Shi, Nov 22, 2009 21:34

Background of Problem

We wanted to create a bullet chart like this screen shot. Although it may be easy for us to draw it in HTML directly, but in GI, it's not easy to show this chart by GI native matrix component. And actual at this time, GI template language will show it is powerful.

Template Language

The General Interface template language simplifies the creation of custom GUI components by using a technique that is already familiar to web developers. It builds on your knowledge of HTML and JavaScript. Using the General Interface template language, you can convert a single snippet of HTML, a widget, into a re-usable component.This means that if you've developed user interface components that combine HTML and JavaScript code, you can convert the functional user interface HTML elements into a General Interface template for custom usage.

How to Create a Template

1.Prepare native HTML snippet to generate bullet charts, four values need.
<div style="background-color:#efefef;height:13px;width:200px;position:relative;">
   <span id="a1" style="float:left;position:absolute;left:0px; background-color:#ccc;width:*180px*;height:13px;"></span>
   <span id="a1" style="float:left;position:absolute;left:0px;background-color:#aaa;width:130px;height:13px;"></span>
   <span id="a1" style=" float:left;position:absolute;left:0px;top:4px;background-color:#000;width:160px;height:5px;font-size:5px"></span>
</div>
2.Convert HTML snippet to a template, also add title, label, chart head and x-coordinate. See source code from line 12 to line 62 in logic.js.
  • Use <table> to hold whole bullet charts, title is outside of this table.
  • <for-each> extract every record from a CDF and draw a <tr> row with three <td>, 1st is row label, 2nd is red notation, 3rd is bullet charts.
  • Last row will hold a fixed x-coordiante with gray color.
3.Define a custom class to use this template, we need inherit jsx3.xml.Cacheable, jsx3.xml.CDF interface, because we should accept a CDF xml as data to draw this bullet.
jsx3.lang.Class.defineClass("BulletChart",jsx3.gui.Template.Block,
  jsx3.gui.Form,jsx3.xml.Cacheable,jsx3.xml.CDF], 
    function(chart, CHART) {
     ...
    }
);

Usage

  • Drag a normal jsx3.gui.Block and drop it into your GUI, open this GUI file by text editor and change the name from jsx3.gui.Block to BulletChart
  • Use the following code to set data to your bulletChart component:
chart.resetCacheData();
var cdf = jsx3.xml.CDF.Document.newDocument();
cdf.insertRecord({jsxid:"Revenue",jsxbg1:"150", jsxbg2:"90", jsxbg3:"70", jsxvalue:"140"});
cdf.insertRecord({jsxid:"Profit",jsxbg1:"150",jsxbg2:"80",jsxbg3:"70",jsxvalue:"60", jsxred:"true"});
cdf.insertRecord({jsxid:"Avg Order Size",jsxbg1:"150",jsxbg2:"75",jsxbg3:"65", jsxvalue:"69"});
cdf.insertRecord({jsxid:"On Time Delivery", jsxbg1:"150", jsxbg2:"90", jsxbg3:"70", jsxvalue:"120"});            
chart.setXMLString(cdf.toString());
chart.repaint();
CDF comment
jsxid value is row label, jsxbg1, jsxbg2, jsxbg3 are three widths to draw different gray background blocks in bullet chart, *jsxvalu*e is the width of thin black block.

Illustration with final source

More information

  • Legend, trend and actual columns are ignored in my implementation, it's easy to do.
  • No implement a vertical line in 100%, I don't find an easy way to locate its position, still need some research
  • Please replace my special character with a actual red-dot image
  • Build in GI 3.8.1 and test in FF3 and IE7.

Source

http://www.generalinterface.org/forums/download/file.php?id=54&sid=6e0b12ce1d10782391f4987b6a736087

Posted at 22 Nov @ 9:00 PM by Eric Shi | 0 Comments
  2009/11/17
Coming in 3.9 - Native HTML Forms
Last Changed by Jesse Costello-Good, Nov 18, 2009 09:57
Labels: gi, 3_9, forms

There are a bunch of features in GI 3.9 that improve support for HTML forms.

In versions prior to 3.9 the only forms support was via the jsx3.net.Form class. This is a programatic class (not part of the GI DOM and not painted) that requires a lot of custom JavaScript coding. For example, when the user submits the form you must instantiate the class and copy all field names and values from GI DOM objects to the form. An additional significant shortcoming of jsx3.net.Form is that it has never supported file upload on Firefox.

In GI 3.9 you have two options if you want to integrate your GI application with services that expect submissions via HTML forms. You can still use jsx3.net.Form or you can use jsx3.gui.NativeForm, which is new in 3.9. This new class supports file upload (via POST) on all browsers and should require a lot less JavaScript code on your part.

jsx3.gui.NativeForm extends jsx3.app.Model so it is part of the GI DOM. We added it to the Component Libraries palette (under Form Elements) so you can drag and drop it into your GI component. Just like with a native HTML form, any form fields that are DOM children of a jsx3.gui.NativeForm become part of that form. When the form is submitted, all constituent fields are submitted with the form. There is very little magic going on here; a NativeForm simply renders as a <form> element in HTML and most functionality is provided by the native browser implementation.

There are several form element controls that are compatible with NativeForm. They are:

  • jsx3.gui.TextBox - (Text Box, Text Area, Number Input, and Password)
  • jsx3.gui.NativeSelect - New in 3.9 (jsx3.gui.Select is not compatible with NativeForm)
  • jsx3.gui.FileUpload - New in 3.9
  • jsx3.gui.NativeHidden - New in 3.9
  • jsx3.NativeButton - New in 3.9 (You can use a jsx3.gui.Button to submit a form by writing an event handler on the button. However, only a NativeButton can submit a CGI parameter.)
  • jsx3.gui.RadioButton
  • jsx3.gui.CheckBox
  • jsx3.gui.DatePicker

In each case besides RadioButton the name of the submitted form field (i.e. CGI parameter) is equal to the Name property of the GI object. In the case of RadioButton the form field is the Group Name property and only one value is submitted for all radio buttons that share the same Group Name.

There are a few important properties of NativeForm that control how the form is submitted. First of all you have Method (GET or POST) and Multipart. Multipart must be true (and Method must be POST) if your form includes a file upload field.

The Action property is the URL of the service to which to submit the form. Note that this can be an absolute or a relative URL. If it is relative, then the URL is resolved relative to the application directory (jsx3.app.Server.resolveURI()).

Don't ignore cross domain issues: you can submit to another domain/scheme/port but you will only be able to read the response from the submission if the URL scheme, domain and port are exactly the same as the URL of the page hosting the GI application.

Finally, the Target and Target IFrame properties control where the response from the submission is displayed. In most cases you will probably want to submit the form invisibly and process the response programatically. In this case set Target to Invisible IFRAME. You can also have the response render in a native popup window (Target = New Window) or have it replace the page hosting the GI application (Target = Current Window). Using the Target IFrame property you can have the response render in an instance of jsx3.gui.IFrame.

We also added a bunch of new components that aid in creating accessible HTML forms. A new class, jsx3.gui.Label, renders a native <label> element. In HTML forms a label is associated with a form field. Set the For Control property of the label to the name of a GI form field to associate it with that field. Using labels helps screen readers and other accessibility software make better sense of the form.

Along with Label there are now components (all instances of jsx3.gui.Block) for <fieldset>, <legend> and <h1> through <h6>. None of these components can do anything visually that a plain old jsx3.gui.Block can't do with the right CSS. However, in general it is better to use the full range of built-in HTML elements when designing for accessibility because each element has meaning independent of how it is rendered.

Here's a screen shot taken from 3.9 Builder of a form being built:

And the source code: form.xml

Related JIRA tickets:

Posted at 17 Nov @ 2:55 PM by Jesse Costello-Good | 0 Comments
  2009/11/09
Coming in 3.9 - Improved JavaScript Console
Last Changed by Jesse Costello-Good, Nov 09, 2009 17:28
Labels: gi, 3_9, builder

I'm excited to announce a completely redesigned JavaScript console for GI Builder. The implementation comes courtesy of community member Eric Shi, who deserves much praise for stepping up and contributing this code.

Here is the JIRA ticket: http://www.generalinterface.org/bugs/browse/GI-421

The new JS console borrows heavily from the Safari Error Console. We really like how the Safari console works so we thought we'd port it to GI Builder so it can run on every browser that Builder supports.

One of the first things you'll notice about the new JS console is that it displays a running list of commands and command output. This is what makes it a "console." In our experience this is much more useful than the test utility in GI 3.8, which only shows the last command and its output.

When focused in the console prompt use the up and down arrow keys to scroll through the command history. This works even across restarts of Builder!

The other key feature of the new JS console is the ability to drill down into nested data structures that a command outputs. For example, enter the command "document.body" or "jsx3" and you'll see the string representation of an object and a collapsed arrow to its left. Click on the arrow to expand the object. Any nested objects can themselves be expanded, indefinitely. Useful hint: property keys are shown in purple except when the value of the property is inherited from the object's prototype, in which case the key is shown in gray. Function and Array outputs are also printed intelligently. No more will you see the cryptic and unhelpful "[Object]" when evaluating a JavaScript expression.

If you ever want to clear the console history just type Ctrl+K or right click on the console for a context menu.

By default the console evaluates the input any time you press the Enter key. The console does support multiline input either by pasting the multiline text at the prompt or by typing Shift+Enter to insert a carriage return.

Eric also implemented GI-705 in the new console, which I described previously in this blog post.

A note about execution context: all code evaluated by the JS console is execute in window scope. That means if you evaluate a statement like:

f = 1;

you have actually set the property f of the top-level object window. If you set a global variable in this way it is available to subsequent expressions that you evaluate in the console. Note that the form var f = 1; does not have the same behavior as f = 1;. The value of f will not be available to subsequent expressions if set in this way.

Posted at 09 Nov @ 5:19 PM by Jesse Costello-Good | 5 Comments
  2009/09/28
Coming in 3.9 - Flexible CDF schema
Last Changed by Jesse Costello-Good, Sep 28, 2009 10:49
Labels: gi, 3_9, cdf, xml, schema

Since GI 3.0 debuted in 2005 with the Common Data Format (CDF), GI has enforced a standard XML data schema with a root element called data, nested elements called record and record attributes such as jsxid, jsxtext and jsximg. This schema applies to the classes that implement the jsx3.xml.CDF interface: Select, Tree, Menu, Table and Matrix (and previously List and Grid).

<data jsxid="jsxroot">
  <record jsxid="1" jsxtext="One" jsximg="one.gif"/>
  <record jsxid="2" jsxtext="Two" jsximg="two.gif"/>
  ...
</data>

There are a lot of benefits to a standard data schema like this such as simplicity and interoperability. However, a significant problem is that few services expose data as CDF unless they happened to be designed specifically for GI. Ever since 3.0 we've been coming up with new ways to work around this problem.

All of the CDF controls use XSLT to transform their XML data sources into HTML for rendering. GI 3.0 included the XSLURL, XSLId and XSLString properties (in jsx3.xml.Cacheable), which allowed the default XSLT template to be overridden for a particular instance of one of the CDF classes. So a developer could copy jsxtree.xsl to their project, change, for example, @jsxtext to @label and set an instance of Tree to use the modified template.

This approach had a number of problems, the most significant of which was that the template files were implicitly part of the API of each class and would need to be forward compatible with new releases. If we wanted to add support for a new CDF attribute like @jsxstyle (and then if Tree.js somehow depended on the new template behavior) all previously branched templates would break. When I realized this in the mid 3.x's we decided that supporting custom templates was clearly untenable and I deprecated the functionality.

At the same time I introduced a replacement called XSL transformers. This feature remains supported today though it has its own issues. Using XSL transformers a developer can define one or more XSL transformations to convert the source data to CDF before it goes into the XML application cache and is used to render a CDF control. This has the advantage of being completely declarative (compared with a custom JavaScript schema conversion). It can also modify the structure of the source XML so that the CDF ends up with more or fewer records than the source XML. However, it forces the developer to know XSL, which is not necessarily a common skill for front-end developers.

In 3.9 I've come up with a solution that I think is better than both of the previous approaches. It accomplishes 90% of what XML transformers does and is much simpler. It also enables scenarios that neither of the previous approaches did like:

  • The core CDF schema elements and attributes data, record and jsxid can be renamed to anything
  • A single data source can drive multiple CDF controls, each with its own view of the data
  • No additional files need to be loaded over HTTP

Here's how it works. Every CDF control has a schema property of type jsx3.xml.CDFSchema. The value of this property defines how the control views its CDF datasource. In other words it defines its schema. If the schema property is not set then the control uses the default CDF schema, which is the same as the schema used in 3.0-3.8.

To implement this feature in 3.9 each CDF control, instead of querying its datasource for, let's say, the jsxtext attribute of a record, first queries its schema for the name of the text attribute and then queries its datasource for that attribute of a record. For example, in 3.8 jsx3.gui.Tree included many expressions like

this.getRecordNode(id).getAttribute("jsxtext")

In 3.9 this type of expression has been rewritten as

this.getRecordNode(id).getAttribute(this.getSchema().getProp("text"))

This additional level of indirection allows the CDFSchema object to control how each CDF control views its datasource. The default CDFSchema object returns "jsxtext" from a call to getProp("text"). However, you can modify the schema so that it returns "label" instead. This change would allow you to have a CDF datasource like

<data jsxid="jsxroot">
  <record jsxid="1" label="One"/>
  ...
</data>

In 3.9 you can control this new functionality from within Builder. To do so,

  • Open or create a component with a CDF control in it, such as Matrix, Tree or Menu.
  • Find the CDF Schema component in the Component Libraries palette and drag it onto the CDF control. It may be easier to drag it onto the corresponding node in the Component Hierarchy palette.
  • Modify the properties of the schema object in the Properties Editor.

In the Properties Editor you'll find an editable property for each attribute in the default CDF schema. In general, the default value of property abc is jsxabc. CDFSchema also has a property called "record." This property allows you to change the element name of the records in the datasource. You can choose another name, such as "item" or you can use set it to "*" to indicate than any XML element should be interpreted as a data record.

For the CDF schema shown in the screenshot, I will have a datasource that looks like

<items id="jsxroot">
  <item id="1" label="One"/>
  <thing id="2" label="Two"/>
  <object id="3" label="Three"/>
  ...
</items>

Using the same datasource for two different controls is simply a matter of defining two different CDF schemas for the controls. I've put together a simple example project: GI-702.zip.

Enjoy, and leave any feedback here as a comment on this post or on the JIRA ticket.

Posted at 28 Sep @ 10:33 AM by Jesse Costello-Good | 2 Comments