/*
 * e107Helper
 * Miscellaneous JS helper object for functions that don't fit anywhere else
 */
var e107Helper = {
   // Useful keycodes
   KEY_TAB        : 9,
   KEY_ENTER      : 13,
   KEY_ESC        : 27,
   KEY_PAGE_UP    : 33,
   KEY_PAGE_DOWN  : 34,
   KEY_END        : 35,
   KEY_HOME       : 36,
   KEY_UP         : 38,
   KEY_DOWN       : 40,

   // Holder for main page HMTL whilst a dialog is displayed
   bodyhtml : "",

   /**
    *
    */
   addTextToField : function(txt, fieldid) {
      document.getElementById(fieldid).value = txt;
      expandit(fieldid+"_box")
   },

   /**
    * Used to confirm an action
    * Normally action is some sort of destroy (e.g. delete row from DB). A Javascript confirm
    * dialog is displayed, if positive response button is selected thenh action is allowed to happen, thenh
    * action being to load the next page (which should effect the destroy)
    * @param   string   the message to be displayed in the confirm dialog
    * @param   string   string representing the URL of the page to load is positive response button selected
    * @return  boolean  true if positive response button selected, otherwise false
    */
   confirmDelete : function (msg, loc){
      if (confirm(msg)) {
         document.location = loc;
         return true;
      } else {
         return false;
      }
   },

   /**
    * Displays a dialog
    * A dialog, in this scenario, is normally a form of some sort. Before the dialog is displayed
    * the page is faded (using CSS opacity). It is up to the caller to call the <code>killDialog</code> function is called
    * when the dialog is completed or cancelled.
    * @param   string   the HTML ID attribute to be given to the dialog (actually the DIV that the dialog is dispalyed in)
    * @param   html     the HTML representing the dialog (form) to be displayed
    * @param   width    the width of the dialog in pixels
    * @param   focus    id of copntrol to be given focus when dialog is loaded
    * @param   key      keyboard handler object, will be attached to the onkeypress, onkeydown and onkeyup events of the dialog DIV
    */
   dialog : function (id, html, width, focus, key) {
      var dialog = document.createElement("DIV");
      dialog.style.visibility    = "hidden";
      dialog.id                  = id;
      dialog.style.position      = 'absolute';
      dialog.style.top           = '25px';
      dialog.innerHTML           = html;
      dialog.style.zIndex        = '9999';
      if (typeof key != undefined && key != null) {
         dialog.onkeydown        = function(ev) { eval(key+"()");};
         //dialog.onkeyup          = function(ev) { eval(key+"(ev)");};
         //dialog.onkeypress       = function(ev) { eval(key+"(ev)");};
      }
      var mask = document.createElement("DIV");
      mask.id                    = id+"_mask";
      mask.style.position        = 'absolute';
      mask.style.top             = '0px';
      mask.style.left            = '0px';
      mask.style.height          = '100%';
      mask.style.width           = (e107HelperBrowser.isIE) ? '110%' : '100%';
      mask.style.backgroundColor = 'black';

      mask.style.zIndex          = '9998';

      document.body.appendChild(mask);
      document.body.appendChild(dialog);

      if (e107HelperBrowser.isIE) {
         mask.style.filter = 'progid:DXImageTransform.Microsoft.Alpha(opacity=66)';
      } else {
         mask.style.opacity      = '0.66';
      }

      var vpsize = e107HelperSize.getViewportSize();

      if (typeof width != undefined && width != null) {
         dialog.style.width = width + 'px';
      }

      var left = (vpsize.width - dialog.offsetWidth) / 2;
      var top  = (vpsize.height - dialog.offsetHeight) / 3;
      if (e107HelperBrowser.isIE) {
         dialog.style.pixelTop   = top;
         dialog.style.pixelLeft  = left;
      } else {
         dialog.style.top        = top+"px";
         dialog.style.left       = left+"px";
      }

      dialog.style.visibility    = "";
      mask.style.visibility      = "";

      if (document.getElementById(focus) && document.getElementById(focus).focus()) {
         document.getElementById(focus).focus();
      }
   },

   dialogKeyHandler : function(ev, cancel_event, ok_event) {
      var ev = e107HelperEvents.getEvent(ev);
      var key = e107HelperEvents.getKeyCode(ev);
      var tgt = ev.srcElement;

      switch(key) {
         case e107Helper.KEY_ENTER: {
            if (typeof tgt != "undefined" && typeof tgt.type != "undefined" && tgt.type != "button") {
               eval(ok_event);
               ev.returnValue = false;
            }
            break;
         }
         case e107Helper.KEY_ESC: {
            eval(cancel_event);
            ev.returnValue = false;
            break;
         }
         default:
            // let the system handle the key
            ev.returnValue = true;
            return;
      }
   },

   /**
    * Stores the current page
    * To be used before displaying a dialog to save the current page
    */
   killDialog : function (id) {
      var el = document.getElementById(id);
      if (el) {
         document.body.removeChild(el)
         el = document.getElementById(id+"_mask");
         document.body.removeChild(el)
      }
   },

   /**
    * Stores the current page
    * To be used before displaying a dialog to save the current page
    * @deprecated
    */
   storeBodyHTML : function () {
      this.bodyhtml = document.body.innerHTML;
   },

   /**
    * Restores the current page
    * To be used after a dialog has been completed or cancelled to restore the current page
    * @deprecated
    */
   restoreBodyHTML : function() {
      document.body.innerHTML = this.bodyhtml;
   },

   /**
    * Display a 'popup' message
    * Generates a 'popup' message on the current page. The 'popup' cannot be closed by the user, it must be closed programatically.
    * The aim of this 'popup' is to indicate to the user that something is happening (e.g. "Processing, please wait...")
    * @param   string   the HTML ID attribute to be given to the message (actually the DIV that the message is dispalyed in)
    * @param   text     the text of the message to be displayed
    */
   message : function (id, text) {
      var msgdiv1 = document.createElement("DIV");
      var msgdiv2 = document.createElement("DIV");
      var msgdiv3 = document.createElement("DIV");
      var br      = document.createElement("BR");
      var button  = document.createElement("INPUT");
      var msgdiv  = document.createElement("DIV");

      e107HelperStyle.addClass(msgdiv1, "forumheader");
      e107HelperStyle.addClass(msgdiv2, "forumheader2");
      e107HelperStyle.addClass(msgdiv3, "forumheader3");
      e107HelperStyle.addClass(button, "button");

      msgdiv1.style.visibility   = "hidden";
      msgdiv1.style.position     = "absolute";
      msgdiv1.id                 = id;

      msgdiv3.style.margin       = "10px";
      msgdiv3.style.padding      = "10px";

      button.type                = "button";
      button.value               = "OK";
      button.style.textAlign     = "right";

      msgdiv.innerHTML = text;
      msgdiv3.appendChild(msgdiv);
      msgdiv2.appendChild(msgdiv3);
      msgdiv1.appendChild(msgdiv2);
      document.body.appendChild(msgdiv1);
      var vpsize = e107HelperSize.getViewportSize();
      var left = (vpsize.width - msgdiv1.offsetWidth) / 2;
      var top  = (vpsize.height - msgdiv1.offsetHeight) / 3;
      if (e107HelperBrowser.isIE) {
         msgdiv1.style.pixelTop     = top;
         msgdiv1.style.pixelLeft    = left;
      } else {
         msgdiv1.style.top          = top+"px";
         msgdiv1.style.left         = left+"px";
      }
      msgdiv1.style.visibility   = "";
   },

   /**
    * Display a timed 'popup' message (@see e107Helper.message)
    * Additionally, this method will cancel the 'popup' after the sepcified number of milliseconds
    * @param   string   the message text to be displayed
    * @param   integer  the numbner of milliseconds that the message should be displayed for (defaults to 2500 = 2.5 seconds if not supplied)
    */
   timedMessage : function (msg, msecs) {
      this.message("e107HelperTimedMessage", msg)
      if (msecs == null) {
         msecs = 2500;
      }
      var timer = setTimeout(
         function () {
            var el = document.getElementById("e107HelperTimedMessage");
            el.parentNode.removeChild(el);
         }
         , msecs);
   },

   /**
    * Kills (cancels) a 'popup' message (@see e107Helper.message)
    * Used to kill a 'poup' message once processing has finished
    * @param   string   the HTML ID attribute of the message to be killed
    */
   killmessage : function (id) {
      var el = document.getElementById(id);
      el.parentNode.removeChild(el);
   },

   /**
    * setvalue
    * Set the value of a form field
    * @param   string   text to be set
    * @param   string   a value to be used as a delimiter - will append the supplied text to existing text,
    *                   empty string (or not set) to just replace value
    */
   setvalue : function(id, txt, append) {
      if (typeof append == "string" && append.length > 0) {
         if (document.getElementById(id).value.length > 0) {
            document.getElementById(id).value = document.getElementById(id).value+append+txt;
         } else {
            document.getElementById(id).value = txt;
         }
      } else {
         document.getElementById(id).value = txt;
      }
   },

   /**
    * alert
    * Duplicates the Javascript alert functionality - may be expanded to allow alerts to be turned on/off for debugging.
    */
   alert : function(txt) {
      alert(txt);
   }
};

