//******************************************************************************
// Author: Craig Andrews
// Date: 1/15/2007
// File: RunEzMap.js
// Description: Wrapper for the relevant functionality of the Google Maps API.
//              Okay, okay... I admit... this went *way* beyond "wrapper" long ago.
//******************************************************************************

window.savePictureResponse = null;
window.saveFlickrPictureResponse = null;

routeId=null; //the routeid of the editable waypoints
routeEditable=false; //is the route editable?
waypoints=new Object(); //associative array of user set waypoints, key being id
notes=new Object(); //associative array of notes, key being id
cities=new Object(); //associative array of cities, key being id
pictures=new Object(); //associative array of pictures, key being id
flickrPictures=new Object(); //associative array of flickrPictures, key being id
smsMessages = new Object(); //associative array of smsMessages, key being id
users = new Object(); //associative array of users, key being id
poverlay=null; //the overlay containing the lines connecting the waypoints
adding=false; //whether we're adding a new Marker right now
var map=null;
selectedMarker=null; //the last selected (clicked, dragged, or added) marker (note, waypoint, picture, etc)
var routes=new Object(); //associative array of all the routes we know about, key being id
var routeOverlays=new Object(); //associative array of the polyline overlays for all the routes currently displayed, key being routeid

//var waypointsMarkerManager;
var notesMarkerManager;
var picturesMarkerManager;
var flickrPicturesMarkerManager;
var smsMessagesMarkerManager;

var newNoteId = 0; //must be a made up id that is not used in the database
var newPictureId = 0; //must be a made up id that is not used in the database
var newFlickrPictureId = 0; //must be a made up id that is not used in flickr
var nextWaypointId = 0; //the next id number to assign to a new waypoints; only used as a uniquifier internally - does not go to the server

var notesDisplayed=false; //boolean indicating if notes should be drawn
var picturesDisplayed=false; //boolean indicating if pictures should be drawn
var routesDisplayed=false; //boolean indicating if routes should be drawn
var smsMessagesDisplayed=false;; //boolean indicating if smsmessages should be drawn

var overviewMapControlDisplayed=false; //boolean indicating if the minimap should be displayed on the map
var largeMapControlDisplayed=false; //boolean indicating if the zoom/pan control should be displayed
var mapTypeControlDisplayed=false; //boolean indicating if the map type (satellite, map, hybrid) control should be displayed

var routeCriteria=new Object(); //the route criteria to use when retrieving routes.
                                //do not including viewport bounds - those will be added for you.
                                
var noteCriteria=new Object(); //the note criteria to use when retrieving notes.
                                // do not include viewport bounds                                

var visibleRoutesPage=1; //the page number (1 indexed) of the visible routes display
//visible routes are paged into pages with as many items as the size of the visibleRoutes array

var visibleRoutesCount=0; //the number of routes that are visible, but including those that are on pages other than the one being displayed

//array of the routes in the current result set
var routesResultSet=[];

var ajaxRequestQueue=new Array();
var ajaxSchedulerActive=false;
var ajaxSchedulerInterval = 50;

var visibleRoutes=[];
visibleRoutes.push({color: '#911832', id:null});
visibleRoutes.push({color: '#231f20', id:null});
visibleRoutes.push({color: '#7f733d', id:null});
visibleRoutes.push({color: '#325b99', id:null});
visibleRoutes.push({color: '#033824', id:null});
visibleRoutes.push({color: '#dc6828', id:null});
visibleRoutes.push({color: '#aa9b71', id:null});
visibleRoutes.push({color: '#76787b', id:null});
//visibleRoutes.push({color: '#99cccc', id:null});
//visibleRoutes.push({color: '#d3dade', id:null});

//an ajaxRequest is:
//action,parameters,onSuccess,onFailure
function performAjaxRequest(ajaxRequest){
    ajaxRequestQueue.push(ajaxRequest);
    if(!ajaxSchedulerActive){
        ajaxSchedulerActive=true;
        setTimeout(ajaxScheduler,ajaxSchedulerInterval);
    }
}

function ajaxScheduler(){
    console.log("ajaxScheduler is running...");
    ajaxSchedulerActive=false;
    
    var xmlDoc = GXml.parse("<Requests />");
    
    for(var i=0;i<ajaxRequestQueue.length;i++){
        var ajaxRequest = ajaxRequestQueue[i];
        var requestNode = xmlDoc.createElement("Request");
        requestNode.setAttribute("type",ajaxRequest.action);
        requestNode.setAttribute("uid",i);
        var parametersNode = xmlDoc.createElement("Parameters");
        for(var key in ajaxRequest.parameters){
            var parameterNode = xmlDoc.createElement(key);
            if(ajaxRequest.parameters[key].tagName)
                //it's an XML node
                parameterNode.appendChild(ajaxRequest.parameters[key]);
            else
                //it's some text
                if(typeof(ajaxRequest.parameters[key])=="number" && ajaxRequest.parameters[key] % 1 != 0)
                    //it's a number, and it's not an integer
                    parameterNode.appendChild(xmlDoc.createTextNode(ajaxRequest.parameters[key].toFixed(14)));
                else
                    parameterNode.appendChild(xmlDoc.createTextNode(ajaxRequest.parameters[key]));
            parametersNode.appendChild(parameterNode);
        }
        requestNode.appendChild(parametersNode);
        xmlDoc.documentElement.appendChild(requestNode);
    }
    
    //ajaxRequestQueue has to be copied into this scope so when the onSuccess function is called,
    //you get the queue when the request was made, not as it is now
    var copyOfAjaxRequestQueue=ajaxRequestQueue;
    
    new Ajax.Request("AjaxRequests/BigRequest.aspx",
    {
        method:'post',
        parameters: {request : serializeXmlDoc(xmlDoc) } ,
        onSuccess: function(transport){
            console.log("ajaxScheduler is running onSuccess...");
            var xml = GXml.parse(transport.responseText);
            var success = xml.documentElement.getElementsByTagName("Success")[0].firstChild.nodeValue;
            if(success.toLowerCase()=="true"){
                var responses = xml.documentElement.getElementsByTagName("Response");
                for(var j=0;j<responses.length;j++){
                    var passTransport=new Object();
                    passTransport.responseText = serializeXmlDoc(responses[j].getElementsByTagName("Result")[0]);
                    copyOfAjaxRequestQueue[parseInt(responses[j].getAttribute("uid"))].onSuccess(passTransport);
                    console.log("ajaxScheduler is done running onSuccess");
                }
                
                 // HACK OF THE CENTURY
                for(var i=0;i<copyOfAjaxRequestQueue.length;i++){
                    console.log(copyOfAjaxRequestQueue[i].action);
				    if(copyOfAjaxRequestQueue[i].action.toLowerCase().endsWith("getpictures.aspx")){
				        console.log('substring hack!!');
				        setTimeout(drawVisibleFlickrPictures,1);
				        break;
				    }
                }
                
            }else{
                alert(xml.documentElement.getElementsByTagName("Explanation")[0].firstChild.nodeValue);
            }
        },
        onFailure: function(){alert(gMsgGenericAjaxError);}
    });
    
    ajaxRequestQueue=new Array();
    console.log("ajaxScheduler is done.");
}

var infoWindows=new Array();
var infoWindowsFlickPictureHandle=null;
var infoWindowsPictureHandle=null;

//toggles the picture popups.
//return true if just turned on popups
//return false if just turned off popups
function togglePicturePopups(){
    if(infoWindowsFlickPictureHandle==null){
        addPicturePopups();
        return true;
    }else{
        removePicturePopups();
        return false;
    }
}

function arePicturePopupsDisplayed(){
    return(infoWindowsFlickPictureHandle!=null);
}

function addPicturePopups(){
    if(infoWindowsFlickPictureHandle==null){
        infoWindowsFlickPictureHandle=GEvent.addDomListener(window,'flickrPictureDiscovered',addPicturePopup);
        infoWindowsPictureHandle=GEvent.addDomListener(window,'pictureDiscovered',addPicturePopup);
        for(var o in flickrPictures){
            addPicturePopup(flickrPictures[o]);
        }
        for(var o in pictures){
            addPicturePopup(pictures[o]);
        }
    }
}

