Reusable Ajax GUI Widgets for the Java Developers

If you feel DHTML is too complicated to build online applications. You may be right. I felt the same way in 1999-2000. Web developers usually create the DHTML components and I felt that they don’t understand the needs of enterprise application developers, who were predominantly Java/C# programmers. Besides why should only web (e.g. DHTML/JavaScript) developers have all the fun with Ajax/Web 2.0 hype?

If I need to create a hierarchical menu, why should I write 1000 lines of DHTML code, which I didn’t know? Why can't I use a pre-built reusable GUI class (as we can do in Java/Swing) to input a string for each ‘cell’ (or menu item) of the hierarchical menu (Internet Explorer Only) and let the Class do the writing? In 1999, I set out to solve the problem for the developers. Only a developer best knows other Java developers' needs.

I had got hold of source code for a free DHTML component (Thanks to all of those, who donated great free DHTML components). I had written a Java Class that takes a String for each cell. The Java class prints the entire Component code, as it is, except replaces the contents of each cell, with the respective input string. After fixing few minor bugs and trivial modifications, Voila! I had a "data driven" reusable GUI Widget. Still I didn’t know how to write DHTML code, but I created a GUI Widget that can do the writing for me.

Let me illustrate using an examples. To be brief, I use only simple DHTML components (however this process can be repeated for other components). Please click on the following component to see a simple “expandable menu”:

 

The DHTML code for the above expandable menu look like this (after some cleanup). Please identify, where each subcomponent's code go. Those parts are highlighted with colors (you may ignore rest of the code).

 

1.   <SCRIPT language=JavaScript1.2>
2.   <!--
3.   var expandIE_UNQ_ID = function(el) {
4.       whichEl = eval(el + "Child");
5.       whichIm = eval(el + "img");
6.       if (whichEl.style.display == "none") {
7.             whichEl.style.display = "block";
8.             whichIm.src = "triUp.gif";
9.       }
10.      else {
11.            whichEl.style.display = "none";
12.            whichIm.src = "triDown.gif";
13.      }
14}
15//-->
16</SCRIPT>
17.    <!---------- begin HTML Code ----------->
18.    <DIV>
19.    <TABLE BORDER='0'><TR><TD ALIGN='CENTER'>
20.    <A href='http://calljs()' onclick="expandIE_UNQ_ID('expand_UNQ_ID1'); return false">
21.    <IMG border=0 height=16 name=imEx id=expand_UNQ_ID1img src="triDown.gif" width=16></A>
22.    </TD><TD VALIGN='CENTER'>
23.    <A href='http://calljs()' onclick="expandIE_UNQ_ID('expand_UNQ_ID1'); return false">
24.    Code for First parent goes here Call: Parent[i].CGM(Out)
25.    </A>
26.    </TD></TR></TABLE>
27.    </DIV>
28.    <DIV id=expand_UNQ_ID1Child STYLE="display:none;">
29.    Code for First child goes here Call: Child[i].CGM(Out)
30.    </DIV>
31.
32.    <DIV>
33.    <TABLE cellpadding="3"><TR><TD ALIGN='CENTER'>
34.    <A href='http://calljs()' onclick="expandIE_UNQ_ID('expand_UNQ_ID2'); return false">
35.    <IMG border=0 height=16 name=imEx id=expand_UNQ_ID2img src="triDown.gif" width=16></A>
36.    </TD><TD VALIGN='CENTER'>
37.    <A href='http://calljs()' onclick="expandIE_UNQ_ID('expand_UNQ_ID2'); return false">
38.    Code for Second parent goes here Call: Parent[i].CGM(Out)

