﻿////////////////////////////////////////////////////////////////////////////////////////////////////
// This code is Copyrighted by Pinpoint
// If you do happen to hack into this code please acknowledge it by including this reference:
// Copyright Pinpoint 2008-2010
// web:   www.pinpointgroup.info
// email: info@pinpointgroup.info
//
// Version number:  1.1
// Last Updated:    2009/12/07
//
////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Standards:
//   1. All functions to be commented.
//   2. All 'tricky' bits of code to be commented.
//   3. Function prefixes:
//       pps = setup functions for registering and configuring html elements.
//       ppo = object functions - ie: methods defined to an html element (these will usually be passed 'this' as an object reference).
//       pp  = standard javascipt functions.
//   4. All constants start with k (there is no 'const' in Javascript so don't overwrite these values!)
//   5. All parameters in lower case.
//   6. All variables / objects prefixed by their type eg: iNumber , objWidget.
//   7. All variables / objects MUST be declared with a 'var' statement. 
//   8. All variables must be inistialised, if they take on a initial parameter value, do it in the var declaration for clarity.
//   9. 'var' defintion at top of function after comments.
//  10. All Global variables / objects must be prefixed with g.
//  11. All function names in Camel Case with the appropriate prefix.
//  12. Don't use the same local variable names as the global variables to avoid confusion.
//  13. Check code for standards
//  14. vars all on one line
//
////////////////////////////////////////////////////////////////////////////////////////////////////
//
// TO DO:
//  * do regression test for range checks on all arrays etc.
//  * animate the moves of the widgets more slowly into position?
//  * save layout to cookie and read from cookie
//  * check when zone is 'too' full for drag / drop??
//  * animate object being dragged over and insert temp space = height of dragged object
//  * sort out screen / client offset when page is scrolled - for drag drop stuff, also how to do autoscroll offscreen?
//  * do up down on min / max button
//  * nb: if object limit is reached (ie: > kMaxWidgetsPerZone) in the zone need to drop some off (into parking?)
//  * if parking zone limit reached, remove the oldest one
//  * check for performance eg: redundant repaints, or object creation
/////////////////////////////////////////////////////////////////
// ***** NB NB NB NB NB NB NB NB ********:
// JSLint Validator:
// copy and paste all javascript into: http://www.jslint.com/
// Javascript Compressor:
// http://developer.yahoo.com/yui/compressor/
//  see: http://developer.yahoo.com/yui/compressor/#using
// java -jar yuicompressor-2.4.2.jar main.js -v -o main-min.js --charset utf-8
//
// JSLint Validation RULES:
// 1. All variables must be declared inside function, otherwise it is assumed to be global.
//  
// 2. var myArray = [];
//      Is the "new" way of defining arrays, and I suppose it is shorter/cleaner.
//
// 3. var myObject = {};
//      Is the "new" way of defining objects 
//
// 4. For parseInt must specifiy number system eg: 10 for decimal.
//
// 5. All IF statements even on one line must use curly brackets and a semicolon inside, and no semi colon outside:
//      eg: Problem at line xxx character yyy: Expected '{' and instead saw 'sState'.
//      ie: if (x==y) { statement here; }
//  6. Problem at line xx character 33: xx '!==' to compare with 'null'.
//  7. Problem at line 239 character 33: Use '===' to compare with 'true'.
//  8. Problem at line 250 character 24: Use '===' to compare with '0'.
//  9. Problem at line 339 character 24: 'iCount' is already defined.
//      don't re-declare "var" in same function
//
//
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
// GLOBAL CONSTANTS
// NB: there is no 'const' keyword in JavaScript yet so don't overwrite the variables prefixed by 'k' below, in any subsequent code!
////////////////////////////////////////////////////////////////////////////////////////////////////

//buttons:
var gWidgetImages = "images/widget/blue/";  //set on dashboard eg. to: "images/widget/grey/";
var kMin_Up = gWidgetImages + "w_min.gif", kMin_Down = gWidgetImages + "w_min.gif", kMax_Up = gWidgetImages + "w_max.gif", kMax_Down = gWidgetImages + "w_max.gif", kClose_Up = gWidgetImages + "w_close.gif", kClose_Down = gWidgetImages + "w_close.gif";

    //set min and max screen drag limits
    var kMinX       = 0; 
    var kMaxX       = 1014; //max right hand limit offset later by actual widget width
    var kMinY       = 0;
    var kMaxY       = 10000;
    var kWidgetGap  = 10;       //vertical spacing of the widgets in normal zone
    var kParkedWidgetGap = 5;   //vertical spacing of the widgets in parking zone
    
    //deafult opacity setting
    var kOpacity    = 5; //50%

    //max dimensions of widget array
    var kMaxZones          = 7;    //number of zones
    var kMaxWidgetsPerZone = 30;   //number of widgets per zone
    var kParkingLimit      = 6;   //max number of widgets in parking zone. Oldest drop off
    kParkingLimit = Math.min(kParkingLimit, kMaxWidgetsPerZone); //limit the parking limit to the number of widgets per zone
    
    //zone and widget snap in pixel limits
    var kZoneSnapIn = 50;
    
    //set search limits for best zone
    //ie: used for seacrh which zone has most space to take a new object
    //will search all zones in this range. Should ideally exclude the parking zone.
    var kBestZoneStart = 1, kBestZoneEnd   = 3;

    var kMove       = 1; //pixel move animation increment

    //spacing div for bottom of page
    var kBottomPageDivId = "bottompagediv";
    var kBottomPageInnerDivId = "innerpagediv";
    var kBottomPageDivSpacingValue = 15; //between bottom widget and the bottom of the innerpagediv
    var kBottomPageDivMinSpacingValue = 800;
    var kBottomSpaceDivId = "bottomspacediv";
    var kBottomSpaceDivSpacingValue = 35; //between inner page div and the bottom of the page
    
