﻿/* Common */


// Gets the absolute y position of an element 
function getY(oElement) {
    var iReturnValue = 0;
    while (oElement !== null) {
        iReturnValue += oElement.offsetTop;
        oElement = oElement.offsetParent;
    }
    return iReturnValue;
}

// Gets the absolute x position of an element 
function getX(oElement) {
    var iReturnValue = 0;
    while (oElement !== null) {
        iReturnValue += oElement.offsetLeft;
        oElement = oElement.offsetParent;
    }
    return iReturnValue;
}

// Gets the top amount of scrolling above the element
function getScrollY(oElement) {
    var iReturnValue = 0;
    while (oElement !== null) {
        iReturnValue += oElement.scrollTop;
        oElement = oElement.parentElement;
    }
    return iReturnValue;
}
/* Suggestions */

/* *** RenderSuggestion ***
	
Renders the suggestion box next to input control if a suggestion is available.
For this method to work, the input control needs to have the following 
event registered:
	
onfocusin='javascript:RenderSuggestion("<theclientidofthiscontrol>" ,true);' 
onfocusout='javascript:RenderSuggestion("<theclientidofthiscontrol>", false);'
	
and the page needs to provide javascript arrays that contain the id of the control,
the contents of the suggestion and the horizontal offset of the comment from 
the input control; -1 means put it immediately to the right of the control 
and another other number forces it to that horizontal position.
	
In this example, the input controls are fred and john are the controls with there
id stored in 'suggestionInputElementId'. There comments are stored in suggestionText
and their offsets are in suggestionHorizOffset.
	
<script type='text/javascript'>
var suggestionInputElementId = ['fred', 
'john'];
										
var suggestionText			= [	'This is the comment to be displayed',
"Enter your name here"];
		
var suggestionHorizOffset = [	-1, 
400];											
</script>
	
Also, the page needs to provide the suggestionbox to be displayed towards the 
end of the html
	
<div id="suggestionBox" style="position:absolute; width:250px; filter:progid:DXImageTransform.Microsoft.Fade(duration=1);">                
<div id='commentBox' style='display:none'>
<img alt='' src="/Images/SuggestionImage.jpg" />
<span class='suggestion' id="comment" style="font-size:13px;font-family:Arial">				
</span>
</div>
</div>


This code only works in IE. It is possible to do it in firefox but it is more 
difficult since it does not support the filter styles. Timers need to be used.
	
filter:progid:DXImageTransform.Microsoft.Fade(duration=1);
	
*/

function renderSuggestion(inputElementId, turnOn) {
    var suggestionBox = $get("suggestionBox");
    var inputElement = $get(inputElementId);

    if (turnOn) {
        var comment = $get("comment");
        var index = Array.indexOf(suggestionInputElementId, inputElementId);

        // Check whether there is a suggestion to be displayed.
        if (index >= 0) {
            // If there is something to be displayed
            if (suggestionText[index] !== '') {
                // Set the suggestion's text					
                comment.innerHTML = suggestionText[index];

                // "" means just put it to the right of the input control
                if (suggestionNextTo[index] === "") {
                    suggestionBox.style.left = (getX(inputElement) + inputElement.offsetWidth + 10) + "px";
                }
                else {
                    // Get a reference to the object that the suggestion is to be displayed to 
                    // the right of.
                    var nextTo = $get(suggestionNextTo[index]);

                    // Otherwise force it to a particular horizontal location.
                    suggestionBox.style.left = (getX(nextTo) + nextTo.offsetWidth + 10) + "px";
                }

                var top = getY(inputElement);
                top += (suggestionBox.clientHeight - suggestionBox.clientHeight / 2) - 2;
                suggestionBox.style.top = top + "px";

                // Some jquery
                $('#suggestionBox').fadeIn(600);
            }
        }
    }
    else {
        // Some jquery
        $('#suggestionBox').fadeOut(0);
    }
}

//For the FOS Only!!
function SuggestionRenderFOS(sender, args) {
    var s = sender._uniqueId;
    var oldS;

    //RegExps won't work!!
    do {
        oldS = s;
        s = s.replace("$", "_");
    }
    while (oldS !== s);

    if (s.substr(s.length - 8, 8) === "_TextBox") {
        s = s.substr(0, s.length - 8);
    }

    renderSuggestion(s, true);
}

//For the FOS Only!!
function SuggestionUnrenderFOS(sender, args) {
    var s = sender._uniqueId;
    var oldS;

    //RegExps won't work!!
    do {
        oldS = s;
        s = s.replace("$", "_");
    }
    while (oldS !== s);

    if (s.substr(s.length - 8, 8) === "_TextBox") {
        s = s.substr(0, s.length - 8);
    }

    renderSuggestion(s, false);
}

/* Validation */

/* ValidatorValidatingControl 
When a client side custom validor validation event occurs, the event
handler has the 'source' which is the validating control.  Using 
source.controltovaliTime you can get the control being valiTimed
but this control may be a web custom control that is made up of
many other controls.  This method find the control that is being 
valiTimed.
		
This code is a modifed version as taken from the AJAX client side
libraries. Is was originally base on ValidatorGetValue.
*/

function ValidatorGetValidatingControlRecursive(control) {
    if (typeof (control.value) === "string" && (control.type !== "radio" || control.checked === true)) {
        return control;
    }

    var i, val;

    for (i = 0; i < control.childNodes.length; i++) {
        val = ValidatorGetValidatingControlRecursive(control.childNodes[i]);

        if (val !== "") {
            return val;
        }
    }

    return "";
}

//See above
function GetValidatingControl(id) {
    var control;

    control = document.getElementById(id);

    if (typeof (control.value) === "string") {
        return control;
    }

    return ValidatorGetValidatingControlRecursive(control);
}

//just trims whitespace from the front and end of a string
function trimAll(sString) {
    while (sString.substring(0, 1) === ' ') {
        sString = sString.substring(1, sString.length);
    }

    while (sString.substring(sString.length - 1, sString.length) === ' ') {
        sString = sString.substring(0, sString.length - 1);
    }

    return sString;
}

// RequiredFieldValidator
// the client side script that is called by the custom validator that is automatically
// associated with any web control that implements IRequiredField. */
function RequiredFieldValidator(source, clientside_arguments) {
    var controlBeingValidated;

    if (source.controltovalidate !== undefined) {
        //if it's empty we'll put it in an errorState
        if (clientside_arguments.Value === "") {
            clientside_arguments.IsValid = false;
            controlBeingValidated = GetValidatingControl(source.controltovalidate);

            //if this element already has a class, add it on the end
            //otherwise, just make its class errorState
            if (controlBeingValidated.parentElement.className !== "" && controlBeingValidated.parentElement.className !== "errorState") {
                //make sure it doesn't already have the class listed
                if (controlBeingValidated.parentElement.className.indexOf(" errorState") === -1) {
                    controlBeingValidated.parentElement.className += " errorState";
                }
            }
            else {
                controlBeingValidated.parentElement.className = "errorState";
            }
        }
        else {
            clientside_arguments.IsValid = true;
            controlBeingValidated = GetValidatingControl(source.controltovalidate);

            //if the only class applied to the element is errorState, simply
            //remove it
            if (controlBeingValidated.parentElement.className === "errorState") {
                //clear the class
                controlBeingValidated.parentElement.className = "";
            }
            else {
                //otherwise, search its classes for the errorState class
                //and remove it if it's there
                var errorStateIndex = controlBeingValidated.parentElement.className.indexOf(" errorState");

                if (errorStateIndex > -1) {
                    var className = controlBeingValidated.parentElement.className;

                    //get the string before and after the errorState class and
                    //concatenate them to form the new class list
                    if (className.length - 11 === errorStateIndex) {       //if it's the last class then
                        className = className.substr(0, errorStateIndex); //just pull the start out
                    }
                    else {
                        className = className.substr(0, errorStateIndex) + className.substr(errorStateIndex + 11, className.length - 1);
                    }

                    //set the class
                    controlBeingValidated.parentElement.className = trimAll(className);
                }
            }
        }
    }
}

/* Foreign Object Selector */

var childWindow = null;

//this is called when a page receives focus
function KillPopups() {
    if (childWindow !== null) {
        try {
            childWindow.closePopup();
        } catch (ex) { }

        childWindow = null;
    }
}

function FormOnFocusForFos() {
    //window.onfocus = function() { KillPopups(); };
}

function OpenWindow(path, options) {
    if (childWindow === null) {
        childWindow = window.open(path, "_blank", options, "");
    }

    if (!childWindow) {
        alert("There is a popup blocker running on your computer.  Please disable it to continue.");
    }
}

//Closes all child windows and this window recursively
function closePopup() {

    if (childWindow !== null) {
        childWindow.closePopup();

        childWindow = null;
    }

    self.close();
}

//Called by the child window so it can update the parent then
//close itself without a fuss.
function ForeignBusinessObjectSelected(primaryKey) {
    window.opener.SetForeignObjectSelectorPrimaryKey(parentWindowForeignObjectSelectorClientID, primaryKey);

    self.close();
}

//Called by the child window if it is closed - ie when the X is hit
function ChildWindowCancelled() {
    if (window.opener.childWindow != null) {
        window.opener.childWindow = null;
    }
}

//Sets the specified foreign object selector's values
function SetForeignObjectSelectorPrimaryKey(clientID, primaryKey) {
    var foreignObjectSelectorHiddenField = $get(clientID + '_HiddenField');

    foreignObjectSelectorHiddenField.value = primaryKey;
    foreignObjectSelectorHiddenField.onchange();

    var radComboBox = $find(clientID + '_TextBox');

    ChangeOccurredForForeignObjectSelector(radComboBox, null);

    // Indicate that the child window is no longer open
    childWindow = null;
}


/* Subtle Drop Down */

var currentOpenList;
var bodyClick = true;

function HideSubtleDropDown() {

    if (currentOpenList && bodyClick) {

        currentOpenList.style.display = "none";

        // clear this just for fun
        currentOpenList = null;

    }

    // reset this flag
    bodyClick = true;

}