/*
 * e107HelperStyle
 * Helper object for style handling
 */
var e107HelperStyle = {
   addClass : function(el, clazz) {
      if (!this.hasClass(el, clazz)) {
         el.className += (" " + clazz);
      }
   },
   removeClass : function(el, clazz) {
      if (el.className == null) {
         return;
      }
      var list = el.className.split(" ");
      for (var i=0; i<list.length; ++i ) {
         if (list[i] == clazz) {
            list[i] = "";
         }
      }
      el.className = list.join(" ");
   },
   hasClass : function(el, clazz) {
      var list = el.className.split(" ");
      for (var i=0; i<list.length; ++i ) {
         if (list[i] == clazz) {
            return true;
         }
      }
      return false;
   }
}

/*
 * e107HelperSize
 * Helper object for size information
 */
var e107HelperSize = {
   getViewportSize : function() {
      function isMozHorizScollBarShowing() {
         return ((document.body.offsetWidth < window.innerWidth ) && ( document.documentElement.offsetWidth > document.body.offsetWidth));
      }
      return {
          width : document.body.clientWidth,
          height: e107HelperBrowser.isIE ? document.body.clientHeight : window.innerHeight - (isMozHorizScollBarShowing() ? 15 : 0)
      }
   }
}

/*
 * e107HelperEvent
 * Helper object for event handling (onclick, onkeydown, etc.)
 */
