// SCRIPT MODULES LIBRARY   ECMAScript v3.0   (c) 2006 Kleeton PL - all rights reserved

try
{

// ===========================================================================
// NAMESPACE MANAGEMENT - ONLY ONE GLOBAL IDENTIFIER RESERVED ('KPL')
var KPL;
if ((KPL !== undefined) && !((KPL !== null) && (KPL.namespaceurl) && (KPL.namespaceurl == 'kleeton.com.au')))  { window.status = 'KPL-LIB: Namespace conflict on [KPL]'; throw new Error(window.status); }
if (!KPL)      { KPL     = {USAGE:'Kleeton PL namespace-object', namespaceurl:'kleeton.com.au'}; }
if (!KPL.lib)  { KPL.lib = {USAGE:'core-ecmascript lib'}; }
if (!KPL.evt)  { KPL.evt = {USAGE:'event lib'}; }
if (!KPL.app)  { KPL.app = {USAGE:'application-ctrl lib'}; }
if (!KPL.img)  { KPL.img = {USAGE:'image lib'}; }
if (!KPL.css)  { KPL.css = {USAGE:'css lib'}; }
if (!KPL.sld)  { KPL.sld = {USAGE:'slide-projector lib'}; }


// ===========================================================================
// CORE ECMASCRIPT-EXTENSION LIB

KPL.lib.bind =
        function (obj, funct)                                                            // bind-as-method - method bindery (callback-object factory, etc)
        {
            var method = (funct instanceof Function) ? funct : obj[funct];                    // 'method' function may be passed by name or reference
            return  function method_proxy ()                                                  // note: by returning function-ref, closure persists
                    {
                        try
                        {
                            return method.apply(obj, arguments);                              // invoke with args as method of obj, returning result if any
                        }
                        catch(e){return null;}                                                // (return is to satisfy JavaScript-Lint)
                    };
        };

KPL.lib.encaseArgs =
        function (argsObject, iFirstArg, optionalFolder)                                 // parse function-arguments & return array
        {
            var fldr = (optionalFolder) ? (optionalFolder + '/') : '';
            var len  = argsObject.length - iFirstArg;                                         // iFirstArg must be idx 0-based
            var args = new Array();                                                           // indexed 0-based
            for (var i=0; i<len; i++)
            {
                args[i] = fldr + argsObject[i + iFirstArg];
            }
            return args;
        };

KPL.lib.Encapsulator =                                                              // CLASS CONSTRUCTOR // - typically inherited by call/apply methods
        function ()
        {
            this.bind        = KPL.lib.bind;
            this.encaseArgs  = KPL.lib.encaseArgs;
            this.bindEvent   = KPL.evt.bindEvent;
            this.unbindEvent = KPL.evt.unbindEvent;
        };                                                                          // END: Encapsulators class //


// ===========================================================================
// EVENTS LIB

KPL.evt.bindEvent =
        function (obj, evtHandler, evt_w3c, onevt_ie)                                    // DOM-event bindery (deprecated bindings not supported)
        {
          try
          {
            if (window.addEventListener)                                                      // w3c-DOM method
            {
                var capture = false;
                obj.addEventListener(evt_w3c, evtHandler, capture);                           // w3c standards-compliant binding
            }
            else
            {
                if (window.attachEvent)                                                       // IE method
                {
                    var evtIE = onevt_ie || ('on' + evt_w3c);                                 // optional arg; default if not supplied
                    obj.attachEvent(evtIE, evtHandler);                                       // non-standard binding for IE
                }
            }
            return evtHandler;                                                                // return function ref for reuse + unbinding
          }
          catch(e){return null;}
        };
KPL.evt.unbindEvent =
        function (obj, evtHandler, evt_w3c, onevt_ie)                                    // DOM-event unbindery
        {
          try
          {
            if (window.removeEventListener)
            {
                var capture = false;
                obj.removeEventListener(evt_w3c, evtHandler, capture);
            }
            else
            {
                if (window.detachEvent)
                {
                    var evtIE = onevt_ie || ('on' + evt_w3c);
                    obj.detachEvent(evtIE, evtHandler);
                }
            }
          }
          catch(e){}
        };


// ===========================================================================
// SCRIPT INITIALISATION
// When page fully loaded, initiate processing, but only if a modern-browser;
// obsolete browsers are not countenanced (as script merely enhances an already competent page).
// As the page is unloaded, this script removes its own bindings to window.
// This script formally binds the page events, hence does not use deprecated 'window.onload' event, etc.
// [review-crossbrowser: consider alternatives to use of 'window-load' event: eg 'window.document.load', etc.]

KPL.app.enableNotifyOnPageLoad =
        function (loadHandler)
        {
            try
            {
                if (!(loadHandler instanceof Function))  { return; }
                loadHandler.pageloadStartTime = (new Date()).getTime();
                var w3cCapture = false;
                bindWindowEvents();
            }
            catch(e){}

            function bindWindowEvents()
            {
                if (window.addEventListener)                                                  // w3c-DOM method
                {
                    window.addEventListener('load',   loadHandler, w3cCapture);               // w3c standard binding
                    window.addEventListener('unload', unbindWindowEvents, w3cCapture);
                }
                else if (window.attachEvent)                                                  // IE method
                {
                    window.attachEvent('onload',   loadHandler);                              // non-standard binding for IE
                    window.attachEvent('onunload', unbindWindowEvents);
                }
            }

            function unbindWindowEvents()
            {
              try
              {
                  if (window.removeEventListener)
                  {
                      window.removeEventListener('load',   loadHandler, w3cCapture);
                      window.removeEventListener('unload', unbindWindowEvents, w3cCapture);
                  }
                  else
                  {
                      window.detachEvent('onload',   loadHandler);
                      window.detachEvent('onunload', unbindWindowEvents);
                  }
              }
              catch(e){}
            }
        };

// PAGE INITIALIZATION (at PageLoaded-time)

KPL.app.slowLoad =
        function (tooLong)
        {
            var PAGELOAD_SLOW_SECS = tooLong || 8.0;
            var pageLoad_secs = ((new Date()).getTime() - KPL.app.hostLoaded.pageloadStartTime) / 1000;
            return (pageLoad_secs >= PAGELOAD_SLOW_SECS);
        };


// ===========================================================================
// CLASS: VIEWER SIZE (HEIGHT) VIA CSS - v1.00
// Provides user-interface to change height of image display-area (view):
// - height is cycled through 3 values specified as css-rules.
// - height is changed by clicking view or clicking one designated element.
// - implemented by dynamically changing element-class to select required rule.
// - implemention is generic and need not only apply to image display.

KPL.img.Viewz =
        function (viewsElmtID, sizeclickElmtID, viewHeightClassBase)                // CLASS CONSTRUCTOR //
        {
          try
          {
            if (!(this instanceof KPL.img.Viewz))              { return null; }          // constructor was CALLed as a function
            if (!window.document.getElementById(viewsElmtID))  { return null; }          // viewer-element invalid
            this.elmt      = window.document.getElementById(viewsElmtID);
            this.sizeElmt1 = this.elmt;
            this.sizeElmt2 = window.document.getElementById(sizeclickElmtID);
            this.classHgt1 = viewHeightClassBase + '1';
            this.classHgt2 = viewHeightClassBase + '2';
            this.classHgt3 = viewHeightClassBase + '3';
            if (this.sizeElmt1 === this.sizeElmt2)  { this.sizeElmt2 = false; }          // avoid duplicate event-bindings

            KPL.lib.Encapsulator.call(this);                                             // 'inherit' Encapsulator's features

            this.sizer = this.bind(this,'size');                                         // the function ref must be stored for unbinding

            this.bindEvent(this.sizeElmt1, this.sizer, 'click');
            this.sizeElmt1.style.visibility = 'visible';
            this.sizeElmt1.style.cursor = 'pointer';
            if (this.sizeElmt2)
            {
                this.bindEvent(this.sizeElmt2, this.sizer, 'click');
                this.sizeElmt2.style.visibility = 'visible';
                this.sizeElmt2.style.cursor = 'pointer';
            }
            this.unload = this.bindEvent(window, this.bind(this,'destroy'), 'unload');   // function ref (evtHdlr) stored for unbinding
          }
          catch(e){this.destroy();}
        };
KPL.img.Viewz.prototype.size =
        function ()
        {
            var c = this.elmt.className;
            switch (c)
            {
                case    (this.classHgt1) : c = this.classHgt2;  break;
                case    (this.classHgt2) : c = this.classHgt3;  break;
                case    (this.classHgt3) : c = this.classHgt1;  break;
                default                  : c = this.classHgt2;  break;
            }
            this.elmt.className = c;
        };
KPL.img.Viewz.prototype.destroy =
        function ()
        {
          try
          {
            if (this.sizeElmt1)
            {
                this.unbindEvent(this.sizeElmt1, this.sizer, 'click');
            }
            if (this.sizeElmt2)
            {
                this.unbindEvent(this.sizeElmt2, this.sizer, 'click');
            }
            this.unbindEvent(window, this.unload, 'unload');
            this.elmt      = null;
            this.sizeElmt1 = null;
            this.sizeElmt2 = null;
            this.sizer     = null;
          }
          catch(e){}
        };
//                                                                                  // END: Viewz CLASS //


// ===========================================================================
// CSS TEXT-SIZE ZOOM and ALTERNATE-SKIN
// Provides two mutually exclusive features (by activating alt-stylesheets):
//   - text-size zooming (small, normal, big), OR
//   - an alternate skin.
// Note1:   singleton class - initialisation values via class-statics, not constructor args.
// Note2:   see end of this section for IN-LINE code which executes
//          when script loaded, rather than later when full-page loaded.
// STATUS:  production release: v1.0
// UPGRADE: review structure, consider possible component classes (UserInterface, TxtSize, Altsheet, etc).


KPL.css.Ctrl =
        function ()                                                                 // CLASS CONSTRUCTOR - also used as 'class-statics' host
        {
          try
          {
              if (!(KPL.css.Ctrl.browserCanSwitchCSS()))  { return null; }               // obsolete browser, abort
              if (!(this instanceof KPL.css.Ctrl))        { return null; }               // constructor was CALLed as a function
              if (KPL.css.Ctrl.instantiated)              { return null; }               // attempt to multi-instantiate a singleton

              KPL.css.Ctrl.instantiated = true;                                          // class is a singleton
              KPL.lib.Encapsulator.call(this);                                           // 'inherit' Encapsulator's features
              this.unload = this.bindEvent(window, this.bind(this,'destroy'), 'unload'); // function ref (evtHdlr) stored for unbinding
              this.enable();
          }
          catch(e){this.destroy();}
        };

// TEXT-SIZE ZOOM and ALTERNATE-SKIN SETTINGS  (customise values to suit site)
// Typically the same for all pages in a site - hence here as common.

KPL.css.Ctrl.txtSmallElmtID = 'hdbuttxtsl';                // small-text click-element (contains button & its hover)
KPL.css.Ctrl.txtBigElmtID   = 'hdbuttxtbg';                // big-text   click-element (contains button & its hover)
KPL.css.Ctrl.txtSmallStyle  = 'o Text Smaller';            // small-text stylesheet-title
KPL.css.Ctrl.txtBigStyle    = 'o Text Larger';             // big-text   stylesheet-title
KPL.css.Ctrl.activeButColor = 'rgb(100%,90%,60%)';         // active-button border-color
KPL.css.Ctrl.altSkinElmtID  = 'hdlogo';                    // alternate-skin click-element (typically has no hover)
KPL.css.Ctrl.altName        = 'o Alternate Style';         // alternate-skin stylesheet-title
KPL.css.Ctrl.cookieName     = 'kplstylesheetcontrol';      // app-name for browser cookie
KPL.css.Ctrl.txtSmallImgID  = 'hdbuttxtslimg';             // image: small-text button
KPL.css.Ctrl.txtSmallImghID = 'hdbuttxtslimgh';            // image: small-text button hover
KPL.css.Ctrl.txtBigImgID    = 'hdbuttxtbgimg';             // image: big-text button
KPL.css.Ctrl.txtBigImghID   = 'hdbuttxtbgimgh';            // image: big-text button hover


KPL.css.Ctrl.prototype.enable =
        function ()
        {
            this.txtSmallElmt = window.document.getElementById(KPL.css.Ctrl.txtSmallElmtID);
            this.txtBigElmt   = window.document.getElementById(KPL.css.Ctrl.txtBigElmtID);
            this.altskinElmt  = window.document.getElementById(KPL.css.Ctrl.altSkinElmtID);
            this.txtSmallImg  = window.document.getElementById(KPL.css.Ctrl.txtSmallImgID);
            this.txtSmallImgh = window.document.getElementById(KPL.css.Ctrl.txtSmallImghID);
            this.txtBigImg    = window.document.getElementById(KPL.css.Ctrl.txtBigImgID);
            this.txtBigImgh   = window.document.getElementById(KPL.css.Ctrl.txtBigImghID);
            this.txtSmallHandler = this.bind(this,'smaller');                            // the function ref must be stored for unbinding
            this.txtBigHandler   = this.bind(this,'bigger');
            this.altskinHandler  = this.bind(this,'altSkin');

            if (this.txtSmallElmt)
            {
                this.bindEvent(this.txtSmallElmt, this.txtSmallHandler, 'click');
                this.txtSmallElmt.style.visibility = 'visible';
            }
            if (this.txtBigElmt)
            {
                this.bindEvent(this.txtBigElmt, this.txtBigHandler, 'click');
                this.txtBigElmt.style.visibility = 'visible';
            }
            if (this.altskinElmt)
            {
                this.bindEvent(this.altskinElmt, this.altskinHandler, 'click');
                this.altskinElmt.style.visibility = 'visible';
            }

            this.setTxtsizeButsStatus();
        };

KPL.css.Ctrl.prototype.destroy =
        function ()
        {
          try
          {
              if (this.txtSmallElmt)
              {
                  this.unbindEvent(this.txtSmallElmt, this.txtSmallHandler, 'click');
              }
              if (this.txtBigElmt)
              {
                  this.unbindEvent(this.txtBigElmt, this.txtBigHandler, 'click');
              }

              if (this.altskinElmt)
              {
                  this.unbindEvent(this.altskinElmt, this.altskinHandler, 'click');
              }
              this.txtSmallHandler = null;
              this.txtBigHandler   = null;
              this.altskinHandler  = null;
              this.txtSmallElmt = null;
              this.txtBigElmt   = null;
              this.altskinElmt  = null;
              this.txtSmallImg  = null;
              this.txtSmallImgh = null;
              this.txtBigImg    = null;
              this.txtBigImgh   = null;
              this.unbindEvent(window, this.unload, 'unload');
          }
          catch(e){}
        };

KPL.css.Ctrl.prototype.smaller =
        function ()
        {
            if (this.isTxtNormal())
            {
              KPL.css.Ctrl.changeStyle(KPL.css.Ctrl.txtSmallStyle);
              KPL.css.Ctrl.rememberStyle(KPL.css.Ctrl.cookieName);
              this.setTxtsizeButsStatus();
            }
            else
            {
               if (this.isTxtBig())
               {
                  this.setNormal();
               }
            }
        };

KPL.css.Ctrl.prototype.bigger =
        function ()
        {
            if (this.isTxtNormal())
            {
              KPL.css.Ctrl.changeStyle(KPL.css.Ctrl.txtBigStyle);
              KPL.css.Ctrl.rememberStyle(KPL.css.Ctrl.cookieName);
              this.setTxtsizeButsStatus();
            }
            else
            {
               if (this.isTxtSmall())
               {
                  this.setNormal();
               }
            }
        };

KPL.css.Ctrl.prototype.altSkin =
        function ()
        {
            if (this.isAlt())
            {
               KPL.css.Ctrl.changeStyle();
            }
            else
            {
               KPL.css.Ctrl.changeStyle(KPL.css.Ctrl.altName);
            }
            KPL.css.Ctrl.rememberStyle(KPL.css.Ctrl.cookieName);
            this.setTxtsizeButsStatus();
        };

KPL.css.Ctrl.prototype.setNormal =
        function ()
        {
            KPL.css.Ctrl.changeStyle();                                                  // turn off all alternate stylesheets
            KPL.css.Ctrl.rememberStyle(KPL.css.Ctrl.cookieName);
            this.setTxtsizeButsStatus();
        };


KPL.css.Ctrl.prototype.setTxtsizeButsStatus =
        function ()
        {
            if (this.isAlt())
            {
               this.txtSmallElmt.style.visibility = 'hidden';
               this.txtBigElmt.style.visibility   = 'hidden';
            }
            else
            {
               this.txtSmallElmt.style.visibility = 'visible';
               this.txtBigElmt.style.visibility   = 'visible';
            }

            switch (KPL.css.Ctrl.ssTitle)
            {
                case (KPL.css.Ctrl.txtSmallStyle):
                         this.txtSmallImg.style.borderColor  = KPL.css.Ctrl.activeButColor;
                         this.txtSmallImgh.style.borderColor = KPL.css.Ctrl.activeButColor;
                         this.txtBigImg.style.borderColor    = 'transparent';
                         this.txtBigImgh.style.borderColor   = 'transparent';
                         break;
                case (KPL.css.Ctrl.txtBigStyle):
                         this.txtSmallImg.style.borderColor  = 'transparent';
                         this.txtSmallImgh.style.borderColor = 'transparent';
                         this.txtBigImg.style.borderColor    = KPL.css.Ctrl.activeButColor;
                         this.txtBigImgh.style.borderColor   = KPL.css.Ctrl.activeButColor;
                         break;
                default: this.txtSmallImg.style.borderColor  = 'transparent';
                         this.txtSmallImgh.style.borderColor = 'transparent';
                         this.txtBigImg.style.borderColor    = 'transparent';
                         this.txtBigImgh.style.borderColor   = 'transparent';
                         break;
            }
        };

KPL.css.Ctrl.prototype.isTxtNormal =
        function ()
        {
            if (KPL.css.Ctrl.txtSmallStyle == KPL.css.Ctrl.ssTitle)  { return false; }
            if (KPL.css.Ctrl.txtBigStyle   == KPL.css.Ctrl.ssTitle)  { return false; }
            return true;
        };

KPL.css.Ctrl.prototype.isTxtSmall =
        function ()
        {
            return (KPL.css.Ctrl.txtSmallStyle == KPL.css.Ctrl.ssTitle);
        };


KPL.css.Ctrl.prototype.isTxtBig =
        function ()
        {
            return (KPL.css.Ctrl.txtBigStyle == KPL.css.Ctrl.ssTitle);
        };


KPL.css.Ctrl.prototype.isAlt =
        function ()
        {
            return (KPL.css.Ctrl.altName == KPL.css.Ctrl.ssTitle);
        };


// class - class STATIC METHODS - (manipulates DOM stylesheets collection & manages cookies)
//
// Caveat Lector: the following 5 functions can differ stylistically to all other
// code in this site. and they require a keen understanding of operator precedence.
//
// Alternate-Stylesheets - Enable/Disable..................
// - Enable an alt-stylesheet (disabling the others), OR,
//   disable all alt-stylesheets.
// - State persists across pages via a session cookie.
// - The preferred W3C method is to use the styleSheets collection.
//   To provide wider browser support, this code accesses styleSheets
//   via a link/style-tag technique. Eventually, when browser support improves,
//   the code will be replaced by a pure styleSheets collection technique.
//   The 5 methods below are based on the investigations & work
//   of Mark Wilton-Jones, v2.2.1. www.howtocreate.co.uk;
//   this code is complex due to irregularites in some browsers (iCab, etc).

KPL.css.Ctrl.browserCanSwitchCSS =
        function ()
        {
            return ( document.styleSheets || (window.opera && document.childNodes) ||
                     (window.ScriptEngine && ScriptEngine().indexOf('InScript') + 1 &&
                      document.createElement) );
        };

KPL.css.Ctrl.changeStyle =
        function ()
        {
            KPL.css.Ctrl.ssTitle = arguments[0];                                  // [ib] updated to get current SS title ********
            KPL.css.Ctrl.userHasChosen = KPL.css.Ctrl.MWJss;
            for (var x = 0, ss = KPL.css.Ctrl.getAllSheets(); ss[x]; x++)         // for each stylesheet ...
            {
                if (ss[x].title)  { ss[x].disabled = true; }                      // disable the stylesheet if it is switchable
                for (var y = 0; y < arguments.length; y++)                        // for each title suppied as an arg
                {
                  if (ss[x].title == arguments[y])  { ss[x].disabled = false; }   // re-enable the stylesheet if matches title suppied
                }
            }
        };

KPL.css.Ctrl.getAllSheets =
        function ()
        {
            var Lt, St;
            switch (true)     // note: switch uses identity (===) comparisions (no coercion) - hence the casting by double negation for this unusual use of switch
            {
                case (!window.ScriptEngine && !!navigator.__ice_version):         // ICEbrowser and NOT IE
                         return document.styleSheets;
                case (!!document.getElementsByTagName):                           // DOM browsers
                         Lt = document.getElementsByTagName('link');
                         St = document.getElementsByTagName('style');
                         break;
                case (!!document.styleSheets && !!document.all):                  // only non-DOM browsers that can switch sheets
                         Lt = document.all.tags('LINK');
                         St = document.all.tags('STYLE');
                         break;
                default: return [];                                               // lesser browser - return a blank array
            }

            for (var x = 0, os = []; Lt[x]; x++)                                                           //for all link tags ...
            {
              var rel = (Lt[x].rel) ? Lt[x].rel : (Lt[x].getAttribute) ? Lt[x].getAttribute('rel') : '';   //check for the rel attribute to see if it contains 'style'
              if (typeof(rel) == 'string' && rel.toLowerCase().indexOf('style') + 1)
              {
                  os[os.length] = Lt[x];                                                                   //fill os with linked stylesheets
              }
            }

            for (var z = 0; St[z]; z++)  { os[os.length] = St[z]; }               // include all style tags too
            return os;                                                            // return array of stylesheets
        };

KPL.css.Ctrl.rememberStyle =
        function (cookieName, cookieLife)
        {
            for (var viewUsed = false, ss = KPL.css.Ctrl.getAllSheets(), x = 0; KPL.css.Ctrl.MWJss && KPL.css.Ctrl.MWJss[x] && ss[x]; x++)
            {
               if (ss[x].disabled != KPL.css.Ctrl.MWJss[x])  { viewUsed = true; break; }
            }

            if (!KPL.css.Ctrl.userHasChosen && !viewUsed)  { return; }

            for (var z = 0, outLine = '', doneYet = []; ss[z]; z++)
            {
              if (ss[z].title && ss[z].disabled === false && !doneYet[ss[z].title])
              {
                  doneYet[ss[z].title] = true;
                  outLine += (outLine ? ' MWJ ' : '') + escape(ss[z].title);
              }
            }
            if (ss.length)
            {
                document.cookie = escape( cookieName ) + '=' +
                                  escape( outLine ) +
                                  ( !cookieLife ? '' : ';expires=' + new Date( ( new Date() ).getTime() + ( cookieLife * 86400000 ) ).toGMTString() ) +
                                  ';path=/';
            }
        };

KPL.css.Ctrl.useStyleAgain =
        function (cookieName)
        {
            try
            {
                for (var x = 0; x < document.cookie.split('; ').length; x++)
                {
                  var oneCookie = document.cookie.split('; ')[x].split('=');
                  if (oneCookie[0] == escape(cookieName))
                  {
                    var styleStrings = unescape( oneCookie[1] ).split(' MWJ ');
                    for (var y = 0, funcStr = ''; styleStrings[y]; y++)
                    {
                        funcStr += ( y ? ',' : '' ) + 'unescape(styleStrings[' + y + '])';
                    }
                    eval('KPL.css.Ctrl.changeStyle(' + funcStr + ');');
                    KPL.css.Ctrl.ssTitle = eval(funcStr);                                // [ib] updated to get current SS title ****
                    break;
                  }
                }
                KPL.css.Ctrl.MWJss = [];
                for (var ss = KPL.css.Ctrl.getAllSheets(), z = 0; ss[z]; z++)
                {
                    KPL.css.Ctrl.MWJss[z] = ss[z].disabled;
                }
            }
            catch(e){}
        };
//                                                                                  // END: CssCtrl CLASS //

// .........................................................................
// KPL.css.Ctrl - early initiation (uses class-static methods)
// .........................................................................
// Note: IN-LINE CODE, executed immediately script is loaded,
//       does NOT wait until host-page is fully loaded.
//       In all XHTML pages, stylesheet-links must precede the script-links.
//       In all XHTML pages, the script-links must be in the head-element.
//       These two conditions above results in this script executing
//       before the XHTML body element is rendered.
try
{
    if (KPL.css.Ctrl.browserCanSwitchCSS())
    {
        KPL.css.Ctrl.useStyleAgain(KPL.css.Ctrl.cookieName);
    }
}
catch(e){}
//                                                                                  // END: CssCtrl //


// ===========================================================================
// COMMON PROJECTOR-LIBRARY - (only used in Projector, ProjctorB)
// [[[[[ these class-scripts is only included in this main-library as they are used on most pages ]]]]]

KPL.sld.Carousel =                                                                  // CLASS CONSTRUCTOR //   common projector-lib
        function (onloadAllSlidesCallback, onload2SlidesCallback, urls)
        {
            this.onloadAllNotify   = onloadAllSlidesCallback;
            this.onload2SldsNotify = onload2SlidesCallback;
            this.size              = urls.length;             // capacity in slides
            this.filled            = false;                   // carousel filled? (all slides fully downloaded?)
            this.received          = 0;                       // count of slides fully downloaded
            this.slideNum          = 1;                       // current slide (at start, assumes 1st slide shown as static)
            this.slides            = new Array();             // indexed as 1-based !!!   (slides[0] unused)

            this.getNextSlideURL = function ()
                                   {
                                       if (this.received < 2)  { return false; }                 // 'false' is type-safe as url can't be only a 0
                                       this.slideNum += 1;
                                       if (this.slideNum > this.received)  { this.slideNum = 1; }
                                       return (this.slides[this.slideNum].url);
                                   };

            this.rewind          = function ()
                                   {
                                       this.slideNum = 1;                                        // slide #2 is to be next-slide
                                   };
            this.destroy         = function ()
                                   {
                                       this.received = 0;
                                       var s = this.size;
                                       for (var i=1; i<=s; i++)  { this.slides[i].destroy(); }
                                       this.slides = null;
                                   };

            this.load            = function ()                                                   // retrieve serially (see also 'onloadSlide')
                                   {
                                       if (this.filled)  { return; }                             // prevent multiple loads
                                       window.status = this.size;
                                       this.slides[1].load();                                    // note: first slide is typically already cached
                                   };
            this.onloadSlide     = function (slideNum)
                                   {
                                       this.received += 1;
                                       if (this.received == 2)  { this.onload2SldsNotify(); }
                                       if (this.received < this.size)
                                       {
                                           window.status = this.size - slideNum;
                                           this.slides[slideNum + 1].load();
                                       }
                                       else
                                       {
                                           this.filled = true;
                                           window.status = '';
                                           this.onloadAllNotify(this.received);
                                       }
                                   };

            var s = this.size;
            for (var i=1; i<=s; i++)
            {
               this.slides[i] = new KPL.sld.Slide(KPL.lib.bind(this,'onloadSlide'), i, urls[i-1]);         // (urls[] is 0-based)
            }
        };                                                                          // END: Carousel CLASS & CONSTRUCTOR //


KPL.sld.Slide =                                                                     // CLASS CONSTRUCTOR //   common projector-lib
        function (onloadCallback, slideNum, url)
        {
            KPL.lib.Encapsulator.call(this);                                                // 'inherit' Encapsulator's features
            this.onloadNotify = onloadCallback;
            this.slideNum     = slideNum;
            this.loaded       = false;
            this.url          = url;
            this.img          = new Image();                                                // note: image obj is retained to ensure browser caching
            this.img_onload   = this.bind(this,'onload');                                   // the function ref must be stored for unbinding
        };                                                                          // END: Slide class CONSTRUCTOR //
KPL.sld.Slide.prototype.load =
        function ()
        {
            if (window.opera && (this.slideNum == 1))  { this.onload(); return; }          // opera remedial (see tech-notes)
            this.bindEvent(this.img, this.img_onload, 'load');
            this.img.setAttribute('src', this.url);               // initiates image retrieval
        };
KPL.sld.Slide.prototype.onload =
        function ()                                               // image-loaded event-handler
        {
            this.loaded = true;
            this.onloadNotify(this.slideNum);
        };
KPL.sld.Slide.prototype.destroy =
        function ()
        {
            try
            {
                this.unbindEvent(this.img, this.img_onload, 'load');
                this.loaded = false;
                this.img = null;
            }
            catch(e){}
        };
//                                                                                  // END: Slide CLASS //


// ===========================================================================
// BEHAVIOUR CLASS: PROJECTOR SLIDESHOW (background)
// [[[[[ this class script is only included in this main-library as it is used on most pages ]]]]]
//
// FEATURES:    provides parameterised auto-advance carousel slideshow:
//              - to aid page readability, carousel pauses when mouse hovers over either of two nominated elements.
//              - carousel ADVANCES to next-slide by mouse-click on 1 nominated element.
//              - carousel ADVANCES to next slide ONLY if 2 or more slides have downloaded.
//              - silent failure for fatal-errors associated with host-environment.
// STATUS:      production release: v1.00
//              audit:   revise/review all comments to ensure current & accurate. caveat lector  - medium priority.
//              review: consider comprehensive checking of ProjctorB arguments                   - low    priority.
// TECH-NOTES:  ECMAScript v3. Protoclass based - prototype, etc, (not v4 formal classes) - see notes at end-of-script.
//              ProjctorB constructor mandates at least 2 slide URL arguments  - more are allowed.
//              ProjctorB constructor mandates exactly  2 slide URL parameters (in function declaration parameter-list).
//              Typically, this class must pause when user has mouse over (hovering) of vital text to aid readability;
//              hence 'hover' events in UserInterfaceBg-class are enabled on instantiation to register the 'pause' state.

KPL.sld.ProjctorB =
        function ( screenElementID,  // target element for slideshow                // CLASS CONSTRUCTOR //
                   slide1Time,       // first-slide  duration in secs
                   slideTime,        // other-slides duration in secs
                   resumeDelay,      // resume-after-pause delay in secs
                   loadDelay,        // start delay within instance, in secs (breathing-space, etc)
                   nextclickElmt1ID, // target element  for next-click  - for optional, use ''
                   nextclickElmt2ID, // target element  for next-click  - for optional, use ''
                   pauseElmt1ID,     // target element1 for hover-pause - for optional, use ''
                   pauseElmt2ID,     // target element2 for hover-pause - for optional, use ''
                   slidesFolder,     // relative to page  [use '' if images in same folder as page]
                   slideName1,       // mandatory (usually same as bkgd image of elementID if it has one)
                   slideName2 )      // mandatory - at least 2 image URL arguments! [only 2 URL parameters!]
{

  try
  {
    if (!(this instanceof KPL.sld.ProjctorB))              { return null; }                   // constructor was CALLed as a function
    if (!(arguments.length >= KPL.sld.ProjctorB.length))   { return null; }                   // mandatory arguments missing
    if (!window.document.getElementById(screenElementID))  { return null; }                   // screen-element-ID invalid

    KPL.lib.Encapsulator.call(this);                                                                    // 'inherit' Encapsulator's features

    var iFirstSlide = KPL.sld.ProjctorB.length - 2;                                                     // 0-based index (2 image urls are mandatory)
    var slidesURLs  = this.encaseArgs(arguments, iFirstSlide, slidesFolder);                            // get array of all image-URLs
    var loadWait_ms = (loadDelay > 0.1) ? Math.round(loadDelay * 1000) : 100;                           // activation delay (browser breathing-space)

    this.carousel   = new KPL.sld.Carousel(this.bind(this,'onloadSlides'),
                                           this.bind(this,'onload2Slides'),
                                           slidesURLs);

    this.screen     = new KPL.sld.ProjctorB.ScreenBg(screenElementID);

    this.changer    = new KPL.sld.ProjctorB.AnimatorBg(this.bind(this,'showSlide'),
                                                       slideTime, slide1Time, loadDelay, resumeDelay);

    this.user       = new KPL.sld.ProjctorB.UserInterfaceBg(this.bind(this,'showSlide'),
                                                            this.bind(this,'pause'), this.bind(this,'resume'),
                                                            nextclickElmt1ID, nextclickElmt2ID,
                                                            pauseElmt1ID,     pauseElmt2ID);

    this.unload  = this.bindEvent(window, this.bind(this,'destroy'), 'unload');                         // function ref (evtHdlr) stored for unbinding
    this.timerID = window.setTimeout(this.bind(this,'start'), loadWait_ms);                             // schedule activation
  }
  catch(e)
  {
    if ((typeof this.timerID) == 'number')  { window.clearTimeout(this.timerID); }
  }

};                                                                                  // END: ProjctorB class-CONSTRUCTOR //


// class ProjctorB - M E T H O D S  (prototype)

KPL.sld.ProjctorB.prototype.showSlide =
        function ()                                    // PLAY-LOOP: schedule next-frame, show slide
        {
            if (!KPL.sld.ProjctorB.enabled)  { this.destroy(); return; }                   // master override to kill each instance
            this.changer.scheduleNext();
            if (this.paused)  { return; }
            this.screen.show(this.carousel.getNextSlideURL());
        };

KPL.sld.ProjctorB.prototype.start =
        function ()                                                            // ACTIVATION
        {
            try
            {
                this.changer.start();
                this.carousel.load();
                this.loaded = true;
            }
            catch(e){this.destroy();}
        };

KPL.sld.ProjctorB.prototype.onload2Slides =
        function ()                                                            // 2 SLIDES LOADED into carousel permit normal operation
        {
            this.user.enableClick();
        };
KPL.sld.ProjctorB.prototype.onloadSlides =
        function (slideCount)  { };                                            // ALL SLIDES LOADED into carousel (unused)

KPL.sld.ProjctorB.prototype.pause =
        function ()                                                            // SUSPEND [bg-only]: stop slide changes, user reading
        {
            this.paused = true;
            if (!this.loaded)  { return; }
            this.changer.cancelNext();
        };
KPL.sld.ProjctorB.prototype.resume =
        function ()                                                            // CONTINUE [bg-only]: play slides as pause finished
        {
            this.paused = false;
            if (!(KPL.sld.ProjctorB.enabled && this.loaded))  { return; }
            this.changer.resume();
        };

KPL.sld.ProjctorB.prototype.destroy =
        function ()                                                            // DEACTIVATE
        {
            try
            {
                if ((typeof this.timerID) == 'number')
                {
                    window.clearTimeout(this.timerID);
                }
                this.changer.destroy();
                this.user.destroy();
                this.screen.destroy();
                this.carousel.destroy();
                this.unbindEvent(window, this.unload, 'unload');
            }
            catch(e){}
        };

//                                                                                  // END: ProjctorB INSTANCE //


// class ProjctorB - C L A S S   F E A T U R E S   ( S T A T I C S )  -  public

// class ProjctorB - class STATICS, COMPONENT CLASSES

KPL.sld.ProjctorB.ScreenBg_STANDARD =                                               // CLASS CONSTRUCTOR //
        function (screenElementID)
        {
            this.elmt        = window.document.getElementById(screenElementID);
            this.elmtStyle   = this.elmt.style;

            this.show        = function (url)
                               {
                                   if (!url)  { return; }
                                   this.elmtStyle.backgroundImage = 'url(' + url + ')';
                               };
            this.destroy     = function ()
                               {
                                   this.elmtStyle  = null;
                                   this.elmt       = null;
                               };
        };                                                                          // END: ScreenBg CLASS & CONSTRUCTOR //
// for normal use, delete the method 'ScreenBg' below & rename method 'ScreenBg_STANDARD' above to 'ScreenBg'
KPL.sld.ProjctorB.ScreenBg =                                    // SPECIAL DEV VERSION (to test IE-transitions)
        function (screenElementID)
        {
            this.elmt        = window.document.getElementById(screenElementID);
            this.elmtStyle   = this.elmt.style;

            this.xtnsElmt    = window.document.getElementById('hdbutnext');              // !! HARDCODED for eval
            this.xtns        = (!!(this.elmt.filters)) && (!!(this.xtnsElmt));
            KPL.sld.ProjctorB.xtnOn  = false;

            if (this.xtns)
            {
                this.elmtStyle.filter = 'progid:DXImageTransform.Microsoft.Fade(Duration=0.9)';
                this.xtnsElmt.ondblclick = function () { KPL.sld.ProjctorB.xtnOn = !KPL.sld.ProjctorB.xtnOn; };
            }

            this.show        = function (url)                                       // IE-transistions
                               {
                                   if (!url)  { return; }
                                   var xON = KPL.sld.ProjctorB.xtnOn;
                                   if (this.xtns && xON)  { this.elmt.filters[0].apply(); }
                                   this.elmtStyle.backgroundImage = 'url(' + url + ')';
                                   if (this.xtns && xON)  { this.elmt.filters[0].play(); }
                               };
            this.destroy     = function ()
                               {
                                   this.elmtStyle  = null;
                                   this.elmt       = null;
                                   if (this.xtns)
                                   {
                                       this.xtnsElmt.ondblclick = null;
                                       this.xtnsElmt = null;
                                   }
                               };
        };                                                                          // END: ScreenBg CLASS & CONSTRUCTOR //

KPL.sld.ProjctorB.AnimatorBg =                                                      // CLASS CONSTRUCTOR //
        function (nextFrameCallback, frameDuration,
                  frame1Duration, frame1DurationElapsed, resumeDelay)
        {
            var rawTF1 = frame1Duration - frame1DurationElapsed;                            // note: frame#1 is first frame
            this.nextFrameNotify = nextFrameCallback;
            this.timeFrame       = frameDuration;
            this.timeFrame1      = (rawTF1 >= 0.001) ? rawTF1 : 0.001;
            this.resumeDelay     = resumeDelay;

            this.start           = function ()  { this.schedule(this.timeFrame1);  };
            this.resume          = function ()  { this.schedule(this.resumeDelay); };
            this.scheduleNext    = function ()  { this.schedule(this.timeFrame);   };
            this.cancelNext      = function ()
                                   {
                                       if ((typeof this.timerID) == 'number')
                                       {
                                           window.clearTimeout(this.timerID);
                                           delete this.timerID;
                                       }
                                   };
            this.schedule        = function (wait_secs)
                                   {
                                       this.cancelNext();
                                       var wait_ms = Math.round(wait_secs * 1000);
                                       this.timerID = window.setTimeout(this.nextFrameNotify, wait_ms);
                                   };
            this.stop            = function ()  { this.cancelNext(); };
            this.destroy         = function ()  { this.cancelNext(); };
        };                                                                          // END: AnimatorBg CLASS & CONSTRUCTOR //


KPL.sld.ProjctorB.UserInterfaceBg =                                                 // CLASS CONSTRUCTOR //
        function (clickCallback, enterCallback, leaveCallback,
                  clickElmt1ID, clickElmt2ID, hoverElmt1ID, hoverElmt2ID)
        {
            this.clickNotify = clickCallback;
            this.enterNotify = enterCallback;
            this.leaveNotify = leaveCallback;
            this.clickElmt1  = window.document.getElementById(clickElmt1ID);
            this.clickElmt2  = window.document.getElementById(clickElmt2ID);
            this.hoverElmt1  = window.document.getElementById(hoverElmt1ID);
            this.hoverElmt2  = window.document.getElementById(hoverElmt2ID);

            if (this.clickElmt1 === this.clickElmt2) { this.clickElmt2 = false; }           // avoid duplicated binding
            if (this.hoverElmt1 === this.hoverElmt2) { this.hoverElmt2 = false; }

            KPL.lib.Encapsulator.call(this);                                                // 'inherit' Encapsulator's features

            if (this.hoverElmt1)                                                            // hover enabled immediately to register actions
            {
                this.bindEvent(this.hoverElmt1, enterCallback, 'mouseover');
                this.bindEvent(this.hoverElmt1, leaveCallback, 'mouseout');
            }
            if (this.hoverElmt2)
            {
                this.bindEvent(this.hoverElmt2, enterCallback, 'mouseover');
                this.bindEvent(this.hoverElmt2, leaveCallback, 'mouseout');
            }

            if (this.clickElmt1)
            {
               this.clickElmt1.style.visibility = 'visible';
            }
            if (this.clickElmt2)
            {
               this.clickElmt2.style.visibility = 'visible';
            }

            this.enableClick = function ()                                                  // click-element enable (typically deferred)
                               {
                                   if (this.clickElmt1)
                                   {
                                       this.bindEvent(this.clickElmt1, this.clickNotify, 'click');
                                       this.clickElmt1.style.cursor = 'pointer';
                                   }
                                   if (this.clickElmt2)
                                   {
                                       this.bindEvent(this.clickElmt2, this.clickNotify, 'click');
                                       this.clickElmt2.style.cursor = 'pointer';
                                   }
                               };
            this.destroy     = function ()                                                  // failsafe against possible IE memory leaks
                               {
                                   if (this.clickElmt1)
                                   {
                                       this.unbindEvent(this.clickElmt1, this.clickNotify, 'click');
                                   }
                                   if (this.clickElmt2)
                                   {
                                       this.unbindEvent(this.clickElmt2, this.clickNotify, 'click');
                                   }
                                   if (this.hoverElmt1)
                                   {
                                       this.unbindEvent(this.hoverElmt1, this.enterNotify, 'mouseover');
                                       this.unbindEvent(this.hoverElmt1, this.leaveNotify, 'mouseout');
                                   }
                                   if (this.hoverElmt2)
                                   {
                                       this.unbindEvent(this.hoverElmt2, this.enterNotify, 'mouseover');
                                       this.unbindEvent(this.hoverElmt2, this.leaveNotify, 'mouseout');
                                   }
                                   this.clickElmt1 = null;
                                   this.clickElmt2 = null;
                                   this.hoverElmt1 = null;
                                   this.hoverElmt2 = null;
                               };
        };                                                                          // END: AnimatorBg CLASS & CONSTRUCTOR //


// class - class STATIC PROPERTIES

KPL.sld.ProjctorB.enabled = true;                                                   // property: master-state for class

//                                                                                  // END: ProjctorB CLASS //


}
catch(e) {}