//opens or closes the subtle drop down accordingly
function ChangeSubtleDropDown(listID) {

    if (currentOpenList) {
        ToggleDisplay(currentOpenList);

        if (listID !== currentOpenList.id) {

            currentOpenList = $get(listID);

            ToggleDisplay(currentOpenList);

        }
    }
    else {

        currentOpenList = $get(listID);

        ToggleDisplay(currentOpenList);

    }

    // this is a flag to tell the
    // body to ignore the click the user
    // just made.  this is because the
    // click was on an SDD so we don't want
    // to just hide it again
    bodyClick = false;

}

function ToggleDisplay(element) {

    if (element.style.display === 'none') {
        element.style.display = 'block';
    }
    else {
        element.style.display = 'none';
    }

}

/* Numeric Text Box */

//when the value of a percent box is about to change
//and its decimal places is set to auto this function
//is called to change the number of required decimal
//places
function NumericInputValueChanging(sender, eventArgs) {
    var value = eventArgs.get_newValue() + '';

    var decimalPlacePos = value.indexOf(".");

    if (decimalPlacePos === -1) {
        sender.get_numberFormat().DecimalDigits = 0;
    }
    else {
        sender.get_numberFormat().DecimalDigits = value.length - decimalPlacePos - 1;
    }
}

/* Dressage */


//dock menu
function DockMenu(menu, args) {
    menu.dockPane("MenuPaneId");
}

/* Disabling of screen in edit mode */

var pane;

function DisablePage(editableSectionId) {

    if (!pane) {
        if (editableSectionId) {
            pane = editableSectionId;
        }
        else {
            return;
        }
    }

    var left = getX(pane);
    var top = getY(pane);
    var width;
    var height;

    try {
        width = pane.get_width();
        height = pane.get_height();
    }
    catch (e) {
        width = pane.clientWidth;
        height = pane.clientHeight;
    }

    var screenWidth = document.getElementById("ParentDivElement").offsetWidth;
    var screenHeight = document.getElementById("ParentDivElement").offsetHeight;

    $get('postbackoverlayleft').style.display = "";
    $get('postbackoverlayleft').style.width = left;

    $get('postbackoverlaytop').style.display = "";
    $get('postbackoverlaytop').style.left = left;
    $get('postbackoverlaytop').style.height = top;
    $get('postbackoverlaytop').style.width = screenWidth - left;

    $get('postbackoverlayright').style.display = "";
    $get('postbackoverlayright').style.left = left + width;
    $get('postbackoverlayright').style.top = top;
    $get('postbackoverlayright').style.height = screenHeight - top;

    if (document.documentElement.clientWidth > left + width) {
        $get('postbackoverlayright').style.width = screenWidth - left - width;
    }
    else {
        $get('postbackoverlayright').style.display = "none";
    }

    $get('postbackoverlaybottom').style.display = "";
    $get('postbackoverlaybottom').style.left = left;
    $get('postbackoverlaybottom').style.width = width;
    $get('postbackoverlaybottom').style.top = top + height;
    $get('postbackoverlaybottom').style.height = screenHeight - top - height;
}

function HidePostBackPrevention() {
    if ($get('postbackoverlay') != null)
        $get('postbackoverlay').style.display = 'none';
}

function EnablePage(editableSectionId) {

    if (pane === null) {
        if (editableSectionId === null) {
            return;
        }
        else {
            pane = editableSectionId;
        }
    }

    $get('postbackoverlayleft').style.display = "none";
    $get('postbackoverlaytop').style.display = "none";
    $get('postbackoverlayright').style.display = "none";
    $get('postbackoverlaybottom').style.display = "none";
}

//cancel all select/deselect operation triggered by non-checkbox row clicks in the RadGrid
function CancelNonInputSelect(sender, args) {
    var e = args.get_domEvent();
    //IE - srcElement, Others - target
    var targetElement = e.srcElement || e.target;

    if (targetElement) {
        //is the clicked element an input checkbox? <input type="checkbox"...>
        if (targetElement.tagName.toLowerCase() !== "input" &&
                        (!targetElement.type || targetElement.type.toLowerCase() !== "checkbox"))// && currentClickEvent)
        {

            //cancel the event
            args.set_cancel(true);
        }
    }
}

//    // saves what they've typed into the hidden field
//    function SaveMaskedTextBox(sender, args) {

//        if (sender) {

//            // the hidden field is the first element of the SM MaskedTextBox, so we need to go
//            // up one level from the actual box to Telerik's wrapper, then one more level to get
//            // to the SM control, where we can get the first child and pull of the DOM element
//            var hiddenField = $get($get(sender._clientID).parentElement.parentElement.firstChild.id);

//            // set it to the value they entered, e.g.: "(07) 545_ 7710"
//            hiddenField.value = sender._projectedValue;

//        }

//    }

//    // loads the value back out of the hidden field
//    function LoadMaskedTextBoxFromHiddenField(sender, args) {

//        if (sender) {

//            // the hidden field is the first element of the SM MaskedTextBox, so we need to go
//            // up one level from the actual box to Telerik's wrapper, then one more level to get
//            // to the SM control, where we can get the first child and pull of the DOM element
//            var hiddenField = $get($get(sender._clientID).parentElement.parentElement.firstChild.id);

//            // it failed the mask test, we need to ensure we haven't mucked about
//            // with the user's entry
//            sender._textBoxElement.value = hiddenField.value;

//        }

//    }

//// checks to ensure that the initial click in a masked text box
//// sets the caret position to 0
//function EnsureCaretPositionCorrect(sender, args) {

//    if (sender) {

//        // make sure they haven't typed in here yet
//        if (sender.get_value() === "") {

//            sender.set_caretPosition(0); // ensure that the zeroth position is where the caret is

//        }

//    }

//}

/* This handles the change made functionality */

function FlagChangeMade() {
    if (document.title.substring(document.title.length - changeMadeIndication.length, document.title.length) !== changeMadeIndication) {
        document.title += changeMadeIndication;
    }

    $get('__changeDetection').value = 'true';
}

function UnFlagChangeMade() {
    if (document.title.substring(document.title.length - changeMadeIndication.length, document.title.length) === changeMadeIndication) {
        document.title = document.title.substring(0, document.title.length - changeMadeIndication.length);
    }

    $get('__changeDetection').value = 'false';
}

warnAboutChanges = true;

function DontWarnAboutChanges() {

    warnAboutChanges = false;

}

function formUnload() {
    if ($get('__changeDetection').value === 'true' && warnAboutChanges === true) {

        if (window.event) {
            window.event.returnValue = unloadMessage; // IE
        }
        else {
            return unloadMessage; // FX
        }

    }
}

function IsASpecialKey(keycode, isCtrlKeyDown, arrowKeysAreSpecial) {

    // switch on the keycode
    switch (keycode) {
        case 8: return true; // the backspace key
        //case 9: return true; // the tab key -- tab is not a special key. This should trip any change detection
        case 16: return true; // the shift key
        case 17: return true; // the ctrl key
        case 18: return true; // the alt key
        case 19: return true; // the pause/break key
        case 20: return true; // the caps lock key
        case 27: return true; // the escape key
        case 33: return true; // the page up key
        case 34: return true; // the page down key
        case 35: return true; // the end key
        case 36: return true; // the home key
        case 45: return true; // the insert key
        case 91: return true; // the left window key key
        case 92: return true; // the right window key key
        case 93: return true; // the select key key
        case 112: return true; // the f1 key
        case 113: return true; // the f2 key
        case 114: return true; // the f3 key
        case 115: return true; // the f4 key
        case 116: return true; // the f5 key
        case 117: return true; // the f6 key
        case 118: return true; // the f7 key
        case 119: return true; // the f8 key
        case 120: return true; // the f9 key
        case 121: return true; // the f10 key
        case 122: return true; // the f11 key
        case 123: return true; // the f12 key
        case 144: return true; // the num lock key
        case 145: return true; // the scroll lock key

            // arrow left
        case 37:
            return arrowKeysAreSpecial;

            // arrow up
        case 38:
            return arrowKeysAreSpecial;

            // arrow right
        case 39:
            return arrowKeysAreSpecial;

            // arrow down
        case 40:
            return arrowKeysAreSpecial;

            // the 'C' key, if pressing ctrl as well
            // they're doing a copy, which means NO CHANGE
        case 67:
            return isCtrlKeyDown;

    }

    // they all fell through, return true;
    return false;

}

/* Event handler for IWarnAboutClosingBrowser */

function ChangeOccurred(sender, args) {

    var event = window.event;

    if (!event)
        event = args;

    // check for a window event
    if (event) {

        // get the key that was pressed
        var key = event.keyCode;

        // check there was a key press
        if (key) {

            var arrowKeys = args[0];

            if (arrowKeys === undefined)
                arrowKeys = true;

            // the zeroth argument is a bool that indicates
            // whether or not the arrow keys have an affect
            // on the calling control (like DatePickers)
            if (!IsASpecialKey(key, event.ctrlKey, arrowKeys)) {
                FlagChangeMade();
            }
            else {
                return;
            }
        }
    }

    // if we fell through the key event
    // go ahead and flag a change
    FlagChangeMade();
}

/* These were sourced from... */
// http://www.softcomplex.com/docs/get_window_size_and_scrollbar_position.html

function filterResults(n_win, n_docel, n_body) {
    var n_result = n_win ? n_win : 0;
    if (n_docel && (!n_result || (n_result > n_docel))) {
        n_result = n_docel;
    }

    return n_body && (!n_result || (n_result > n_body)) ? n_body : n_result;
}

function scrollLeft() {
    return filterResults(
		window.pageXOffset ? window.pageXOffset : 0,
		document.documentElement ? document.documentElement.scrollLeft : 0,
		document.body ? document.body.scrollLeft : 0
	);
}

function scrollTop() {
    return filterResults(
		window.pageYOffset ? window.pageYOffset : 0,
		document.documentElement ? document.documentElement.scrollTop : 0,
		document.body ? document.body.scrollTop : 0
	);
}

/* Change events for integrating VSP and change detection */