var e107HelperEvents = {
   getEvent : function(evt) {
      if (typeof window.event != "undefined") {
         evt = window.event;
      }
      return evt;
   },
   getCharCode : function( evt ) {
      // Old versions of IWB populate keyCode incorrectly.
      var cc = evt.charCode;
      if ( Browser.isIWB ) {
         // for Ctrl+cursor key/Insert/Delete/Home/End/Page Up/Page Down we
         // get cc of 945 instead of 0. The keyCode is correct.
         if ( cc == 945 && evt.type == "keypress" && evt.ctrlKey ) {
            cc = 0;
         }
      }
      return cc;
   },
   getKeyCode : function( evt ) {
      // Old versions of IWB populate keyCode incorrectly.
      var kc = evt.keyCode;
      if ( Browser.isIWB20 ) {
         // The VKs for lowercase should be the same as uppercase
         if ( evt.type == "keyup" ) {
            // kc has flags in high byte
            if ( kc > 255 ) {
               kc &= 0xff;
               if ( 97 <= kc && kc <= 122 ) {
                  kc -= 32;
               }
            }
         } else if ( evt.type == "keydown" ) {
            // This is not perfect e.g. lowercase W is the same as F8
            if ( 97 <= kc && kc <= 122 ) {
               kc -= 32;
            }
         }
      }
      return kc;
   },
   getTarget : function (event) {
      return ( (Browser.isMoz) ? event.target : window.event.srcElement)
   },
   boundEvents : [],
   bindHandler : function( target, eventName, handler ) {
      if ( target.addEventListener ) {
         target.addEventListener( eventName, handler, false );
      } else if ( target.attachEvent ) {
         target.attachEvent( "on" + eventName, handler );
      }
      e107HelperEvents.boundEvents.push( arguments );
   },
   unbindHandler : function( target, eventName, handler ) {
      if ( target.removeEventListener ) {
         target.removeEventListener( eventName, handler, false );
      } else if ( target.removeEvent ) {
         target.removeEvent( "on" + eventName, handler );
      }
   },
   dispose : function() {
      for(var i = e107HelperEvents.boundEvents.length - 1; i >= 0; --i ) {
         var be = e107HelperEvents.boundEvents[i];
         if (be[1] != "unload") {
            e107HelperEvents.unbindHandler( be[0], be[1], be[2] );
         }
         be[i] = null;
      }
   }
}