function addPicturePopup(marker){
    var ewindow = new EWindow(map, E_STYLE_2);
    map.addOverlay(ewindow);
    infoWindows.push(ewindow);
    var html="<img src='" + marker.getSmallUrl() + "' />";
    ewindow.openOnMarker(marker,html);
}

function removePicturePopups(){
    while(infoWindows.length>0){
        var ewindow = infoWindows.pop();
        ewindow.hide();
        map.removeOverlay(ewindow);
    }
    if(typeof(infoWindowsFlickPictureHandle)!="undefined")
        GEvent.removeListener(infoWindowsFlickPictureHandle);
    infoWindowsFlickPictureHandle=null;
    if(typeof(infoWindowsPictureHandle)!="undefined")
        GEvent.removeListener(infoWindowsPictureHandle);
    infoWindowsPictureHandle=null;
}

function openFlickrFrame()
{
    new Lightbox.base('myFlickr', { url : flickrAuthorizeUrl, width : 800, height : 500}); 
}

//draw lines between the waypoints
function drawWaypointLines(){
    var points=[];
    for(var i in waypoints){
        points.push(waypoints[i].getPoint());
    }
    if(poverlay!=null) map.removeOverlay(poverlay);
    poverlay=new GPolyline(points,'#FF0000', 5, .65); // width and opacity
    map.addOverlay(poverlay);
}

function saveWaypoints(functionAfter){
    if(routeId!=null){
        var xmlDoc = GXml.parse("<SaveWaypointsAction />");
        var routeIdElement = xmlDoc.createElement("RouteId");
        routeIdElement.appendChild(xmlDoc.createTextNode(routeId));
        xmlDoc.documentElement.appendChild(routeIdElement);
        for(var i in waypoints)
            xmlDoc.documentElement.appendChild(waypoints[i].serializeToXmlDoc().documentElement);
        performAjaxRequest({action: 'AjaxRequests/SaveWaypoints.aspx',
            parameters: { action: xmlDoc.documentElement },
            onSuccess: function(transport){
                var xml = GXml.parse(transport.responseText);
                var success = xml.documentElement.getElementsByTagName("success")[0].firstChild.nodeValue;
                if(success.toLowerCase()=="true"){
                    if(typeof(functionAfter)!="undefined"){
                        console.log("saveWaypoints was successful! Calling functionAfter...");
                        functionAfter();
                    }
                }else{
                    alert(xml.documentElement.getElementsByTagName("explanation")[0].firstChild.nodeValue);
                }
            },
            onFailure: function(){
                alert('Could not connect to server to save the route');
            }
        });
    }else{
        alert("There is no route id set so you cannot save");
    }
}

//insert the specified picture, then call functionAfterInsert with the new picture as its parameter
function insertPicture(formObject, picture,functionAfterInsert){
    window.savePictureResponse=null;
    var xmlDoc = GXml.parse("<SavePictureAction />");
    xmlDoc.documentElement.appendChild(picture.serializeToXmlDoc().documentElement);
    node = xmlDoc.createElement("Action");
    node.appendChild(xmlDoc.createTextNode("Insert"));
    xmlDoc.documentElement.appendChild(node);
    formObject.elements["action"].value = serializeXmlDoc(xmlDoc);
    console.log(formObject.elements["action"].value);
    formObject.submit();
    console.log("picture form submitted");
    setTimeout(function(){insertPicturePart2(formObject,picture,functionAfterInsert);},500);
}

function insertPicturePart2(formObject, picture,functionAfterInsert){
    console.log("insertPicturePart2");
    if(window.savePictureResponse == null){
        setTimeout(function(){insertPicturePart2(formObject,picture,functionAfterInsert);},500);
    }else{
        console.log("savePictureResponse: " + window.savePictureResponse);
        var xml = GXml.parse(window.savePictureResponse);
        var success = xml.documentElement.getElementsByTagName("success")[0].firstChild.nodeValue;
        if(success.toLowerCase()=="true"){
            Picture.deserializeFromXml(xml.documentElement.getElementsByTagName("Picture")[0],{draggable:false},picture);
            console.log("new picture id is " + picture.id);
            addMarker(picture,undefined,undefined,true);
            if(typeof(functionAfterInsert)!="undefined"){
                console.log("InsertPicture was successful! Calling functionAfterInsert...");
                functionAfterInsert(picture);
            }
        }else{
            alert(xml.documentElement.getElementsByTagName("explanation")[0].firstChild.nodeValue);
        }
    }
}

//insert the specified picture, then call functionAfterInsert with the new picture as its parameter
function insertFlickrPicture(formObject, picture,functionAfterInsert){
    window.saveFlickrPictureResponse=null;
    picture.flickrUserId = currentUserFlickrId;
    var xmlDoc = GXml.parse("<SaveFlickrPictureAction />");
    xmlDoc.documentElement.appendChild(picture.serializeToXmlDoc().documentElement);
    node = xmlDoc.createElement("Action");
    node.appendChild(xmlDoc.createTextNode("Insert"));
    xmlDoc.documentElement.appendChild(node);
    formObject.elements["action"].value = serializeXmlDoc(xmlDoc);
    console.log(formObject.elements["action"].value);
    formObject.submit();
    console.log("flickrpicture form submitted");
    setTimeout(function(){insertFlickrPicturePart2(formObject,picture,functionAfterInsert);},500);
}

function insertFlickrPicturePart2(formObject, picture,functionAfterInsert){
    console.log("insertFlickrPicturePart2");
    if(window.saveFlickrPictureResponse == null){
        setTimeout(function(){insertFlickrPicturePart2(formObject,picture,functionAfterInsert);},500);
    }else{
        console.log("saveFlickrPictureResponse: " + window.saveFlickrPictureResponse);
        var xml = GXml.parse(window.saveFlickrPictureResponse);
        var success = xml.documentElement.getElementsByTagName("success")[0].firstChild.nodeValue;
        if(success.toLowerCase()=="true"){
            FlickrPicture.deserializeFromXml(xml.documentElement.getElementsByTagName("FlickrPicture")[0],{},picture);
            addMarker(picture,undefined,undefined,true);
            if(typeof(functionAfterInsert)!="undefined"){
                console.log("InsertFlickrPicture was successful! Calling functionAfterInsert...");
                functionAfterInsert(picture);
            }
        }else{
            alert(xml.documentElement.getElementsByTagName("explanation")[0].firstChild.nodeValue);
        }
    }
}

//insert the specified note, then call functionAfterInsert with the new noteId as its parameter
function insertNote(note,functionAfterInsert){
    var xmlDoc = GXml.parse("<SaveNoteAction />");
    xmlDoc.documentElement.appendChild(note.serializeToXmlDoc().documentElement);
    node = xmlDoc.createElement("Action");
    node.appendChild(xmlDoc.createTextNode("Insert"));
    xmlDoc.documentElement.appendChild(node);
    performAjaxRequest({action: 'AjaxRequests/SaveNote.aspx',
        parameters: { action: xmlDoc.documentElement }, //used to be serializeXmlDoc(xmlDoc)
        onSuccess: function(transport){
            var xml = GXml.parse(transport.responseText);
            var success = xml.documentElement.getElementsByTagName("success")[0].firstChild.nodeValue;
            if(success.toLowerCase()=="true"){
                //deleteMarker(note);
                Note.deserializeFromXml(xml.documentElement.getElementsByTagName("Note")[0],{draggable:false},note);
                addMarker(note,undefined,undefined,true);
                if(typeof(functionAfterInsert)!="undefined")
                    functionAfterInsert(note);
            }else{
                alert(xml.documentElement.getElementsByTagName("explanation")[0].firstChild.nodeValue);
            }
        },
        onFailure: function(){
            alert(gMsgGenericAjaxError);
        }
    });
}