function ShouldChangeDetectionMessageBeDisplayed() {

    return ($get('__changeDetection').value === 'true' && warnAboutChanges === true);

}

function GetChangeDetectionMode(sender) {

    return sender.attributes.ChangeDetectionMode.value;

}

function RichTextBoxLoad(editor, args) {

    editor.attachEventHandler("onkeydown", function(e) {

        ChangeOccurred(editor, e);

    });

}

var warnAboutChangesMessage = "Are you sure you want to navigate away from this page? \r\n\r\nThere are unsaved changes.\r\n\r\nPres OK to continue to the new page or Cancel to stay on this page.";

function ChangeOccurredForCheckBox(sender, autoPostBack) {

    // this gets the Telerik ClientState Field, like this: 'Page_UserControl_dtDatePicker_DatePicker_ClientStateField'
    // we strip it back to: 'Page_UserControl_dtDatePicker' and then grab the HTML element for reading the attributes
    var checkBox = $get(sender.id.substr(0, sender.id.lastIndexOf('_CB')));

    // vsp framework
    var state = sender.checked;

    var useVSP = checkBox.attributes.VSP.value;

    if (useVSP === 'True') {

        var visualStateGroup = checkBox.attributes.vspGroup.value;

        VisualStateChanged(visualStateGroup, 'Checked', state);

    }

    var warnAboutChanges = (GetChangeDetectionMode(checkBox) === 'WarnAboutChanges');

    // change detection
    if (autoPostBack && warnAboutChanges) {
        return WarnAboutChanges(checkBox);
    }
    else {
        ChangeDetectionEvent(checkBox, autoPostBack);
    }
}

function ChangeOccurredForGrid(sender, args) {

    // Only do this when it is a non-ajax postback
    if (Sys.WebForms.PageRequestManager.getInstance()._postBackSettings == null) {
    
        // this gets the Telerik ClientState Field, like this: 'Page_UserControl_dtDatePicker_DatePicker_ClientStateField'
        // we strip it back to: 'Page_UserControl_dtDatePicker' and then grab the HTML element for reading the attributes
        var clientId = sender._clientStateFieldID.replace('_ClientState', '');    
        var grid = $get(clientId);

        var warnAboutChanges = (GetChangeDetectionMode(grid) === 'WarnAboutChanges');

        // change detection
        if (warnAboutChanges) {
            WarnAboutChanges(grid);
        }
        else {
            ChangeDetectionEvent(grid, true);
        }
    }
}


function ChangeOccurredForDatePicker(sender, args) {

    // this gets the Telerik ClientState Field, like this: 'Page_UserControl_dtDatePicker_DatePicker_ClientStateField'
    // we strip it back to: 'Page_UserControl_dtDatePicker' and then grab the HTML element for reading the attributes
    var datePicker = $get(sender._clientStateFieldID.substr(0, sender._clientStateFieldID.lastIndexOf('_DatePicker')));

    // clear the control's background colour in case it was
    // visually restored
    // sender._dateInput._element.style.backgroundColor = "";

    // get the telerik date picker
    var autoPostBack = (datePicker.attributes.autoPostBack.value === 'true');

    // vsp framework
    var state = sender.get_selectedDate();

    var useVSP = datePicker.attributes.VSP.value;

    if (useVSP === 'True') {

        var visualStateGroup = datePicker.attributes.vspGroup.value;

        VisualStateChanged(visualStateGroup, 'Date', state);

    }

    var warnAboutChanges = (GetChangeDetectionMode(datePicker) === 'WarnAboutChanges');

    // change detection
    if (autoPostBack && warnAboutChanges) {
        args.set_cancel(!WarnAboutChanges(datePicker));
    }
    else {
        ChangeDetectionEvent(datePicker, autoPostBack);
    }
}

function ChangeOccurredForDropDownList(sender, key) {

    // this gets the Telerik ClientState Field, like this: 'Page_UserControl_dtDatePicker_DatePicker_ClientStateField'
    // we strip it back to: 'Page_UserControl_dtDatePicker' and then grab the HTML element for reading the attributes
    var dropDown = $get(sender.id.substr(0, sender.id.lastIndexOf('_DropDownListId')));

    // clear the control's background colour in case it was
    // visually restored
    sender.style.backgroundColor = "";

    var autoPostBack = (dropDown.attributes.autoPostBack.value === 'true');

    // vsp framework
    var state = sender.options[sender.selectedIndex].value;

    var useVSP = dropDown.attributes.VSP.value;

    if (useVSP === 'True') {

        var visualStateGroup = dropDown.attributes.vspGroup.value;

        VisualStateChanged(visualStateGroup, key, state);

    }

    var warnAboutChanges = (GetChangeDetectionMode(dropDown) === 'WarnAboutChanges');

    // change detection
    if (autoPostBack && warnAboutChanges) {
        return WarnAboutChangeForDropDownList(sender, true);
    }
    else {
        ChangeDetectionEvent(dropDown, autoPostBack);
    }
}

function WarnAboutChangeForDropDownList(sender, vspAlreadyRun) {

    var smDropDown = $get(sender.id.substr(0, sender.id.lastIndexOf('_DropDownListId')));

    var prevIndex = $get(smDropDown.id + "_prevIndex");

    if (ShouldChangeDetectionMessageBeDisplayed()) {

        if (confirm(warnAboutChangesMessage)) {

            UnFlagChangeMade();
            DontWarnAboutChanges();

            return true;

        }

        // if they clicked cancel, reset the drop down
        sender.selectedIndex = prevIndex.value;

        return false;

    }

    UnFlagChangeMade();
    DontWarnAboutChanges();

    return true;

}

function ChangeOccurredForDateTimePicker(sender, args) {

    // this gets the Telerik ClientState Field, like this: 'Page_UserControl_dttDateTimePicker_DateTimePicker_ClientStateField'
    // we strip it back to: 'Page_UserControl_dttDateTimePicker' and then grab the HTML element for reading the attributes
    var dateTimePicker = $get(sender._clientStateFieldID.substr(0, sender._clientStateFieldID.lastIndexOf('_DateTimePicker')));

    // clear the control's background colour in case it was
    // visually restored
    // sender._dateInput._element.style.backgroundColor = "";

    // get the auto post back property
    var autoPostBack = (dateTimePicker.attributes.autoPostBack.value === 'true');

    // vsp framework
    var state = sender.get_selectedDate();

    var useVSP = dateTimePicker.attributes.VSP.value;

    if (useVSP === 'True') {

        var visualStateGroup = dateTimePicker.attributes.vspGroup.value;

        VisualStateChanged(visualStateGroup, 'DateTime', state);

    }

    var warnAboutChanges = (GetChangeDetectionMode(dateTimePicker) === 'WarnAboutChanges');

    // change detection
    if (autoPostBack && warnAboutChanges) {
        args.set_cancel(!WarnAboutChanges(dateTimePicker));
    }
    else {
        ChangeDetectionEvent(dateTimePicker, autoPostBack);
    }
}


function ChangeOccurredForRadioButton(sender, autoPostBack) {

    // this gets the Telerik ClientState Field, like this: 'Page_UserControl_dtDatePicker_DatePicker_ClientStateField'
    // we strip it back to: 'Page_UserControl_dtDatePicker' and then grab the HTML element for reading the attributes
    var radioButton = $get(sender.id.substr(0, sender.id.lastIndexOf('_RadioButton')));

    // vsp framework
    var state = sender.checked;

    var warnAboutChanges = (GetChangeDetectionMode(radioButton) === 'WarnAboutChanges');

    // change detection
    if (autoPostBack && warnAboutChanges) {
        return WarnAboutChanges(radioButton);
    }
    else {
        ChangeDetectionEvent(radioButton, autoPostBack);
    }
}

function ChangeOccurredForForeignObjectSelector(sender, args) {

    // this gets the Telerik ClientState Field, like this: 'Page_UserControl_dttDateTimePicker_DateTimePicker_ClientStateField'
    // we strip it back to: 'Page_UserControl_dttDateTimePicker' and then grab the HTML element for reading the attributes
    var fos = $get(sender._clientStateFieldID.substr(0, sender._clientStateFieldID.lastIndexOf('_TextBox')));

    // clear the control's background colour in case it was
    // visually restored
    // sender.get_inputDomElement().style.backgroundColor = "";

    // read out the auto post back property
    var autoPostBack = (fos.attributes.autoPostBack.value === 'true');

    // vsp framework
    try {
        var state = args._item._properties._data.value;

        var useVSP = fos.attributes.VSP.value;

        if (useVSP === 'True') {

            var visualStateGroup = fos.attributes.vspGroup.value;

            VisualStateChanged(visualStateGroup, 'PrimaryKey', state);

        }
    }
    catch (exception) {

    }

    var warnAboutChanges = (GetChangeDetectionMode(fos) === 'WarnAboutChanges');

    // change detection
    if (autoPostBack && warnAboutChanges) {

        // if the event came from the drop down list
        // we have args and we set the cancel on it
        if (args) {
            args.set_cancel(!WarnAboutChanges(fos));
        }
        else {
            // otherwise, it came from our BusinessObjectSelected Method
            return WarnAboutChanges(fos);
        }
    }
    else {
        ChangeDetectionEvent(fos, autoPostBack);
    }
}

function ChangeOccurredForGlobalCurrencyBox(sender, args) {

    var gcb;
    var autoPostBack;

    // try assuming it's the amount box
    gcb = $get(sender._clientStateFieldID.substr(0, sender._clientStateFieldID.lastIndexOf("_Amt")));

    if (!gcb) {
        // if it wasn't the amount box it must be the date picker
        gcb = $get(sender._clientStateFieldID.substr(0, sender._clientStateFieldID.lastIndexOf("_Cal")));

        // the datepicker is ALWAYS AutoPostBack
        autoPostBack = true;
    }
    else {
        // if this is the amount box, use the GCB's value, it bounces
        // off the SM GCB's AutoPostBack property
        autoPostBack = (gcb.attributes.autoPostBack.value == "true" ? true : false);
    }

    var warnAboutChanges = (GetChangeDetectionMode(gcb) === 'WarnAboutChanges');

    // change detection
    if (autoPostBack && warnAboutChanges) {
        args.set_cancel(!WarnAboutChanges(gcb));
    }
    else {
        ChangeDetectionEvent(gcb, autoPostBack);
    }
}

