// SCRIPT MODULES LIBRARY   ECMAScript v3.0   (c) 2007 Codesmiths codesmiths.com.au - all rights reserved
//  
// a Global object CSS07 is assumed declared  CSS07  may be made unique for deployment 
// this relies on CSS07 being populated with CSS07.kpl
try
{

// ===========================================================================
// NAMESPACE MANAGEMENT - ONLY ONE GLOBAL IDENTIFIER RESERVED ('CSS07')
  if (!CSS07.kpl) { window.status = 'CSM-LIB: CSS07.kpl not loaded]'; throw new Error(window.status); }

  if (!CSS07.csm) { CSS07.csm = { USAGE:'codesmiths.com.au ecmascript & event lib' }; } 

// ===========================================================================
// CLASS: Controls for a player, A mouse Tracker and general utility functions  --  PJM
// Provides user-interface to define generic player operations:

// Operation          Symbol     Suggested ID for buttons

// Play                 |>       sldshow1_play 
// Pause                ||       sldshow1_pause
// Stop                 []       sldshow1_stop   
// Change forward       >|       sldshow1_forward
// Change back          |<       sldshow1_back
// Speed up             >>       sldshow1_fast
// Slow Down            <<       sldshow1_slow
// Suspend changes      <>       sldshow1_hold
// Fade transition      //       sldshow1_fade

// Implementation is generic - subject to the following interface 

// - implemention requires a "player" object with interface functions as follow:
// -  projector.getEventFunctionList() //  (and functions to match actions to this list to be used as suggested above)
// -  projector.bindControlSet(callBackActivateFunction)  // Optional - needed for deferred binding
// -  syntax of ctrlSpecifiers:
// -    comma separated list of element/event specifiers's (actually ID suffixes if a prefix argument (2) is supplieded i.e. any prefix will be prepended)
// -    the ID suffixes may be followed by a special char (. or :) and a function name (supported by the player)
// -    if there is no such char the one name must double as an ID suffix and function name.
// -    the default event is a click.
// -    if there is a colon (:) in the specifyer and no pair operator, then a double click event is assigned (it may have a function name following)
// -    a slash (/) in a function name indicates it is a mouseover/mouseout pair 
// -    a caret (^) in a function name indicates it is a mousedown/mouseup  pair 
// -    (these "pair operators" can be in an ElementSpecifyer for a custom pair or in the ops string (playerFunctionList)
// -    a colon after an element ID instead of a dot means a double click is required
// -    
// -    Syntax summary:
// -    [ID suffix][(. OR :)[func name]][(/ OR ^)[paired func]]
// -      at least one of ID suffix or func name must be supplied.
// -      the first  pair of operators (. OR :) are named click operators.
// -      the second pair of operators (/ OR ^) are named pair  operators.
// -      the pair  operators have precence if present so either of the click operators may be used as ID/func name separators if a 
// -      if a click operator starts the string, there is no ID suffix and the prefix is the whole ID, which must be supplied 
// -      if a click operator ends the string, the ID suffix and function name are the same
// -

CSS07.csm.Controls = function (player, ctrlElementPrefix, ctrlSpecifiers)        // CLASS CONSTRUCTOR //
        {
          try
          {
            if (!(this instanceof CSS07.csm.Controls ))             { return null; }          // constructor was CALLed as a function
            
            var c = ',';                                        // special characters
            var d = '.';
            var h = '/';
            var k = ':';
            var m = '^';  

            var ops = c + player.getEventFunctionList() + c ;   // these are the UI functions of the player instance used by (in) the player

            this.eventElmts = new Array;                        // elements with associated click events
            this.eventFuncs = new Array;
            this.eventTypes = new Array;                        // single or double
 
            CSS07.kpl.Encapsulator.call(this);                   // 'inherit'   Encapsulator's features
            var ii = 0;                                         // index for mapped events
            
            for(var jj = 0; jj<ctrlSpecifiers.length; jj++) {
              // scan for user supplied duplicate strings's
              var curr_name = ctrlSpecifiers[jj];
              for(var kk = 0; kk < jj; kk++) {
                if (ctrlSpecifiers[kk] == curr_name) { break; }
              }  
              if (kk < jj || curr_name.length === 0) { break; }
              
              // see what special chars are in the specifyer
              var func_posn = curr_name.indexOf(d);          // optional click separator
              if (func_posn < 0) { 
                  func_posn = curr_name.indexOf(k);          // double click separator
              } 
              var pair_posn = curr_name.indexOf(h);          // iff > -1 bind to a pair of hover events
              if (pair_posn < 0) { 
                  pair_posn = curr_name.indexOf(m);          // mouse up/down separator
              }
              var elmt_term = (func_posn > -1) ? func_posn : pair_posn;
              var elmt_name = (elmt_term > -1) ? curr_name.substr(0,elmt_term) : curr_name;
              
              var func_name = curr_name.substr(func_posn +1, curr_name.length);
              if (func_name.length === 0) { func_name = elmt_name; }

              var func_pair = "";
              var hoverEvent = false;
              var mouseEvent = false;
              
              if (pair_posn >= 0) {                            // user-defined event fn has a paired event 
                pair_posn = func_name.indexOf(h);              // find it in the string without the element ID
                if (pair_posn > -1) {
                   hoverEvent = true;
                } else {
                    pair_posn = func_name.indexOf(m);          // mouse up/down separator
                    mouseEvent = (pair_posn > -1);
                }
                func_pair = func_name.substr(pair_posn +1, func_name.length);
                func_name = func_name.substr(0,pair_posn); 
              }
              
              var eltID = ctrlElementPrefix + (elmt_name); 
              var elt = window.document.getElementById(eltID);              
                
              if(elt) {

                // look up the function required -- potential multiple schemes here
                
                // A hover event may be user-defined by a slash between two fn names OR defined by a slash in the player's fn list
                var operator = ops.indexOf(c + func_name + h);
                if (operator > 0) {
                  if (!mouseEvent) {
                    hoverEvent = true;
                  }
                  if (!(func_pair.length > 0)) {
                    func_pair = CSS07.csm.extractValue(ops, c + func_name + h, c);
                  }
                   
                } else {
                // A mouse down/mouse up pair may be user-defined by a caret between two fn names OR defined by a slash in the player's fn list
                  operator = ops.indexOf(c + func_name + m);
                  if (operator > 0) {
                    mouseEvent = true;
                    if (!(func_pair.length > 0)) {
                      func_pair = CSS07.csm.extractValue(ops, c + func_name + h, c);
                    }
                  } 
                }
 
                var clickEvent = !hoverEvent && !mouseEvent && ops.indexOf(c + func_name + c > -1);
                if (hoverEvent || mouseEvent) {

                  this.eventElmts[ii] = elt;
                  this.eventFuncs[ii] = this.bind(player,func_name);  // the function refs must be stored for unbinding
                  this.eventTypes[ii] = hoverEvent ? 'mouseover' : 'mousedown';
                  ii++; 

                  // check on the pair fn
                  if (!(func_pair.length > 0) || ops.indexOf(func_pair + c) < 0) {  // the user has given no pair or it does not exist in the fn list
                      func_pair = CSS07.csm.extractValue(ops, c + func_name + h, c);
                  }
                  if (!(func_pair.length > 0)) {
                      func_pair = CSS07.csm.extractValue(ops, c + func_name + m, c); 
                  }
                  if (func_pair.length > 0) {
                    this.eventElmts[ii] = elt;
                    this.eventFuncs[ii] = this.bind(player,func_pair);  
                    this.eventTypes[ii] = hoverEvent ? 'mouseout' : 'mouseup';
                    ii++; 
                  }
                } 
                if (clickEvent) {
                  this.eventElmts[ii] = elt;
                  this.eventFuncs[ii] = this.bind(player,func_name);  // the function refs must be stored for unbinding
                  this.eventTypes[ii] = (func_posn > -1 && curr_name.charAt(func_posn) == k) ? 'dblclick' : 'click';
                  ii++;  
                }
              }
            }
            this.enableEvents = function ()              // event enable (typically deferred)
            {
              for(var ii = 0; ii<this.eventElmts.length; ii++) {  
                this.bindEvent(this.eventElmts[ii], this.eventFuncs[ii], this.eventTypes[ii]);
                this.eventElmts[ii].style.cursor = 'pointer';
              }
            };
            // if player has a deferred binding fn use it
            if (!!player.bindControlSet) { player.bindControlSet(this.bind(this,this.enableEvents)); }
            else { this.enableEvents(); }                // else just execute the bind
            //player.bindControlSet(this.enableClicks);  //  tried this to see if the bind was necessary here
            this.unload = this.bindEvent(window, this.bind(this,'destroy'), 'unload');   // function ref (evtHdlr) stored for unbinding
          }
          catch(e){this.destroy();}
        };
        
CSS07.csm.Controls.prototype.destroy = function ()
        {
          try
          {
            for(var ii = 0; ii<this.eventElmts.length; ii++) {
              if (this.eventElmts[ii])
              {
                this.unbindEvent(this.eventElmts[ii], this.eventFuncs[ii], this.eventTypes[ii]);
                this.eventElmts[ii] = null;
                this.eventFuncs[ii] = null;
                this.eventTypes[ii] = null;
              }
            }
            this.unbindEvent(window, this.unload, 'unload');
            this.proj = null;
          }
          catch(e){} 
        };
//                                                                                  // END: Controls 

CSS07.csm.Sequence = function (baseName, nmbrVariants)        // CLASS CONSTRUCTOR //   // Sequence
        {
          try
          {
            if (!(this instanceof CSS07.csm.Sequence))              { return null; }        // constructor was CALLed as a function

            this.nmbrItems = nmbrVariants;
            this.nameBase  = baseName;
            this.prevItem  = 0;
          }
          catch(e){this.destroy();}
        };
CSS07.csm.Sequence.prototype.next = function ()
        {
           this.prevItem = (this.prevItem >= this.nmbrItems) ? 1 : this.prevItem + 1;
           return (this.nameBase + this.prevItem);
        };
CSS07.csm.Sequence.prototype.current = function ()
        {
           return (this.nameBase + this.prevItem);
        };
CSS07.csm.Sequence.prototype.destroy = function ()
        {
          try
          {
            this.nmbrItems = null;
            this.nameBase  = null;
            this.prevItem  = null;
          }
          catch(e){}
        };
//                                                                                  // END: Sequence CLASS //

CSS07.csm.ClassChanger = function (eltID, baseName, nmbrVariants)        // CLASS CONSTRUCTOR //
        {
          try
          {
            if (!(this instanceof CSS07.csm.ClassChanger))              { return null; }        // constructor was CALLed as a function

            this.target = window.document.getElementById(eltID);              
            this.classes = new CSS07.csm.Sequence(baseName, nmbrVariants);
            if (this.target.className.length ===0) { this.next(); }
          }
          catch(e){this.destroy();}
        };
CSS07.csm.ClassChanger.prototype.next = function ()
        {
           var c = this.target.className;  
           // How to deal with multiple classnames?
           if (c.indexOf(this.classes.current()) > -1) {
             c = c.replace(this.classes.current(), this.classes.next());
           }
           else { c = ((c && c.length) ?  c + ' ' : '') + this.classes.next(); }
           this.target.className = c;
        };
 CSS07.csm.ClassChanger.prototype.getEventFunctionList = function ()
        {
           return('next');
        };
 
CSS07.csm.ClassChanger.prototype.destroy = function ()
        {
          try
          {
            this.styles.destroy();
            this.styles = null;
            this.target = null;
          }
          catch(e){}
        };
//                                                                                  // END: ClassChanger CLASS //

// ===========================================================================
// CLASS:     Tracker   // CLASS CONSTRUCTOR //
// Causes an element (the ball) to move in various ways based on mouse movement within another element (the field)
// Because multiple instances of the ball and/or field may exist as "includes" inside containers 
//   it is not always convenient to have an id on each active item
//   for this reason we can scan down the DOM from an ID to fild the field or the ball based on class name or element name
//   This is done with selector strings which are simiolar to CSS selectors
//   each selector must match an element in the document by ID, which ID is its first token
//   each following token (if any) may be a tag name OR a class name preceded by "."
//   each successive selector must exist in the children of the previous token or the selection fails
//   if the token matches a child the FIRST such match becomes the parent element for the next token match
//   the tokens are separated by spaces or ">" chars (the latter is closer to CSS syntax but less readable).
//    eg 'id1 .class1 div qill 

CSS07.csm.Tracker = function (fieldSpec,         // Selector for field  eg 'id1'  OR  'id1>.class1>div'
                            ballSpec, 
                            sx, 
                            sy, 
                            motion) 
        {
          try
          {
            
            if (!(this instanceof CSS07.csm.Tracker ))             { return null; }          // constructor was CALLed as a function
            CSS07.kpl.Encapsulator.call(this);           // 'inherit'   Encapsulator's features
            
            // ball and field Spec must start with an ID followed by Optional CSS selectors for class or tagName of each level of descendents

            this.field = CSS07.csm.selectElt(fieldSpec);
            if (!(this.field))             { return null; }       // must have a field ID to play On
            this.ball  = CSS07.csm.selectElt(ballSpec);
            if (!(this.ball))              { return null; }       // must have a ball ID to play with

            this.fact = 8;                                        // "Leverage" factor in movement ball moves 1/fact of mouse
            this.xoffset = (sx -50)/100/this.fact;                          // sx, sy is the "start" mouse position (% of field)
            this.yoffset = (sy -50)/100/this.fact;                          // stored offsets are fractions of the field from centre
            //  following maybe args in due course
            this.trackCursor = 'crosshair';
            
            
            if (motion.indexOf('ride') > -1) {
              this.ball_hite = this.ball.clientHeight;  // balls offset corrections
              this.ball_span = this.ball.clientWidth;
              
              this.rideit = this.bind(this, 'ride');
              this.bindEvent(this.field, this.rideit, 'mousemove');
              this.ball.style.cursor = this.trackCursor;
            }
            if (motion.indexOf('shadow') > -1) {
              this.centX = this.field.offsetLeft +(this.field.offsetWidth /2); 
              this.centY = this.field.offsetTop  +(this.field.offsetHeight/2);
              this.origX = this.ball.offsetLeft;
              this.origY = this.ball.offsetTop;
              var startX = this.origX -(this.field.offsetWidth  *this.xoffset);
              var startY = this.origY -(this.field.offsetHeight *this.yoffset);
              
              this.place(this.ball, startX, startY);
              
              this.biasit = this.bind(this, 'bias');
              this.bindEvent(this.field, this.biasit, 'mousemove');
            }
            this.field.style.cursor = this.trackCursor;
            this.unload = this.bindEvent(window, this.bind(this,'destroy'), 'unload');   // function ref (evtHdlr) stored for unbinding
          }
          catch(e) { this.destroy(); alert('Tracker failed'); }
        };
        
CSS07.csm.Tracker.prototype.place = function (elt, newX, newY)
        {
          try
          {
              elt.style.left = '' + newX + 'px' ;
              elt.style.top  = '' + newY + 'px' ;             
          }
          catch(e){}
        };

CSS07.csm.Tracker.prototype.ride = function(evt)
        {
          try
          {
              
              var newX = evt.clientX - Math.round(this.ball_span/2.00);
              var newY = evt.clientY - Math.round(this.ball_hite/1.33);
              
              this.place(this.ball, newX, newY);
          }
          catch(e){}
        };
CSS07.csm.Tracker.prototype.bias = function(evt)
        {
          try
          {            
            // if a child div is offset out of the field it still bubbles up the mouse events, so we filter
            if (CSS07.csm.eltTest(evt, this.field))
            {
              var mousRx = evt.clientX - this.centX;  // translate to Central Origin
              var mousRy = evt.clientY - this.centY;

              var newX = this.origX - Math.round(mousRx/this.fact);
              var newY = this.origY - Math.round(mousRy/this.fact);

              this.place(this.ball, newX, newY);
            } 
          }
          catch(e){}
        };
CSS07.csm.Tracker.prototype.destroy = function ()
        {
          try
          {
            this.unbindEvent(this.field, this.rideit, 'mousemove');
            this.unbindEvent(this.field, this.biasit, 'mousemove');
            this.unbindEvent(window, this.unload, 'unload');
          }
          catch(e){}
        };
//                                                                                  // END: Tracker Class

// CLASS:     XmlReq   // CLASS CONSTRUCTOR //

CSS07.csm.XmlReq = function(onLoadCallback)
        {
          try {
            if (!(this instanceof CSS07.csm.XmlReq ))             { return null; }          // constructor was CALLed as a function

            CSS07.kpl.Encapsulator.call(this);           // 'inherit'   Encapsulator's features

            var xmlRequest=null;
            if (window.XMLHttpRequest){ 
              // Mozilla
              xmlRequest = new XMLHttpRequest()
            if (xmlRequest.overrideMimeType)
              xmlRequest.overrideMimeType('text/xml')
            }
            else if (window.ActiveXObject){ 
              //IE
              try {
                xmlRequest = new ActiveXObject("Msxml4.XMLHTTP");
              } 
              catch (e){
                try{
                  xmlRequest = new ActiveXObject("Microsoft.XMLHTTP");
                }
                catch (e){}
              }
            }
            this.xmlRequest = xmlRequest;
            this.callback = onLoadCallback;
          }
          catch(e){}
        };
        
        
CSS07.csm.XmlReq.prototype.getXMLfile=function(xmlfile)
        {
          if (this.xmlRequest){
            this.xmlFile = xmlfile;
            this.onLoad = this.bind(this, this.load);
            this.bindEvent(this.xmlRequest, this.onLoad, 'readystatechange');
            this.xmlRequest.open('GET', this.xmlfile, true)
            this.xmlRequest.send(null)
          }
        };
        
CSS07.csm.XmlReq.prototype.load=function(evt)
        {
          var success = false;
          if (this.xmlRequest.readyState == 4)
          { //if request of file completed
            if (this.xmlRequest.status==200 || window.location.href.indexOf("http")==-1)
            { //if request was successful
              this.xmldata=this.xmlRequest.responseText
              success = true;
            }
          }
          if (this.callback) { this.callback(success); }
        };
//                                                                                  // END: XMLObj Class
          
// CLASS:     ClickHandler   // CLASS CONSTRUCTOR //
// allows click and dblClick on same elt ()

   CSS07.csm.ClickHandler = function(oneClickHandler, dblClickHandler)
        {
          try {
            if (!(this instanceof CSS07.csm.ClickHandler))             { return null; }          // constructor was CALLed as a function

            CSS07.kpl.Encapsulator.call(this);           // 'inherit'   Encapsulator's features
            this.getTime = CSS07.csm.getCurrentTime;
            
            this.dcWaitTime= 300; // time to wait for a second click
            this.dcTimeout = 200; // time when follow-on clicks are ignored (should be < dcWaitTime)
            this.lastClk = 0;
            this.lastDbl = 0;
          
            this.oneClick = oneClickHandler;
            this.dblClick = dblClickHandler;
          }
          catch(e){}
        };
   
   CSS07.csm.ClickHandler.prototype.handleEvent = function(evt)
        {
          try {
            //window.status = 'Checking Event: ' +evt.type;
            switch (evt.type) {
              case 'click':
                
                if (this.ignoreClick()) {
                  //evt.cancelBubble = true;
                  //evt.stopPropagation();
                  return true;  // handled
                }
                //if (evt.clientX === 0 && evt.clientY === 0) {
                //  window.status = 'zero client coords!';
                //}
                this.savedEvt = { clientX:evt.clientX, clientY:evt.clientY };
                
                if (this.timerRef) { 
                  // safety - don't leave two pending
                  clearTimeout(this.timerRef); 
                }
                this.lastClk = this.getTime();
                this.timerRef = window.setTimeout(this.bind(this, this.timerCallback), this.dcWaitTime);
                return true;
              break;
              
              case 'dblclick':
                if (this.timerRef) {
                  window.clearTimeout(this.timerRef); 
                  this.timerRef = null;
                }
                this.savedEvt = null;
                //window.status =  window.status + '; Dble Click Fired';
                this.lastDbl = this.getTime();
                return (this.dblClick(evt));
              break
            }
          }
          catch(e){}
        };
        
   CSS07.csm.ClickHandler.prototype.ignoreClick   = function(evt) 
        {
          // ignore clicks in the shadow of another click or dbleclick event 
          var now = this.getTime();
          var ch1 = now - (this.lastClk || 0);
          var ch2 = now - (this.lastDbl || 0);
          
          if ((ch1 < this.dcWaitTime) || (ch2 < this.dcTimeout)) {
             //window.status = 'Sngl Click Ignore';
             return true;
           }
           return false;
        };
        
   CSS07.csm.ClickHandler.prototype.timerCallback   = function(evt) 
        {
           var now = this.getTime();
           this.timerRef = null;
           //if (this.savedEvt.clientX === 0) {
           //  window.status = 'zero client coords saved!';
           //}
           this.oneClick(this.savedEvt);
           this.savedEvt = null;
           return true;
        };

//                                                                                  // END: ClickHandler Class


// CLASS:     TabMenu Manager     // CLASS CONSTRUCTOR //
// allows tab to be maintained when changing between a pair of tab menus (e.g. producta and prices)

   CSS07.csm.TabMenu = function(firstSuffix, secondSuffix, cookieName) 
        {
          try
          {
            this.suffix1 = firstSuffix;
            this.suffix2 = secondSuffix;
            this.cookieName = cookieName;
            
          }
          catch(e) { alert("Error initialising TabHandler"); }
        };
        
// if the previous page was in the other menu, change to matching tab in this menu       
   CSS07.csm.TabMenu.prototype.checkReferrer = function()
        {
          try
          {
            var thisPage = window.location.pathname;
            if (thisPage) {
              var isMenu1 = (thisPage.indexOf(this.suffix1) > 0);
              var thisMenuSuffix  =  isMenu1 ? this.suffix1: this.suffix2;
              var otherMenuSuffix =  isMenu1 ? this.suffix2: this.suffix1;
              // assert that page contains the thisMenuSuffix
              if (isMenu1 || thisPage.indexOf(this.suffix2) > 0) {
                // get the previous page which saved a cookie
                var prevPage = CSS07.csm.getCookie(this.cookieName);

                var is_other_menu = prevPage && (prevPage.indexOf(otherMenuSuffix) > 0);
                if (is_other_menu) {
                  var target = prevPage.replace( otherMenuSuffix , thisMenuSuffix);
                  if (target != thisPage) {
                    window.location.href = target;
                  }
                }
                CSS07.csm.setCookie(this.cookieName, thisPage);
              }
            }
          }
          catch(e) {}
        };
   
//                                                                                  // END: ClickHandler Class

      // ===========================================================================
// CSM  CORE ECMASCRIPT - EXTENSION LIB  copyright 2007 codesmiths.com.au
// Utility fns

// not universal
CSS07.csm.getMouseX = function(evt)
        {
          try
          {
            var mouseX;
            var e = evt;
            if (!e) e = window.event;
            if (e.pageX )   {
              posx = e.pageX;
            }
            else if (e.clientX)  {
              posx = e.clientX + document.body.scrollLeft
                               + document.documentElement.scrollLeft;
            }
            else if (e.screenX) {
              
            }
          } catch(e) {}
        };
CSS07.csm.getMouseX = function(evt)
        {
          try
          {
            var mouseY;
            var e = evt;
            if (!e) e = window.event;
            if (e.pageY )   {
              posx = e.pageY;
            }
            else if (e.clientY)  {
              posx = e.clientY + document.body.scrollTop
                               + document.documentElement.scrollTop;
            }
            else if (e.screenY) {

            }
          } catch(e) {}
        };


CSS07.csm.getCurrentTime = function()
        {
          try
          {
            var d = new Date();
            return(d.getTime());
          } catch (e) {}
        };

CSS07.csm.EnhanceArray = function () // Upper case because part of constructors
        {
          try
          {
            if (!Array.prototype.indexOf) {
              Array.prototype.indexOf = function (val, start) {
                try {
                  var ii = start || 0;
                  if (ii < 0) ii = 0;
                  for (; ii < this.length; ii++) {
                    if (this[ii] == val) { return(ii); }
                  }
                } catch (e) {}
              };
            }
            if (!Array.prototype.lastIndexOf) {
              Array.prototype.lastIndexOf = function (val, start) {
                try {
                  var ii = start || this.length -1; 
                  if (start > this.length -1) start = this.length -1
                  for (; ii > -1; ii--) {
                    if (this[ii] == val) { return(ii); }
                  }
                } catch (e) {}
              };
            }
            if (!Array.prototype.indexOfString) {
              Array.prototype.indexOf = function (val, start) {
                try {
                  var ii = start || 0;
                  if (ii < 0) ii = 0;
                  for (; ii < this.length; ii++) {
                    if (this[ii].toLowerCase() == val.toLowerCase()) { return(ii); }
                  }
                } catch (e) {}
              };
            }
            if (!Array.prototype.lastIndexOfString) {
              Array.prototype.lastIndexOf = function (val, start) {
                try {
                  var ii = start || this.length -1; 
                  if (start > this.length -1) start = this.length -1
                  for (; ii > -1; ii--) {
                    if (this[ii].toLowerCase() == val.toLowerCase()) { return(ii); }
                  }
                } catch (e) {}
              };
            }
            /*
            The iterative methods are:
            
                * every() - runs a function on every item in the array and returns true if the function returns true for every item.
                * filter() - runs a function on every item in the array and returns an array of all items for which the function returns true.
                * forEach() - runs a function on every item in the array.
                * map() - runs a function on every item in the array and returns the results in an array.
                * some() - runs a function on every item in the array and returns true if the function returns true for any one item.
            */
          }
          catch(e){}

        };

// find a child element node from a selector string modelled on CSS, each generation must have a match
// the first selector must be an ID and after that either a tagName or a class Name
// the first matching child is followed down the tree to the end of the selectors
// if there is no match to the next selector in the previous match's children, the select fails.
// e.g. 'mytest .test div'   finds a div element which is a grand-child of the element with id 'mytest' 
// the element returned (if any) is a div which is a child of : the first child of 'mytest' of class 'test' 
//
// The utility of this fn is to access elements in include (SSI) fragments which may be included more than once per page.
// The included fragment can be inside an element with a unique ID, but the elements in the fragment itself cannot have ID's.

CSS07.csm.selectElt = function (selector) 
        {
          try
          {
            var fs = selector.split(/[ >]+/);
            var elt = window.document.getElementById(fs[0]);
            if (!(elt))             { return null; }       // must have an existing  ID 
            
            // selector may have multiple descendent class or tag names (first encountered is accepted)
            for (var ii=1; ii < fs.length; ii++) {
              var nod  = null;
              var cNam = null;
              var tNam = null;
              // selector is a class name if it starts with '.' else its a tag Name
              if (fs[ii].substr(0,1) == '.') { 
                cNam = fs[ii].substr(1,fs[ii].length); 
              }
              else { 
                tNam = fs[ii]; 
              }
              for (var jj=0; jj< elt.childNodes.length; jj++) {
                nod = elt.childNodes[jj];
                if (cNam && nod.className && nod.className.length >= cNam.length) 
                {
                  var cNames = nod.className.split(/[ \t]+/);
                  for (var kk=0; kk<cNames.length; kk++) 
                  {
                    if (cNam == cNames[kk]){
                      elt = nod;
                      break;
                    }
                  }
                }
                if (tNam && nod.tagName && nod.tagName.indexOf(tNam) > -1){
                  elt = nod;
                  break;
                }               
              }
            }
            return elt;
          }
          catch(e){return null;}
        };

// return a child element node similar to fn above, but recursive and has only one selector token
// the selector token may be a class name (preceded by '.') or a tag name
// this variant returns the first match in any descendant(depth first) 
CSS07.csm.selectDescendant = function (elt, selector)
        {
          try
          {
            if (!(elt))             { return null; }       // must have an existing  ID 
            var desc = null;
            
            var nod;
            var cNam = null;
            var tNam = null;
            // selector is a class name if it starts with '.' else its a tag Name
            if (selector.substr(0,1) == '.') { 
              cNam = selector.substr(1,selector.length); 
            }
            else { 
              tNam = selector.toUpperCase(); 
            }
            for (var jj=0; !desc && jj< elt.childNodes.length; jj++) {
              nod = elt.childNodes[jj];
              if (cNam && nod.className && nod.className.length >= cNam.length) 
              {
                var cNames = nod.className.split(/[ \t]+/);
                for (var kk=0; kk<cNames.length; kk++) 
                {
                  if (cNam == cNames[kk]){
                    desc = nod;
                    break;
                  }
                }
              }
              if (tNam && nod.tagName && nod.tagName.indexOf(tNam) > -1){
                desc = nod;
                break;
              } 
              if (!desc && (desc = CSS07.csm.selectDescendant(nod, selector))){
                break;
              }
            }
            return desc;
          }
          catch(e){return null;}
        };


// populate the Array nodes with descendants of elt matching the selector 
// similar to fn above, has only one selector
CSS07.csm.selectDescendants = function (elt, selector, nodes)
        {
          try
          {
            if (!(elt))             { return; }       // must have an existing  ID 
            if (!nodes)             { return; }
            // selector may have recursive descendent class or tag names (first encountered is accepted)
            var nod;
            var cNam = null;
            var tNam = null;
            if (selector.substr(0,1) == '.') { 
              cNam = selector.substr(1,selector.length); 
              CSS07.csm.selectDescendantTags(elt, cNam, nodes);
            }
            else { 
              tNam = selector.toUpperCase(); 
              CSS07.csm.selectDescendantTags(elt, tNam, nodes);
            }
          }
          catch(e){}
        };
               
// populate the Array nodes with descendants of elt of tagName tNam
CSS07.csm.selectDescendantTags = function (elt, tNam, nodes)
        {
          try
          {
            if (tNam.length === 0) return;
            var desc = null;
            var nod;
            for (var jj=0; jj< elt.childNodes.length; jj++) {
              nod = elt.childNodes[jj];
              if (tNam && nod.tagName && (nod.tagName.toLowerCase() == tNam.toLowerCase())){
                desc = nod;
              } 
              if (nod.childNodes.length > 0){  
                CSS07.csm.selectDescendantTags(nod, tNam, nodes);
              }
              if (desc) { nodes[nodes.length] = desc; desc = null; }
            }
          }
          catch(e){}
        };

// populate the Array nodes with descendants of elt of class cName (depth first)
CSS07.csm.selectDescendantsByClass = function (elt, cName, nodes)
        {
          try
          {
            var desc = null;
            var nod;
            for (var jj=0; jj< elt.childNodes.length && !(elt === nod); jj++) {
              nod = elt.childNodes[jj];
              if (cNam && nod.className && nod.className.length >= cNam.length) 
              {
                var cNames = nod.className.split(/[ \t]+/);
                for (var kk=0; kk<cNames.length; kk++) 
                {
                  if (cNam == cNames[kk]){
                    desc = nod;
                    break;
                  }
                }
              }
              if (nod.childNodes.length > 0) {
                CSS07.csm.selectDescendantsByClass(nod, cName, nodes);
              }
              if (desc) { nodes[nodes.length] = desc;  desc = null; }
            }
          }
          catch(e){}
        };

// test if an event applies to currentElement
CSS07.csm.hitTest = function (evt)                    
        {
          try
          {
            // TODO IE version
            if (window.attachEvent) { return true; } // IE over-ride
            var field = evt.currentTarget;
            var inside = evt.clientX >= field.offsetLeft && evt.clientX <= field.offsetLeft + field.offsetWidth ;
            inside    &= evt.clientY >= field.offsetTop  && evt.clientY <= field.offsetTop  + field.offsetHeight;
            return inside;
          }
          catch(e){return null;}
        };

 // test if an event is inside the elt field
 CSS07.csm.eltTest = function (evt, field)             
        {
          try
          {
            var inside = evt.clientX >= field.offsetLeft && evt.clientX <= field.offsetLeft + field.offsetWidth ;
            inside    &= evt.clientY >= field.offsetTop  && evt.clientY <= field.offsetTop  + field.offsetHeight;
            return inside;
          }
          catch(e){return null;}
        };

// extract the value of a var from a queryString or similar key/value string
CSS07.csm.extractValue = function(list, key, sep)
        {
          var val = null;
          try
          {
             //var re = new RegExp(key + '([^' + sep + ']*)' + sep);
             // re version is too prone to error from special chars
             var index = list.indexOf(key);
             if (index > -1) {
                index += key.length;
                var nextSep = list.indexOf(sep, index);
                var len = (nextSep > -1 ? nextSep : list.length) - index;
                val = list.substr(index, len);
             }
          }
          catch(e){ val = null; }
          return val;
        };
        
// extract the file part of a string from a path 
CSS07.csm.filePart = function(path)
        {
          try
          {
            return path.slice(path.lastIndexOf('/')+1);
          } catch (e) {}
        };
        
// extract the folder part of a string from a path 
CSS07.csm.folderPart = function(path)
        {
          try
          {
            return path.slice(0, path.lastIndexOf('/')+1);
          } catch (e) {}
        };
        
// Dynamic Height
CSS07.csm.setHeight = function(divID, adjust)
        {
          var val = null;
          try
          {
            var elt = window.document.getElementById(divID);
            if (elt){ 
              var pa = elt.parentNode;
              var ht = pa.clientHeight;
              var si = pa.childNodes;
              var ii;
              for(ii=si[elt].index +1; ii < si.length; ii++) {
                if(si > elt && si[ii].tagName == 'DIV') { break; }
              }
              si = si[ii];
              if (ht && si) {
                elt.style.height = ht -adjust -si.clientHeight;
                val = true;
              }
            } 
          }
          catch(e){}
          return val;
        };

//  set the height of a div (id=divID1) to the remainder after accommodating divID2
CSS07.csm.setHeight2 = function(divID1, divID2)
        {
          var val = null;
          try
          {
            var elt1 = window.document.getElementById(divID1);
            var elt2 = window.document.getElementById(divID2);
            
            if (elt1 && elt2){ 
              var pa = elt1.parentNode;
              var ht = pa.clientHeight;
              var ad = elt2.clientHeight;
              var pc = (100 - (100*ad/ht)) -0.5;
              elt1.style.height = '' + pc + '%';
              val = true;
            } 
          }
          catch(e){}
          return val;
        }

//  Centre a div (id=divID) in its parent dynamically
CSS07.csm.centre = function(divID)
        {
          var val = null;
          try
          {
            var elt1 = window.document.getElementById(divID);
            var pa = elt1.parentNode;
            
            if (elt1 && pa) { 
              var ht = pa.clientHeight;
              var wd = pa.clientWidth;
              var pc = (100 - (100*ad/ht)) -0.5;
              elt1.style.top  = "" + (ht-elt1.clientHeight)/2 +"px";
              elt1.style.left = "" + (wd-elt1.clientWidth )/2 +"px";
              val = true;
            }      
          }
          catch(e){}
          return val;
        };
        
//  round an integer value down by steps of intIncr
CSS07.csm.round_down = function  (intVal, intIncr)
        {
          try
          {
            return intIncr*Math.floor((intVal/intIncr));
          }
          catch(e){ return null; }
        };

CSS07.csm.make_path = function (folderName)              // ensure the path name has a terminating "/"
        {
            var fn = folderName.replace('\\', '/');
            var last = (fn.length > 0) ? fn.charAt(fn.length -1) : '/';
            if (last != '/') { fn += '/'; }
            return fn;
        };
// Switch to the url argument after a timeout to workaround some behaviour issues
CSS07.csm.viewer_show = function (urlQuery)
        {
          try
          {
            var waitTimeMS       = 100;
            window.setTimeout('window.location.href="' + urlQuery + '"', waitTimeMS);
            return false;
          } catch(e){
            alert (e.message);
          }
        };

// Switch the id of an element - useful for making unique body tags in the instance of a template.
CSS07.csm.change_id = function (oldID, newID)
        {
          try
          {
              var elt = window.document.getElementById(oldID);              
              if (elt) { elt.setAttribute("id", newID); }
          } catch(e){
            alert (e.message);
          }
        };

// Set a style the of an element from its ID
CSS07.csm.set_style = function (eltID, styleName, styleValue)
        {
          try
          {
              var elt = window.document.getElementById(eltID);              
              if (elt) { elt.style[styleName] = styleValue; }
          } catch(e){
            alert (e.message);
          }
        };

// Set a background - for making unique viewport backgrounds in the instance of a template.
CSS07.csm.set_back_image = function (eltID, folder, image)
        {
          try
          {
             var url = 'url(' + CSS07.csm.make_path(folder) + image +')';
             var elt = window.document.getElementById(eltID);              
             if ((elt) && (url)) { elt.style['backgroundImage'] = url; }
          } catch(e){
            alert (e.message);
          }
        };
        

CSS07.csm.getAgentVersion = function ()
//  MSIE returns an object
        {
          try
          {
             if (!window.document.all) {
               return(window.navigator.appVersion);
             } else {
               var version=/MSIE \d+.\d+/;
               return (window.navigator.appVersion.match(version));
             }
           return null;
          }
          catch(e){ return null; }
        };
        
CSS07.csm.browserIsCompliant = function ()
        {
          try
          {
             if (!window.document.all) {
               return true;
             } else {
               var av = CSS07.csm.getAgentVersion();  // IE returns an object (array)
               if (av) {
                 var sv = av.toString();
                 var vn = parseInt(sv.slice(4));
                 //alert('Browser version ' + (typeof av) + ' ' + av + ' ' + vn);
                 return (vn > 6);
               }
             }
          }
          catch(e){ return null; }
        };


CSS07.csm.replaceTextWithQueryString = function (eltID, key, alt)
        {
          try
          {
             var elt = window.document.getElementById(eltID); 
             if (elt) {
               var qKey = key + ((key.charAt(key.length-1) == '=') ? '' : '=');
               // defaults to blank
               var txt = CSS07.csm.extractValue(window.location.href,qKey,'&') || alt || '';
               elt.innerHTML = decodeURIComponent(txt); 
             }
          } catch(e){
            alert (e.message);
          }
        };

CSS07.csm.replaceAttrWithQueryString = function (eltID, attrName, key, alt)
        {
          try
          {
             var elt = window.document.getElementById(eltID); 
             if (elt) {
               var qKey = key + ((key.charAt(key.length-1) == '=') ? '' : '=');
               if (qKey && qKey.length > 1) {
                 var txt = CSS07.csm.extractValue(window.location.href,qKey,'&') || alt ;
                 // defaults to unchanged
                 if (txt) { 
                   elt.setAttribute(attrName, decodeURIComponent(txt)); 
                 }
               }
             }
          } catch(e){
            alert (e.message);
          }
        };

  CSS07.csm.makeElement = function(parentElt, tagName, id, clas, visible)
        // for divs, img etc
        {
          try
          {
              var elt = window.document.createElement(tagName);
              if (id) { elt.id = id; }
              if (clas) { elt.className = clas; }
              if (visible != null) elt.style.visibility  = visible ? 'visible' : 'hidden';
              if (parentElt) parentElt.appendChild(elt);
              return elt;
           } catch(e){
             alert (e.message);
           }
        };

     CSS07.csm.makeTextElement = function(parentElt, tagName, id, content, visible, zindex)
        // only for span, p, li, a or h tags
        {
          try
          {
              var elt = window.document.createElement(tagName);
              if (id) { elt.id = id; }
              var dso = elt.style;
              if (zindex) dso.zIndex       = zindex;
              if (visible != null) dso.visibility   = visible ? 'visible' : 'hidden';

              elt.appendChild(window.document.createTextNode(content || ''));

              if (parentElt) parentElt.appendChild(elt);
              return elt;
           } catch(e){
             alert (e.message);
           }
        };

   CSS07.csm.randomInteger = function(min, max, seed)
        //
        {
          try
          {
            if (!seed || typeof seed != 'number') {
              var now = new Date();
              seed = now.getSeconds();
            }
            var range = max -min +1;
            var result = Math.round(Math.random(seed)*range);
            return (result > max ? min : Math.round(result + min)); 
          } catch(e){
            alert (e.message);
          }
        };
        
   CSS07.csm.setCookie = function(sName, sValue, oExpires, sPath, sDomain, bSecure) 
        {
          try
          {
            var sCookie = sName + "=" + encodeURIComponent(sValue);

            if (oExpires) {
                sCookie += "; expires=" + oExpires.toGMTString();
            }

            if (sPath) {
                sCookie += "; path=" + sPath;
            }

            if (sDomain) {
                sCookie += "; domain=" + sDomain;
            }

            if (bSecure) {
                sCookie += "; secure";
            }

            document.cookie = sCookie;
          } catch(e){
            alert (e.message);
          }
        };
      

   CSS07.csm.getCookie = function(sName) 
        {
          try
          {
            var sRE = "(?:; )?" + sName + "=([^;]*);?";
            var oRE = new RegExp(sRE);

            if (oRE.test(document.cookie)) {
                return decodeURIComponent(RegExp["$1"]);
            } else {
                return null;
            }
          } catch(e){
            alert (e.message);
          }
        };

   CSS07.csm.delCookie = function(sName, sPath, sDomain) 
        {
          try
          {
            var sCookie = sName + "=; expires=" + (new Date(0)).toGMTString();
            if (sPath) {
                sCookie += "; path=" + sPath;
            }

            if (sDomain) {
                sCookie += "; domain=" + sDomain;
            }
            document.cookie = sCookie;
          } catch(e){
            alert (e.message);
          }
        };
   

        
   
   // (c) 2007 Codesmiths

}
catch(e) {}

// end script