//insert the specified route, then call functionAfterInsert with the new route id as its parameter
function insertRoute(route,functionAfterInsert){
    var xmlDoc = GXml.parse("<SaveRouteAction />");
    xmlDoc.documentElement.appendChild(route.serializeToXmlDoc().documentElement);
    node = xmlDoc.createElement("Action");
    node.appendChild(xmlDoc.createTextNode("Insert"));
    xmlDoc.documentElement.appendChild(node);
    console.log(serializeXmlDoc(xmlDoc));
    performAjaxRequest({action:'AjaxRequests/SaveRoute.aspx',
        parameters: { action: xmlDoc.documentElement },
        onSuccess: function(transport){
            var xml = GXml.parse(transport.responseText);
            var success = xml.documentElement.getElementsByTagName("success")[0].firstChild.nodeValue;
            if(success.toLowerCase()=="true"){
                if(typeof(functionAfterInsert)!="undefined")
                    functionAfterInsert(xml.documentElement.getElementsByTagName("id")[0].firstChild.nodeValue);
            }else{
                alert(xml.documentElement.getElementsByTagName("explanation")[0].firstChild.nodeValue);
            }
        },
        onFailure: function(){
            alert(gMsgGenericAjaxError);
        }
    });
}

function addPicture(pnt){
    var old=picturesDisplayed;
    picturesDisplayed=true;
    selectedMarker=addMarker("Picture",pnt);
    adding=true;
    picturesDisplayed=old;
    return selectedMarker;
}

function addFlickrPicture(pnt){
    var old=picturesDisplayed;
    picturesDisplayed=true;
    selectedMarker=addMarker("FlickrPicture",pnt);
    adding=true;
    picturesDisplayed=old;
    return selectedMarker;
}

function addNote(pnt){
    var old=notesDisplayed;
    notesDisplayed=true;
    selectedMarker=addMarker("Note",pnt);
    adding=true;
    notesDisplayed=old;
    return selectedMarker;
}

function addWaypoint(pnt){
    selectedMarker=addMarker("Waypoint",pnt);
    //adding=true;
}

//marker can either be a string representing the type of marker ("Note", "Waypoint", etc)
//or a marker itself, in which case pnt and opts will be ignored
//return the marker that was just added
function addMarker(marker,pnt,opts,doNotAddToMarkerManager){
    var markerType;
    if(typeof(doNotAddToMarkerManager)=="undefined"){
        doNotAddToMarkerManager=false;
    }
    if(typeof(marker) == "string"){
        if(typeof(pnt) == "undefined" || pnt == null) pnt = map.getCenter();
        if(typeof(opts)=="undefined") opts={};
        markerType=marker;
        marker=null;
    }else{
        markerType=marker.className;
    }
    
    switch(markerType){
        case "Waypoint":
            if(marker == null){
                if(typeof(opts.draggable)=="undefined" && routeEditable)
                    opts.draggable=true; //only make this marker editable if the map is editable, and the marker wasn't already set not to be draggable
                marker=new Waypoint(pnt,opts);
                marker.id = nextWaypointId;
                nextWaypointId++;
            }
            waypoints[marker.id + ""]=marker; //it's important that the key be a string
                                              //if it's an int, it will be treated as an array index
            if(! doNotAddToMarkerManager) map.addOverlay(marker); //waypointsMarkerManager.addMarker(marker,0);
            GEvent.trigger(window,"waypointMoved");
            break;
        case "Note":
            if(marker == null){
                if(typeof(opts.draggable)=="undefined")
                    opts.draggable=true; //new notes are always draggable, unless opts is predefined otherwise
                marker=new Note(pnt,opts);
                marker.authorId=currentUserId;
                marker.authorNickName = currentUserNickname;
                marker.routeId=routeId;
                marker.id = newNoteId;
            }
            if(marker.id!=newNoteId)
                notes[marker.id + ""]=marker;
            if(notesDisplayed){
                if(marker.getPoint().lat()!=0){
                    if(! doNotAddToMarkerManager) notesMarkerManager.addMarker(marker,0);
                    console.log("Added noteid = " + marker.id + " to notesMarkerManager");
                }
            }
            GEvent.trigger(window,'noteDiscovered',notes[marker.id + ""]);
            break;
        case "Picture":
            if(marker == null){
                if(typeof(opts.draggable)=="undefined")
                    opts.draggable=true; //new pictures are always draggable, unless opts is predefined otherwise
                marker=new Picture(pnt,opts);
                marker.authorId=currentUserId;
                marker.authorNickName = currentUserNickname;
                marker.id = newPictureId;
            }
            if(marker.id!=newPictureId)
                pictures[marker.id + ""]=marker;
            
            if(picturesDisplayed){
                if(marker.getPoint().lat()!=0){
                    if(! doNotAddToMarkerManager) picturesMarkerManager.addMarker(marker,0);
                    console.log("Added pictureid = " + marker.id + " to picturesMarkerManager");
                }
            }
            GEvent.trigger(window,'pictureDiscovered',pictures[marker.id + ""]);
            break;
        case "FlickrPicture":
            if(marker == null){
                if(typeof(opts.draggable)=="undefined")
                    opts.draggable=true; //new pictures are always draggable, unless opts is predefined otherwise
                marker=new FlickrPicture(pnt,opts);
                marker.id = newFlickrPictureId;
            }
            if(marker.id!=newPictureId)
                flickrPictures[marker.id + ""]=marker;
            
            if(picturesDisplayed){
                if(marker.getPoint().lat()!=0){
                    if(! doNotAddToMarkerManager) flickrPicturesMarkerManager.addMarker(marker,0);
                    console.log("Added flickrPictureid = " + marker.id + " to flickrPicturesMarkerManager");
                }
            }
            GEvent.trigger(window,'flickrPictureDiscovered',pictures[marker.id + ""]);
            break;
        case "SMSMessage":
            if(marker == null){
                if(typeof(opts.draggable)=="undefined")
                    opts.draggable=false; //new SMSMessage are never draggable, unless opts is predefined otherwise
                marker=new SMSMessage(pnt,opts);
                marker.id = newSMSMessageId;
            }
            smsMessages[marker.id + ""]=marker;
            
            if(smsMessagesDisplayed){
                if(marker.getPoint().lat()!=0){
                    if(! doNotAddToMarkerManager) smsMessagesMarkerManager.addMarker(marker,0);
                    console.log("Added smsmessageid = " + marker.id + " to smsMessagesMarkerManager");
                }
            }
            break;
        default:
            console.error("addMarker called with unknown marker type");
            return null;
    }
    //map.addOverlay(selectedMarker);
    addEventListenersToMarker(marker);
    return marker;
}

//get the length of the route (specified by the waypoints) in meters
function getRouteLength(){
    var distance=0;
    var previousWaypoint=null;
    for(var i in waypoints){
        if(previousWaypoint!=null)
            distance+=waypoints[i].getPoint().distanceFrom(previousWaypoint.getPoint());
        previousWaypoint=waypoints[i];
    }
    return distance;
}

//load a route given its routeid
function loadEditableRoute(routeid){
    routeId=routeid;
    routeEditable=true;
    clearEditableRoute();
    
    performAjaxRequest({action: 'AjaxRequests/GetWaypoints.aspx',
        parameters: { routeId: routeid},
        onSuccess: function(transport) {
            var xml = GXml.parse(transport.responseText);
            var success = xml.documentElement.getElementsByTagName("success")[0].firstChild.nodeValue;
            if(success.toLowerCase()=="true"){
                if(xml.documentElement.getElementsByTagName("waypoints").length>0){
                    //some waypoints were returned - yay!
                    var mapPoints = xml.documentElement.getElementsByTagName("waypoints")[0].getElementsByTagName("Waypoint");
                    for (var i = 0; i < mapPoints.length; i++) {
                        var waypoint = Waypoint.deserializeFromXml(mapPoints[i]);
                        addMarker(waypoint);
                    }
                }
                adding=false;
            }else{
                routeId=null;
                routeEditable=false;
                alert(xml.documentElement.getElementsByTagName("explanation")[0].firstChild.nodeValue);
            }
        },
        onFailure: function(){
            alert(gMsgGenericAjaxError);
        }
    });
}

//remove all points in the currently displayed editable route
function clearEditableRoute(){
    for(var i in waypoints){
        map.removeOverlay(waypoints[i]);
        delete waypoints[i];
    }
    waypoints=new Object();
    GEvent.trigger(window,"waypointMoved");
}