e107HelperEvents.bindHandler(window, "unload", e107HelperEvents.dispose);

/*
 * e107HelperBrowser
 * Helper object to determine the client browser
 */
var e107HelperBrowser = {
   ua       : navigator.userAgent,
   isMoz    : /Gecko/i.test(navigator.userAgent),
   isIE55   : /msie 5\.5/i.test(navigator.userAgent),
   isIE6    : /msie 6/i.test(navigator.userAgent),
   isIE     : /msie/i.test(navigator.userAgent),
   isIWB    : /Gecko/i.test(navigator.userAgent) && (/OS\/2/.test(navigator.platform) || /^Warp/.test(navigator.platform)),
   isIWB20  : /Gecko/i.test(navigator.userAgent) && /^Warp/.test(navigator.platform),
   platform : navigator.platform
}

/*
 * e107HelperAutoSuggest
 * Ajax helper object for auto suggest text fields
 * Based on code from http://gadgetopia.com/autosuggest/
 */
var e107HelperAutoSuggest = {
   suggestions : new Array(), // Array to store suggestions that match the user's input
   urls        : new Array(), // List of URL's that are the Ajax request processors
   inputText   : null,        // The text input by the user.
   highlighted : -1,          // A pointer to the index of the highlighted suggestions item. -1 means nothing highlighted.
   div         : null,        // A div to use to create the dropdown, set in add function

   /**
    * Adds the auto suggest functionality to a text input control.
    * @param   elem  the text input control that is to get auto suggest functionality
    * @param   url   the URL of the Ajax request processor
    */
   add : function(elem, url) {
      if (this.div == null) {
         // First time in so create a hidden DIV which we wil luse to display suggestions
         //this.div                   = document.createElement("DIV");
         //var emptyList              = document.createElement("UL");
         //this.div.id                = "e107helperautosuggest";
         //this.div.style.visibility  = "hidden";
         //this.div.appendChild(emptyList);
         //document.body.appendChild(this.div);
         document.writeln("<div id='e107helperautosuggest' style='height:200px;overflow:scroll;display:none;'><ul></ul></div>");
         this.div = document.getElementById("e107helperautosuggest");
      }

      e107HelperAutoSuggest.urls[elem.id] = url;

      /**
       * Key down event handler for the input element.
       * Enter                      use the highlighted suggestion, if there is one.
       * Esc                        hide the suggestion dropdown
       * Up/down/page up/page down  Move the highlight up and down in the suggestions.
       * @param   event the event object
       */
      elem.onkeydown = function(ev) {
         var ev = e107HelperEvents.getEvent(ev);
         var key = e107HelperEvents.getKeyCode(ev);
         var tgt = ev.srcElement;

         switch(key) {
            case e107Helper.KEY_TAB :
               if (!ev.isAlt && !ev.isCtrl) {
                  // Only do this if tab navigation is to another control on this page
                  e107HelperAutoSuggest.showDiv(false);
                  this.focus();
               }
               break;
            case e107Helper.KEY_ENTER :
               e107HelperAutoSuggest.useSuggestion(tgt);
               e107HelperAutoSuggest.showDiv(false);
               ev.returnValue = false;
               break;
            case e107Helper.KEY_ESC :
               e107HelperAutoSuggest.showDiv(false);
               break;
            case e107Helper.KEY_UP :
               if (e107HelperAutoSuggest.highlighted > 0) {
                  e107HelperAutoSuggest.highlighted--;
               }
               e107HelperAutoSuggest.changeHighlight();
               break;
            case e107Helper.KEY_DOWN :
               if (e107HelperAutoSuggest.highlighted == -1 && e107HelperAutoSuggest.suggestions.length > 0) {
                  e107HelperAutoSuggest.getSuggestions(tgt);
               } else if (e107HelperAutoSuggest.highlighted < (e107HelperAutoSuggest.suggestions.length - 1)) {
                  e107HelperAutoSuggest.highlighted++;
               }
               e107HelperAutoSuggest.changeHighlight();
               break;
            case e107Helper.KEY_HOME :
               if (e107HelperAutoSuggest.highlighted > 0) {
                  e107HelperAutoSuggest.highlighted = 0;
               }
               e107HelperAutoSuggest.changeHighlight();
               break;
            case e107Helper.KEY_END :
               if (e107HelperAutoSuggest.highlighted < e107HelperAutoSuggest.suggestions.length-1) {
                  e107HelperAutoSuggest.highlighted = e107HelperAutoSuggest.suggestions.length-1;
               }
               e107HelperAutoSuggest.changeHighlight();
               break;
         }
      };

      /**
       * Key up event handler for the input element.
       * If there is some text and it has changed from the last time we got a keyup event display a list of suggestions.
       * @param   ev the event Object
       */
      elem.onkeyup = function(ev) {
         var ev = e107HelperEvents.getEvent(ev);
         var key = e107HelperEvents.getKeyCode(ev);
         var tgt = ev.srcElement;

         switch(key) {
            // The control keys were already handled by onkeydown, so do nothing.
            case e107Helper.KEY_ENTER:
            case e107Helper.KEY_ESC:
            case e107Helper.KEY_UP:
            case e107Helper.KEY_DOWN:
            case e107Helper.KEY_PAGE_UP:
            case e107Helper.KEY_PAGE_DOWN:
            case e107Helper.KEY_HOME:
            case e107Helper.KEY_END:
               return;
            default:
               if (this.value.length > 0) {
                  if (this.value != e107HelperAutoSuggest.inputText) {
                     e107HelperAutoSuggest.inputText = this.value;
                     e107HelperAutoSuggest.getSuggestions(tgt);
                  }
               } else {
                  e107HelperAutoSuggest.inputText = "";
                  e107HelperAutoSuggest.showDiv(false);
               }
         }
      };

      /**
       * Insert the highlighted suggestion into the input box, and remove the suggestion dropdown.
       */
      this.useSuggestion = function(tgt) {
         if (this.highlighted > -1) {
            tgt.value = this.suggestions[this.highlighted];
            this.showDiv(false);
         }
      };

      /**
       * Show or hide the DIV used to display suggestions.
       * @param   showit   true to show DIV, false to hide it
       */
      this.showDiv = function(showit) {
         if (showit) {
            e107HelperAutoSuggest.div.style.display      = 'block';
            e107HelperAutoSuggest.div.style.visibility   = '';
         } else {
            e107HelperAutoSuggest.div.style.display      = 'none';
            e107HelperAutoSuggest.div.style.visibility   = 'hidden';
            this.highlighted = -1;
         }
      };

      /**
       * Modify the HTML in the dropdown to move the highlight.
       */
      this.changeHighlight = function() {
         var lis = e107HelperAutoSuggest.div.getElementsByTagName('LI');
         for (i in lis) {
            var li = lis[i];

            if (this.highlighted == i) {
               li.style.backgroundColor = "Highlight";
               li.firstChild.style.color = "HighlightText";
               li.scrollIntoView(false);
            } else {
               if (li.style) {
                  li.style.backgroundColor = "";
                  li.firstChild.style.color = "";
               }
            }
         }
      };

      /**
       * Build the HTML for the dropdown div.
       * @param   tgt   text input control that DIV is to be created for
       */
      this.createDiv = function(tgt) {
         var ul                  = document.createElement('ul');
         ul.style.padding        = "0px";
         ul.style.margin         = "0px";
         ul.style.listStyleType  = "none";

         //Create an array of LI's for the words.
         for (i in this.suggestions) {
            var word                = this.suggestions[i].replace(/\s/g, "&nbsp;"); // Convert spaces to non-breaking spaces for display
            var li                  = document.createElement('li');
            var a                   = document.createElement('a');
            a.href                  = "javascript:return false";
            a.id                    = "e107helperautocomplete_"+tgt.id;
            a.style.textDecoration  = "none";
            a.innerHTML             = word;
            li.appendChild(a);
            if (e107HelperAutoSuggest.highlighted == i) {
               ////li.className = "selected";
            }
            ul.appendChild(li);
         }

         e107HelperAutoSuggest.div.replaceChild(ul,e107HelperAutoSuggest.div.childNodes[0]);

         /**
          * mouseover event handler for the dropdown ul.
          * move the highlighted suggestion with the mouse
          * @param   ev the event Object
          */
         ul.onmouseover = function(ev) {
            //Walk up from target until you find the LI.
            var ev = e107HelperEvents.getEvent(ev);
            var target = ev.srcElement;
            while (target.parentNode && target.tagName.toUpperCase() != 'LI') {
               target = target.parentNode;
            }

            var lis = e107HelperAutoSuggest.div.getElementsByTagName('LI');


            for (i in lis) {
               var li = lis[i];
               if (li == target) {
                  e107HelperAutoSuggest.highlighted = i;
                  break;
               }
            }
            e107HelperAutoSuggest.changeHighlight();
         };

         /**
          * mouseover event handler for the dropdown ul.
          * Insert the clicked suggestion into the text input control
          * @param   ev the event Object
          */
         ul.onclick = function(ev) {
            var ev = e107HelperEvents.getEvent(ev);
            e107HelperAutoSuggest.useSuggestion(document.getElementById(ev.srcElement.id.substring(23)));
            e107HelperAutoSuggest.showDiv(false);
            ev.returnValue = false;
            return false;
         };

         e107HelperAutoSuggest.div.className="tbox";
         e107HelperAutoSuggest.div.style.border = "1px solid";
         e107HelperAutoSuggest.div.style.padding = "4px";
         e107HelperAutoSuggest.div.style.position = 'absolute';

         // Position the DIV in the right place, relative to the text input control
         var x = 0;
         var y = tgt.offsetHeight;

         //Walk up the DOM and add up all of the offset positions.
         while (tgt.offsetParent && tgt.tagName.toUpperCase() != 'BODY') {
            x += tgt.offsetLeft;
            y += tgt.offsetTop;
            tgt = tgt.offsetParent;
         }

         x += tgt.offsetLeft;
         y += tgt.offsetTop;

         e107HelperAutoSuggest.div.style.left = x + 'px';
         e107HelperAutoSuggest.div.style.top = y + 'px';
      };

      /**
       * Get a list of suggestions from the server
       * @param   tgt   the text field control for which suggestions are required
       */
      this.getSuggestions = function(tgt) {
         e107HelperAjax.addParm("action", "getautosuggestions");
         e107HelperAjax.addParm("id", tgt.id);
         e107HelperAjax.addParm("value", tgt.value);
         e107HelperAjax.post(e107HelperAutoSuggest.urls[tgt.id]);
      };
   },

   /**
    * Ajax callback function to set suggested values obtained from server in the dropdown DIV.
    * @param   id             id of the input text control for which suggestions are to be displayed for
    * @param   value          original value of input text field
    * @param   suggestionss   comma seperated list of suggestions to be displayed
    */
   setSuggestions : function(id, value, suggestions) {
      var tgt = document.getElementById(id);
      // Ensure we have a valid target
      if (typeof tgt != "undefined") {
         // Only process of content of target has not changed
         if (tgt.value == value) {
            suggestions = suggestions.split(",");

            this.suggestions.length = 0;
            for (var i=0; i<suggestions.length; i++) {
               if (suggestions[i].length > 0) {
                  this.suggestions.push(suggestions[i]);
               }
            }

            e107HelperAutoSuggest.createDiv(tgt);
            e107HelperAutoSuggest.showDiv(true);
            e107HelperAutoSuggest.highlighted = 0;
            e107HelperAutoSuggest.changeHighlight();
         }
      }
   }
}

