RADICORE for PHP - Inserting optional JavaScript

By Tony Marston

9th August 2006
Amended 2nd May 2008

Introduction
Disclaimer
Implementation
Adding javascript to the <HEAD> element
Adding javascript to the <BODY> element
Adding javascript to individual controls
Adding javascript to controls with options
Hide and Seek
Adding an ID element to <tr> tags
Headers and Footers
Amendment History

Introduction

I am not a fan of javascript for reasons which are stated in the FAQ. Anyone who says you must use javascript is making a mistake, and anyone who creates a web application which cannot run without it is making an even bigger mistake. It is an option, not a requirement, and it is an option which some users choose to disable.

However, there are some people who think it is really cool to use javascript, even more so with the current hype of the buzzword du jour which is AJAX. As the RADICORE framework contains many features which are entirely optional I have decided to make the ability to insert javascript into the HTML pages also optional. The purpose of this document is therefore to explain how you can insert the javascript of your choice into the pages of your choice.

Disclaimer

Just because I am providing the ability to use javascript within the RADICORE framework does not mean that I support its use. Please bear in mind the following:.

In other words... use javascript at your own risk, and don't come crying to me if it breaks.

Implementation

The ability to add javascript into the RADICORE framework requires changes in the following areas:

The only reason that I could include this ability into the framework was because I was provided with some HTML documents which contained working javascript code, so all I had to do was change the framework until the same code appeared in the HTML output.

Adding javascript to the <HEAD> element

The first step is to allow entres to be inserted into the documents <HEAD> element, as shown in the following example:

  <head>
    <title>This is a title</title>
    <script language="javascript">...code...</script>  (1)
    <script language="javascript" src="...filename..."/> (2)
  </head>

Note that there are two types of entry:

  1. This contains a block of javascript code.
  2. This contains a reference to an external file which contains a block of javascript code.

Note also that there can be any number of these entries in any order. They can be included in a web page by inserting code into an object's _cm_setJavaScript() method similar to the following:

    function _cm_setJavaScript ($javascript)
    insert any javascript to be included in the <HEAD> or <BODY> elements.
    {
        global $mode;

        if ($mode == 'insert' or $mode == 'update') {
            $javascript['head'][]['file'] = 'javascript/usableforms.js';
            $javascript['head'][]['file'] = 'javascript/CalendarPopup.js';
            $javascript['head'][]['code'] = 'document.write(getCalendarStyles());';
            $javascript['head'][]['code'] = 'var cal1 = new CalendarPopup(); cal1.showYearNavigation();';
        } // if

        ....

        return $fieldarray;

    } // _cm_setJavaScript

Adding javascript to the <BODY> element

The next step is to allow entries to be inserted into the documents <BODY> element, as shown in the following example:

  <head>
  <body onload="...code..." onunload="...code...">
    ...

These can be included in a web page by inserting more code into an object's _cm_setJavaScript() method similar to the following:

    // specify scripting events on <body> tag
    $javascript['body']['onload'] = 'startForm()';
    $javascript['body']['onunload'] = 'endForm()';

Adding javascript to individual controls

This is more tricky as there are several different variations that all need to be covered. Take a simple example of a TEXT control which should appear in the HTML output as follows:

<input type="text" name="col27" value="initial value" 
       onfocus="doStuff(2);" onblur="doStuff(3);">

This can be achieved by inserting code into an object's _cm_setJavaScript() method similar to the following:

    $this->fieldspec['col27']['javascript'] = array('onclick' => 'doStuff(1);',
                                                    'onfocus' => 'doStuff(2);',
                                                    'onblur' => 'doStuff(3);',
                                                    'onselect' => 'doStuff(4);',
                                                    'onchange' => 'doStuff(5)');

Note here that the array can contain an entry for any valid scripting event, not just those which are shown.

Here is a more complicated version:

<td>
    <script language="javascript">...code1...</script>
    <input name="col27" type="text" value=""/>
    <a href="#" onClick="...code2..." name="name27" id="id27">label27</a>
</td>