function deleteMarker(pushpin){
    console.log("removing marker: " + pushpin.id);
    map.removeOverlay(pushpin);
    var thearray=null;
    var thenewid;
    if(pushpin instanceof Note){
        thenewid=newNoteId;
        thearray=notes;
    }else if(pushpin instanceof Waypoint){
        thearray=waypoints;
    }else if(pushpin instanceof Picture){
        thenewid=newPictureId;
        thearray=pictures;
    }else if(pushpin instanceof FlickrPicture){
        thenewid=newPictureId;
        thearray=pictures;
    }else{
        console.error("Unknown type of marker passed to deleteMarker");
        return false;
    }
    if(typeof(thenewid)=="undefined" || pushpin.id!=thenewid){
        if(typeof(thearray[pushpin.id])=="undefined"){
            console.error("Could not delete marker because it was not found in its array");
            return false;
        }
        delete thearray[pushpin.id];
    }
    
    if(selectedMarker == pushpin)
        selectedMarker=null;
    
    if(pushpin instanceof Waypoint){
        GEvent.trigger(window,"waypointMoved");
    }
    return true;
}

function deleteSelectedMarker(){
    if(selectedMarker == null){
        console.error("Tried to deleted the selected marker, but no marker is selected");
    }else{
        if(deleteMarker(selectedMarker)){
            map.closeInfoWindow();
        }
    }
}

function addEventListenersToMarker(pushpin){
    if(typeof(pushpin.hasEventListeners)=="undefined" || !pushpin.hasEventListeners){
        if(pushpin instanceof Waypoint){
            if(routeEditable){
                GEvent.addListener(pushpin, "dragstart", function() {
                    selectedMarker=pushpin;
                    map.closeInfoWindow();
                });
                GEvent.addListener(pushpin, "dragend", function() {
                    selectedMarker=pushpin;
                    GEvent.trigger(window,"waypointMoved");
                });
                GEvent.addListener(pushpin, "click", function() {
                    selectedMarker = pushpin;
                    pushpin.openInfoWindowHtml("<div>"+gLbDragMarker+"<br /><br /> <a href='#' onclick='deleteSelectedMarker(); return false;'> "+gLbRemoveMarker+" </a></div>",{maxWidth: 100});
                });
            }
        }else if(pushpin instanceof Note){
            GEvent.addListener(pushpin, "dragstart", function() {
                selectedMarker=pushpin;
                map.closeInfoWindow();
            });
            GEvent.addListener(pushpin, "dragend", function() {
                selectedMarker=pushpin;
                GEvent.trigger(selectedMarker,"click",selectedMarker,null);
            });
            GEvent.addListener(pushpin, "infowindowclose",function(){
                if(pushpin.draggingEnabled() && (pushpin.id.toString()==newNoteId.toString())){
                    deleteMarker(pushpin);
                }
            });
            GEvent.addListener(pushpin, "click", function() {
                selectedMarker = pushpin;
                if(pushpin.draggingEnabled() && (pushpin.id.toString()==newNoteId.toString())){
                    //the note is editable
                    //this is a new note
                    var html = "<b>";
                    html += gLbAddNote+"</b><br /><input type='text' id='noteTextBox' value='" + selectedMarker.text + "' />";
                    html += "<br /><div class='submit'><input class='actBtn' type='button' value='"+gLbSave+"' onclick='selectedMarker.text=noteTextBox.value;insertNote(selectedMarker,function(note){ note.disableDragging();map.closeInfoWindow(); });' />";
                    html += "</div>";
                    pushpin.openInfoWindowHtml(html,{maxWidth: 400});
                }
                else{
                    //this is a note that already exists
                    pushpin.openInfoWindowHtml("<div class='noteiw infowindow'><p>" + pushpin.text + "</p><div class='byline'>"+ gLbLeftby + "<a href=\"#\" onclick=\"DisplayProfile("+ pushpin.authorId +"); return false;\">"+ pushpin.authorNickName +"</a> " + pushpin.updatedDesc + "</div></div>",{maxWidth: 400});
                }
            });
        }else if(pushpin instanceof SMSMessage){
            GEvent.addListener(pushpin, "dragstart", function() {
                selectedMarker=pushpin;
                map.closeInfoWindow();
            });
            GEvent.addListener(pushpin, "dragend", function() {
                selectedMarker=pushpin;
                GEvent.trigger(selectedMarker,"click",selectedMarker,null);
            });
            GEvent.addListener(pushpin, "click", function() {
                selectedMarker = pushpin;
                if(pushpin.draggingEnabled()){
                    //the SMSMessage is editable
                    pushpin.openInfoWindowHtml("Well, this should never happen...");
                    console.error("An SMS message is editable?!");
                }else{
                    pushpin.openInfoWindowHtml("SMS: " + pushpin.message,{maxWidth: 400});
                }
            });
        }else if(pushpin instanceof Picture){
            GEvent.addListener(pushpin, "dragstart", function() {
                selectedMarker=pushpin;
                map.closeInfoWindow();
            });
            GEvent.addListener(pushpin, "dragend", function() {
                selectedMarker=pushpin;
                GEvent.trigger(selectedMarker,"click",selectedMarker,null);
            });
            GEvent.addListener(pushpin, "infowindowclose",function(){
                console.log("infowindowclose " + pushpin.id);
                if(pushpin.draggingEnabled() && (pushpin.id.toString()==newPictureId.toString())){
                    deleteMarker(pushpin);
                }
            });
            GEvent.addListener(pushpin, "click", function() {
                selectedMarker = pushpin;
                if(pushpin.draggingEnabled() && (pushpin.id.toString()==newPictureId.toString())){
                    //the picture is editable
                    //this is a new picture
                    /*var html="<form id='savePictureForm' action='AjaxRequests/SavePicture.aspx' target='newPictureFrame' method='post' enctype='multipart/form-data'>";
                    html+="<input type='hidden' name='javascript' value='true' />";
                    html+="<input type='hidden' name='action' /><b>"+gLbAddPicture+"</b><br />";
                    html+="<a href='#' onclick='newMarker=addMarker(\"FlickrPicture\",new GLatLng(" + selectedMarker.getPoint().lat() + "," + selectedMarker.getPoint().lng() + ")); deleteSelectedMarker(); selectedMarker = newMarker; GEvent.trigger(selectedMarker,\"click\",selectedMarker,null); return false;'>"+gLbUseFlickr+"</a><br/>";
                    html+="<input type='file' name='picture' />";
                    html+="<br />"+gLbTitle+"<br/><input type='text' id='pictureTitleBox' value='" + selectedMarker.title + "' /><br/>";
                    html+="<div class='submit'><input class='actBtn' type='button' value='"+gLbSave+"' onclick='selectedMarker.title=$(\"pictureTitleBox\").value;insertPicture($(\"savePictureForm\"),selectedMarker,function(picture){ picture.disableDragging();map.closeInfoWindow(); });' />";
                    html+="</div>";
                    html+="</form>";*/
                    
                    var html="<b>"+gLbAddPicture+"</b>";
                    html+="<br /><a href='#' onclick='oldMarker=selectedMarker;addFlickrPicture(new GLatLng(" + selectedMarker.getPoint().lat() + "," + selectedMarker.getPoint().lng() + ")); deleteMarker(oldMarker); GEvent.trigger(selectedMarker,\"click\",selectedMarker,null); return false;'>"+gLbUseFlickr+"</a><br/>";
                    html+="<div id='pictureUploadDiv'><iframe id='pictureUploadIFrame' src='AjaxRequests/PictureUpload.aspx' frameborder='0' style='height: 100px;width: 250px;' ></iframe></div>";
                    pushpin.openInfoWindowHtml(html,{maxWidth: 400});
                    
                    
                }else{
                    //this is a picture that already exists
                    pushpin.openInfoWindowHtml("<div class='pictureiw infowindow'><h4>" + pushpin.title + "</h4><div style=' height:118px; width:220px;'><img src='" + pushpin.getThumbnailUrl() + "' /></div><div class='byline'>" + gLbLeftby + "<a href=\"#\" onclick=\"DisplayProfile("+ pushpin.authorId +"); return false;\">"+ pushpin.authorNickName +"</a> " + pushpin.updatedDesc + "</div></div>");
                }
            });
        }else if(pushpin instanceof FlickrPicture){
            GEvent.addListener(pushpin, "dragstart", function() {
                selectedMarker=pushpin;
                map.closeInfoWindow();
            });
            GEvent.addListener(pushpin, "dragend", function() {
                selectedMarker=pushpin;
                GEvent.trigger(selectedMarker,"click",selectedMarker,null);
            });
            GEvent.addListener(pushpin, "infowindowclose",function(){
                if(pushpin.draggingEnabled() && pushpin.id==newPictureId){
                    deleteMarker(pushpin);
                }
            });
            GEvent.addListener(pushpin, "click", function() {
                selectedMarker = pushpin;
                if(pushpin.draggingEnabled() && selectedMarker.id==newPictureId){
                    //the picture is editable
                    //this is a new picture
                    /*var html="<form id='savePictureForm' action='AjaxRequests/SaveFlickrPicture.aspx' target='newPictureFrame' method='post' enctype='multipart/form-data'>";
                    html+="<input type='hidden' name='javascript' value='true' />";
                    html+="<input type='hidden' name='action' /><b>"+gLbAddPictureFromFlickr+"</b><br /><input type='file' name='picture' />";
                    html+="<br />"+gLbTitle+"<br/><input type='text' id='pictureTitleBox' value='" + selectedMarker.title + "' /><br/>";
                    html+="<div class='submit'><input class='actBtn' type='button' value='"+gLbSave+"' onclick='selectedMarker.title=$(\"pictureTitleBox\").value;insertFlickrPicture($(\"savePictureForm\"),selectedMarker,function(flickrPicture){ flickrPicture.disableDragging();map.closeInfoWindow(); });' />";
                    html+="</div>";
                    html+="</form>";*/
                    
                    
                    var html="<b>"+gLbAddPictureFromFlickr+"</b>";
                    html+="<div id='pictureUploadDiv'><iframe id='pictureUploadIFrame' src='AjaxRequests/FlickrPictureUpload.aspx' frameborder='0' style='height: 100px;width: 250px;' ></iframe></div>";
                    pushpin.openInfoWindowHtml(html,{maxWidth: 400});
                    
                    var html2="<a href='#' onclick='openFlickrFrame(); return false;'>"+gLbAuthorizeFlickr+"</a>";
                    if(currentUserFlickrId==null)
                        pushpin.openInfoWindowHtml(html2);
                    else
                        pushpin.openInfoWindowHtml(html);
                }else{
                    //this is a picture that already exists
                    pushpin.openInfoWindowHtml("<div class='pictureiw infowindow'><h4>" + pushpin.title + "</h4><div style=' height:240px; width:240px;'><a target='_blank' href='" + pushpin.url + "'><img src='" + pushpin.getThumbnailUrl() + "' /></a><br/><a target='_blank' href='" + pushpin.url + "'><img src='Images/imagesharedonflickr.gif' width='132' height='14'/></a></div>");
                   
                }
            });
        }
        pushpin.hasEventListeners=true;
    }
}