function ChangeOccurredForMaskedTextBox(sender, args) {

    // this gets the Telerik ClientState Field, like this: 'Page_UserControl_dttDateTimePicker_DateTimePicker_ClientStateField'
    // we strip it back to: 'Page_UserControl_dttDateTimePicker' and then grab the HTML element for reading the attributes
    var textBox = $get(sender._clientStateFieldID.substr(0, sender._clientStateFieldID.lastIndexOf('_MaskedTextBox')));

    // clear the control's background colour in case it was
    // visually restored
    ClearInputControlBackColor(sender);

    // vsp framework
    var state = sender._projectedValue;

    var useVSP = textBox.attributes.VSP.value;

    if (useVSP === 'True') {

        var visualStateGroup = textBox.attributes.vspGroup.value;

        VisualStateChanged(visualStateGroup, 'Text', state);

    }

    var autoPostBack = sender.get_autoPostBack();
    var warnAboutChanges = (GetChangeDetectionMode(textBox) === 'WarnAboutChanges');

    // change detection
    if (autoPostBack && warnAboutChanges) {
        args.set_cancel(!WarnAboutChanges(textBox));
    }
    else {
        ChangeDetectionEvent(textBox, autoPostBack);
    }
}

function ChangeOccurredForNumberBox(sender, args) {

    // this gets the Telerik ClientState Field, like this: 'Page_UserControl_dttDateTimePicker_DateTimePicker_ClientStateField'
    // we strip it back to: 'Page_UserControl_dttDateTimePicker' and then grab the HTML element for reading the attributes
    var numBox = $get(sender._clientStateFieldID.substr(0, sender._clientStateFieldID.lastIndexOf('_NumberBox')));

    // clear the control's background colour in case it was
    // visually restored
    ClearInputControlBackColor(sender);

    // vsp framework
    var state = args.get_newValue();

    var useVSP = numBox.attributes.VSP.value;

    if (useVSP === 'True') {

        var visualStateGroup = numBox.attributes.vspGroup.value;

        VisualStateChanged(visualStateGroup, 'Value', state);

    }

    var autoPostBack = sender.get_autoPostBack();

    var warnAboutChanges = (GetChangeDetectionMode(numBox) === 'WarnAboutChanges');

    // change detection
    if (autoPostBack && warnAboutChanges) {
        args.set_cancel(!WarnAboutChanges(numBox));
    }
    else {
        ChangeDetectionEvent(numBox, autoPostBack);
    }
}

function ChangeOccurredForPercentBox(sender, args) {

    // this gets the Telerik ClientState Field, like this: 'Page_UserControl_dttDateTimePicker_DateTimePicker_ClientStateField'
    // we strip it back to: 'Page_UserControl_dttDateTimePicker' and then grab the HTML element for reading the attributes
    var percentBox = $get(sender._clientStateFieldID.substr(0, sender._clientStateFieldID.lastIndexOf('_PercentBox')));

    // clear the control's background colour in case it was
    // visually restored
    ClearInputControlBackColor(sender);

    // vsp framework
    var state = args.get_newValue();

    var useVSP = percentBox.attributes.VSP.value;

    if (useVSP === 'True') {

        var visualStateGroup = percentBox.attributes.vspGroup.value;

        VisualStateChanged(visualStateGroup, 'Value', state);

    }

    var autoPostBack = sender.get_autoPostBack();
    var warnAboutChanges = (GetChangeDetectionMode(percentBox) === 'WarnAboutChanges');

    // change detection
    if (autoPostBack && warnAboutChanges) {
        args.set_cancel(!WarnAboutChanges(percentBox));
    }
    else {
        ChangeDetectionEvent(percentBox, autoPostBack);
    }
}

function ChangeOccurredForTextBox(sender, args) {

    // this gets the Telerik ClientState Field, like this: 'Page_UserControl_dttDateTimePicker_DateTimePicker_ClientStateField'
    // we strip it back to: 'Page_UserControl_dttDateTimePicker' and then grab the HTML element for reading the attributes
    var textBox = $get(sender._clientStateFieldID.substr(0, sender._clientStateFieldID.lastIndexOf('_TextBox')));

    // clear the control's background colour in case it was
    // visually restored
    ClearInputControlBackColor(sender);

    // vsp framework
    var state = args.get_newValue();

    var useVSP = textBox.attributes.VSP.value;

    if (useVSP === 'True') {

        var visualStateGroup = textBox.attributes.vspGroup.value;

        VisualStateChanged(visualStateGroup, 'Text', state);

    }

    var autoPostBack = sender.get_autoPostBack();
    var warnAboutChanges = (GetChangeDetectionMode(textBox) === 'WarnAboutChanges');

    // change detection
    if (autoPostBack && warnAboutChanges) {
        return args.set_cancel(!WarnAboutChanges(textBox));
    }
    else {
        ChangeDetectionEvent(textBox, autoPostBack);
    }
}

function ClearInputControlBackColor(sender) {

    var styles = sender.get_styles();

    styles.EnabledStyle[0] += "background-color: white;";
    styles.HoveredStyle[0] += "background-color: white;";
    styles.FocusedStyle[0] += "background-color: white;";

    sender.updateCssClass();

}

function ChangeOccurredForTimePicker(sender, args) {

    // this gets the Telerik ClientState Field, like this: 'Page_UserControl_dttDateTimePicker_DateTimePicker_ClientStateField'
    // we strip it back to: 'Page_UserControl_dttDateTimePicker' and then grab the HTML element for reading the attributes
    var timePicker = $get(sender._clientStateFieldID.substr(0, sender._clientStateFieldID.lastIndexOf('_TimePicker')));

    // clear the control's background colour in case it was
    // visually restored
    // sender._dateInput._element.style.backgroundColor = "";

    var autoPostBack = (timePicker.attributes.autoPostBack.value === 'true');

    // vsp framework
    var state = args.get_newValue();

    var useVSP = timePicker.attributes.VSP.value;

    if (useVSP === 'True') {

        var visualStateGroup = timePicker.attributes.vspGroup.value;

        VisualStateChanged(visualStateGroup, 'Time', state);

    }

    var warnAboutChanges = (GetChangeDetectionMode(timePicker) === 'WarnAboutChanges');

    // change detection
    if (autoPostBack && warnAboutChanges) {
        args.set_cancel(!WarnAboutChanges(timePicker));
    }
    else {
        ChangeDetectionEvent(timePicker, false);
    }
}

function VisualStateChangedForPanelBar(sender, args, state) {

    // this gets the Telerik ClientState Field, like this: 'Page_UserControl_dttDateTimePicker_DateTimePicker_ClientStateField'
    // we strip it back to: 'Page_UserControl_dttDateTimePicker' and then grab the HTML element for reading the attributes
    var panelBar = $get(sender._clientStateFieldID.substr(0, sender._clientStateFieldID.lastIndexOf('_ClientState')));

    // vsp framework
    var key = args.get_item().get_text();

    var useVSP = panelBar.attributes.VSP.value;

    if (useVSP === 'True') {

        var visualStateGroup = panelBar.attributes.vspGroup.value;

        VisualStateChanged(visualStateGroup, key, state);

    }

}

function PanelBarItemCollapse(sender, args) {

    VisualStateChangedForPanelBar(sender, args, "collapsed");

}

function PanelBarItemExpand(sender, args) {

    VisualStateChangedForPanelBar(sender, args, "expanded");

}

function SlidingPaneDocked(sender, args) {

    VisualStateChanged('LeftPageMenu', '', 'docked');

}

function SlidingPaneUndocked(sender, args) {

    VisualStateChanged('LeftPageMenu', '', 'undocked');

}

function SlidingPaneResized(sender, args) {

    VisualStateChanged('LeftPageMenu', 'Width', sender.get_width());

}

/* Visual State Persistence Framework */

function VisualStateChangedForCheckBox(clientId, visualStateGroup, key) {

    var checked = $get(clientId).checked;
    var state = '';

    if (checked === true) {
        state = 'True';
    } else {
        state = 'False';
    }

    VisualStateChanged(visualStateGroup, key, state);

}

function SucceededCallback(sender, eventArgs) {

}

function FailedCallback(sender, eventArgs) {

}

/* Menu functionality */

function SwapMenu(panelItem) {



}

var recordMenuTimer;

function RecordMenuScrollPosition() {

    var slidingPane = $get(window.event.srcElement.id);

    clearTimeout(recordMenuTimer);
    recordMenuTimer = setTimeout("VisualStateChanged('LeftPageMenu', 'scrollX', '" + slidingPane.scrollLeft + "'); VisualStateChanged('LeftPageMenu', 'scrollY', '" + slidingPane.scrollTop + "');", 400);

}

/* File uploading */
/* This assumes that there is a variable called "previewImageId" This would have been 
dynamically created */
function OnClientFileSelectedHandler(sender, eventArgs) {

    // Flag the change detection
    ChangeOccurred(sender, eventArgs);

    GetRotation(eventArgs.get_rowIndex());

    ShowPreview(sender, eventArgs);

}

// Allows the hooking into the onfocus of the upload control
// so that the image can be previewed
function OnClientFileCreatedHandler(sender, eventArgs) {
    var inputField = eventArgs.get_fileInputField();
    inputField.onfocus = function() { OnClientFileSelectedHandler(sender, eventArgs); };
}