39.    </A>
40.    </TD></TR></TABLE>
41.    </DIV>
42.    <DIV id=expand_UNQ_ID2Child STYLE="display:none;">
43.    Code for Second child goes here Call: Child[i].CGM(Out)
44.    </DIV>
45.
46.    <DIV>
47.    <TABLE BORDER='0'><TR><TD ALIGN='CENTER'>
48.    <A href='http://calljs()' onclick="expandIE_UNQ_ID('expand_UNQ_ID3'); return false">
49.    <IMG border=0 height=16 name=imEx id=expand_UNQ_ID3img src="triDown.gif" width=16></A>
50.    </TD><TD VALIGN='CENTER'>
51.    <A href='http://calljs()' onclick="expandIE_UNQ_ID('expand_UNQ_ID3'); return false">
52.    Code for Third parent goes here Call: Parent[i].CGM(Out)
53.    </A>
54.    </TD></TR></TABLE>
55.    </DIV>
56.    <DIV id=expand_UNQ_ID3Child STYLE="display:none;">
57.    Code for Third child goes here Call: Child[i].CGM(Out)
58.    </DIV>
59. <!---------- end HTML Code ----------->

Now write Java class for the GUI Widget, which prints the above GUI component code. The Class supports a method at lines 14-18 below, which can be used to input a pair of components (A parent and a Child ). This method may be called repeatedly to input each pair of components. The Java class contains a Code generation Method (or "CGM" in short) starting at lines 20 below, which prints JavaScript code at lines 1-16 above. Then it prints above lines 18-30 in a for loop for each pair. Please note all it does is:  replace Blue & Red colored lines in the above code, by the code for input components.

1.  package com.agile.libtemplate;
2.  import java.io.*;
3.  import java.util.*;
4.  import javax.servlet.http.HttpServletRequest;

5.  public class ExpandMenu extends AgileTemplate   {
6     int count = 0;
7     Object parents[] = new Object[20];
8     Objects childs[] = new Object[20];
9     String  UNQ_ID = null;
10.
11.     
public ExpandMenu(AC_Info ACI) {
12.         super(ACI);
13.         UNQ_ID = ACI.getUniqString();
14.     }

15.      public void setPair( Object par, Object chi) {
16.          if (count >= 20)   return;
//Just an example ... Don't sue me!
17.          parents[count] =
par;     childs[count++] = chi;
18.     }


19. //CGM:The Abstract '
Code Generation Method' for AgileTemplates.
20. public int CGM (PrintWriter out) {
21.            //Section-1: Print the JavaScript
22.     out.println("   <SCRIPT language=JavaScript1.2> ");
23.     out.println("   <!-- ");
24.     out.println("   var expandIE_"
+UNQ_ID+" = function(el) { ");
25.     out.println("      whichEl = eval(el + 'Child'); ");
26.     out.println("      whichIm = eval(el + 'img'); ");
27.     out.println("      if (whichEl.style.display == 'none') { ");
28.     out.println("           whichEl.style.display = 'block'; ");
29.     out.println("           whichIm.src = 'triUp.gif'; ");
30.     out.println("      } ");
31.     out.println("      else { ");
32.     out.println("           whichEl.style.display = 'none'; ");
33.     out.println("           whichIm.src = 'triDown.gif'; ");
34.     out.println("      } ");
35.     out.println("   } ");
36.     out.println("   //--> ");
37.     out.println("   </SCRIPT> ");

38.           //Section-2: Print HTML code
39.     out.println("   <!---------- begin HTML Code -----------> ");
40.          for (int i = 0; i , count; i++) {
41.                        // Print code for Parent
42.            out.println(" <DIV> ");
43.            out.println(" <TABLE BORDER='0'><TR><TD ALIGN='CENTER'>");
44.              out.println(" <A href='http://calljs()' onclick=\"expandIE_UNQ_ID('expand_"
+UNQ_ID+i+"'); return false\"> ");
45.              out.println(" <IMG border=0 height=16 name=imEx id=expand_"
+UNQ_ID + i + "img src='triDown.gif' width=16></A> ");
46.              out.println(" </TD><TD VALIGN='CENTER'> ");
47.              out.println(" <A href='http://calljs()' onclick=\"expandIE_UNQ_ID('expand_"
+UNQ_ID
+i+"'); return false\"> ");
48.           if(parents[i] instanceof AgileTemplate) // If our GUI Widget
49.                     status = ((
AgileTemplate)parents[i]).CGM(out);
50.           else
// Allow user to input HTML Code as well
51.                     out.print(parents[i]);
// Use default String
52.              out.println(" </A> ");
53.              out.println(" </TD></TR></TABLE> ");
54.              out.println(" </DIV> ");
55.
56.                        // Print code for Child
57.              out.println(" <DIV id=expand_"
+UNQ_ID
+ i + "Child STYLE='display:none;'> ");
59.        // Include the component code for the subcomponent.
60.         
if(childs[i]
instanceof AgileTemplate)
61.                 status = ((
AgileTemplate)childs[i]).CGM(out);
62.          else
// Allow user to pass HTML Code as well
63.                 out.print(childs[i]);
// It must support ".toString()"
64.               out.println(" </DIV> ");
65.          }
66.          out.println(" <!---------- end HTML Code -----------> ")
;
67.     }    // END of the CGM()
68. }    // END of the Class Definition.

 