function handleKey(keyCode){
    if(keyCode == 27) {
        if(adding){
            deleteSelectedMarker();
            adding=false;
        }
    }
}

function isBrowserCompatible(){
    if(GBrowserIsCompatible()){
        return true;
    }else{
        return false;
        alert(gMsgIncompatibleBrowser);
    }
}

//get info for a route, then call functionAfterRetrieve with the id of the route after the info is retrieved
function getRouteInfo(routeid,functionAfterRetrieve){
    performAjaxRequest({
        action: 'AjaxRequests/GetRoutes.aspx',
        parameters: { routeid: routeid },
        onSuccess: function(transport){
            var xml = GXml.parse(transport.responseText);
            var success = xml.documentElement.getElementsByTagName("success")[0].firstChild.nodeValue;
            if(success.toLowerCase()=="true"){
                //if(force)
                //   visibleRoutesCount = xml.documentElement.getElementsByTagName("resultCount")[0].firstChild.nodeValue;
                var newlyVisibleRouteIds=[];
                var routeTag = xml.documentElement.getElementsByTagName("Route")[0];
                var routeId=routeTag.getElementsByTagName("Id")[0].firstChild.nodeValue;
                if(typeof(routes[routeId + ""]=="undefined")) routes[routeId + ""]=Route.deserializeFromXml(routeTag);
                if(typeof(functionAfterRetrieve) != "undefined" ) functionAfterRetrieve(routeId);
            }else{
                alert(xml.documentElement.getElementsByTagName("explanation")[0].firstChild.nodeValue);
            }
        },
        onFailure: function(){
            alert(gMsgGenericAjaxError);
        }
    });
}

//draw the (non-editable) route specified
function drawRoute(routeid, color){
    if(routeOverlays[routeid]){
        console.log("drawRoute routeid=" + routeid + " is already displayed");
    }else{
        console.log("the route isn't currently displayed");
        // this was:   if(routes[routeid + ""].points==null)
        // but i changed it because when the route isnt found,
        // the if will fail before it even looks for the point property
        // -paul
        if(typeof(routes[routeid + ""])=="undefined"){
            //we don't have anything for this route.. so we need to get its information
            getRouteInfo(routeid,function(a){drawRoute(a,color)});
        }else{
            if(routes[routeid + ""].points==null){
                //cache miss
                console.log("drawRoute routeid=" + routeid + " had a cache miss");
                performAjaxRequest({
                    action: 'AjaxRequests/GetWaypoints.aspx',
                    parameters: { routeId: routeid },
                    onSuccess: function(transport) {
                        var xml = GXml.parse(transport.responseText);
                        var success = xml.documentElement.getElementsByTagName("success")[0].firstChild.nodeValue;
                        if(success.toLowerCase()=="true"){
                            if(xml.documentElement.getElementsByTagName("waypoints").length>0){
                                //some waypoints were returned - yay!
                                var points=[];
                                var mapPoints = xml.documentElement.getElementsByTagName("waypoints")[0].getElementsByTagName("Waypoint");
                                for (var i = 0; i < mapPoints.length; i++) {
                                    var waypoint = Waypoint.deserializeFromXml(mapPoints[i]);
                                    points.push(waypoint.getPoint());
                                }
                                routes[routeid + ""].points=points;
                                drawRoute(routeid,color);
                            }
                        }else{
                            alert(xml.documentElement.getElementsByTagName("explanation")[0].firstChild.nodeValue);
                        }
                    },
                    onFailure: function(){
                        alert(gMsgGenericAjaxError);
                    }
                });
            }else{
                //cache hit
                if(typeof(routeOverlays[routeid])=="undefined"){
                    console.log("drawRoute routeid=" + routeid + " had a cache hit, it is being draw in the color: " + color);
                    routeOverlays[routeid]=new GPolyline(routes[routeid + ""].points,color, 5, .8); // width and opacity
                    map.addOverlay(routeOverlays[routeid]);
                    GEvent.trigger(window,'routeVisibilityChanged',routeid,true);
                }
            }
        }
    }
}

function drawVisibleSMSMessages(){
    if(smsMessagesDisplayed){
        console.log("drawVisibleSMSMessages");
        var bounds = map.getBounds();
        var southWest = bounds.getSouthWest();
        var northEast = bounds.getNorthEast();
        getSMSMessagesByCriteria({ south: southWest.lat(), east: northEast.lng(), north: northEast.lat() , west: southWest.lng() });
    }
}

function getSMSMessagesByCriteria(criteria,functionAfter){
    performAjaxRequest({action:'AjaxRequests/GetSMSMessages.aspx',
        parameters: criteria,
        onSuccess: function(transport){
            var xml = GXml.parse(transport.responseText);
            var success = xml.documentElement.getElementsByTagName("success")[0].firstChild.nodeValue;
            if(success.toLowerCase()=="true"){
                console.log("Returned " + xml.documentElement.getElementsByTagName("SMSMessage").length + " SMSMessages.");
                for(var i=0; i<xml.documentElement.getElementsByTagName("SMSMessage").length;i++){
                    var tag = xml.documentElement.getElementsByTagName("SMSMessage")[i];
                    var id = tag.getElementsByTagName("Id")[0].firstChild.nodeValue
                    var smsMessage;
                    if(typeof(pictures[id + ""])=="undefined"){
                        console.log("SMSMessage id=" + id + " was unknown before now");
                        smsMessage=SMSMessage.deserializeFromXml(tag,{draggable:false});
                        addMarker(smsMessage);
                    }
                    GEvent.trigger(window,'smsMessageVisibilityChanged',id,true);
                if(typeof(functionAfter)!="undefined")
                    functionAfter();
                }

            }else{
                alert(xml.documentElement.getElementsByTagName("explanation")[0].firstChild.nodeValue);
            }
        },
        onFailure: function(){
            alert(gMsgGenericAjaxError);
        }
    });
}