function ShowPreview(sender, eventArgs) {
    var input = eventArgs.get_fileInputField();

    //check the extension
    if (sender.isExtensionValid(input.value)) {

        var img = document.getElementById(previewImageId);

        if (img) {


            var fileName = input.value;

            if (fileName == "")
            {
                img.style.visibility = "hidden";
                return;
            }
            
            // Only preview jpgs
            var extension = fileName.substring(fileName.lastIndexOf('.') + 1, fileName.lastIndexOf('.')  + 1 + 3);
            if (extension != "jpg")         
            {
                img.style.visibility = "hidden";
                return;
            }
            else {
                img.style.visibility = "";

                img.src = input.value;
                ScaleImageWithLoad(img);

                if (fileNameControls) {
                    var index = eventArgs.get_rowIndex();
                    var controlId = fileNameControls[index];
                    var fileNameControl = document.getElementById(controlId);

                    // rotationHiddenFieldControlId was created by the Upload control.
                    // It is refering to the textbox that contains the comma separated values
                    // of the rotions of the photos.  Arbitarily assign a property called
                    // 'currentIndex' so that it know what index into its own CSV we are 
                    // referring to.                       
                    document.getElementById(rotationHiddenFieldControlId).currentIndex = index;

                    fileName = fileName.substring(fileName.lastIndexOf('\\') + 1);
                    fileName = fileName.substring(0, fileName.lastIndexOf('.'));

                    if (fileNameControl.value == "")
                        fileNameControl.value = fileName;
                }
            }
        }
    }
}

// Rescales the image
function ScaleImageWithLoad(img) {

    var tmp_img = new Image();

    // The loading of the images may take a short moment. This is called once loaded
    tmp_img.onload = function() {

        // Resets the size of the orginal image to the size of the image that was 
        // just loaded into memory
        img.width = tmp_img.width;
        img.height = tmp_img.height;

        // Rescale it now.
        ScaleImageInternal(img);
    }

    // set the source here - which will call the code above once loaded
    tmp_img.src = img.src;
}


// Scales the image
function ScaleImageInternal(img) {
    var height = img.height;
    var width = img.width;

    var maxHeight = 238;
    var maxWidth = 220;

    // Check whether it has been rotated around - in this case no
    //if ((img.twisted == false) || (!img.twisted)) {
    if (width > maxWidth) {
        img.height = height / (width / maxWidth);
        img.width = maxWidth;
    }
    if (height > maxHeight) {
        img.height = maxHeight;
        img.width = width / (height / maxHeight);
    }
    //}

    /*// In this case yes
    else {
    if (width > maxHeight) {
    img.height = height / (width / maxWidth);
    img.width = maxHeight;
    }
    if (height > maxWidth) {
    img.height = maxWidth;
    img.width = width / (height / maxWidth);
    }
    }*/
}

// Set the rotation in the CSV
function SetRotationInCsv(rotation) {

    var index = document.getElementById(rotationHiddenFieldControlId).currentIndex;

    var csv = document.getElementById(rotationHiddenFieldControlId).value;

    var bits = csv.split(',');

    bits[index] = rotation;

    csv = bits.join(',');

    document.getElementById(rotationHiddenFieldControlId).value = csv;

    RotateImage(rotation);
}


// Get the rotation
function GetRotation(index) {
    var csv = document.getElementById(rotationHiddenFieldControlId).value;

    var bits = csv.split(',');

    var rotation = bits[index];

    document.getElementById(rotation0ControlId).checked = false;
    document.getElementById(rotation90ControlId).checked = false;
    document.getElementById(rotation180ControlId).checked = false;
    document.getElementById(rotation270ControlId).checked = false;

    if ((rotation == "0") || (rotation == "") || (!rotation))
        document.getElementById(rotation0ControlId).checked = true;

    if (rotation == "90")
        document.getElementById(rotation90ControlId).checked = true;

    if (rotation == "180")
        document.getElementById(rotation180ControlId).checked = true;

    if (rotation == "270")
        document.getElementById(rotation270ControlId).checked = true;

    RotateImage(rotation);
}

function RotateImage(rotation) {

    var i = document.getElementById(previewImageId);

    if (rotation == 0)
        i.style.filter = 'progid:DXImageTransform.Microsoft.BasicImage(rotation=0)';
    else if (rotation == 90)
        i.style.filter = 'progid:DXImageTransform.Microsoft.BasicImage(rotation=1)';
    else if (rotation == 180)
        i.style.filter = 'progid:DXImageTransform.Microsoft.BasicImage(rotation=2)';
    else if (rotation == 270)
        i.style.filter = 'progid:DXImageTransform.Microsoft.BasicImage(rotation=3)';
}


// Used to set the radio buttons up correctly when clicked
function SetUniqueRadioButton(nameregex, current) {
    re = new RegExp(nameregex);
    for (i = 0; i < document.forms[0].elements.length; i++) {
        elm = document.forms[0].elements[i]
        if (elm.type == 'radio') {
            if (re.test(elm.name)) {
                elm.checked = false;
            }
        }
    }
    current.checked = true;
}



// ---------------------
// Scripts for UMap.ascx
// ---------------------

function setUpLayers(map) {
    // get the layers
    GDownloadUrl(url + "GeographicServices/API/GetLayers.aspx",

			function(data) {

			    var xml = GXml.parse(data);

			    // make sure we have some data to work with
			    if (xml.documentElement != null) {

			        // create a container for the checkboxes - needs to be named uniquely to the map it is in
			        var chkBoxContainer = document.createElement("div");
			        chkBoxContainer.setAttribute("id", "chkBoxContainer" + map.containerName);

			        // get a nodeList of LAyers
			        var nodeList = xml.documentElement.getElementsByTagName("Layer");

			        for (var loop = 0; loop < nodeList.length; loop++) {

			            var id = nodeList[loop].getAttribute("id");
			            var name = nodeList[loop].getAttribute("name");

			            // need to check if the check box being created is already 

			            createCheckBox(map, id, name, chkBoxContainer);
			        }

			        map.layerContainer.appendChild(chkBoxContainer);
			    }

			})
}

function setUpClients(map) {

    // TODO: this needs to be changed when in production
    // get the layers
    GDownloadUrl(url + "GeographicServices/API/GetClients.aspx",

			function(data) {

			    var xml = GXml.parse(data);

			    // make sure we have some data to work with
			    if (xml.documentElement != null) {
			        // get a nodeList of LAyers
			        var nodeList = xml.documentElement.getElementsByTagName("Client");

			        createDropDownList(map, nodeList);
			    }
			})
}



// creates a single checkbox based on the id & name and inserts it into the checkbox container
// also hook up to the 'click' event
function createCheckBox(map, id, name, chkBoxContainer) {
    var divLayer = document.createElement("div");
    var chkSigns = document.createElement("input");
    chkSigns.setAttribute("type", "checkbox");
    chkSigns.setAttribute("id", id);

    // need to decide if it should be checked or not
    // if the id of this checkbox is in the map.LayerIds collection then checked = true
    for (var loop = 0; loop < map.layerIds.length; loop++) {
        if (map.layerIds[loop] == id) {
            chkSigns.setAttribute("checked", "checked");
            break;
        }
    }


    divLayer.appendChild(chkSigns)
    divLayer.appendChild(document.createTextNode(name));

    chkBoxContainer.appendChild(divLayer);

    GEvent.addDomListener(chkSigns, "click", function() {
        layerChanged(map, id, divLayer);
    });

}

// takes a list of nodes and create the dropdown list
function createDropDownList(map, nodes) {
    var selectList = document.createElement("select");
    selectList.setAttribute("name", "clientSelect");

    // need to give the dropdownlist a unique name to to find it later - use the container's name
    selectList.setAttribute("id", "ddlClients" + map.containerName);

    // iterate through the nodes and add them to the list
    for (var loop = 0; loop < nodes.length; loop++) {

        var option = document.createElement("option");
        option.setAttribute("value", nodes[loop].getAttribute("id"));
        option.appendChild(document.createTextNode(nodes[loop].getAttribute("name")));

        selectList.appendChild(option);

    }

    // hook into select changed event
    GEvent.addDomListener(selectList, "change", function() {
        clientChanged(map, selectList);
    });

    map.layerContainer.appendChild(selectList);

}

// handles the 'change' event of the client dropdown list
function clientChanged(map, selectList) {
    // need to do a get markers using the client selected
    var clientId = document.getElementById("ddlClients" + map.containerName).value;

    alert("Client: " + clientId);

    // set the clientId property of the map"
    map.clients = clientId;

    // clear out the Map
    ClearMap(map);

    // do a call to getMarkersXml
    panning(map);

}

// handler of click event of check boxes
function layerChanged(map, id) {
    // id is the id of the checkbox that has just been checked/unchecked
    // divLayer is the container with all the checkboxes - it si named to the Map

    var containerName = "chkBoxContainer" + map.containerName;

    var selectNodes = document.getElementById(containerName).getElementsByTagName("input");
    var list = "";
    // iterate through the nodes and add them to the list
    for (var loop = 0; loop < selectNodes.length; loop++) {
        if (selectNodes[loop].checked) {
            if (list == "")
                list = selectNodes[loop].getAttribute("id");
            else
                list = list + "," + selectNodes[loop].getAttribute("id");
        }
    }

    // set the property on the map
    map.layerIds = list;

    ClearMap(map)

    // call getMArkersXml
    panning(map);

}






function ClearMap(map) {
    // need to remove all the markers which are a GOverlay
    // can't do a map.clearOverlays() as this will remove all our controls that are overlays ????

    // clear each point from the map based on our collection of points
    map.points.forEach(function(element, index, array) { map.removeOverlay(element); });

    // remove all the listeners that were created for the GMarkers
    map.markerListeners.forEach(clearMarkerListeners);

    // clear out all the current markers
    map.viewPortHistory = [];
    map.viewPortHistoryIndex = 0;
    map.smMarkers = [];
    map.points = [];

    // clear out our collection of Signmanager Icons
    map.smIcons = [];

    // remove all the markers from the clusters then clear the markerClusterer array
    map.markerClusterers.forEach(clearClusterers);
    map.markerClusterers = [];
    
    // clear the overlays to remove all the points - even though we have done a removeOverlay on each marker?
    map.clearOverlays();


}

// callback to clear out the markers from the markerClusterers on the page
function clearClusterers(element, index, array) {
    element.clearMarkers();
}

// callback to clear out the markers from the map on the page
function clearMarkerOverlays(element, index, array, map) {
    map.removeOverlay(element);
}