This can be achieved by inserting code into an object's _cm_setJavaScript() method similar to the following:

    $this->fieldspec['col27']['javascript'] = array('javascript' => '...code1...',
                                                    'href_onclick' => '...code2...',
                                                    'href_name' => 'name27',
                                                    'href_id' => 'id27',
                                                    'href_label' => 'label27');
                                                    // variations
                                                    'href_image' => '...image...'
                                                    'href_link' => '...url...'
                                                    'hidecontrol' => 'y'

Note the following variations:

Adding javascript to controls with options

Some controls, such as radio groups and dropdown lists, do not have single values but a group of options, and it is possible to have different scripting events on each option, as shown in the followng:

  <select name="star_sign" onchange="alert('change');">
    <option value=""> </option>
    <option value="ARI" onkeypress="alert('1');">Aries</option>
    <option value="AQU" onkeypress="alert('2');">Aquarius</option>
    <option value="CAN" onkeypress="alert('3');">Cancer</option>
    <option value="CAP" onkeypress="alert('4');">Capricorn</option>
    <option value="GEM" onkeypress="alert('5');">Gemini</option>
    <option value="LEO" onkeypress="alert('6');">Leo</option>
    <option value="LIB" onkeypress="alert('7');">Libra</option>
    <option value="PIS" onkeypress="alert('8');">Pisces</option>
    <option value="SAG" onkeypress="alert('9');">Sagittarius</option>
    <option value="SCO" onkeypress="alert('10');">Scorpio</option>
    <option value="TAU" onkeypress="alert('11');">Taurus</option>
    <option value="VIR" onkeypress="alert('12');" selected="selected">Virgo</option>
  </select>

The options are normally supplied in an associative array consisting of a key and a value, as in:

$array['star_sign'] = array('ARI' => 'Aries',
                            ...
                            'VIR' => 'Virgo');

In order to supply an additional set of scripting events for each key it is necessary to replace the value part with an array, as in the following:

$array['star_sign'] = array('ARI' => array('value' => 'Aries',
                                           'onkeyup' => "alert('up');",
                                           'onkeydown' => "alert('down');"),
                            ...
                            'VIR' => array('value' => 'Virgo',
                                           'onkeypress' => "alert('12');"));

Note that it is possible to specify any valid scripting event for each key, and if a particular key does not have any scripting events it can be specified in the short form.

Hide and Seek

One of the javascript effects that I was asked to incorporate into my framework was the ability to make fields appear and disappear dynamically. Take the following snippet of HTML:

    .....
    <script language="JavaScript" src="usableforms.js"/>       <-- (1)
    <script language="JavaScript">document.write();</script>   <-- (1)
  </head>
  <body onload="prepareForm()">    <-- (2)
    <form method="post" action="script.php">
      <table>                                            <-- (3)
        <tbody id="waitingRoom" style="display:none"/>   <-- (3)
      </table>                                           <-- (3)
      <table>
        <tr>
          <td>Type</td>
          <td>
            <select name="report_type">
              <option value="1" show="relationONE">One</option>       <-- (4)
              <option value="2" show="relationTWO">Two</option>       <-- (4)
              <option value="3" show="relationTHREE">Three</option>   <-- (4)
            </select>
          </td>
        </tr>
              
        <tr relation="relationONE">    <-- (5)
          <td>One</td>
          <td>
            <input name="field_1" type="text" value="ONE" />
          </td>
        </tr>
              
        <tr relation="relationTWO">    <-- (5)
          <td>Two</td>
          <td>
            <input name="field_2" type="text" value="TWO" />
          </td>
        </tr>
              
        <tr relation="relationTHREE">   <-- (5)
          <td">Three</td>
          <td>
            <input name="field_3" type="text" value="THREE" />
          </td>
        </tr>
      </table>
    </form>  
  </body>
  .....

What this does is make various rows in the table appear or disappear depending on which option in a dropdown list is selected. What the javascript does is link the relation="something" on individual rows to the show="something" against each selectable option. If the relation attribute does not match the show attribute then the row is hidden.