function drawVisiblePictures(){
    if(! picturesDisplayed) return;
    console.log("drawVisiblePictures");
    var bounds = map.getBounds();
    var southWest = bounds.getSouthWest();
    var northEast = bounds.getNorthEast();
    performAjaxRequest({action: 'AjaxRequests/GetPictures.aspx',
        parameters: { south: southWest.lat(), east: northEast.lng(), north: northEast.lat() , west: southWest.lng() },
        onSuccess: function(transport){
            var xml = GXml.parse(transport.responseText);
            var success = xml.documentElement.getElementsByTagName("success")[0].firstChild.nodeValue;
            if(success.toLowerCase()=="true"){
                console.log("Returned " + xml.documentElement.getElementsByTagName("Picture").length + " pictures.");
                for(var i=0; i<xml.documentElement.getElementsByTagName("Picture").length;i++){
                    var pictureTag = xml.documentElement.getElementsByTagName("Picture")[i];
                    var id = pictureTag.getElementsByTagName("Id")[0].firstChild.nodeValue
                    var picture;
                    if(typeof(pictures[id + ""])=="undefined"){
                        console.log("Picture id=" + id + " was unknown before now");
                        picture=Picture.deserializeFromXml(pictureTag,{draggable:false});
                        addMarker(picture);
                    }
                    console.log("Picture id=" + id + " is within the viewport");
                    GEvent.trigger(window,'pictureDiscovered',pictures[id + ""]);
                }

            }else{
                alert(xml.documentElement.getElementsByTagName("explanation")[0].firstChild.nodeValue);
            }
        },
        onFailure: function(){
            alert(gMsgGenericAjaxError);
        }
    });
}

function drawVisibleFlickrPictures(){
    if(picturesDisplayed){
        console.log("drawVisibleFlickrPictures");
        var bounds = map.getBounds();
        var southWest = bounds.getSouthWest();
        var northEast = bounds.getNorthEast();
        getFlickrPicturesByCriteria({ south: southWest.lat(), east: northEast.lng(), north: northEast.lat(), west: southWest.lng()});
    }
}

function getFlickrPicturesByCriteria(criteria,functionAfter){
    //setTimeout(function() {
        new Ajax.Request('AjaxRequests/GetFlickrPictures.aspx',
            {
            parameters: criteria,
            onSuccess: function(transport){
                var xml = GXml.parse(transport.responseText);
                var success = xml.documentElement.getElementsByTagName("success")[0].firstChild.nodeValue;
                if(success.toLowerCase()=="true"){
                    console.log("Returned " + xml.documentElement.getElementsByTagName("FlickrPicture").length + " FlickrPictures.");
                    for(var i=0; i<xml.documentElement.getElementsByTagName("FlickrPicture").length;i++){
                        var tag = xml.documentElement.getElementsByTagName("FlickrPicture")[i];
                        var id = tag.getElementsByTagName("Id")[0].firstChild.nodeValue
                        var o;
                        if(typeof(flickrPictures[id + ""])=="undefined"){
                            console.log("flickrPicture id=" + id + " was unknown before now");
                            o=FlickrPicture.deserializeFromXml(tag,{draggable:false});
                            addMarker(o);
                        }
                        console.log("flickrPicture id=" + id + " is within the viewport");
                        GEvent.trigger(window,'flickrPictureDiscovered',flickrPictures[id + ""]);
                    }
                    if(typeof(functionAfter)!="undefined")
                        functionAfter();
                }else{
                    alert(xml.documentElement.getElementsByTagName("explanation")[0].firstChild.nodeValue);
                }
            },
            onFailure: function(){
                alert(gMsgGenericAjaxError);
            }
        });
    
    //},ajaxSchedulerInterval+10);
}

function drawVisibleNotes(){
    if(notesDisplayed){
        console.log("drawVisibleNotes");
        var bounds = map.getBounds();
        var southWest = bounds.getSouthWest();
        var northEast = bounds.getNorthEast();
        
        var myNoteCriteria = noteCriteria;
        if(typeof(myNoteCriteria["routeid"])=="undefined"){
            myNoteCriteria["south"] = southWest.lat();
            myNoteCriteria["north"]=northEast.lat();
            myNoteCriteria["east"]=northEast.lng();
            myNoteCriteria["west"]=southWest.lng();
        }
        
        getNotesByCriteria(myNoteCriteria);
    }
}

function getNotesByCriteria(criteria,functionAfter){
    performAjaxRequest({action:'AjaxRequests/GetNotes.aspx',
        parameters: criteria,
        onSuccess: function(transport){
            var xml = GXml.parse(transport.responseText);
            var success = xml.documentElement.getElementsByTagName("success")[0].firstChild.nodeValue;
            if(success.toLowerCase()=="true"){
                console.log("Returned " + xml.documentElement.getElementsByTagName("Note").length + " notes.");
                for(var i=0; i<xml.documentElement.getElementsByTagName("Note").length;i++){
                    var noteTag = xml.documentElement.getElementsByTagName("Note")[i];
                    var id = noteTag.getElementsByTagName("Id")[0].firstChild.nodeValue
                    var note;
                    if(typeof(notes[id + ""])=="undefined"){
                        console.log("Note id=" + id + " was unknown before now");
                        note=Note.deserializeFromXml(noteTag,{draggable:false});
                        addMarker(note);
                    }
                    console.log("Note id=" + id + " is within the viewport");
                    GEvent.trigger(window,'noteDiscovered',notes[id + ""]);
                }
                if(typeof(functionAfter)!="undefined")
                    functionAfter();
            }else{
                alert(xml.documentElement.getElementsByTagName("explanation")[0].firstChild.nodeValue);
            }
        },
        onFailure: function(){
            alert(gMsgGenericAjaxError);
        }
    });
}

function getCitiesByCriteria(criteria,functionAfter){
   performAjaxRequest({action:'AjaxRequests/GetCities.aspx',
        parameters: criteria,
        onSuccess: function(transport){
            var xml = GXml.parse(transport.responseText);
            var success = xml.documentElement.getElementsByTagName("success")[0].firstChild.nodeValue;
            if(success.toLowerCase()=="true"){
                console.log("Returned " + xml.documentElement.getElementsByTagName("City").length + " cities.");
                for(var i=0; i<xml.documentElement.getElementsByTagName("City").length;i++){
                    var tag = xml.documentElement.getElementsByTagName("City")[i];
                    var id = tag.getElementsByTagName("Id")[0].firstChild.nodeValue;
                    var o;
                    if(typeof(cities[id + ""])=="undefined"){
                        console.log("City id=" + id + " was unknown before now");
                        o=City.deserializeFromXml(tag);
                        cities[o.id + ""]=o;
                        GEvent.trigger(window,'cityDiscovered',cities[id + ""]);
                    }
                }
                if(typeof(functionAfter)!="undefined")
                    functionAfter();
            }else{
                alert(xml.documentElement.getElementsByTagName("explanation")[0].firstChild.nodeValue);
            }
        },
        onFailure: function(){
            alert(gMsgGenericAjaxError);
        }
    });
}

function getUserById(id,functionAfter){
    if(typeof(users[id + ""])=="undefined"){
        getUsersByCriteria({'id': id},functionAfter);
    }else{
        functionAfter(users[id + ""]);
    }
}

function getUsersByCriteria(criteria,functionAfter){
    performAjaxRequest({action: 'AjaxRequests/GetUsers.aspx',
        parameters: criteria,
        onSuccess: function(transport){
            var xml = GXml.parse(transport.responseText);
            var success = xml.documentElement.getElementsByTagName("success")[0].firstChild.nodeValue;
            if(success.toLowerCase()=="true"){
                console.log("Returned " + xml.documentElement.getElementsByTagName("User").length + " users.");
                for(var i=0; i<xml.documentElement.getElementsByTagName("User").length;i++){
                    var userTag = xml.documentElement.getElementsByTagName("User")[i];
                    var id = userTag.getElementsByTagName("Id")[0].firstChild.nodeValue
                    var user;
                    if(typeof(users[id + ""])=="undefined"){
                        console.log("User id=" + id + " was unknown before now");
                        users[id + ""]=User.deserializeFromXml(userTag);
                    }
                }
                if(typeof(functionAfter)!="undefined")
                    functionAfter(users[id + ""]);
            }else{
                alert(xml.documentElement.getElementsByTagName("explanation")[0].firstChild.nodeValue);
            }
        },
        onFailure: function(){
            alert(gMsgGenericAjaxError);
        }
    });
}