// callback to remove all the listeners from the markers
function clearMarkerListeners(element, index, array) {
    GEvent.removeListener(element); 
}




function handleNoFlash(errorCode, map) {
    if (errorCode == '600') {
        //map.errorOccurred = true; 
        /*alert("Error: No panaorama nearby")*/;
    }

    if (errorCode == '601') {
        alert("Error: No user photo");
    }

    if (errorCode == '603') {
        alert("Error: No FLASH on this browser");
    }


    return;

    // see the error codes here:
    // http://code.google.com/apis/maps/documentation/reference.html#GStreetviewPanorama.ErrorValues

}


// testing alert
function displayResult(map) {
    var vpHx;

    for (var loop = 0; loop < map.viewPortHistoryIndex; loop++) {
        vpHx = vpHx + "viewPortHistoryIndex: " + loop + ": " + map.viewPortHistory[loop];
    }

    alert(vpHx + " ///////////" + map.viewPortHistory);
}


function resizeMapAtStartup(map) {
    function resizeMapDelayed() {
        map.checkResize();
    }

    window.setTimeout(resizeMapDelayed, 500);
}

// event handler for zooming in & out
function zooming(map, oldlevel, newlevel) 
{
    panning(map);
//    var markerCount = map.smMarkers.length;
//    
//    if (newlevel < oldlevel) {
//        panning(map);
//    }
//    else {
//        // if we are zooming OUT and there aren't
//        //if (!((map.smMarkers != null) && (map.smMarkers.length != 0))) {
//            panning(map);
//        }
//    }
}

// add a padded view port to the history of view ports - clear the view port if the Hx > 10
function panning(map) {

    // now need to decide if we call the server
    // is the current view port within any of the padded viewports
    if (viewPortWithinHistory(map)) {
        // no need to call server as we already have all the markers in our collection
        //alert("Within padded viewport Hx");
    }
    else {
        // the current viewport does not fall fully within a paddded view port in Hx

        // if there are 10 viewport Histories - clear the whole map
        if (map.viewPortHistoryIndex > maximumPaddedViewPortCount) 
        {
            ClearMap(map)
        }


        // get the GLatLngBounds of the current ViewPort
        var bounds = map.getBounds();
        var southWest = bounds.getSouthWest()  // a GLatLng
        var northEast = bounds.getNorthEast()  // a GLatLng

        // for testing
        var mapZoom = map.getZoom();
        var mapSize = map.getSize();

        // check the values:
        var northEastLat = northEast.lat();
        var southWestLat = southWest.lat();
        var northEastLong = northEast.lng();
        var southWestLong = southWest.lng();

        // account for -ve in maths
        var tmpNorthEastLong;
        var tmpSouthWestLong;

        if (northEastLong < 0)
            tmpNorthEastLong = northEastLong + 360;
        else
            tmpNorthEastLong = northEastLong;

        if (southWestLong < 0)
            tmpSouthWestLong = southWestLong + 360;
        else
            tmpSouthWestLong = southWestLong;

        // work out the lengths
        var intLatLength = northEast.lat() - southWest.lat();
        var intLngLength = tmpNorthEastLong - tmpSouthWestLong;


        // only add the padding if the lngLength < 90
        if (intLngLength < 90) {
            // get the lat & lngs for the new padded viewport
            var paddedSouthWestLat = southWest.lat() - (viewPortPadding * intLatLength);
            var paddedSouthWestLng = southWest.lng() - (viewPortPadding * intLngLength);
            var paddedNorthEastLat = northEast.lat() + (viewPortPadding * intLatLength);
            var paddedNorthEastLng = northEast.lng() + (viewPortPadding * intLngLength);
        }
        else {
            var paddedSouthWestLat = southWest.lat();
            var paddedSouthWestLng = southWest.lng();
            var paddedNorthEastLat = northEast.lat();
            var paddedNorthEastLng = northEast.lng();  
        }

        // create a new GLatLngBounds: GLatLngBounds(sw?:GLatLng, ne?:GLatLng)  
        var paddedViewPort = new GLatLngBounds(new GLatLng(paddedSouthWestLat, paddedSouthWestLng), new GLatLng(paddedNorthEastLat, paddedNorthEastLng));

        // need to make sure we are not requesting markers over an area greater than a single hemisphere
        // as the SQL Geography functions don't like this
        if (paddedSouthWestLng < 0)
            paddedSouthWestLng = paddedSouthWestLng + 360;

        if (paddedNorthEastLng < 0)
            paddedNorthEastLng = paddedNorthEastLng + 360;

        var lngDistance = paddedNorthEastLng - paddedSouthWestLng;

        // check to make sure the viewport calculated based on the points is not going to cover more than hald the earth
//        if (Math.abs(lngDistance) > 90) {
//            // turn off viewportpadding
//            paddedViewPort = bounds;
//        }

        var layerList = map.layerIds;
        
        // the panning method actually calls the GetMarkersXml which we may not want to do at this stage
        // if we are displaying a blank map (clientIds is null, layerIds is null, GsgIds is null AND GsgId is null)
        // then don't want to call this method to stop a trip to the server
        if (((map.clients == "") && (map.layerIds == "") && (map.gsgIds == "") && (map.gsgId == 0)) == false) {

            GDownloadUrl(url + "GeographicServices/API/GetMarkersXml.aspx?CurrentViewPort=" + paddedViewPort + "&PreviousViewPorts=" + map.viewPortHistory + "&ClientIds=" + map.clients + "&LayerIds=" + map.layerIds + "&Context=" + map.contextId + "&DisplayPrimaryClientsIcon=true&GSGIds=" + map.gsgIds,

			    function(data) { dealWithDataFromGetMarkersXml(data, map, paddedViewPort); });
        }


    }
}

// check if the currentViewPort is within any of the Padded ViewPorts in Hx
function viewPortWithinHistory(map) {
    // is the current viewport with any of the padded view ports in Hx
    // if(viewPortHistory != null)
    {
        for (var i = 0; i < map.viewPortHistory.length; i++) {
            if (map.viewPortHistory[i].containsBounds(map.getBounds()))
                return true;
        }
    }

    return false;
}



// parses the Xml returned from GetLocationsXml and adds required markers to overlay
function dealWithDataFromGetMarkersXml(data, map, paddedViewPort) {

    // an array of SignmanagerIcons
    //var smIcons = [];

    // parse the xml returned from the server
    var xml = GXml.parse(data);

    // make sure we have some data to work with
    if (xml.documentElement != null) {

        // first check if there were too many
        if (xml.documentElement.getElementsByTagName("TooManyMarkers").length != 0) 
        {
            // alert the user
            var count = parseInt(xml.documentElement.getElementsByTagName("TooManyMarkers")[0].getAttribute("Count"));

            alert("This view has " + count + " markers - Please zoom in");

            // if the user now zooms in the viewport should be within the hx and no call to the server
            // will happen. The points will still be on the map.

            ClearMap(map);

        }
        else 
        {

            // look for the ServerMessage in the xml
            var messageNode = xml.documentElement.getElementsByTagName("ServerMessage");
            var clearHx = messageNode[0].getAttribute("ClearHistory");
            if (clearHx == "true") 
            {
                // something has happened server side which means we should clear the points, the GSGIds, the ViewPortHx
                ClearMap(map);}
            }


            var message = messageNode[0].getAttribute("Message");
            if (message != null) {
                // there is a message coming back from the server
                alert(message);
        }
        else {
            // all good  - go ahead and display the points

            // first go through the icons and add any to list that we don't have
            var icons = xml.documentElement.getElementsByTagName("Icon");

            for (var i = 0; i < icons.length; i++) {
                // this methods creates a disctinct list of icons
                // it will also create a Markerclusterer if it is a new icon
                addIcon(icons[i], map);
            }


            // look at reccomended view port if it has not bee set before
            if (map.recommendedViewPortSet == false) {
                // get the nw & se xml node
                var recViewPort = xml.documentElement.getElementsByTagName("ReccomendedViewPort")[0];

                // if there is no recommended view port this element will have no child elements
                if (recViewPort.getElementsByTagName("NE").length != 0) {
                    // get the info from the xml element
                    var recViewPortNE = recViewPort.getElementsByTagName("NE")[0];
                    var recViewPortSW = recViewPort.getElementsByTagName("SW")[0];

                    var neLat = parseFloat(recViewPortNE.getAttribute("lat"));
                    var neLong = parseFloat(recViewPortNE.getAttribute("long"));
                    var swLat = parseFloat(recViewPortSW.getAttribute("lat"));
                    var swLong = parseFloat(recViewPortSW.getAttribute("long"));

                    // create a GLatLngBounds
                    var latlngBounds = new GLatLngBounds(new GLatLng(swLat, swLong), new GLatLng(neLat, neLong));

                    // get the map to work out the zoom required to cover it and set it on the map, just step out one level for easier viewing
                    var zoomForRecViewPort = map.getBoundsZoomLevel(latlngBounds)
                    map.setZoom(zoomForRecViewPort); // - 1);

                    // need to reset the centre of the map as it has been set up when the map first created 
                    var centreLat = (neLat + swLat) / 2;
                    var centreLong = (neLong + swLong) / 2;
                    map.setCenter(new GLatLng(centreLat, centreLong));

                    map.recommendedViewPortSet = true;

                }
                else {
                    // no recommended view port
                }
            } // end of if(map.recommendedViewPortSet = false)

            var markers = xml.documentElement.getElementsByTagName("Markers")[0].getElementsByTagName("Marker");  // a nodelistcollection

            // add to the viewportHx no matter if there was points found or not
            // but don't want to do it if there were too many points found
            map.viewPortHistory[map.viewPortHistoryIndex] = paddedViewPort;
            map.viewPortHistoryIndex++;

            // just checking:
            var currentZoom = map.getZoom();
            
            // want to count the number of markers that come down twcie
            var repeatedMarkerCount = 0;
            

            // add the marker to the list we have if they are not already there
            for (var loop = 0; loop < markers.length; loop++) {
                var gsgid = parseInt(markers[loop].getAttribute("GSGID"));

//                // add the marker to our own collection using the GSGId as the index
//                if (map.smMarkers == null)
//                    map.smMarkers = [];

                var thisMarker = map.smMarkers[gsgid];

                // check if this point already being used
                if (map.smMarkers[gsgid] == null) {

                    // if not add it to the dictionary, create the Marker and add the marker to the markerClusterer
                    map.smMarkers[gsgid] = gsgid;

                    // get the coord
                    var point = new GLatLng(parseFloat(markers[loop].getAttribute("lat")),
                                parseFloat(markers[loop].getAttribute("long")));

                    // get the GIcon to use
                    var iconTypeId = parseInt(markers[loop].getAttribute("Icon"));
                    var icon = map.smIcons[iconTypeId].gIcon;  // a GIcon

                    // create the marker with the icon as a parameter in the constructor
                    var gMarker = createMarker(point, gsgid, map, icon, iconTypeId);
                }
                else {
                    // to be here means we have received a point from SQL that we already have
                    // this shouldn't really happen
                    repeatedMarkerCount++;

                }


            }
            
            // if repeatedMarkerCount > 0 then display message
            //if (repeatedMarkerCount > 0)
              //  alert("repeated markers: " + repeatedMarkerCount);




            // may have added more markers so refresh
            if(map.markerClusterers.length != 0)
                map.markerClusterers.forEach(function(element, index, array) { element.resetViewport(); });
            
        }
    }
}