To make use of this javascript module the following elements need to appear in the HTML output, and this can be achieved with the following steps:

  1. This can be added in to the <HEAD> element using this technique.
  2. This can be added in to the <BODY> element using this technique.
  3. This can be included by inserting code into the object's _cm_setJavaScript() method similar to the following:
    $javascript['tbody'] = array('id' => 'waitingRoom', 'style' => 'display:none');
    
  4. This can be added to individual options by using this technique to add 'show' => '...' to the array of option values.
  5. As the contents of each row in the screen is defined in the screen structure file this is the best place to identify additional attributes for particular rows. Depending on how many fields there are to a row the additional code is slightly different:

    If the row contains only one field:

    $structure['main']['fields'][] = array('field1' => 'Id', 'colspan' => 3, 
                                           'javascript' => array('relation' => 'relationONE'));
    

    If the row contains more than one field:

    $structure['main']['fields'][2][] = array('label' => 'Foo Bar');
    $structure['main']['fields'][2][] = array('field' => 'foobar');
    $structure['main']['fields'][2][] = array('javascript' => array('relation' => 'relationTWO'));
    

Note that at present this feature can only be employed in screens which utlise the ADD 1 or UPDATE 1 patterns.

Adding an ID element to <tr> tags

This tip was provided by Kyle Brost.

How do you to add an ID element to a <tr> (table row) tag, such as in the following sample?

<tr id="tracker_stop_row">
  <td class="label"><span class="required">* </span>Stop Time</td>
  <td>
    <input name="tracker_stop" class="text" type="text" value="" maxlength="20" size="20" />
  </td>
</tr>

If the row contains a single field then this can be achieved with code in your screen structure file which is similar to the following:

$structure['main']['fields'][] = array('tracker_stop' => 'Stop Time',
                                       'javascript' => array('id'=>'tracker_stop_row'));

If the row contains more than one field then you will need something like the following:

$structure['main']['fields'][1][] = array('javascript' => array('id'=>'tracker_start_row'));
$structure['main']['fields'][1][] = array('label' => 'Date');
$structure['main']['fields'][1][] = array('field' => 'tracker_date');
$structure['main']['fields'][1][] = array('label' => 'Time');
$structure['main']['fields'][1][] = array('field' => 'tracker_time');

Headers and Footers

UPDATE: I have been informed that this header/footer issue was actually being caused by a well-known brand of anti-virus software, and that my solution was no real solution at all. I have tested my software without this extra code, and as it works without any problem I have removed it permanently.

While I was testing these changes I noticed that several blocks of javascript code which I had not supplied would appear in the HTML output. This block would appear within the <HEAD> element:

<script language="JavaScript">
<!--

function SymError()
{
  return true;
}

window.onerror = SymError;

var SymRealWinOpen = window.open;

function SymWinOpen(url, name, attributes)
{
  return (new Object());
}

window.open = SymWinOpen;

//-->
</script>

This block would appear right at the end of the document after the closing </HTML>:

<script language="JavaScript">
<!--
var SymRealOnLoad;
var SymRealOnUnload;

function SymOnUnload()
{
  window.open = SymWinOpen;
  if(SymRealOnUnload != null)
     SymRealOnUnload();
}

function SymOnLoad()
{
  if(SymRealOnLoad != null)
     SymRealOnLoad();
  window.open = SymRealWinOpen;
  SymRealOnUnload = window.onunload;
  window.onunload = SymOnUnload;
}

SymRealOnLoad = window.onload;
window.onload = SymOnLoad;
//-->
</script>

Does anyone know where these come from, or what their purpose is? This actually caused me some grief as I discovered that when I uploaded my code to my web hosting company my javascript pages failed to work as the 2nd of the two blocks was missing. This confused me as both my home PC and web host are running PHP 4, and I could not detect anything else which was different. The only way I could resolve this issue was to create a new global variable and set it to TRUE on my web host, as in the following:

$GLOBALS['force_javascript_footer'] = true;

It's a clunky solution, but in the absense of anything better it will have to do.


© Tony Marston
9th August 2006

http://www.tonymarston.net
http://www.radicore.org

Amendment history:

02 May 2008 Added Adding an ID element to <tr> tags.
25 Jul 2007 Amended Headers and Footers to indicate that this section no longer applies.

counter