One suggested method to create a reusable GUI Widget is to, first find the GUI Component and make sure it runs on all browsers. Once the code is ready, it is a mechanical process to wrap it in a reusable GUI Class. If you notice, there is a strange string “UNQ_ID”, which gets a unique string and appends the string to many global identifiers to avoid name collisions.

Q. Why UNQ_ID was used? 
Ans
. What happens, if a web page uses two instances of ExpandMenus? For example, they both end up naming their first child “expand1Child”. Boom! Name collision. When I click on one, the other expands. If I need to use only one instance per page, then I may not need to use “UNQ_ID” (unless another DHTML or a Hierarchical menu component inadvertently end up using the same name).

// Instantiate an Object to present an Expandable-Menu GUI Component
ExpandMenu ExpMenu = new ExpandMenu (HTTP_Request);
// Initialize the Object: set/input pairs of Subcomponents
ExpMenu.setPair(Parent,Child);//Pass HTML-Code strings or GUI Objects
// Repeat the above call to add more pairs ... Isn't it simple?

Now you know how to create a reusable GUI Widget, if you can find a DHTML component. Please be nice to your Web developer friend, he may give you DHTML components. Also he might help you fix problems in certain component you find on the web. Some implementations might not be quite suitable to build reusable GUI Classes. Please see how you are going to create a reusable GUI Class for the following DHTML Scrolled component?

 
 

The following is the code for the above "Component Scroller" DHTML component. As I didn’t know any thing about DHTML Code, I only look for the location, where I should put my subcomponents. It turns out, it is quite simple as shown in the blue color below. I didn't need to worry about rest of the scare looking code. However there is a small catch ... continues below ...

<TABLE border='1'><TR><TD>
//Section-1: Initialize the variables.
<SCRIPT language=JavaScript1.2>
// GUI-Class can have methods to input values & init following Vars 
      var scrollerwidth = 300
      var scrollerheight=
180
      var scrollerbgcolor = '
#FFFFE1'
      var scrollerbackground='
back.gif'
      var scrollerwait =
3000
      var scrollerspeed =
80
      var scrollerjump  =
10

//Section-2: Initialize the subcomponents to be scrolled.
      var messages=new Array()
      messages[0]= "
First Component's HTML Code Goes Here";
      messages[1]= "
Second Component's HTML Code Goes Here";
      messages[2]= "
Third Component's HTML Code Goes Here";