function drawVisibleRoutes(){
    if(! routesDisplayed){
        visibleRoutesCount=0;
    }else{
        console.log("drawing visible routes");
        getVisibleRoutes(function(){ setVisibleRoutesPage(1); });
    }
}

function getVisibleRoutes(functionAfter){
    var bounds = map.getBounds();
    var southWest = bounds.getSouthWest();
    var northEast = bounds.getNorthEast();
    
    var myRouteCriteria = routeCriteria;
    if(typeof(myRouteCriteria["routeid"])=="undefined"){
        myRouteCriteria["south"] = southWest.lat();
        myRouteCriteria["north"]=northEast.lat();
        myRouteCriteria["east"]=northEast.lng();
        myRouteCriteria["west"]=southWest.lng();
    }
    getRoutesByCriteria( myRouteCriteria ,functionAfter );
}

function getRoutesByCriteria(criteria,functionAfter){
    performAjaxRequest({action:'AjaxRequests/GetRoutes.aspx',
        parameters: criteria,
        onSuccess: function(transport){
            var xml = GXml.parse(transport.responseText);
            var success = xml.documentElement.getElementsByTagName("success")[0].firstChild.nodeValue;
            if(success.toLowerCase()=="true"){
                routesResultSet=[];
                for(var i=0; i<xml.documentElement.getElementsByTagName("Route").length;i++){
                    var routeTag = xml.documentElement.getElementsByTagName("Route")[i];
                    var routeId=parseInt(routeTag.getElementsByTagName("Id")[0].firstChild.nodeValue);
                    if(typeof(routes[routeId + ""])=="undefined") routes[routeId + ""]=Route.deserializeFromXml(routeTag);
                    routesResultSet.push(routes[routeId + ""]);
                }
                if(typeof(functionAfter)!="undefined") functionAfter();
                    
            }else{
                alert(xml.documentElement.getElementsByTagName("explanation")[0].firstChild.nodeValue);
            }
        },
        onFailure: function(){
            alert(gMsgGenericAjaxError);
        }
    });
}

function setVisibleRoutesPage(newpage){
    visibleRoutesPage=newpage;
    //clear the visibleRoutes array
    for(var i=0;i<visibleRoutes.length;i++){
		//WAS TEAM
		if (visibleRoutes[i].id != null)
        {
			map.removeOverlay(routeOverlays[visibleRoutes[i].id + ""]);
	        delete routeOverlays[visibleRoutes[i].id + ""];
	        //free up the spot in visibleRoutes
	        var temp = visibleRoutes[i].id;
	        visibleRoutes[i].id=null;
	        GEvent.trigger(window,'routeVisibilityChanged',temp,false);
		}
    }
    
    var i=0;
    var upperBounds = newpage * visibleRoutes.length;
    if(upperBounds > routesResultSet.length) upperBounds = routesResultSet.length;
    for(var j=(newpage-1) * visibleRoutes.length;j<upperBounds;j++){
        console.log("i=" + i + " j=" + j);
        visibleRoutes[i].id=routesResultSet[j].id;
        console.log("routeid=" + routesResultSet[j].id + " was not visible, but is now, drawing it");
        drawRoute(visibleRoutes[i].id,visibleRoutes[i].color);
        i++;
        i%=visibleRoutes.length;
    }
}

function displayNewLocation() { 

	$('browselocationerror').innerHTML = "<img src='Images/indicator.gif'/>";
	
	showAddress( $F('browselocation'),function(point) {
		if (!point)
			$('browselocationerror').innerHTML = msgLocationNotFound; 
		else { 
			setMyLocationCookie(point);
			map.setCenter(point, 13); // TODO: hardcoded zoom level??!?! 
			$('browselocationerror').innerHTML = '';
		}
	});

}

function yahooGeocoder(address,onSuccess,onFailure){
    performAjaxRequest({action: 'AjaxRequests/YahooGeocoder.aspx',
        parameters: { appid: yahooAppId, location: address },
        onSuccess: function(transport){
            console.log(transport.responseText);
            var xml = GXml.parse(transport.responseText);
            var result = xml.documentElement.getElementsByTagName("Result")[0];
            if(result){
                var lat = result.getElementsByTagName("Latitude")[0].firstChild.nodeValue;
                var lng = result.getElementsByTagName("Longitude")[0].firstChild.nodeValue;
                onSuccess(new GLatLng(lat,lng));
            }else{
                onSuccess();
            }
        },
        onFailure: onFailure
    });
}

//move the map to the address specified (address could be 'Cambridge, MA' for example)
function showAddress(address,functionAfter){
    if (address == '')
    {
        alert(gMsgEmptyAddress);
        return false;
    }
    
    var geocoder = new GClientGeocoder();
    geocoder.getLatLng(address,
        function(point) {
                if (!point){
                    //If the google geocoder can't find the location, try the Yahoo! Geocoder
                    yahooGeocoder(
                        address,
                        function(point){
                            if(point){
                                console.log("Yahoo! found the location");
                                functionAfter(point);
                            }else{
                                //the address wasn't found
                                alert(address + gMsgNotFound);
                                $('browselocationerror').innerHTML = msgLocationNotFound; 
                            }
                        },
                        function(){
                            //there was an error with the yahoo service
                            alert(address + gMsgNotFound);
                            $('browselocationerror').innerHTML = msgLocationNotFound; 
                        }
                    );
                }else{
                    console.log("Google found the location");
                    functionAfter(point);
                }
        }
    );
}

function setMyLocationCookie(point) {
	
	var date = new Date();
	var value;
	// expires 30 years from now
	date.setTime(date.getTime()+(30*365*24*60*60*1000));
	var expires = "; expires="+date.toGMTString();

	//removing parentesis
    value = point.y+"~ "+point.x

	document.cookie = "RunEasy_MyLocation="+value+expires+"; path=/";
}

function addNaver(){
    var copyright = new GCopyright(1, new GLatLngBounds(new GLatLng(-90, -180), new GLatLng(90, 180)),0,"Naver.com");
    var copyrightCollection = new GCopyrightCollection('Naver');
    copyrightCollection.addCopyright(copyright);
    var tilelayers = [new GTileLayer(copyrightCollection , 7, 11)];
    tilelayers[0].getTileUrl = function CustomGetTileUrl(point,zoom) {
        console.log(point);
	    var z = 18 - zoom;
	    var x=point.x;
	    var y=point.y;
	    
	    //x=x-108*(12-z);
	    //y=51*(12-z)-y;
	    
	    
        switch (z)
        {
            case 11:
                x = x - 108;
                y = 51 - y;
                break;
            case 10:
                x = x - 216;
                y = 103 - y;
                break;
            case 9:
                x = x - 431;
                y = 207 - y;
                break;
            case 8:
                x = x - 861;
                y = 416 - y;
                break;
            case 7:
                x = x - 1728;
                y = 825 - y;
                break;
        }
        

	    var f="http://imap.local.naver.com/themap/LEVEL" + padDigits(z,2) + "/00000-00000/" + padDigits(x,5) + "-" + padDigits(y,5) + ".png";
	    console.log(f);
	    return f;
	    
	    /*var f = "NaverTile.aspx?zoom=" + zoom + "&x=" + point.x + "&y=" + point.y;
	    console.log(f);
	    console.log("x=" + point.x + " y=" + point.y + " zoom=" + zoom);
	    return f;*/
    }
    var custommap = new GMapType(tilelayers, new GMercatorProjection(12), "Naver", {errorMessage:"No data available"});
    map.addMapType(custommap);
}

function padDigits(n, totalDigits) 
{ 
    n = n.toString(); 
    var pd = ''; 
    if (totalDigits > n.length) 
    { 
        for (i=0; i < (totalDigits-n.length); i++) 
        { 
            pd += '0'; 
        } 
    } 
    return pd + n.toString(); 
}