// checks if the icon already exists in the collection - if not it adds a 
// this is just a collection of Icons, they are used when creating GMArkers
function addIcon(iconNode, map)  // icon is a node
{
    var id = parseInt(iconNode.getAttribute("IconTypeId"));

    // first check if we already have this icon in our collection
    if (map.smIcons[id] == null) {

        var zoomToDisplay = parseInt(iconNode.getElementsByTagName("OnlyAppearAtZoomLevelsLessThan")[0].text);

        // need to create a GIcon but base it on the default
        var gIcon = new GIcon(G_DEFAULT_ICON);
        gIcon.image = iconNode.getElementsByTagName("Img")[0].text;
        gIcon.shadow = iconNode.getElementsByTagName("Shadow")[0].text;
        gIcon.iconSize = new GSize(parseInt(iconNode.getElementsByTagName("IconSize")[0].getElementsByTagName("width")[0].text), parseInt(iconNode.getElementsByTagName("IconSize")[0].getElementsByTagName("height")[0].text));
        gIcon.shadowSize = new GSize(parseInt(iconNode.getElementsByTagName("ShadowSize")[0].getElementsByTagName("width")[0].text), parseInt(iconNode.getElementsByTagName("ShadowSize")[0].getElementsByTagName("height")[0].text));
        gIcon.iconAnchor = new GPoint(8, 8);
        //gIcon.iconAnchor = new GPoint(parseInt(iconNode.getElementsByTagName("AnchorPoint")[0].getElementsByTagName("x")[0].text), parseInt(iconNode.getElementsByTagName("AnchorPoint")[0].getElementsByTagName("y")[0].text));
        //gIcon.infoWindowAnchor = new GPoint(parseInt(iconNode.getElementsByTagName("InfoWindowAnchor")[0].getElementsByTagName("x")[0].text), parseInt(iconNode.getElementsByTagName("InfoWindowAnchor")[0].getElementsByTagName("y")[0].text));
        //gIcon.transparent = iconNode.getElementsByTagName("Transparent")[0].text;
        //gIcon.imageMap = Number[] from string
        //gIcon.dragCrossImage = iconNode.getElementsByTagName("DragCrossImage")[0].text;
        //gIcon.dragCrossSize = new GSize(parseInt(iconNode.getElementsByTagName("DragCrossSize")[0].getElementsByTagName("width")[0].text), parseInt(iconNode.getElementsByTagName("DragCrossSize")[0].getElementsByTagName("height")[0].text));
        //gIcon.dragCrossAnchor = new GPoint(parseInt(iconNode.getElementsByTagName("DragCrossAnchor")[0].getElementsByTagName("x")[0].text), parseInt(iconNode.getElementsByTagName("DragCrossAnchor")[0].getElementsByTagName("y")[0].text));

        // add the SignmanagerIcon to our collection
        map.smIcons[id] = new SignmanagerIcon(id, zoomToDisplay, gIcon);

        // now add a new MarkerClusterer to the [] and use the IconTypeid as the key
        // the style for the clustermarker will be in the above xml
        // define the markerClustererOptions here

        // create the style first
        var styles = [{url:gIcon.image, height: 35, width: 35, opt_anchor: [24,0], opt_textColor: '#FF0000'}];
        var mcOptions = { gridSize: 60, styles: styles };
        map.markerClusterers[id] = new MarkerClusterer(map, [], mcOptions);
                  
    }

}

// object containing the iconId, zoom to display at, and a GIcon
function SignmanagerIcon(id, zoomToDisplay, gIcon) {
    this.id = id;
    this.zoomToDisplay = zoomToDisplay;
    this.gIcon = gIcon;  // a GIcon
}

// function to highlight a particular marker
function highlightMarker(gsgId, containerId) {
    var map = mapsOnPage[containerId];

    var p = map.points[gsgId];

    if (p != null) {
        var point = p.getPoint();

        var polyPoints = Array();

        if (map.highlightCircle) {
            map.removeOverlay(map.highlightCircle);
            map.highlightCircle = null;
        }
        else {

            var mapNormalProj = G_NORMAL_MAP.getProjection();
            var mapZoom = map.getZoom();
            var clickedPixel = mapNormalProj.fromLatLngToPixel(point, mapZoom);
            var polySmallRadius = 20;

            var polyNumSides = 20;
            var polySideLength = 18;



            for (var a = 0; a < (polyNumSides + 1); a++) {
                var aRad = polySideLength * a * (Math.PI / 180);
                var polyRadius = polySmallRadius;
                var pixelX = clickedPixel.x + polyRadius * Math.cos(aRad);
                var pixelY = clickedPixel.y + polyRadius * Math.sin(aRad);
                var polyPixel = new GPoint(pixelX, pixelY);
                var polyPoint = mapNormalProj.fromPixelToLatLng(polyPixel, mapZoom);
                polyPoints.push(polyPoint);
            }
            // Using GPolygon(points,  strokeColor?,  strokeWeight?,  strokeOpacity?,  fillColor?,  fillOpacity?)
            map.highlightCircle = new GPolygon(polyPoints, "#000000", 2, 0.0, "#FF0000", .5);
            map.addOverlay(map.highlightCircle);
        }
    }
}



// create the marker and
function createMarker(point, gsgid, map, icon, iconTypeId) {
    // icon is a GIcon

    // create the marker
    var marker = new GMarker(point, { icon: icon, draggable: true });

    // need to store a reference of the marker so we can find it based on it's gsgId
    map.points[gsgid] = marker;

    var context = map.contextId;

    // now add the marker to the appropriate MarkerClusterer
    if (map.markerClusterers[iconTypeId] != null) 
        map.markerClusterers[iconTypeId].addMarker(marker);

    var markerClickListener = null;
    // click event will go and get usercontrol's html to display
    markerClickListener = GEvent.addListener(marker, "click", function(latlng) {

        // as soon as the marker is clicked the map's gsgid becomes that of the marker:
        map.gsgId = gsgid;

        // should go to streetview saved for this point
        var lat = latlng.lat();
        var lng = latlng.lng();
        getStreetView(map, lat, lng);

        // when the marker is clicked we will update streetview but not yet save it to the GSG:
        // saving of the map's view port and streetview details only happens when the 'Save' button is clicked
        // need to set POV to default otherwise will use what it was
        // map.streetView.setLocationAndPOV(new GLatLng(lat, lng), { zoom: 0, yaw: 0, pitch: 0 });

        // need to get the HTML of the usercontrol from the server
        GDownloadUrl(url + "GeographicServices/API/GetInfoWindowXml.aspx?GeographicServicesGatewayId=" + gsgid + "&ContextId=" + map.contextId,
			function(data) { dealWithDataFromGetInfoWindowXml(data, marker); });
    });

    // add the listener from above to a list
    map.markerListeners.push(markerClickListener);

    // hook up to marker being moved
    var markerDragListener;
    marker.enableDragging();
    markerDragListener = GEvent.addListener(marker, "dragend", function(latlng) {

        // as soon as we ove the marker we can clear the error
        //map.errorOccurred = false;

        var lat = latlng.lat();
        var lng = latlng.lng();

        // as soon as a marker is moved the map's gsgid becomes that of the marker:
        map.gsgId = gsgid;

        // when the marker is moved we will update streetview but not yet save it to the GSG:
        // saving of the map's view port and streetview details opnly happens when the 'Save' button is clicked
        // need to set POV to default otherwise will use what it was
        if (map.streetView != null)
            map.streetView.setLocationAndPOV(new GLatLng(lat, lng), { zoom: 0, yaw: 0, pitch: 0 });

        // store the points in the local variables
        map.panoCurrentLat = lat;
        map.panoCurrentLng = lng;

        // if there is no streetview found above it should cause an error but it doesn't seem to hit the
        // error handling code qucik enough which sets the map.errorOccurred = true which setStreetView checks
        map.streetViewClient.getNearestPanorama(new GLatLng(lat, lng), function(data) { checkStreetView(data, map); });

        // need to save the new position of the marker in the DB
        GDownloadUrl(url + "GeographicServices/API/MoveMarkerXml.aspx?GeographicServicesGatewayId=" + gsgid + "&Lat=" + lat + "&Long=" + lng,
            function(data) { /*do nothing */; });

    });

    // add the listener from above to a list
    map.markerListeners.push(markerDragListener);

    return marker;
}