/*
 * e107HelperAjax
 * Ajax helper object for JavasCript/HTTP Request communication with the server
 */
var e107HelperAjax = {
   parms : "",
   getRequester : function() {
      // Create an instance of the XML HTTP Request object
      var oXMLHTTP;

      try {
         oXMLHTTP=new ActiveXObject("Msxml2.XMLHTTP")
      } catch (e) {
         try {
            oXMLHTTP=new ActiveXObject("Microsoft.XMLHTTP")
         } catch (e) {
         }
      }

      // Non IE browsers start here.
      if (typeof oXMLHTTP == "undefined") {
         try {
            oXMLHTTP = new XMLHttpRequest();
         } catch (e) {
         }
      }
      return oXMLHTTP;
   },
   post : function(uri, async) {
      //alert("Posting");
      // default to async
      if (typeof async == "undefined") {
         async = true;
      }
//      var req = this.getRequester();
      var req = new XMLHttpRequest();  // Need to ensure e_ajax.php is included as this caters for different browser implementations of XMLHttpRequest
      if (async) {
         req.onreadystatechange = function() {
            // 0 uninitialized   Object created, open not called yet
            // 1 loading         Called open but not send
            // 2 loaded          Called send, got status and headers but not body
            // 3 interactive     Some data received (and available) but not all
            // 4 completed       All the data has been received
            //alert(req.readyState);
            if (req.readyState == 4) {
               e107HelperAjax.responseHandler(req);
            }
         };
      }
      req.open("POST", uri, async);
      req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
      //alert(this.parms);
      req.send(this.parms);
      this.parms = "";
      if (!async) {
         this.responseHandler(req);
      }
   },
   addParm : function(name, value) {
      if (this.parms.length > 0){
         this.parms += "&";
      }
      this.parms += escape(name) + "=" + escape(value);
   },
   responseHandler : function (req) {

      if (req.responseXML == null) {
         // It's text
      } else {
         // It's an XML message so get a node list represneting a list of all nodes inside the e107helperajax node
         // other nodes outside of e107helperajax will be ignored
         var nodes = req.responseXML.getElementsByTagName("e107helperajax")[0].childNodes;

         // Now process each direct child node, according to it's type attribute
         for (i=0; i<nodes.length ; i++) {

            // Do not process anything other than element nodes
            if (nodes[i].nodeType != 1) {
               //alert("Ignoring nodeType"+nodes[i].nodeType);
               continue;
            }

            // Pick out the nodes we know how to handle
            switch (nodes[i].getAttribute("type")) {
               // A simple Javascript alert
               case "alert" : {
                  alert(nodes[i].firstChild.data);
                  break;
               }
               // An e107Helper dialog box
               case "dialog" : {
                  e107Helper.dialog(nodes[i].getAttribute("id"), nodes[i].firstChild.data, nodes[i].getAttribute("width"), nodes[i].getAttribute("focus"), nodes[i].getAttribute("key"));
                  break;
               }
               // Remove an e107Helper dialog box
               case "killdialog" : {
                  e107Helper.killDialog(nodes[i].getAttribute("id"));
                  break;
               }
               // HTML to be added as the innerhtml property of a page element (replacing what was there)
               case "innerhtml" : {
                  document.getElementById(nodes[i].getAttribute("id")).innerHTML = nodes[i].firstChild.data;
                  break;
               }
               // Javascript to be evaluated
               case "js" : {
                  // old not used by bugrain! eval(nodes[i].getAttribute("func"));
                  eval(nodes[i].firstChild.data);
                  break;
               }
               // Set the value of a form field
               case "setvalue" : {
                  e107Helper.setvalue(nodes[i].getAttribute("id"), nodes[i].firstChild.data, nodes[i].getAttribute("append"));
                  break;
               }
               // Kills a 'popup' message (@see e107Helper.message)
               case "killmessage" : {
                  e107Helper.killmessage(nodes[i].getAttribute("id"));
                  break;
               }
               // Restores the saved HTML source
               case "restorebody" : {
                  e107Helper.restoreBodyHTML();
                  break;
               }
               // Display a timed 'popup' message (@see e107Helper.message)
               case "timedmessage" : {
                  e107Helper.timedMessage(nodes[i].firstChild.data, nodes[i].getAttribute("msecs"));
                  break;
               }
               // Display a timed 'popup' message (@see e107Helper.message)
               case "autosuggestions" : {
                  e107HelperAutoSuggest.setSuggestions(nodes[i].getAttribute("id"), nodes[i].getAttribute("value"), nodes[i].firstChild.data);
                  break;
               }
               default : {
                  // Do nothing
               }
            }
         }
      }
   },
   refreshImageTag : function(uri, tagId, base) {
      this.addParm("action", "refreshImageTag");
      this.addParm("id", tagId);
      this.addParm("base", base);
      var dir = document.getElementById(tagId+"_dir");
      this.addParm("dir", dir.value);
      this.post(uri);
   },
   toggleImage : function(uri, id, img1, img2) {
      this.addParm("action", "toggleImage");
      this.addParm("id", id);
      this.addParm("img1", img1);
      this.addParm("img2", img2);
      this.post(uri);
   },
   showCustomField : function(uri, id) {
      this.addParm("action", "showCustomFields");
      this.addParm("id", id);
      this.addParm("customfields", document.getElementById(id).value);
      this.post(uri);
   },
   addCustomField : function(uri, id, step, fieldtype, fieldname) {
      // Create and send message
      this.addParm("action", "addCustomField");
      this.addParm("id", id);
      if (typeof step != "undefined") {
         this.addParm("fieldtype", fieldtype);
      }
      if (typeof fieldname != "undefined") {
         this.addParm("fieldname", fieldname);
      }
      if (document.getElementById(id).value != null && document.getElementById(id).value != "undefined") {
         this.addParm("customfields", document.getElementById(id).value);
      }
      if (typeof step != "undefined") {
         this.addParm("step", step);
         if (step == 3) {
            // Add values for each attribute of this custom field
            var els = document.getElementsByName(id+"_attrib");
            for (var i=0; i<els.length; i++) {
               this.addParm(els[i].id, els[i].value);
            }
            if (fieldtype == "select" || fieldtype == "radio") {
               els = document.getElementsByName(id+"_option[]");
               if (fieldtype == "select") {
                  for (var i=0; i<els.length; i++) {
                     this.addParm(els[i].id+i, els[i].value);
                  }
               } else {
                  for (var i=0; i<els.length; i++) {
                     var j = parseInt(i/2);
                     this.addParm(els[i].id+j, els[i].value);
                  }
               }
            }
         }
      }
      this.post(uri);
   }
}