// add a control to the bottom right corner of the map
// takes three booleans
function addCornerControl(note,picture,runlength){
	  if(note){
	    $('addNoteDiv').style.display="";
		$('BRcornerDiv').style.display="";
	  }
	  if(picture){
	    $('addPictureDiv').style.display="";
	    $('BRcornerDiv').style.display="";
	  }
	  if(runlength){
	    $('runLengthDiv').style.display="";
	    var updateRouteLength = function () {
	        if(routeId==null){
	            $('runLengthDiv').innerHTML=Route.displayDistance(getRouteLength());
	        }else{
	            if(typeof(routes[routeId])=="undefined"){
	                $('runLengthDiv').innerHTML=Route.displayDistance(0);
	            }else{
	                $('runLengthDiv').innerHTML=routes[routeId].displayDistance();
	            }
	        }
	    };
	    GEvent.addDomListener(window, 'routeVisibilityChanged', updateRouteLength);
	    GEvent.addDomListener(window, 'waypointMoved', updateRouteLength);
	    updateRouteLength();
	    $('BLcornerDiv').style.display="";
	  }

	  
}


// A TextualZoomControl is a GControl that displays textual "Zoom In"
// and "Zoom Out" buttons (as opposed to the iconic buttons used in
// Google Maps).
function TextualZoomControl() {
}
	TextualZoomControl.prototype = new GControl();
	
	// Creates a one DIV for each of the buttons and places them in a container
	// DIV which is returned as our control element. We add the control to
	// to the map container and return the element for the map class to
	// position properly.
	TextualZoomControl.prototype.initialize = function(map) {
	  var container = document.createElement("div");
	  container.style.backgroundImage = "url('Images/zoomcontrol.png')";
	  container.style.height = '204px';
	  container.style.width = '54px';
	  container.style.position = 'relative';
	
	  var zoomInDiv = document.createElement("div");
	  this.setButtonStyle_(zoomInDiv);
	  zoomInDiv.style.top = '63px';
	  zoomInDiv.style.left = '15px';
	  container.appendChild(zoomInDiv);
	  GEvent.addDomListener(zoomInDiv, "click", function() {
		map.zoomIn();
	  });
	
	  var zoomOutDiv = document.createElement("div");
	  this.setButtonStyle_(zoomOutDiv);
	  zoomOutDiv.style.top = '174px';
	  zoomOutDiv.style.left = '15px';
	  container.appendChild(zoomOutDiv);
	   GEvent.addDomListener(zoomOutDiv, "click", function() {
		map.zoomOut();
	  });
	
	  map.getContainer().appendChild(container);
	  return container;
	}
	
	// By default, the control will appear in the top left corner of the
	// map with 7 pixels of padding.
	TextualZoomControl.prototype.getDefaultPosition = function() {
	  return new GControlPosition(G_ANCHOR_TOP_LEFT, new GSize(7, 7));
	}
	
	// Sets the proper CSS for the given button element.
	TextualZoomControl.prototype.setButtonStyle_ = function(button) {
	  button.style.position = "absolute";
	  button.style.width = "22px";
	  button.style.height = "22px";
	  button.style.cursor = "pointer";
	}

function addAjaxSpinnerToMap(){
	var myGlobalHandlers = {
		onCreate: function(){
		    $('mapspinner').show();
		    //Effect.Appear($('mapspinner'),{duration:0.2});
			
		},

		onComplete: function() {
			if(Ajax.activeRequestCount == 0){
				Effect.Fade($('mapspinner'),{duration:0.5});
			
			}
			console.log("Ajax requests in progress: " + Ajax.activeRequestCount);
		}
	};

	Ajax.Responders.register(myGlobalHandlers);
}

function doPageLoad(mapType,bounds,centerPoint,zoom){
    console.log("doPageLoad");
    if(isBrowserCompatible()){
        
        /*
        //the iframe is used to upload pictures asynchronously
        var iFrameID  = 'randomIFrame';
        var myIFrame = document.createElement('iframe');
        myIFrame.setAttribute('src', 'about:blank');
        myIFrame.setAttribute('id', iFrameID);
        myIFrame.setAttribute('NAME', iFrameID);
        myIFrame.style.display = 'none';
        document.body.appendChild(myIFrame);
        if(self.frames[iFrameID].name != iFrameID) {
            // *** IMPORTANT: This is a WORKAROUND for a BUG in Internet Explorer ***
            self.frames[iFrameID].name = iFrameID;
        }
        */
        map = new GMap2(document.getElementById("map"));
        if(largeMapControlDisplayed)
            map.addControl(new GLargeMapControl(0,1));
        if(mapTypeControlDisplayed)
            map.addControl(new GMapTypeControl(1));
        //map.addControl(new TextualZoomControl());
        if(overviewMapControlDisplayed)
            map.addControl(new GOverviewMapControl());
        //map.addControl(new GScaleControl(300));
        new GKeyboardHandler(map);
        addAjaxSpinnerToMap();
        
        if(bounds==null){
            map.setCenter(centerPoint,zoom);
        }else{
             map.setCenter(bounds.getCenter());
             map.setZoom(map.getBoundsZoomLevel(bounds));
        }
        map.setMapType(mapType);
        //waypointsMarkerManager = new GMarkerManager(map, {trackMarkers: true} );
        notesMarkerManager = new GMarkerManager(map, {trackMarkers: true}  );
        picturesMarkerManager = new GMarkerManager(map, {trackMarkers: true} );
        flickrPicturesMarkerManager = new GMarkerManager(map, {trackMarkers: true} );
        smsMessagesMarkerManager = new GMarkerManager(map, {trackMarkers: true} );
        
        GEvent.addDomListener(document, "keyup", function(e) {
            if(e.keyCode == 27) {
                if(adding){
                    deleteSelectedMarker();
                    adding=false;
                }
            }
        } );
        
        GEvent.addListener(map, "mouseover", function(edge){
            /*if(adding){
                selectedMarker.setPoint(edge);
            }*/
            GEvent.trigger(map,"mousemove",edge);
        });

        GEvent.addListener(map, "mousemove", function(pnt){
            /*if(adding){
                selectedMarker.setPoint(pnt);
                if(selectedMarker instanceof Waypoint){
                    GEvent.trigger(window,"waypointMoved");
                }
            }*/
            if(adding && !(selectedMarker instanceof Waypoint)){
                selectedMarker.setPoint(pnt);
            }
        });
        
        GEvent.addListener(map, "click", function(marker,pnt){
            /*if(pnt==null){
                if(adding){
                    adding=false;
                    if(selectedMarker instanceof Note || selectedMarker instanceof Picture)
                        GEvent.trigger(selectedMarker,"click",selectedMarker,null);
                }
            }else{
                if(routeEditable){
                    addWaypoint(pnt); //the user can click to add Waypoints if the map is editable
                }
            }*/
            if(adding){
                adding=false;
                if(selectedMarker instanceof Note || selectedMarker instanceof Picture)
                    GEvent.trigger(selectedMarker,"click",selectedMarker,null);
            }else{
                if(pnt!=null && routeEditable){
                    addWaypoint(pnt); //the user can click to add Waypoints if the map is editable
                }
            }
        });
        
        GEvent.addListener(window,"waypointMoved",drawWaypointLines);
        GEvent.addListener(map,"moveend",drawVisibleRoutes);
        GEvent.addListener(map,"zoomend",drawVisibleRoutes);
        
        GEvent.addListener(map,"moveend",drawVisibleNotes);
        GEvent.addListener(map,"zoomend",drawVisibleNotes);
        
        GEvent.addListener(map,"moveend",drawVisibleSMSMessages);
        GEvent.addListener(map,"zoomend",drawVisibleSMSMessages);
        
        GEvent.addListener(map,"moveend",drawVisiblePictures);
        GEvent.addListener(map,"zoomend",drawVisiblePictures);
        
        /*GEvent.addListener(map,"moveend",drawVisibleFlickrPictures);
        GEvent.addListener(map,"zoomend",drawVisibleFlickrPictures);*/
        
        GEvent.addListener(window, 'waypointMoved', function(){if(routeId!=null) routes[routeId].distance=getRouteLength();});

        //give the the rest of the functions a chance to be called before drawing the items on the map
        setTimeout(function(){GEvent.trigger(map,"moveend");},1);
    }
}