//Section-3: Don't read The scare looking JavaScript code below.
      var     mi = 0;
      if (messages.length>1)         mi=2
      else                           mi=0

      function move1(whichlayer){
          tlayer=eval(whichlayer)
          if (tlayer.top>0&&tlayer.top<=scrollerjump){
             tlayer.top=0
             setTimeout("move1(tlayer)",scrollerwait)
             setTimeout("move2(document.main.document.second)",scrollerwait)
             return
          }
          if (tlayer.top>=tlayer.document.height*-1){
             tlayer.top-=scrollerjump
             setTimeout("move1(tlayer)",scrollerspeed)
          } else{
             tlayer.top=scrollerheight
             tlayer.document.write(messages[mi])
             tlayer.document.close()
             if (mi==messages.length-1)   mi=0
             else                         mi++
          }
      }

     
function move2(whichlayer)  {
           tlayer2=eval(whichlayer)
           if (tlayer2.top>0&&tlayer2.top<=scrollerjump){
                tlayer2.top=0
                setTimeout("move2(tlayer2)",scrollerwait)
                setTimeout("move1(document.main.document.first)",scrollerwait)
                return
           }
           if (tlayer2.top>=tlayer2.document.height*-1){
                tlayer2.top-=scrollerjump
                setTimeout("move2(tlayer2)",scrollerspeed)
           } else{
                tlayer2.top=scrollerheight
                tlayer2.document.write(messages[mi])
                tlayer2.document.close()
                if (mi==messages.length-1)      mi=0
                                        else                                                             mi++
           }

      function move3(whichdiv){
           tdiv=eval(whichdiv)
           if (tdiv.style.pixelTop>0&&tdiv.style.pixelTop <= scrollerjump){
                tdiv.style.pixelTop=0
                setTimeout("move3(tdiv)",scrollerwait)
                setTimeout("move4(second2)",scrollerwait)
                return
           }
           if (tdiv.style.pixelTop>=tdiv.offsetHeight*-1){
                tdiv.style.pixelTop-=scrollerjump
                setTimeout("move3(tdiv)",scrollerspeed)
           } else{
                tdiv.style.pixelTop=scrollerheight

                tdiv.innerHTML=messages[mi]
                if (mi==messages.length-1)   mi=0
                else                         mi++
           }
      }
 
      function move4(whichdiv){
           tdiv2=eval(whichdiv)
           if (tdiv2.style.pixelTop>0&&tdiv2.style.pixelTop <= scrollerjump){
                tdiv2.style.pixelTop=0
                setTimeout("move4(tdiv2)",scrollerwait)
                setTimeout("move3(first2)",scrollerwait)
                return
           }
           if (tdiv2.style.pixelTop>=tdiv2.offsetHeight*-1){
                tdiv2.style.pixelTop-=scrollerjump
                setTimeout("move4(second2)",scrollerspeed)
           }
           else{
                tdiv2.style.pixelTop=scrollerheight
                tdiv2.innerHTML=messages[mi]
                if (mi==messages.length-1)
                    mi=0
                else
                    mi++
           }
      }

      function startscroll(){
           if (document.all){
                move3(first2)
                second2.style.top=scrollerheight
                second2.style.visibility='visible'
           }
           else if (document.layers){
                document.main.visibility='show'
                move1(document.main.document.first)
                document.main.document.second.top=scrollerheight+scrollerjump
                document.main.document.second.visibility='show'
           }
      }

document.writeln('<span id="main2" style="position:relative;width:'+scrollerwidth+'; height:'+scrollerheight+'; overflow:hiden;background-color:'+scrollerbgcolor+' ;background-image:url('+scrollerbackground+')">')
document.writeln('<div style="position:absolute;width:'+scrollerwidth+'; height:'+scrollerheight+'; clip:rect(0 '+scrollerwidth+' '+scrollerheight+' 0);left:0;top:0">')
document.writeln('<div class="scrollxx" id="first2" style="position:absolute;width:'+scrollerwidth+';left:0;top:1;">')
document.write(messages[0])
document.writeln('</div>')
document.writeln('<div class="scrollxx" id="second2"
style="position:absolute;width:'+scrollerwidth+'; left:0;top:0;visibility:hidden">')
document.write(messages[1])
document.writeln('</div>')
document.writeln('</div>')
document.writeln('</span>')
</SCRIPT>
</TD></TR></TABLE>

It can accept only string, but how can I use other GUI Widgets such as a HTML table in one or more of the Cells as subcomponents? I wanted a data driven GUI Class that can be used with one line of code in my JSP file: 

// Construct a Scroll Object and Pass Items Array (Please Click Here for a Sample). 
ScrollWidget SC = new ScrollWidget ( subcomponents_array ); 

However, I can only initialize a String to replace the BLUE text. I was not a DHTML developer to redesign the scary looking DHTML code to use "DIV" (as ExpandMenu did for subcomponents. This is also using DIV but using document.writeln to write). After thinking a while, (I found a Java solution) I can write the code into a String buffer. Then write a utility to insert a back-slash before each special character such as double-quote and new-line in the string. Now I can print the HTML code between the quotes. For example:

void CGM(out) { //CGM for the above Scroll Widget (Please see Sections above)
     // Print the Section-1. Use the Object Input values to set JavaScript variables
    
Write_javascript_init_variables(out);

     // Print the Section-2. Use the Object Input subcomponents to generate code.
     for(int i = 0; i < count; i++) {
            String  HTML_Code =
subcomponent[i].toString(); //Get Component code
            // Insert a 'back-slash' before each double quotes or 'new-line'
            String  HTML_Code_String = Util.HTML_to_String(HTML_Code);
            out.println(" messages["+i+" ]= \"" + HTML_Code_String  + "\";");
    }

    // Print the Section-3. Write the section 3 in the above DHTML Code.
   
Write_the_scary_javascript_code(out);
}

The above was not a perfect solution but worked for many test cases (note the Component Hierarchy figure at bottom Internet Explorer Only). My Objective is provide a quick & brief introduction. Basic principles are same, but I found many useful techniques for building GUI Widgets, which are discussed in other pages in the web site. As you could see 80% of the work is to create the perfect DHTML component. Please note, all Java class does is:  replace Blue Colored lines in section-2 by the code for input subcomponent.

Now, being a Java (or C#) programmer you learned enough about reusable GUI Widgets. If you have a best friend who is an excellent JavaScript programmer, you both might create better GUI Widgets than many Ajax framework companies out there today. Most of them offer between 7 & 15 components. How long it takes to match? Once a perfect DHTML Component is created (or found on the Web), it may take couple of hours to create a Java code generator wrapper (i.e. GUI Class). You need to pay attention to few other simple issues such as variable-name collisions and use relative coordinate system (if you want this to be used as a subcomponent and/or has pop-up subcomponents).

For an exercise, think how you could create a reusable GUI Widget for "Google suggest" type text box. You may take code from Google-suggest and customizing it. Your Widget must take a URL as input, and use the URL to quarry the "suggest" strings. Also try to avoid name-collision, just in case, if you need more than one such Input text components in a web page (e.g. two for inputting emails and one for search-phrase).

Int size = 32; // Text box size.
// There are certain differences, how the Text box functions based on Type.
// CGM can use simple “
if-then-else” to print appropriate set of JavaScript functions
// for the Component for given type. (Make it highly customizable & reusable)
String Type = "SUGGEST_TYPE_EMAILS";   // Type of the input, E.g: email/search-phrase
// The following URL is used to get the list of suggest-strings, as user typing something
String URL = "http://email_list.Pioneer-soft.jsp"; 
SuggestTextBox STB = new SuggestTextBox ( size, style, URL, Type);

The GUI Class let the application developers focus on 'Business-logic' & "HIDE" Ajax code.
One must be free to refine the GUI Class independently, if he wish to do so later.

If one makes a bread, he can eat one day. If he builds a bread factory, many people can eat everyday. It is possible to create reusable Ajax GUI Class (a factory) for any cool component or system we find on the web.

The only objective here is to briefly teach how one can build a Java class wrapper, for any DHTML component one finds on the Web. If one wishes to create a great cross browser DHTML Widget, one must start with a great cross browser DHTML code.

It is also possible to use if-then-else to dynamically create browser specific code. One may also implement a single GUI Class that use if-then-else to dynamically generate any XML language such as SVG, XAML or MXML (for each requesting viewer or version). If a component uses other GUI Objects to build subcomponents, each GUI Objects in the hierarchy could generate appropriate code compatible to the requesting browser/version, hence the resultant component will be browser compatible.

If it is not possible to implement certain features of the component on one of the browsers, of course, no one can create full-featured cross browser GUI component. Each browser needs to support at least one way to implement the component.

Remember browser wars during 1998-2000 and nightmares we had, to maintain bug-to-bug compatibility to run on the competing browsers and their newer versions? This was one of the problem, which led me to create the reusable GUI Classes in the first place (to separate and encapsulate all the if-then-else statements). Do you like multilingual support with that? It also offers an elegant method for that too

The cross browser issues can be handled: (A) in the DHTML code - the resultant code is cross browser (the DHTML code contains if-then-else to handle cross browser issues). (B) In Java at server: In this case, Java-code checks the type and uses if-then-else to generates browser compatible DHTML-code. So, one can't save the resultant page and view using other browser-types.

Remember, both the Java-classes are cross-browser. But in the later case, the Java-class creates DHTML code specific for the requesting browser-type. Hence the resultant code runs on only one type of browser (for which it was created, if I save the webpage code for later viewing). Now you know, why many of our examples ended up Internet-explorer only.

Now, you know more about reusable GUI API than most Ajax or web application framework vendors out there!...Please click here to see proof that they are absolutely clueless...Have fun with Web 2.0!

It is too bad, even the best JavaScript developers can create at most two or three layer of data-driven Ajax component hierarchy, which is inadequate to build next generation 2D/3D graphics intensive online Applications.

Please click here, if you wish to learn creating reusable GUI Classes to present 2D/3D vector graphics based animated interactive Ajax components.

Pioneer-soft plans to create GUI-API, where each GUI Class generates either XAML or SVG components dynamically for requesting client. This website has already demonstrated that we can create a Class for any GUI component. Also the Ajax GUI Classes can be far more flexible than possible for GUI Classes in the traditional platforms, such as Java/Swing or Windows/VC++. Please click here for proof and sample GUI Components.

If you wish to know what Pioneer-soft has accomplished in the recent years and our future goals, Please click here. Thank you for visiting our web site!

Don’t you feel that most other Ajax frameworks for building GUI or Rich Internet Applications today are too complex and yet offer incomplete solution to such simple problems? ( We think, we just figured out why Google created GWT? )

If one wants to include a 2D/3D Ajax GUI Component in his web application, why can’t he just use a reusable GUI Class to instantiate and initialize an Object, as we can do in Windows/VC++ or Java/Swing? Why do their reusable Ajax GUI-API end up with fundamental limitations and primitive? Why can’t developers snap many GUI Objects together to build custom component-hierarchies and integrate them to build the online application?

If one wants to use just one or two components, why the browser end up downloading large JavaScript files, which contains code for nearly the entire framework? Doesn't it potentially cause each page contains all the associated bugs and incompatibilities? Why they upload entire GUI system to present a page containing just one Ajax component?  How many Ajax components can they support in their GUI-API using increasingly large JavaScript files?

How long such complex and yet "incomplete" solutions survive? If they don’t survive, can you afford to maintain their buggy API (e.g. port to newer browser/versions, evolving technologies or business needs etc.) along with your application to protect your investment?

Most vendors lock you to one framework and can't co-exist with other frameworks or tools!

Miscellaneous Documents Privacy Policy | Site Map | Services
Designed By SINDHU SYNERGY
Copy Right © 2006 Pioneer Soft, LLC. All Rights Reserved.