// checks if a street view exists - if it doesn't we hide the streetView
function checkStreetView(data, map) {

    if (map.streetView != null) {
        // ErrorCodes: 200 == success; 500 == server error; 600 == no pano nearby
        if (data.code == 200) {
            // make sure the street view shows and call the method to save the streetview in the db
            if (map.streetView.isHidden()) {
                map.streetView.show();
                //setStreetView(map);
            }
        }
        else {
            // if no a success i.e. no street view available then hide the map to avoid confusion
            var hidden = map.streetView.isHidden();

            if (map.streetView.isHidden() == false)
                map.streetView.hide();
        }
    }
}

function dealWithDataFromGetInfoWindowXml(data, marker) {
    // parse the xml returned from the server
    var xml = GXml.parse(data);

    var width = xml.documentElement.getElementsByTagName("Width")[0].nodeTypedValue;
    var height = xml.documentElement.getElementsByTagName("Height")[0].nodeTypedValue; // height isn't used
    var content = xml.documentElement.getElementsByTagName("Contents")[0].nodeTypedValue;

    // openInfoWindowHtml(content:String, opts?:GInfoWindowOptions)
    marker.openInfoWindowHtml(content, { maxWidth: width })

}


function setViewPort(map) {

    var zoom = map.getZoom();

    // need the mapmode from the map
    var mapMode = map.getCurrentMapType().getName(true);
    var mapModeId;

    // this could be done better?
    if (mapMode == "Map") { mapModeId = 1; }
    if (mapMode == "Sat") { mapModeId = 2; }
    if (mapMode == "Hyb") { mapModeId = 3; }

    // this method will be hit when the user 'saves' the current view from the UI
    GDownloadUrl(url + "GeographicServices/API/SetViewPort.aspx?GeographicServicesGatewayId=" + map.gsgId + "&Zoom=" + zoom + "&MapMode=" + mapModeId,
            function(data) { /*do nothing */; });

    // just for testing        
    //alert("Map State saved-> Mode: " + mapMode + "; Zoom: " + zoom);
}


function getStreetView(map, lat, lng) {
    // check if this GSGId has a Streetview in the DB, if not just load up the one from the lat & lng
    GDownloadUrl(url + "GeographicServices/API/GetStreetView.aspx?GeographicServicesGatewayId=" + map.gsgId,
            function(data) {


                // check for xml coming back if good then use this to load up the streetview
                var xml = GXml.parse(data);
                if (xml.documentElement != null) {

                    // get data from xml
                    var latx = xml.documentElement.getAttribute("lat");
                    var lngx = xml.documentElement.getAttribute("lng");
                    var yawx = parseInt(xml.documentElement.getAttribute("Yaw"));
                    var pitchx = parseInt(xml.documentElement.getAttribute("Pitch"));
                    var zoomx = parseInt(xml.documentElement.getAttribute("Zoom"));

                    // create some parameters for the streetview constructor
                    coord = new GLatLng(latx, lngx);
                    povx = { yaw: yawx, pitch: pitchx, zoom: zoomx };
                    panoramaOptions = { latlng: coord, pov: povx };

                    // create the StreetView
                    map.streetView = new GStreetviewPanorama(document.getElementById(map.streetViewContainer), panoramaOptions);

                    // if there is no streetview found above it should cause an error but it doesn't seem to hit the
                    // error handling code qucik enough which sets the map.errorOccurred = true which setStreetView checks

                    map.streetViewClient.getNearestPanorama(new GLatLng(latx, lngx), function(data) { checkStreetView(data, map); });

                    // as GetLatLng() of the StreetView returns the original LatLng given to the StreetView
                    // we need a work around to get the actual LatLng when the Streetview has been moved.
                    // thhis is available during the initialized event of the StreetView
                    // store them as dynamic properties on the StreetView so we can access them in setStreetview()

                    // hook into intialize event - seems to happen on panning & setLocationAndPOV
                    GEvent.addListener(map.streetView, "initialized", function(loc) {
                        recordCurrentStreetView(map, loc);
                    });

                    // hook into error event
                    GEvent.addListener(map.streetView, "error", function(error) {
                        handleNoFlash(error, map);
                    });

                }

                else  // no xml from GetStreetView
                {
                    // just use the co-ords
                    var curCoord = new GLatLng(lat, lng);

                    panoramaOptions = { latlng: curCoord };
                    map.streetView = new GStreetviewPanorama(document.getElementById(map.streetViewContainer), panoramaOptions);

                    // if there is no streetview found above it should cause an error but it doesn't seem to hit the
                    // error handling code qucik enough which sets the map.errorOccurred = true which setStreetView checks
                    //var streetViewClient = new GStreetviewClient();

                    map.streetViewClient.getNearestPanorama(new GLatLng(lat, lng), function(data) { checkStreetView(data, map); });


                    // hook into intialize event - seems to happen on panning & setLocationAndPOV
                    GEvent.addListener(map.streetView, "initialized", function(loc) {
                        recordCurrentStreetView(map, loc);
                    });

                    //  hook into error event
                    GEvent.addListener(map.streetView, "error", function(error) {
                        handleNoFlash(error, map);
                    });
                }


            });

}


// records the current lat & long of the streetview.
// this method is used by the initialise event of the streetview which is raised everytime the 
// streetview is moved or SetLocationAndPOV called.
function recordCurrentStreetView(map, loc) {
    // the info is stored as a property of the map object to provide the right scope
    map.panoCurrentLat = loc.latlng.lat();
    map.panoCurrentLng = loc.latlng.lng();
}




// this method will be hit when the user 'saves' the current view from the UI
function setStreetView(map, machineGenerated) {
    // need to be able to know if the streetView is not visible or a panorama is not available then we would 
    // need to delete the StreetView

    // machineGenerated will only be true if this methid is called by the eventhandler of the 'Save Map State'
    // button. if this method is called from elsewhere it will be because the marker has been clicked or
    // dragged and the streetview is being refreshed
    if (machineGenerated == null)
        machineGenerated = true;

    // map.streetView.getLatLng() returns the original values given to the streetView
    // if the streetview has been moved we need to the new location: panoCurrentLat & panoCurrentLng are
    // set each time the streetView is 'initialised'
    //var location = new GLatLng(map.streetView.panoCurrentLat, map.streetView.panoCurrentLng);
    var location = new GLatLng(map.panoCurrentLat, map.panoCurrentLng);

    // if it hasn't moved the location retruned from above will be null so we can used the 
    // original location given to the streetview
    if ((location == null) || (location.x == NaN))
        location = map.streetView.getLatLng();

    // in some circumstances the street view may not return a latlng if so use the latlng of the map
    if (location == null)
        location = map.getCenter();

    // now need to check if there is actually a StreetView at the location if there is not we need to
    var streetViewFound = false;

    map.streetViewClient.getNearestPanorama(location, function(data) {

        //streetViewFound = (data.code == 200);
        checkForPanorama(data, map, location, machineGenerated);
    });

}

//    // method called by Winforms app to save the state on the screen
//    function ConfirmState() {
//        setViewPort(mapRef);
//        setStreetView(mapRef, false);
//    }



function checkForPanorama(data, map, location, machineGenerated) {


    if (data.code != 200) {
        // clear the location
        location = new GLatLng(0, 0);
    }

    // get the POV from the StreetView
    var pov = map.streetView.getPOV();
    var pitch = pov.pitch;
    var yaw = pov.yaw;
    var zoom = pov.zoom;

    // call the API to set the StreetView
    // it will handle whether the StreetView is actually updated / created / deleted.
    GDownloadUrl(url + "GeographicServices/API/SetStreetView.aspx?GeographicServicesGatewayId=" + map.gsgId + "&Location=" + location + "&Yaw=" + yaw + "&Pitch=" + pitch + "&Zoom=" + zoom + "&MachineGenerated=" + machineGenerated,
        function(data) { /*do nothing */; });
}






// -----------------
// end of UMap.sascx
// -----------------


// LoadImageWithProgressIndicator
function LoadImageWithProgressIndicator(clientId, src) {
    var img = new Image();

    // wrap our new image in jQuery, then:
    $(img).load(function() {
        var image = $('#' + clientId);
        image.hide()
        image.attr('src', img.src);
        image.fadeIn()
    })

    // *finally*, set the src attribute of the new image to our image
	    .attr('src', src);
}


// Hook into the page number changed event
function SetupOnPageNumberChanged(nextButtonClientId, thumbnailClientId, pageNumberClientId, url, totalPages) {
    var button = document.getElementById(pageNumberClientId);

    button.onchange = function() {

        var pageNumberLabel = document.getElementById(pageNumberClientId);

        var image = $('#' + thumbnailClientId);
        image.hide()

        var img = new Image();

        // wrap our new image in jQuery, then:
        $(img).load(function() {
            image.attr('src', img.src);
            image.fadeIn();
        })

        // *finally*, set the src attribute of the new image to our image
	        .attr('src', url + pageNumberLabel.value);
    }

    button.ondblclick = button.onclick;
}

// Hook into the document details open (mouse click on preview) event
function OpenDocumentDetails(imageClientId, pageNumberClientId, urlFormat, top, left, height, width) {
    var image = document.getElementById(imageClientId);

    image.onclick = function() {
        var pageNumber = document.getElementById(pageNumberClientId);
        if (pageNumber != null)
            var page = pageNumber.value;
        else
            var page = 1;

        var url = urlFormat + page;
        window.open(url, '_blank', 'top=' + top + ', left=' + left + ', height=' + height + ', width=' + width + ', status=no, menubar=no, resizable=yes, scrollbars=yes, toolbar=no, location=no, directories=no');
    }
}




/* ----------------------------------  */

/* Adds foreach to arrays */

if (!Array.prototype.forEach) {
    Array.prototype.forEach = function(fun /*, thisp*/) {
        var len = this.length;
        if (typeof fun != "function")
            throw new TypeError();

        var thisp = arguments[1];
        for (var i = 0; i < len; i++) {
            if (i in this)
                fun.call(thisp, this[i], i, this);
        }
    };
}



function closeWindowAtStartup() {
    function closeWindow() {
        self.close();
    }

    window.setTimeout(closeWindow, 200);
}

function runCodeAfterDelay(codeToRun, delay) {
    function runCode() {
        eval(codeToRun);
    }

    window.setTimeout(runCode, delay);
}