// ===========================================================================

// NAMESPACE-MANAGEMENT - only one global-identifier reserved ('KPL')
//
// A namespace-object (single global-reference) is used to manage the global-variable space.
// Any other use of global-variables is strictly avoided and eschewed.
// Also, the use of the namespace-object provides an ideal structure for
// runtime examination of the application dataspace.
// For unrestricted deployment, namespace [KPL] would be replaced by [KLEETON_COM_AU].

// TECH-NOTES: CALLBACKS (the 'bind-function)
//
// Callbacks into instance-methods are established by custom 'bind' method (or function) which,
// for each call, manufactures (& returns) a function which invokes the callback-handler as
// a method (rather than merely as a function). 'bind' is short for 'bind-as-method [reference]'.
// - 'bind' effectively internalises the callback & instance-object reference issue,
//   removing it as a consideration in the code/coding (other than the need to use 'bind'
//   functionality). As 'bind' is frequently used for event-callbacks into an instance,
//   it traps & suppresses any errors (so as to be "unobtrusive").
// - the inner return-statement of 'bind' is an ideal place for a breakpoint to trace callbacks.
// - 'bind' is not be implemented as a method of the ECMAScript Function due to namespace management.

// APP-NOTES: PROJCTORB CLASS
// See notes at end of Projector.js script and also in KPL.sld classes above.

// ===========================================================================

// (c) 2006 Ian Brown

// end script