/*
 * Prototyping stuff - not for general use
 */

// ****************************************************************************
// IE emulation stuff for Mozilla based browsers
// ****************************************************************************
if (e107HelperBrowser.isMoz) {
   Event.prototype.__defineSetter__("returnValue",
      function (b) {
         if (!b)
            this.preventDefault();
         return b;
      }
   );
   Event.prototype.__defineSetter__("cancelBubble",
      function (b) {
         if (b)
            this.stopPropagation();
         return b;
      }
   );
   Event.prototype.__defineGetter__("srcElement",
      function () {
         var node = this.target;
         while (node.nodeType != 1 && node.nodeType != 9)
            node = node.parentNode;
         return node;
      }
   );
}

function editCell(id, cellSpan) {
   var inputWidth = (document.getElementById(id).offsetWidth / 7);
   var oldCellSpan = cellSpan.innerHTML;
   var temp = "<form name='activeForm' onsubmit='parseForm(\""+id+"\", \""+id+"input\");return false;' style='margin:0;' action=''>";
   temp += "<input type='text' id='"+id+"input' size='"+inputWidth+"' onblur='parseForm(\""+id+"\", \""+id+"input\");return false;'>";
   temp += "<br /><noscript><input value='OK' type='submit'></noscript></form>";
   document.getElementById(id).innerHTML = temp;
   document.getElementById(id+"input").value = oldCellSpan;
   document.getElementById(id+"input").focus();
   document.getElementById(id).style.background = '#ffc';
//   document.getElementById(id).style.border = '1px solid #fc0';
}

function parseForm(cellID, inputID) {
   var st = document.getElementById(inputID).value + '~~|~~' + cellID;
   document.getElementById(cellID).innerHTML = "<span class=\"update\">Updating...</span>";
   //x_changeText(st, textChanger_cb);
//   document.getElementById(cellID).style.border = 'none';
}