////////////////////////////////////////////////////////////////////////////////////////////////////
// GLOBAL VARIABLES
////////////////////////////////////////////////////////////////////////////////////////////////////

    var giZindex = 10;

    //zone info
    //gaZone array structure: [zoneno] ( [id, top, left, width, height] )
    var gaZone = [];
        gaZone[1]    = []; //add first row of second dimension - others are added to it later
    var giZoneCount  = 0;

    //parking zone info
    var gbParkingZone = false, giParkingZone = 0, gbParkingZoneVisible = false, gobjParkingZone;

    //widget info
    var gaWidget = [], gaRegisteredWidgets = [], giWidgetCount = 0, gaWidgetBackup = [], gaProfileBackup = '';
    
    //predimension the "full" array by adding a second dimension
    //gaWidget array structure: [zoneno] ([seqno]( [id , state, widgetcode]))
    var i; //zone number
    var j; //seq no
    for (i=1; i<=kMaxZones; i++) {
        gaWidget[i] = []; //add second dimension
        for (j=1; j<=kMaxWidgetsPerZone; j++) {
            gaWidget[i][j]    = []; //add third dimension
            gaWidget[i][j][1] = "";
            gaWidget[i][j][2] = "";
            gaWidget[i][j][3] = "";
        }
    }
    
    //drag / drop info
    var gobjDrag         = {};     //temporary object to hold the current drag object
    var gobjDragWidget   = {};     //temporary object to hold the parent of current Drag Object
    var giZoneFrom       = 0;                //save the zone that widget is being moved from 
    var giDragTopStart   = 0;                // save starting position to check if object has moved, so to cancel dragend stuff if only a mouse down and up????
    var giDragLeftStart  = 0; 
    var giDropZone       = 0;        //which zone is it being dragged into
    var giDropSeqNo      = 0;        //which object in zone is it being dropped onto
    
    //customize object
    var gobjCustomize = {};

    //cookie expiry
    var giCookieExpiryDays = 30;

    ////////////////////////////////////////////////////////////////////////////////////////////////////
    // Functions that must be preloaded first
    ////////////////////////////////////////////////////////////////////////////////////////////////////
    
    function ppBringToFront(id) {
        //Bring the widget to the front
        var obj = document.getElementById(id);
        if (obj) {
            giZindex += 1;
            if (obj) { obj.style.zIndex = giZindex; }
        }
    }
    
    function ppSetOpacity(id, opacity) {
        //set the opacity of the widget, 10 = max
        //2 methods are used to be browser compatible
        var obj = document.getElementById(id);
        if (obj) {
            obj.style.opacity = opacity / 10;
            obj.style.filter = 'alpha(opacity=' + opacity * 10 + ')';
        }
    }

    function ppoFixE(e) {
        if (typeof e == 'undefined') { e = window.event; }
        if (typeof e.layerX == 'undefined') { e.layerX = e.offsetX; }
        if (typeof e.layerY == 'undefined') { e.layerY = e.offsetY; }
        return e;
    }
   

    function ppoDrag(e) {
        //window.status = "ppoDrag:" + window.event.srcElement.id;

        e = ppoFixE(e);
        var ey = e.clientY, ex = e.clientX;
        var y = parseInt(gobjDragWidget.style.top, 10), x = parseInt(gobjDragWidget.style.left, 10);
        var nx, ny;

        if (gobjDragWidget.minX !== null) { ex = Math.max(ex, gobjDragWidget.minMouseX); }
        if (gobjDragWidget.maxX !== null) { ex = Math.min(ex, gobjDragWidget.maxMouseX); }
        if (gobjDragWidget.minY !== null) { ey = Math.max(ey, gobjDragWidget.minMouseY); }
        if (gobjDragWidget.maxY !== null) { ey = Math.min(ey, gobjDragWidget.maxMouseY); }

        nx = x + (ex - gobjDrag.lastMouseX);
        ny = y + (ey - gobjDrag.lastMouseY);

        gobjDragWidget.style.left = nx + "px";
        gobjDragWidget.style.top = ny + "px";
        gobjDrag.lastMouseX = ex;
        gobjDrag.lastMouseY = ey;

        return false;
    }

    function ppFindDropZone(top, middle) { //returns drop zone (global var giDropZone) or zero for not found
        //find the zone that the widget was dropped into 
        //within top, middle, right and bottom limits of the zone
        //the zone snap in applies to the middle hand border only???
        //calculate zone boundaries
        //gaZone array structure: [zoneno] ( [id, top, middle, width, height] )

        var iCount = 0, iZoneTop, iZoneLeft, iZoneRight, iZoneBottom, bIsDropZone;

        giDropZone = 0;    //gobal var

        for (iCount = 1; iCount <= giZoneCount; iCount++) {
            iZoneTop = gaZone[iCount][2];
            iZoneLeft = gaZone[iCount][3];
            iZoneRight = gaZone[iCount][3] + gaZone[iCount][4];
            iZoneBottom = gaZone[iCount][2] + gaZone[iCount][5];
            bIsDropZone = gaZone[iCount][6];

            //if ((top >= iZoneTop) && (top < iZoneBottom) && (left >= iZoneLeft - kZoneSnapIn) && (left < iZoneRight - kZoneSnapIn + kWidgetGap)) {
            if ((top >= iZoneTop) && (top < iZoneBottom) && (middle >= iZoneLeft) && (middle < iZoneRight)) {
                //if this zone is a drop zone
                if (bIsDropZone === true) {
                    giDropZone = iCount;
                }
                else {
                    break;
                }
                break;
            }
        }

        //if no zone found, check if widget was dropped below a zone
        if (giDropZone === 0) {
            for (iCount = 1; iCount <= giZoneCount; iCount++) {
                iZoneTop = gaZone[iCount][2];
                iZoneLeft = gaZone[iCount][3];
                iZoneRight = gaZone[iCount][3] + gaZone[iCount][4];
                iZoneBottom = gaZone[iCount][2] + gaZone[iCount][5];
                bIsDropZone = gaZone[iCount][6];

                //check if widget was dropped below this zone but whitin the left/right borders
                if ((top >= iZoneBottom) && (middle >= iZoneLeft) && (middle < iZoneRight)) {
                    //if this zone is a drop zone
                    if (bIsDropZone === true ){//&& iCount != giParkingZone) {
                        giDropZone = iCount;
                    }
                    else {
                        break;
                    }
                    break;
                }
            }
        }

        //alert('giDropZone'+giDropZone)
        return giDropZone;
    }

    function ppFindDroppedOnWidget(zoneno, top) { // returns seqno (global var giDropSeqNo) of widget or zero for not found
        //find any widgets in the zone that this widget has been dropped onto
        // widget array: [zoneno] ([seqno]( [id , state]))
        var iCount = 0, objWidget, iWidgetTop, iWidgetHeight;

        giDropSeqNo = 0;    //gobal var

        //get number of widgets in zone??

        //alert('zoneno=' + zoneno + ', top=' + top);
        for (iCount = 1; iCount <= kMaxWidgetsPerZone; iCount++) {
            if (gaWidget[zoneno][iCount][1] != "") {
                objWidget = document.getElementById(gaWidget[zoneno][iCount][1]);
                if (objWidget) {
                    iWidgetTop = parseInt(objWidget.style.top, 10);
                    iWidgetHeight = parseInt(objWidget.offsetHeight, 10);
                    //alert('id=' + gaWidget[zoneno][iCount][1] + ', gobjDragWidget.id=' + gobjDragWidget.id + ', iWidgetTop=' + iWidgetTop + ', iWidgetHeight=' + iWidgetHeight);
                    //must exclude itself as it gets it's own updated height after the drag and chooses itself!
                    if ((gaWidget[zoneno][iCount][1] != gobjDragWidget.id) && (top >= iWidgetTop) && (top < iWidgetTop + iWidgetHeight + kWidgetGap)) {
                        //found a dropped on widget
                        //alert(iCount);
                        giDropSeqNo = iCount;
                        break;
                    }
                }
            }
        } //end for
        //alert('giDropSeqNo'+giDropSeqNo)
        return giDropSeqNo;
    }

    function ppRemoveWidgetFromArray(rootid, leaveempty) {
        //remove the widget from the control array

        var iCount = 0, objWidget, iZone, iSeq;

        if (leaveempty) {
            //only set the widget as "". Leave the other widgets in their place

            //get the widget object
            objWidget = document.getElementById(rootid);
            if (objWidget) {
                iZone = parseInt(objWidget.zoneno, 10);
                iSeq = parseInt(objWidget.seqno, 10);

                //set the widget's current array position to empty
                if (!isNaN(iZone) && !isNaN(iSeq) && iZone && iSeq) {
                    //alert(iCount);
                    gaWidget[iZone][iSeq][1] = "";
                    gaWidget[iZone][iSeq][2] = null;
                    gaWidget[iZone][iSeq][3] = null;
                }
            }
        }
        else {
            //delete specified widget and move other widgets to take it's place

            iCount = 0;

            objWidget = document.getElementById(rootid);
            if (objWidget) {
                iZone = parseInt(objWidget.zoneno, 10);
                iSeq = parseInt(objWidget.seqno, 10);
                //alert('iZone='+iZone+', iSeq='+iSeq);
                if (!isNaN(iZone) && !isNaN(iSeq) && iZone && iSeq) {
                    // now move the items up to take this spot
                    for (iCount = iSeq; iCount < kMaxWidgetsPerZone; iCount++) {
                        if ((gaWidget[iZone][iCount][1] != "") && (iCount < kMaxWidgetsPerZone)) {
                            gaWidget[iZone][iCount][1] = gaWidget[iZone][iCount + 1][1];  //move it up
                            gaWidget[iZone][iCount][2] = gaWidget[iZone][iCount + 1][2];  //change the array
                            gaWidget[iZone][iCount][3] = gaWidget[iZone][iCount + 1][3];

                            //update the position in the widget object
                            if (gaWidget[iZone][i]) {
                                objWidgetMoved = document.getElementById(gaWidget[iZone][i][1]);
                                if (objWidget) {
                                    objWidget.seqno -= 1;
                                }
                            }
                        }
                        else {
                            break;  //ok that's enough
                        }
                    } //end for
                    //set the last (previous) one to empty
                    if (iCount > 1) {
                        //alert(iCount);
                        gaWidget[iZone][iCount][1] = "";
                        gaWidget[iZone][iCount][2] = null;
                        gaWidget[iZone][iCount][3] = null;
                    }
                }
            }
        }
        /*var iCount      = 0;
        var objWidget;
        var iZone;
        var iSeq;

    objWidget = document.getElementById(rootid);
        if (objWidget) {objWidget.is
        //hide the widget if it is currently in the parking zone
        if (gbParkingZone && objWidget.zoneno == giParkingZone) {
        objWidget.style.display = "none";   //hide it from the screen
        }

        //update "user customize" panel
        //ppUncheckProfiles ();
        ppSetCustomizeOption(rootid,false);
        
        if (objWidget) {
        iZone = parseInt(objWidget.zoneno, 10);
        iSeq  = parseInt(objWidget.seqno, 10);
        //alert('iZone='+iZone+', iSeq='+iSeq);
        if (!isNaN(iZone) && !isNaN(iSeq) && iZone && iSeq) {
        // now move the items up to take this spot
        for (iCount=iSeq; iCount < kMaxWidgetsPerZone; iCount++) {
        if ((gaWidget[iZone][iCount][1] != "") && (iCount < kMaxWidgetsPerZone)) { 
        gaWidget[iZone][iCount][1] = gaWidget[iZone][iCount + 1][1];  //move it up
        gaWidget[iZone][iCount][2] = gaWidget[iZone][iCount + 1][2];  //change the array
        gaWidget[iZone][iCount][3] = gaWidget[iZone][iCount + 1][3];
        }
        else {
        break;  //ok that's enough
        }
        } //end for
        //set the last (previous) one to empty
        if (iCount > 1) {
        //alert(iCount);
        gaWidget[iZone][iCount][1] = "";
        gaWidget[iZone][iCount][2] = null;
        gaWidget[iZone][iCount][3] = null;
        }
        }
        }
        }*/
    }

    function ppWriteCookie(cname, cvalue, cexpire) {
        var cookieLine = cname + '=' + escape(cvalue) +
    (cexpire !== null ? ';expires=' + cexpire.toGMTString() : '') + ';path=/';
        document.cookie = cookieLine;
    }

    function ppStoreWidgetArrayToCookie() {
        var iZone = 0, iSeq = 0, sText = "", objWidget;

        for (iZone = 1; iZone < gaWidget.length; iZone++) {
            for (iSeq = 1; iSeq < gaWidget[iZone].length; iSeq++) {
                objWidget = document.getElementById(gaWidget[iZone][iSeq][1]);

                //this old line writes the rootid of the widget to the cookie but this is unneccesary
                //sText = sText + iZone + "," + iSeq + "," + gaWidget[iZone][iSeq][1] + "," + gaWidget[iZone][iSeq][2] + "," + gaWidget[iZone][iSeq][3] + ",";

                //add the widget's details to the cookie string. (Leave out the rootid because it's unneccesary and and to reduce the cookie's size)
                sText = sText + iZone + "." + iSeq + "." + gaWidget[iZone][iSeq][2] + "." + gaWidget[iZone][iSeq][3] + ".";
            }
        }

        var expiryDate = new Date();
        expiryDate.setDate(expiryDate.getDate() + giCookieExpiryDays);

        ppWriteCookie(gCurrentPageCode + "WA", sText, expiryDate);
    }

    function ppStateDisplay(rootid) {
        //display the widget according to its state
        var objWidget = document.getElementById(rootid);
        var objContent = document.getElementById(objWidget.contentid);
        var objAction = document.getElementById(objWidget.actionid);
        var objMiniMax = document.getElementById(objWidget.minimaxid);

        if (objWidget && objContent && objMiniMax) {
            ppBringToFront(rootid);

            if (objWidget.state == "min") {
                objWidget.style.display = "";
                objContent.style.display = "none";
                objAction.style.display = "none";
                objMiniMax.src = kMax_Up;
            }
            else if (objWidget.state == "max") {
                objWidget.style.display = "";
                objContent.style.display = "";
                objAction.style.display = "";
                objMiniMax.src = kMin_Up;
            }
            else {
                objWidget.style.display = "none";
            }
        }
    }

    function ppSetCustomizeOption(rootid, checked) {
        //set customize panel check boxes here
        var objCheck = document.getElementById(rootid + "_check");
        if (objCheck) { objCheck.checked = checked; }
    }

    function ppRepaintScreen() {
        //alert('ppRepaintScreen');

        //go through widget array
        //add items to each zone in correct sequence and state
        //gaWidget array structure: [zoneno] ([seqno]( [id , state]))
        //can handle zone size changes on subsequent visits - or need version number in cookie
        //if the stored cookie info is no longer valid for the current screen layout

        var iZone, iSeq, objWidget, objHandle, iRootId = 0, iZoneTop = 0, iZoneLeft = 0, iZoneWidth = 0, iWidgetTop = 0, lowestWidgetBottom = 0;
        //ppHideAllWidgets ();  //temp requirement??
        //uncheck all customize boxes? - no because some are combined into profiles.

        for (iZone = 1; iZone <= giZoneCount; iZone++) {
            //get zone info:
            iZoneTop = gaZone[iZone][2];  //zone top position
            iZoneLeft = gaZone[iZone][3];  //zone left position
            iZoneWidth = gaZone[iZone][4];  //zone width
            iWidgetTop = iZoneTop;

            //go through widgets in zone:
            for (iSeq = 1; iSeq <= kMaxWidgetsPerZone; iSeq++) {

                objWidget = document.getElementById(gaWidget[iZone][iSeq][1]);
                if (objWidget) {
                    iRootId = objWidget.id;
                    //set object properties
                    objWidget.zoneno = iZone;
                    objWidget.seqno = iSeq;
                    objWidget.state = gaWidget[iZone][iSeq][2];
                    objWidget.widgetcode = gaWidget[iZone][iSeq][3];
                    //set in position and limit to zone width
                    objWidget.style.display = "";
                    objWidget.style.top = iWidgetTop + "px";
                    objWidget.style.left = iZoneLeft + 1 + "px";
                    objWidget.style.width = iZoneWidth + "px";
                    //display minimise / maximise / hidden state according to object state property
                    ppStateDisplay(iRootId);
                    //set iWidgetTop for next item
                    objHandle = document.getElementById(iRootId.substring(0, iRootId.length - 5) + "_handle");
                    if (gbParkingZone && iZone == giParkingZone) {
                        if (objHandle) { objHandle.className = "widget-handleparked"; }
                        iWidgetTop = iWidgetTop + parseInt(objWidget.offsetHeight, 10);
                        if (objWidget.state != 'hid') {
                            iWidgetTop += +kParkedWidgetGap;
                        }
                        if (gbParkingZoneVisible) {
                            //objWidget.style.display = "";
                        }
                        else {
                            //objWidget.style.display = "none";
                        }
                        //uncheck customize click here
                        ppSetCustomizeOption(iRootId, false);
                    }
                    else {
                        if (objHandle) { objHandle.className = "widget-handle"; }

                        iWidgetTop = iWidgetTop + parseInt(objWidget.offsetHeight, 10);
                        //only add the kWidgetGap constant if the widget is visible
                        if (objWidget.state != 'hid') {
                            iWidgetTop = iWidgetTop + kWidgetGap;
                        }
                        //set customize click here
                        ppSetCustomizeOption(iRootId, true);
                    }

                    /*if ((iWidgetTop + objWidget.offsetHeight) > lowestWidgetBottom) {
                    lowestWidgetBottom = (iWidgetTop + objWidget.offsetHeight)
                    }*/
                    /*if (iWidgetTop > lowestWidgetBottom) {
                    lowestWidgetBottom = iWidgetTop;
                    }*/

                }

                if (iWidgetTop > lowestWidgetBottom) {
                    lowestWidgetBottom = iWidgetTop;
                }
            }
        }

        var bottomPageDivHeight = (lowestWidgetBottom - 155 + kBottomPageDivSpacingValue);
        bottomPageInnerDivheight = bottomPageDivHeight;
        if (bottomPageDivHeight < kBottomPageDivMinSpacingValue) {
            bottomPageDivHeight = kBottomPageDivMinSpacingValue;
            bottomPageInnerDivheight = kBottomPageDivMinSpacingValue;
        }
        document.getElementById(kBottomPageDivId).style.height = bottomPageDivHeight + "px";
        document.getElementById(kBottomPageInnerDivId).style.height = bottomPageInnerDivheight + "px";
        document.getElementById(kBottomSpaceDivId).style.top = (bottomPageDivHeight + 275 + kBottomPageDivSpacingValue) + "px";
        document.getElementById(kBottomSpaceDivId).style.height = kBottomSpaceDivSpacingValue + "px";
        //alert('inner div calc:' + bottomPageInnerDivheight);
    }

    function ppHideWidget(rootid) {
        var objWidget = document.getElementById(rootid);
        objWidget.state = "hid";
        gaWidget[objWidget.zoneno][objWidget.seqno][2] = "hid";

        //unccheck the customize checkbox
        for (i = 0; i < gaCheckBoxArray.length; i++) {
            var checkBox = document.getElementById(gaCheckBoxArray[i]);
            if (objWidget.widgetcode == checkBox.value) {
                checkBox.checked = false;
            }
        }

        ppStoreWidgetArrayToCookie();
        ppRepaintScreen();
    }

    //add the widget to the array
    function ppAddWidgetToArray(rootid, state, zoneno, position, widgetcode) {//optional: state, zoneno, position and widgetcode
        var objWidget = {}, iTargetPosition = position;

        if (isNaN(iTargetPosition) || iTargetPosition === null) {
            iTargetPosition = gaWidget[zoneno].length - 1;
        }

        //check if there is a widget in the target location
        if (gaWidget[zoneno][iTargetPosition] && gaWidget[zoneno][iTargetPosition][1] != "") {
            //look for the last empty slot after in the zone
            var lastEmpty = 0;
            for (i = gaWidget[zoneno].length - 1; i > 0; i--) {
                if (gaWidget[zoneno][i][1] == "") {
                    lastEmpty = i;
                    break;
                }
            }

            if (lastEmpty !== 0) {
                if (lastEmpty > iTargetPosition) {
                    //move all widgets between the target location(inclusive) and the empty location(exclusive) 
                    //down one place in order to fill the "" location and open up the target location.
                    for (i = lastEmpty; i > iTargetPosition; i--) {
                        //replace current array entry with the one before it
                        gaWidget[zoneno][i][1] = gaWidget[zoneno][i - 1][1];
                        gaWidget[zoneno][i][2] = gaWidget[zoneno][i - 1][2];
                        gaWidget[zoneno][i][3] = gaWidget[zoneno][i - 1][3];

                        //update the position in the widget object
                        objWidget = document.getElementById(gaWidget[zoneno][i][1]);
                        if (objWidget) {
                            objWidget.seqno += 1;
                        }
                    }
                }
                else {
                    //move all widgets between the target location(inclusive) and the empty location(exclusive) 
                    //up one place in order to fill the "" location and open up the target location.
                    for (i = lastEmpty; i < iTargetPosition; i++) {
                        //replace current array entry with the one before it
                        gaWidget[zoneno][i][1] = gaWidget[zoneno][i + 1][1];
                        gaWidget[zoneno][i][2] = gaWidget[zoneno][i + 1][2];
                        gaWidget[zoneno][i][3] = gaWidget[zoneno][i + 1][3];

                        //update the position in the widget object
                        objWidget = document.getElementById(gaWidget[zoneno][i][1]);
                        if (objWidget) {
                            objWidget.seqno -= 1;
                        }
                    }


                    //iTargetPosition -= 1;
                }
            }
            else {
                //no space for widget in current zone, find zone that has space
                alert('No more space in this zone:');
                for (j = 1; j < gaWidget.length; j++) {
                    if (gaWidget[j]) {
                        for (i = 1; i < gaWidget[j].length; i++) {
                            if (gaWidget[j][i] && gaWidget[j][i][1] == "") {

                                //space found, add widget here
                                //alert('space found in zone: ' + j + ' position: ' + i);
                                ppAddWidgetToArray(rootid, state, j, null, widgetcode);
                                return true;
                            }
                        }
                    }
                }
            }
        }

        //check for parking zone
        if (gbParkingZone) {//if going to parking zone minimise the widget, if coming from parking zone maximise it
            //check if going to parking zone
            if (zoneno == giParkingZone && state != "hid") {//going to parking zone
                state = "min";

                //if the parking zone limit has been reached hide the last visible item
                var visibleParkedWidgets = 0, indexOfLastVisibleWidget = 0;
                for (i = 1; i < gaWidget[giParkingZone].length; i++) {
                    objWidget = document.getElementById(gaWidget[giParkingZone][i][1]);

                    if (objWidget) {
                        if (gaWidget[giParkingZone][i][2] == 'max' || gaWidget[giParkingZone][i][2] == 'min') {
                            //visible widget found in parking zone, update the variables
                            visibleParkedWidgets += 1;
                            indexOfLastVisibleWidget = i;
                        }
                    }
                }

                //hide last widget if limit has been reached
                if (visibleParkedWidgets > kParkingLimit) {
                    ppHideWidget(gaWidget[giParkingZone][indexOfLastVisibleWidget][1]);
                }

            }
            //check if coming from parking zone
            else if (giZoneFrom == giParkingZone) {//coming from parking zone
                state = "max";
                ppLoadWidgetContent(rootid, true);
            }
        }

        //add widget in the target location
        gaWidget[zoneno][iTargetPosition][1] = rootid;
        gaWidget[zoneno][iTargetPosition][2] = state;
        gaWidget[zoneno][iTargetPosition][3] = widgetcode;

        return true;
        /*
        //alert('ppAddWidgetToArray: id='+rootid + ',state=' + state + ',zone=' + zoneno + ',position=' + position);
        var iCount      = 0;
        var iArrayEnd   = 0;
        var iPosition   = 0;
        var iZone       = parseInt(zoneno, 10);
        var sState = state;
        var sWidgetCode = widgetcode
    
    //need to handle optional zone number as well.
        //put in previous if objects still exists, or put in "smallest" zone. will need to sum all the heights
    
    //found flag
        var bFoundSpot;
    
    //if no zone supplied find the zone with most space available
        if (isNaN(iZone) || !iZone) iZone = ppFindBestZone(kBestZoneStart,kBestZoneEnd);
    
    if (gbParkingZone) {//if going to parking zone minimise the widget, if coming from parking zone maximise it
        //get zone from
        if (iZone == giParkingZone && state != "hid") {//going to parking zone
        sState = "min";
        }
        else if (giZoneFrom == giParkingZone) {//coming from parking zone
        sState = "max";
        ppLoadWidgetContent(rootid, true);
        }
        }
  
    //commented out to allow for third status "hidden"
        //if ((state != "min") && (state != "max")) sState = "max";
    
    //update "user customize" panel
        //ppSetCustomizeOption(rootid,true);
  
    //find the end of the array:
        //loop through widget array from the bottom up
        //to find end of array for the given zone
        for (iCount = kMaxWidgetsPerZone; iCount > 0 ; iCount--) {
        if (gaWidget[iZone][iCount][1] != "") break;
        }
        iArrayEnd = iCount + 1;
    
    //if the position is not defined or invalid add it at the end of the list
        if (isNaN(position) || (position > iArrayEnd )) {
        iPosition = iArrayEnd;
        }
        else {
        iPosition = position;
        }

    //scan through the array and add the widget entry in the right position
        //look from the bottom to find the spot
        bFoundSpot = false;
        while (!bFoundSpot && iArrayEnd > 0) {
        for (iCount=iArrayEnd; iCount > 0; iCount--) {
        if (gaWidget[iZone][iCount][1] != "") { //found one in the way, move it down
        gaWidget[iZone][iCount + 1][1] = gaWidget[iZone][iCount][1];  //change the array
        gaWidget[iZone][iCount + 1][2] = gaWidget[iZone][iCount][2];  //change the array
        gaWidget[iZone][iCount + 1][3] = gaWidget[iZone][iCount][3];  //change the array
        }
        if (iPosition==iCount) { //found the spot add it here
        gaWidget[iZone][iPosition][1] = rootid;         //add it to the array
        gaWidget[iZone][iPosition][2] = sState;         //add it to the array
        gaWidget[iZone][iPosition][3] = sWidgetCode;    //add it to the array
        bFoundSpot  = true;
        break;  //ok that's enough
        }
        iArrayEnd = iCount;
        } //end for
        } //end while
        //ppRepaintScreen();
        return bFoundSpot;
        */
    }

    function ppMoveWidgetZones(rootid, state, zoneno, position, leaveempty) {
        //need to change order at some stage, ie: check if successful add before removing
        //tricky becuase it could moving in the same zone?
        ppRemoveWidgetFromArray(rootid, leaveempty);

        objWidget = document.getElementById(rootid);
        objWidget.state = state;
        objWidget.zoneno = zoneno;
        objWidget.seqno = position;

        ppAddWidgetToArray(rootid, state, zoneno, position, objWidget.widgetcode);

        ppStoreWidgetArrayToCookie();
    }

    function ppoDragEnd() {
        giDropZone = 0;
        giDropSeqNo = 0;

        var iWidgetTop, iWidgetLeft;

        ppSetOpacity(gobjDragWidget.id, 10);

        iWidgetTop = parseInt(gobjDragWidget.style.top, 10);
        iWidgetLeft = parseInt(gobjDragWidget.style.left, 10);
        //iWidgetWidth  = parseInt(gobjDragWidget.style.width, 10);
        //alert(Math.abs(giDragTopStart - iWidgetTop));
        //alert(Math.abs(giDragLeftStart - iWidgetLeft));

        if ((Math.abs(giDragTopStart - iWidgetTop) > 10) || (Math.abs(giDragLeftStart - iWidgetLeft) > 10)) {//did the object actually move or is this a double click to toggle only?
            //find drop zone and place it square left

            //remove from previous zone and add to new if necessary
            //giDropZone = ppFindDropZone (iWidgetTop, iWidgetLeft);
            objCurrentWidget = document.getElementById(gobjDragWidget.id);
            var iWidgetWidth = parseInt(objCurrentWidget.style.width, 10);
            var iWidgetMiddle = (iWidgetWidth / 2) + iWidgetLeft;
            giDropZone = ppFindDropZone(iWidgetTop, iWidgetMiddle);

            //alert("drop zone:" + giDropZone)

            if (giDropZone > 0) { //found a valid drop zone
                giDropSeqNo = ppFindDroppedOnWidget(giDropZone, iWidgetTop);
                if (giDropSeqNo > 0) { //found a valid widget dropped on
                    ppMoveWidgetZones(gobjDragWidget.id, gobjDragWidget.state, giDropZone, giDropSeqNo);
                }
                else { //no widget so add in default position
                    ppMoveWidgetZones(gobjDragWidget.id, gobjDragWidget.state, giDropZone);
                }
            }
            else { // no zone found so ignore
            }
        }

        ppRepaintScreen();

        document.onmousemove = null;
        document.onmouseup = null;

        gobjDrag = null;
        gobjDragWidget = null;
    }

    function ppoDragStart(e) {
        //window.status = "ppoDragStart:" + window.event.srcElement.id;

        gobjDrag = this;
        gobjDragWidget = document.getElementById(gobjDrag.rootid);

        giZoneFrom = gobjDragWidget.zoneno;

        //alert('gobjDragWidget.id=' + gobjDragWidget.id + ', gobjDragWidget.state=' + gobjDragWidget.state);

        ppBringToFront(gobjDragWidget.id);
        ppSetOpacity(gobjDragWidget.id, kOpacity);

        e = ppoFixE(e);
        var y = parseInt(gobjDragWidget.style.top, 10), x = parseInt(gobjDragWidget.style.left, 10);

        // save starting position to check if object has moved, so to cancel dragend stuff if only a mouse down and up????	
        giDragTopStart = y;
        giDragLeftStart = x;

        gobjDrag.lastMouseX = e.clientX;
        gobjDrag.lastMouseY = e.clientY;

        if (gobjDragWidget.minX !== null) { gobjDragWidget.minMouseX = e.clientX - x + gobjDragWidget.minX; }
        if (gobjDragWidget.maxX !== null) { gobjDragWidget.maxMouseX = gobjDragWidget.minMouseX + gobjDragWidget.maxX - gobjDragWidget.minX; }

        if (gobjDragWidget.minY !== null) { gobjDragWidget.minMouseY = e.clientY - y + gobjDragWidget.minY; }
        if (gobjDragWidget.maxY !== null) { gobjDragWidget.maxMouseY = gobjDragWidget.minMouseY + gobjDragWidget.maxY - gobjDragWidget.minY; }

        //	gobjDrag.onmousemove = ppoDrag;
        //	gobjDrag.onmouseup   = ppoDragEnd;

        document.onmousemove = ppoDrag;
        document.onmouseup = ppoDragEnd;

        return false;
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////
    // End Functions preloaded
    ////////////////////////////////////////////////////////////////////////////////////////////////////

    ////////////////////////////////////////////////////////////////////////////////////////////////////
// Setup Pinpoint Widget and other controls
// This defines the properties (attributes), methods (functions) and events 
// for the specific HTML elements
// This happens at page load time
////////////////////////////////////////////////////////////////////////////////////////////////////

    function ppsRegisterZone(id, parkingzone, isdropzone) {
        //setup the zone object
        var objZone = document.getElementById(id);
        if (objZone) {
            //add this item to the zone array
            giZoneCount += 1;
            gaZone[giZoneCount][1] = id;
            gaZone[giZoneCount][2] = parseInt(objZone.style.top, 10);
            gaZone[giZoneCount][3] = parseInt(objZone.style.left, 10);
            gaZone[giZoneCount][4] = parseInt(objZone.style.width, 10);
            gaZone[giZoneCount][5] = parseInt(objZone.offsetHeight, 10);
            gaZone[giZoneCount][6] = isdropzone;

            gaZone[giZoneCount + 1] = [];    //add second dimension to this array.
            gaZone[giZoneCount + 1][3] = gaZone[giZoneCount][3] + gaZone[giZoneCount][4]; //add next position by width

            if (parkingzone) {
                gbParkingZone = true;
                giParkingZone = giZoneCount;
                gbParkingZoneVisible = true;
                gobjParkingZone = objZone;
                gobjParkingZone.style.display = "";
            }
        }
    }

    function ppoBringToFront() {
        ppBringToFront(this.rootid);
    }

    function ppMinimise(rootid) {
        var objWidget = document.getElementById(rootid);

        objWidget.state = "min";
        gaWidget[objWidget.zoneno][objWidget.seqno][2] = "min";
        ppStoreWidgetArrayToCookie();
        ppRepaintScreen();
    }

    function ppMaximise(rootid) {
        var objWidget = document.getElementById(rootid);

        objWidget.state = "max";
        gaWidget[objWidget.zoneno][objWidget.seqno][2] = "max";
        ppStoreWidgetArrayToCookie();

        ppLoadWidgetContent(rootid, true);

        ppRepaintScreen();
    }


    function ppToggleState(rootid) {
        var objWidget = document.getElementById(rootid);
        if (objWidget) {
            if (objWidget.state == "min") {
                ppMaximise(rootid);
            }
            else {
                ppMinimise(rootid);
            }
        }
        ppStoreWidgetArrayToCookie();
    }

    function ppoToggleState() {
        ppToggleState(this.rootid);
    }

    function ppClose(rootid) {
        var objWidget;

        if (gbParkingZone) {
            objWidget = document.getElementById(rootid);
            if (objWidget) {
                //alert(objWidget.id);
                if (objWidget.zoneno == giParkingZone) {//remove completely if already parked
                    //ppRemoveWidgetFromArray(rootid);
                    ppHideWidget(rootid);
                }
                else {
                    ppMoveWidgetZones(objWidget.id, "min", giParkingZone, 1);
                }
            }
        }
        else {
            ppRemoveWidgetFromArray(rootid);
        }
        ppStoreWidgetArrayToCookie();
        ppRepaintScreen();
    }

    function ppoClose() {
        ppClose(this.rootid);
    }

    function ppsRegisterWidget(rootid, handleid, minimaxid, closeid, contentid, actionid, state, widgetcode) {
        //set properties and methods for the Widget object
        //The widgets has a hierarchical structure eg: root > handle > content

        var objRoot = document.getElementById(rootid);

        //add this widget to the registered widgets list
        giWidgetCount += 1;
        gaRegisteredWidgets[giWidgetCount] = rootid;

        //Setup the main Widget object
        objRoot.zoneno = 0;            //not known yet
        objRoot.seqno = 0;            //not known yet
        objRoot.handleid = handleid;     //the id of the handle (title bar)
        objRoot.minimaxid = minimaxid;    //the id of the control buttons
        objRoot.closeid = closeid;      //the id of the close button
        objRoot.contentid = contentid;    //the id of the content
        objRoot.actionid = actionid;     //the id of the action bar
        objRoot.state = "max";        //set to maximised by default
        objRoot.widgetcode = widgetcode;   //the widget code of the widget

        //set window drag limits for the widget
        if (!objRoot.minX) { objRoot.minX = kMinX; }
        if (!objRoot.maxX) { objRoot.maxX = kMaxX; } //- parseInt(objRoot.style.width)***temporarily commented out because empty value is returned for some reason***;  //reset this if width changes in drag drop
        if (!objRoot.minY) { objRoot.minY = kMinY; }
        if (!objRoot.maxY) { objRoot.maxY = kMaxY; }

        //Setup the Widget Handle
        var objHandle = document.getElementById(handleid);
        if (objHandle) {
            objHandle.rootid = rootid;           //the id of parent div conbtainer
            objHandle.onmousedown = ppoDragStart;     //event handler for drag
            objHandle.ondblclick = ppoToggleState;   //event handler for double click on title bar ie: minimise, maximise
        }

        //Setup the Widget Control bar - minimise & maximise button
        var objMiniMax = document.getElementById(minimaxid);
        if (objMiniMax) {
            objMiniMax.rootid = rootid;
            objMiniMax.border = 0;
            objMiniMax.src = kMin_Up;
            objMiniMax.onclick = ppoToggleState;
            //	objMiniMax.onmousedown = new Function("this.src = kMin_Down;");
            //	objMiniMax.onmouseout  = new Function("this.src = kMin_Up;");
            //	objMiniMax.onmouseup   = new Function("this.src = kMin_Up;");
        }

        //Setup the Widget Close button
        var objClose = document.getElementById(closeid);
        if (objClose) {
            objClose.rootid = rootid;
            objClose.border = 0;
            objClose.src = kClose_Up;
            objClose.onclick = ppoClose;
            objClose.onmousedown = new Function("this.src = kClose_Down;");
            objClose.onmouseout = new Function("this.src = kClose_Up;");
            objClose.onmouseup = new Function("this.src = kClose_Up;");
        }

        //Setup the Widget Content Box
        var objContent = document.getElementById(contentid);
        objContent.rootid = rootid;
        objContent.onclick = ppoBringToFront;
    }

////////////////////////////////////////////////////////////////////////////////////////////////////
// OBJECT METHODS : (prefixed: ppo)
// called with an object reference
// eg: these are called with an object reference eg: from the widget object itself
// eg: the object passed is 'this'
// these object methods are intermediate ones to get for eg: the root id out of the object to pass it to the necessary funtion
////////////////////////////////////////////////////////////////////////////////////////////////////


    function ppoMinimise(rootid) {
        ppMinimise(this.rootid);
    }

    function ppoMaximise() {
        ppMaximise(this.rootid);
    }


////////////////////////////////////////////////////////////////////////////////////////////////////
// FUNCTIONS : (prefixed: pp)
////////////////////////////////////////////////////////////////////////////////////////////////////



    function ppUnHideWidget(rootid) {
        var objWidget = document.getElementById(rootid);

        if (objWidget) {
            //ppMoveWidgetZones(rootid, 'max', objWidget.zoneno, 1);
            var newState = 'max';
            //if widget is in parking zone
            if (gbParkingZone && objWidget.zoneno == giParkingZone) {
                newState = 'min';

                //check if parking zone limit has been reached
                var visibleParkedWidgets = 0;
                for (i = 1; i < gaWidget[giParkingZone].length; i++) {
                    if (gaWidget[giParkingZone][i][2] == 'max' || gaWidget[giParkingZone][i][2] == 'min') {
                        //visible widget found in parking zone, update the variables
                        visibleParkedWidgets += 1;
                    }
                }

                //move widget to a different zone if parking limit has been reached
                if (visibleParkedWidgets >= kParkingLimit) {
                    var objWidget2 = {};
                    objWidget2 = document.getElementById(rootid);

                    ppMoveWidgetZones(rootid, 'max', 1, 1);
                }
            }
            objWidget = document.getElementById(rootid);

            objWidget.state = newState;
            gaWidget[objWidget.zoneno][objWidget.seqno][2] = newState;
            ppLoadWidgetContent(rootid, true);  //problemmatic because loaded on page??

            //check the customize checkbox
            for (i = 0; i < gaCheckBoxArray.length; i++) {
                var checkBox = document.getElementById(gaCheckBoxArray[i]);
                if (objWidget.widgetcode == checkBox.value) {
                    checkBox.checked = true;
                    break;
                }
            }
        }
        ppStoreWidgetArrayToCookie();
    }


////////////////////////////////////////////////////////////////////////////////////////////////////
// Drag / Drop object methods:
////////////////////////////////////////////////////////////////////////////////////////////////////


////////////////////////////////////////////////////////////////////////////////////////////////////
// End of Drag drop methods
////////////////////////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////////////////////////
// Cookie methods:
////////////////////////////////////////////////////////////////////////////////////////////////////

    function ppDelete_cookie(cookie_name) {
        var cookie_date = new Date();  // current date & time
        cookie_date.setTime(cookie_date.getTime() - 1);
        document.cookie = cookie_name += "=; expires=" + cookie_date.toGMTString() + ';path=/';
    }

    function ppGetCookie(cookie_name) {
        var results = document.cookie.match('(^|;) ?' + cookie_name + '=([^;]*)(;|$)');

        if (results) {
            return (unescape(results[2]));
        }
        else {
            return null;
        }
    }

    function ppLoadWidgetArrayFromCookie() {
        //read the cookie into a temp array and update the real widget array accordingly

        var strCookieValue = ppGetCookie(gCurrentPageCode + "WA");
        if (strCookieValue === null || strCookieValue == "") {
            return null;
        }

        //create the temp array to hold the widgets retrieved from the cookie
        var aTempWidgetArray = [];

        for (i = 1; i <= kMaxZones; i++) {
            aTempWidgetArray[i] = []; //add second dimension
            for (j = 1; j <= kMaxWidgetsPerZone; j++) {
                aTempWidgetArray[i][j] = []; //add third dimension
                aTempWidgetArray[i][j][1] = "";
            }
        }

        //declare variables for looping through cookie
        var iZone = 1, iSeq = 1, id = "", state = "", widgetCode = "", index = 0, iNextCommaIndex;

        //loop through the entire cookie string (one widget at a time)
        while (index < strCookieValue.length) {
            //zone
            iNextCommaIndex = strCookieValue.indexOf(".", index);
            iZone = strCookieValue.substring(index, iNextCommaIndex);
            index = iNextCommaIndex + 1;
            //sequence
            iNextCommaIndex = strCookieValue.indexOf(".", index);
            iSeq = strCookieValue.substring(index, iNextCommaIndex);
            index = iNextCommaIndex + 1;
            //state
            iNextCommaIndex = strCookieValue.indexOf(".", index);
            state = strCookieValue.substring(index, iNextCommaIndex);
            index = iNextCommaIndex + 1;
            //widgetcode
            iNextCommaIndex = strCookieValue.indexOf(".", index);
            widgetCode = strCookieValue.substring(index, iNextCommaIndex);
            index = iNextCommaIndex + 1;

            //add values to the temp array
            aTempWidgetArray[iZone][iSeq][1] = id;
            aTempWidgetArray[iZone][iSeq][2] = state;
            aTempWidgetArray[iZone][iSeq][3] = widgetCode;
        }

        return aTempWidgetArray;
    }

    function ppSyncWidgetArrayWithCookie() {
        var aTempWidgetArray = ppLoadWidgetArrayFromCookie();

        if (aTempWidgetArray === null) {
            return;
        }
        else {
            //this replaces the current widget array with the one from the cookie
            //gaWidget = ppLoadWidgetArrayFromCookie();

            //this loops the cookie array and only updates the "widgetcodes" found in the live array
            var iZone = 0;
            var iSeq = 0;
            var iZone2 = 0;
            var iSeq2 = 0;

            var widgetCode;

            for (iZone = 1; iZone < aTempWidgetArray.length; iZone++) {
                for (iSeq = 1; iSeq < aTempWidgetArray[iZone].length; iSeq++) {
                    if (aTempWidgetArray[iZone][iSeq] && aTempWidgetArray[iZone][iSeq][3] !== null) {
                        //widget code found
                        widgetCode = aTempWidgetArray[iZone][iSeq][3];

                        //loop gaWidget array to find corresponding widget code
                        for (iZone2 = 1; iZone2 < gaWidget.length; iZone2++) {
                            for (iSeq2 = 1; iSeq2 < gaWidget[iZone2].length; iSeq2++) {
                                if (gaWidget[iZone2][iSeq2] && gaWidget[iZone2][iSeq2][3] !== null) {
                                    //widget code found - check if it is the same as the current one in the temp array
                                    if (gaWidget[iZone2][iSeq2][3] == aTempWidgetArray[iZone][iSeq][3]) {
                                        //widget codes are the same, therefore update the values (state)
                                        gaWidget[iZone2][iSeq2][2] = aTempWidgetArray[iZone][iSeq][2];
                                        //if the new widget details specify a new location, move it there but move the widget in that location to the current widget's old location
                                        if (iZone != iZone2 || iSeq != iSeq2) {
                                            //swop
                                            var objWidgetArr = gaWidget[iZone][iSeq];
                                            gaWidget[iZone][iSeq] = gaWidget[iZone2][iSeq2];
                                            gaWidget[iZone2][iSeq2] = objWidgetArr;
                                            break;
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        //ppRepaintScreen();
    }

////////////////////////////////////////////////////////////////////////////////////////////////////
// Other screen setup functions
////////////////////////////////////////////////////////////////////////////////////////////////////

    function ppFindBestZone(fromZoneNo, toZoneNo) {//returns zone number
        //go through widget array, to find the zone which has the most space available
        //ie: the zone with the smallest height of widgets already in it.
        //this is used to add a widget here by default where necessary

        var iZoneStart = fromZoneNo;
        var iZoneFinish = toZoneNo;
        var iZone;
        var iSeq; //seq no
        var objWidget;
        var iTotalZoneHeight = 0;
        var iSmallestZone = 0;
        var iSmallestZoneHeight = 99999;

        if ((isNaN(iZoneStart)) || (iZoneStart < 1)) { iZoneStart = 1; }
        if ((isNaN(iZoneFinish)) || (iZoneFinish > giZoneCount)) { iZoneFinish = giZoneCount; }

        for (iZone = iZoneStart; iZone <= iZoneFinish; iZone++) {
            //go through all widgets in zone:
            iTotalZoneHeight = 0;
            for (iSeq = 1; iSeq <= kMaxWidgetsPerZone; iSeq++) {
                if (gaWidget[iZone][iSeq][1] == "") { break; }  //no more widgets in this zone
                objWidget = document.getElementById(gaWidget[iZone][iSeq][1]);
                if (objWidget) { // get it's height plus the gap
                    iTotalZoneHeight = iTotalZoneHeight + parseInt(objWidget.offsetHeight, 10) + kWidgetGap;
                }
            }
            if (iTotalZoneHeight < iSmallestZoneHeight) {
                iSmallestZone = iZone;
                iSmallestZoneHeight = iTotalZoneHeight;
            }
        }

        return iSmallestZone;
    }

    function ppHideAllWidgetsOld() { //nb: these are not removed from the widget array, only from the screen
        var iCount;
        var objWidget;

        for (iCount = 1; iCount <= giWidgetCount; iCount++) {
            objWidget = document.getElementById(gaRegisteredWidgets[iCount]);
            if (objWidget) {
                objWidget.style.display = "none";
                ppSetCustomizeOption(objWidget.id, false);
            }
        }
    }

    function ppHideAllWidgets() { //nb: these are not removed from the widget array, only from the screen
        var iCount;
        var objWidget;

        for (iCount = 1; iCount <= giWidgetCount; iCount++) {
            objWidget = document.getElementById(gaRegisteredWidgets[iCount]);
            if (objWidget) {
                objWidget.state = 'hid';
                gaWidget[objWidget.zoneno][objWidget.seqno][2] = "hid";

                //unccheck the customize checkbox
                for (i = 0; i < gaCheckBoxArray.length; i++) {
                    var checkBox = document.getElementById(gaCheckBoxArray[i]);
                    if (objWidget.widgetcode == checkBox.value) {
                        checkBox.checked = false;
                    }
                }
            }
        }
        ppStoreWidgetArrayToCookie();
        ppRepaintScreen();
    }

    function CloneArray(oldArray) {
        //for JSLint this needs:  if (oldArray.hasOwnProperty(i)) check
        var newArray = [], i = 0, j = 0, k = 0;
        // version 1
        //        for (i in oldArray) {
        //            if (oldArray.hasOwnProperty(i)) {
        //                newArray[i] = [];

        //                for (j in oldArray[i]) {
        //                    if (oldArray.hasOwnProperty(j)) {
        //                        newArray[i][j] = [];

        //                        for (k = 1; k < 4; k++) {
        //                            if (oldArray.hasOwnProperty(k)) {
        //                                newArray[i][j][k] = oldArray[i][j][k];
        //                            }
        //                        }
        //                    }
        //                }
        //            }
        //        }

        //version 2
        for (i = 1; i <= kMaxZones; i++) {
            newArray[i] = [];
            for (j = 1; j <= kMaxWidgetsPerZone; j++) {
                newArray[i][j] = [];
                for (k = 1; k <= oldArray[i][j].length; k++) {
                    if (oldArray.hasOwnProperty(k)) {
                        newArray[i][j][k] = oldArray[i][j][k];
                    }
                }
            }
        }

        return (newArray);
    }

    function ppWidgetBackup() {
        //alert('ppWidgetBackup');
        //gaWidgetBackup = gaWidget.clone(); //have to use custom prototype to copy the values not just the reference to the original object!
        gaWidgetBackup = CloneArray(gaWidget);
    }


    function ppWidgetRestore() {
        //alert('ppWidgetRestore');
        //gaWidget = gaWidgetBackup.clone();
        gaWidget = CloneArray(gaWidgetBackup);
        ppRepaintScreen();
        ppStoreWidgetArrayToCookie();
        ppCheckCurrentCustomizeWidgets();
    }

    function ppProfileBackup() {
    //tony: what's this usage of PP??
        gaProfileBackup = PP.FrontEnd.UI.dashboard.GetProfile().value;
    }

    function ppProfileRestore(profilePicId) {
        //set the new profile server side
        var profPicUrl = PP.FrontEnd.UI.dashboard.SetProfile(gaProfileBackup);
        //change the profile image
        document.getElementById(profilePicId).src = profPicUrl.value;
    }

    function ppSetParkingZone(obj) {
        if (obj.checked) {
            gbParkingZoneVisible = true;
            gobjParkingZone.style.display = "";
        }
        else {
            gbParkingZoneVisible = false;
            gobjParkingZone.style.display = "none";
        }
        ppStoreWidgetArrayToCookie();
        ppRepaintScreen();
    }

    function ppGetRootIdByWidgetCode(widgetCode) {
        for (iZone = 1; iZone < gaWidget.length; iZone++) {
            for (iSeq = 1; iSeq < gaWidget[iZone].length; iSeq++) {
                if (gaWidget[iZone][iSeq][3] == widgetCode) {
                    return gaWidget[iZone][iSeq][1];
                }
            }
        }
        return null;
    }

////////////////////////////////////////////////////////////////////////////////////////////////////
// Custom Prototype methods
// (extension of object methods)
////////////////////////////////////////////////////////////////////////////////////////////////////

//special prototype to copy objects by value not by reference
/*Object.prototype.clone = function() {
  var newObj = (this instanceof Array) ? [] : {};
  for (i in this) {
    if (i == 'clone') continue;
    if (this[i] && typeof this[i] == "object") {
      newObj[i] = this[i].clone();
    } else newObj[i] = this[i]
  } return newObj;
}*/

////////////////////////////////////////////////////////////////////////////////////////////////////
// UTILS
////////////////////////////////////////////////////////////////////////////////////////////////////

//clones the given array by value (not by reference)


////////////////////////////////////////////////////////////////////////////////////////////////////
//END
////////////////////////////////////////////////////////////////////////////////////////////////////

