/*
*  Written by Loren Cress
*  Esri Professional Services
*  Last updated 22Sep09
*  Copyright 2009 Esri, Inc.
*/
    var ATPoptions = {};
        ATPoptions.proxyPath = "/proxy/proxy.ashx";
        ATPoptions.imagePath = "images/";

    dojo.require("esri.map");
    dojo.require("esri.tasks.locator");
//    dojo.require("esri.tasks.query");
    dojo.require("esri.tasks.route");
    dojo.require("esri.utils");

    dojo.require("dojo.parser");
    dojo.require("dojo.string");
    dojo.require("dojo.data.util.sorter");
    dojo.require("dojo.fx");

    dojo.require("dojox.data.XmlStore");
    dojo.require("dojox.gfx");
    dojo.require("dojox.gfx.shape");
    dojo.require("dojox.gfx.matrix");
    dojo.require("dojox.xml.DomParser");
    dojo.require("dojox.xml.parser");

    dojo.require("dijit.dijit");
    dojo.require("dijit.Dialog");
    dojo.require("dijit.form.Button");
    dojo.require("dijit.form.CheckBox");
    dojo.require("dijit.layout.AccordionContainer");
    dojo.require("dijit.layout.BorderContainer");
    dojo.require("dijit.layout.ContentPane");

    var map = null;
    var locator = null, placesLocator = null;
    var instrFS = null;
    var defaultSymbol, highlightSymbol, instrSymbol, shadowSymbol, tempSymbol, moTemplate, resultTemplate;
    var fromSymbol, toSymbol, routeSymbol, segmentSymbol;
    var routeTask, routeParams, routes = [];
    var from, to, directions, directionFeatures, segmentGraphic;
    var origin, destination, targets;
    var continueDebugPrompts = true;
    var tempTargetId, showInfoClick = false;
    var courseArray = [];
    var searchComplete = false;
    var infoWindowBaseWidth = 260, infoWindowBaseHeight = 95;
    var loading, layersLoaded = 0;  //variable to keep track of when all layers have been loaded.

    var GFX_CIRCLE = "circle";
    var GFX_SQUARE = "square";
    var GFX_DIAMOND = "diamond";
    var GFX_CROSS = "cross";
    var GFX_X = "x";

    var GFX_RED = new dojo.Color([255,0,0,1.0]);
    var GFX_REDT = new dojo.Color([255,0,0,0.5]);
    var GFX_GREEN = new dojo.Color([0,255,0,1.0]);
    var GFX_GREENT = new dojo.Color([0,255,0,0.5]);
    var GFX_YELLOW = new dojo.Color([255,255,0,1.0]);
    var GFX_YELLOWT = new dojo.Color([255,255,0,0.5]);
    var GFX_BLUE = new dojo.Color([0,0,255,1.0]);
    var GFX_BLUET = new dojo.Color([0,0,255,0.5]);

    function init() {
        // Initialize proxy page.  See http://resources.esri.com/help/9.3/arcgisserver/apis/javascript/arcgis/help/jshelp_start.htm
        esri.config.defaults.io.proxyUrl = ATPoptions.proxyPath;
        esri.config.defaults.io.alwaysUseProxy = false;

        window.scrollTo(0,0);

        loading = dojo.byId("loadingImg");  //loading image. id

        map = new esri.Map("map", {
                extent: new esri.geometry.Extent(-135, 10, -60, 60, new esri.SpatialReference({wkid:4326})),
                showInfoWindowOnClick:true,
                isZoomSlider:true
            });
        dojo.connect(map, "onLoad", initMap);

        var layer = new esri.layers.ArcGISTiledMapServiceLayer("http://server.arcgisonline.com/ArcGIS/rest/services/Esri_StreetMap_World_2D/MapServer");
        map.addLayer(layer);
        dojo.connect(layer, "onUpdate", hideLoading);
        map.infoWindow.resize(infoWindowBaseWidth,infoWindowBaseHeight);

        initSymbols();

        //initialize the locator services
        locator = new esri.tasks.Locator("http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/Locators/Esri_Geocode_USA/GeocodeServer");
        placesLocator = new esri.tasks.Locator("http://tasks.arcgisonline.com/ArcGIS/rest/services/Locators/Esri_Places_World/GeocodeServer");
        dojo.connect(locator, "onAddressToLocationsComplete", showResults);
        //dojo.connect(locator, "onError", errorHandler);
        dojo.connect(placesLocator, "onAddressToLocationsComplete", showResults);
        //dojo.connect(placesLocator, "onError", errorHandler);

        //initialize the routing service
        /*routeTask = new esri.tasks.RouteTask("http://tasks.arcgisonline.com/ArcGIS/rest/services/NetworkAnalysis/Esri_Route_NA/NAServer/Route");
        longRouteTask = new esri.tasks.RouteTask("http://tasks.arcgisonline.com/ArcGIS/rest/services/NetworkAnalysis/Esri_Route_NA/NAServer/Long_Route");
        routeParams = new esri.tasks.RouteParameters();
        routeParams.stops = new esri.tasks.FeatureSet();
        routeParams.returnRoutes = false;
        routeParams.returnDirections = true;
        routeParams.directionsLengthUnits = esri.Units.MILES;
        routeParams.outSpatialReference = new esri.SpatialReference({ wkid:4326 });

        dojo.connect(routeTask, "onSolveComplete", showRoute);
        dojo.connect(routeTask, "onError", errorHandler);
        dojo.connect(longRouteTask, "onSolveComplete", showRoute);
        dojo.connect(longRouteTask, "onError", errorHandler);*/

        //info template for points returned
        moTemplate = new esri.InfoTemplate("${fullname}", "<div style=\"font-size: 0.8em;\">${iw_content}</div>");

    }

    function initMap() {
        showLoading();
        // put instructors on the map
        getInstructorData();
    }

    function initSymbols() {
        //initialize symbology
        defaultSymbol = new esri.symbol.SimpleMarkerSymbol(esri.symbol.SimpleMarkerSymbol.STYLE_CIRCLE, 10, new esri.symbol.SimpleLineSymbol(), GFX_YELLOWT);
        highlightSymbol = new esri.symbol.SimpleMarkerSymbol(esri.symbol.SimpleMarkerSymbol.STYLE_CIRCLE, 10, new esri.symbol.SimpleLineSymbol(), GFX_REDT);
        instrSymbol = new esri.symbol.PictureMarkerSymbol(ATPoptions.imagePath + "billboard_blue.png", 32, 32);
        shadowSymbol = new esri.symbol.PictureMarkerSymbol(ATPoptions.imagePath + "billboard_shadow.png", 42, 16).setOffset(10,12);
        tempSymbol = defaultSymbol;

        //Configure symbols to be used for destinations and route segments
        fromSymbol = new esri.symbol.SimpleMarkerSymbol(esri.symbol.SimpleMarkerSymbol.STYLE_CIRCLE, 15, new esri.symbol.SimpleLineSymbol(), GFX_GREEN);
        toSymbol = new esri.symbol.SimpleMarkerSymbol().setColor(GFX_RED);

        routeSymbol = new esri.symbol.SimpleLineSymbol().setColor(GFX_BLUET).setWidth(4);
        segmentSymbol = new esri.symbol.SimpleLineSymbol().setColor(GFX_REDT).setWidth(8);
    }

    function showLoading() {
        esri.show(loading);
        map.disableMapNavigation();
        map.hideZoomSlider();
    }

    function hideLoading() {
        layersLoaded++;
        if (map.layerIds) {
            if (layersLoaded === map.layerIds.length) {
                esri.hide(loading);
                map.enableMapNavigation();
                map.showZoomSlider();
                layersLoaded = 0;
            }
        }
    }

    function getInstructorData() {
        var xml = new JKL.ParseXML( "POI.xml" );
        xml.async( function(response, ioArgs){
                var myJsonObject=response;
                //console.debug(myJsonObject);

                courseArray = myJsonObject.Root.courses.course;

                instrFS = new esri.tasks.FeatureSet();

                dojo.forEach(myJsonObject.Root.instructors.instructor,function(instructor){
                    var feature1 = new esri.Graphic();
                    var canTeachCourses = (instructor.Description_3.substring(0,2)=='A-') ? instructor.Description_3.substring(2,instructor.Description_3.length) : instructor.Description_3;
                    feature1.setGeometry(new esri.geometry.Point(instructor.Long,instructor.Lat));
                    feature1.setAttributes({
                        street:(typeof(instructor.Street)!=='undefined' ? instructor.Street : ''),
                        fullname:instructor.Description_2 + ' ' + instructor.Description_1,
                        firstname:instructor.Description_2,
                        lastname:instructor.Description_1,
                        city:instructor.City,
                        state:instructor.State__Province,
                        address:(typeof(instructor.Street)!=='undefined' ? instructor.Street + ', ' : '') + instructor.City + ', ' + instructor.State__Province + ', ' + instructor.Postal_Code,
                        co_name:(typeof(instructor.Name)!=='undefined' ? instructor.Name : ''),
                        postal_code:dojo.string.pad(instructor.Postal_Code,5,"0"),
                        email:(instructor.Description_4) ? instructor.Description_4 : "",
                        url:(instructor.Description_5) ? ((instructor.Description_5.substring(0,4)=='http') ? "" : "http://") + instructor.Description_5 : "",
                        phone:(instructor.Description_6) ? instructor.Description_6 : "",
                        extension:(instructor.Description_7) ? instructor.Description_7 : "",
                        courses:[],
                        enabled:true
                    });
                    dojo.forEach(courseArray,function(course){
                        if (canTeachCourses.search(course.id)>-1){
                            feature1.attributes.courses.push({
                                id: course.id,
                                title: course.title,
                                enabled: true
                            });
                        }
                    });
                    // prewrite the content (and size) of the infoWindow so
                    // to prevent having to figure it out for every mouseover
                    var iw_div = document.createElement('div');
					if (feature1.attributes.co_name!="") iw_div.innerHTML += (feature1.attributes.url!="") ? '<a href="' + feature1.attributes.url + '"' + ' \">' + feature1.attributes.co_name + '</a><br />' : feature1.attributes.co_name + '<br />';
					if (feature1.attributes.address !="") iw_div.innerHTML +=  feature1.attributes.address + '<br />';
					//if (feature1.attributes.city !="") iw_div.innerHTML += feature1.attributes.city;
                    //if (feature1.attributes.state !="") iw_div.innerHTML += ', ' + feature1.attributes.state + '<br />';
					if (feature1.attributes.phone!="") iw_div.innerHTML += feature1.attributes.phone + '<br />';
                    //if (iw_div.innerHTML!=null) iw_div.innerHTML += ' | ';
                    if (feature1.attributes.email!="") iw_div.innerHTML += '<a href="mailto:' + feature1.attributes.email + '">' + feature1.attributes.email + '</a>';
                    if (iw_div.innerHTML!=null) iw_div.innerHTML += '<br/>';
                    iw_div.innerHTML += '<span style="display:inline-block;">Courses offered:</span><br/>';
                    var iw_courses = document.createElement('div'); iw_courses.style.fontSize = "0.9em";
                    var c_ul = document.createElement('ul'); c_ul.className = 'detailli'; iw_courses.appendChild(c_ul);
                    dojo.forEach(feature1.attributes.courses,function(course){
                        var li = document.createElement('li'); li.innerHTML = course.title; c_ul.appendChild(li);
                    });
                    iw_div.appendChild(iw_courses);
                    feature1.attributes.iw_content = iw_div.innerHTML;
                    var iw_height = 40;
                    iw_height += (15 * feature1.attributes.courses.length);
                    iw_height += (21 * (Math.ceil(feature1.attributes.co_name.length/43))) ;
                    feature1.attributes.iw_height = iw_height;

//                    if (continueDebugPrompts) { continueDebugPrompts = confirm(feature1.attributes.courses.length + '. Continue?'); }
                    instrFS.features.push(feature1);
                });
                addPointsToMap(instrFS);
                //Dojo recommends that you always return(response); to propagate
                //the response to other callback handlers. Otherwise, the error
                //callbacks may be called in the success case.
                return response;
            } );
        xml.onerror( function(response, ioArgs){
                alert("An error occurred, with response: " + response);
                return response;
            } );
        xml.parse();
    }

    //add points to map and set their symbology + info template
    function addPointsToMap(featureSet) {
        //Create graphics layer for instructor locations
        var targetLayer = clearLayer('instructorLayer', 1);
        dojo.forEach(featureSet.features,function(feature,i){
            feature.attributes.targetId = i;
            if (feature.attributes.enabled) {
                targetLayer.add(feature.setSymbol(defaultSymbol).setInfoTemplate(moTemplate));
            }
        });
        dojo.connect(targetLayer, "onMouseMove", function(evt) {
            if (!showInfoClick) {
                if (evt.graphic.symbol!=highlightSymbol) {
                    tempSymbol = evt.graphic.symbol;
                    evt.graphic.setSymbol(highlightSymbol);
                }
                tempTargetId = evt.graphic.attributes.targetId;
                showInfoWindow(tempTargetId);
            }
        });
        dojo.connect(targetLayer, "onMouseOut", function(evt) {
            if (!showInfoClick) { // prevents other mouseovers/mousemove/mouseouts from interfering
                map.infoWindow.hide();
                evt.graphic.setSymbol(defaultSymbol);
            }
        });
        dojo.connect(targetLayer, "onClick", function(evt) {
            showInfoClick = true; // "locks" the infowindow open until explicitly closed by user clicking the close button
            tempTargetId = evt.graphic.attributes.targetId;
            showInfoWindow(tempTargetId);
            //showDetailTable(evt.graphic);
        });
        dojo.connect(map.infoWindow, "onHide", function(evt) {
            var target = dojo.filter(map.getLayer('instructorLayer').graphics,function(graphic){return graphic.attributes.targetId==tempTargetId;})[0];
            if (showInfoClick) target.setSymbol(defaultSymbol);
            showInfoClick = false;
        });
    }

    function sortCourses(a,b){
        return (a.title < b.title) ? -1 : 1;
    }

    function sortByDistance(a,b){
        return ((a.attributes.distance < b.attributes.distance) ? -1 : ((a.attributes.distance > b.attributes.distance) ? 1 : 0));
    }

    function hilite(targetId) {
        dojo.byId(targetId).style.background = "rgb(255,255,0)";
    }

    function getDistance(originX, originY, destX, destY, calcMethod, units) {
        calcMethod = (calcMethod) ? calcMethod : 'sphere';
        units = (units) ? units : esri.Units.MILES;
        var dist = 0;

        var pi=Math.PI;
        var R; /* radius of earth */
        var rM = 6371221; /* ...in meters */
        var rMi = 3958.761; /* ...in miles */
        var rNMi = 3440.069; /* ...in nautical miles */
        switch(units) {
            case esri.Units.CENTIMETERS:
                R=rM*100;
                break;
            case esri.Units.DECIMAL_DEGREES:
                R=180/pi;
                break;
            case esri.Units.DECIMETERS:
                R=rM*10;
                break;
            case esri.Units.FEET:
                R=rMi*5280;
                break;
            case esri.Units.INCHES:
                R=rMi*5280*12;
                break;
            case esri.Units.KILOMETERS:
                R=rM/1000;
                break;
            case esri.Units.METERS:
                R=rM;
                break;
            case esri.Units.MILES:
                R=rMi;
                break;
            case esri.Units.MILLIMETERS:
                R=rM*1000;
                break;
            case esri.Units.NAUTICAL_MILES:
                R=rNMi;
                break;
            case esri.Units.POINTS:
                R=1;
                break;
            case esri.Units.YARDS:
                R=rMi*1760;
                break;
            case esri.Units.UNKNOWN:
            default:
                R=0;
                break;
        }

        var dist = 0;
        if (calcMethod === 'planar') {
            // simple planar distance - not very accurate if
            // you're using a geographic coordinate system!
            dist = esri.geometry.getLength({x:originX, y:originY}, {x:destX, y:destY});
        } else if (calcMethod === 'sphere') {
            var dLat = (destY-originY) / 180 * pi;
            var dLon = (destX-originX) / 180 * pi;
            var a = Math.sin(dLat/2) * Math.sin(dLat/2) +
                Math.cos(originY / 180 * pi) * Math.cos(destY / 180 * pi) *
                Math.sin(dLon/2) * Math.sin(dLon/2);
            var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
            dist = R * c;
        }
        return dist;
    }

    function showInfoWindow(targetId) {
        var target = dojo.filter(map.getLayer('instructorLayer').graphics,function(graphic){return graphic.attributes.targetId==targetId;})[0];
        if (!map.extent.contains(target.geometry)) {
            map.centerAt(target.geometry);
        }

        map.infoWindow.resize(infoWindowBaseWidth, infoWindowBaseHeight + target.attributes.iw_height);

        map.infoWindow.setContent(target.getContent());
        map.infoWindow.setTitle(target.getTitle());
        map.infoWindow.show(map.toScreen(target.geometry),map.getInfoWindowAnchor(map.toScreen(target.geometry)));

        return true;
    }

    function centerTarget(targetId) {
        var target = dojo.filter(map.getLayer('instructorLayer').graphics,function(graphic){return graphic.attributes.targetId==targetId;})[0];
        map.infoWindow.hide();
        map.centerAt(target.geometry);
        map.setLevel(map.getLevel() + 1);
    }

    function makeGraphics(graphicType, color) {
        var node = document.createElement("span");
        node.style.position = "absolute";
        var surfaceWidth = 14;
        var surfaceHeight = 14;
        var surface = dojox.gfx.createSurface(node,surfaceWidth, surfaceHeight);
        var group = surface.createGroup();

        switch(graphicType) {
            case GFX_CIRCLE:
                var parms = { cx: 7, cy: 7, r: 6 };
                var circle = group.createCircle(parms)
                    .setFill(color)
                    .setStroke({color: "black", width: 1, cap: "butt", join: 4})
                    .applyTransform(dojox.gfx.matrix.identity);
                break;
            case GFX_SQUARE:
                var parms = { x: 1, y: 1, width: 11, height: 11 };
                var rect = group.createRect(parms)
                    .setFill(color)
                    .setStroke({color: "black", width: 1, cap: "butt", join: 4})
                    .applyTransform(dojox.gfx.matrix.identity);
                break;
            case GFX_DIAMOND:
                var parms = { x: 0, y: 0, width: 7, height: 7 };
                var rect = group.createRect(parms)
                    .setFill(color)
                    .setStroke({color: "black", width: 1, cap: "butt", join: 4})
                    .setTransform([dojox.gfx.matrix.translate(7,-4)]);
                group.applyTransform([dojox.gfx.matrix.rotateg(45)]);
                break;
            case GFX_CROSS:
                break;
            case GFX_X:
                break;
            default:
                break;
        }
        return node;
    }

    function makePrintWindow() {
        var p_win = window.open('print.html','p_win','toolbar=1,resizable=1,scrollbars=1');
        var p_win_doc = p_win.document;

        p_win_doc.write("<style type=\"text/css\">#detailTable, #route {\nwidth:100\%;\n}\n</style>");
        p_win_doc.write(dojo.byId('details_pane').innerHTML);
        if (!dojo.isIE<=6) {
            p_win_doc.getElementById('print_link').href = 'javascript:window.print();';
        } else {
            p_win_doc.write('<scri' + 'pt type="text/javasc' + 'ript">document.print_link.href = "javasc' + 'ript:window.print();";</scr' + 'ipt>');
        }
    }

/*********************************************
 * Begin course filter
**********************************************/

    function populateFilter(courses) {
        if (!dojo.byId("filterDialog")) {
            var filterDialog = new dijit.Dialog({
                id:"filterDialog",
                title:"Course Selection"
            });

            var f_c_container = document.createElement("div");
            f_c_container.className = "coursefilter";
            var filterCheckAllButton = new dijit.form.Button({label:"Select All", onClick:function() {
                    dojo.forEach( dojo.query(".dijitCheckBoxInput"), function(cb) {
                        dijit.byId(cb.id).attr("checked", true);
                    });
                }
            });
            var filterClearAllButton = new dijit.form.Button({label:"Clear All", onClick:function() {
                    dojo.forEach( dojo.query(".dijitCheckBoxInput"), function(cb) {
                        dijit.byId(cb.id).attr("checked", false);
                    });
                }
            });
            var f_c_formcontrol = document.createElement("div");
            f_c_formcontrol.className = "formcontrol";
            f_c_formcontrol.appendChild(filterCheckAllButton.domNode);
            f_c_formcontrol.appendChild(filterClearAllButton.domNode);

            f_c_container.appendChild(f_c_formcontrol);

            courses.sort(sortCourses);
            dojo.forEach(courses, function(course, i) {
                var checkbox = new dijit.form.CheckBox({
                    name:"filter_course_" + i,
                    id:"filter_course_" + i,
                    value:course.id,
                    checked:true
                });
                var label1 = document.createElement('label'); label1["for"] = "filter_course_" + i; label1.innerHTML = course.title;

                f_c_container.appendChild(checkbox.domNode);
                f_c_container.appendChild(label1);
                f_c_container.appendChild(document.createElement('br'));
            });

            var f_c_bottom = document.createElement("div");
            f_c_bottom.className = "bottom";
            var filterOkButton = new dijit.form.Button({label:"OK", type:"submit"});
            var filterCancelButton = new dijit.form.Button({label:"Cancel", type:"cancel", onClick:function(){filterDialog.hide();}});
            f_c_bottom.appendChild(filterOkButton.domNode);
            f_c_bottom.appendChild(filterCancelButton.domNode);
            f_c_container.appendChild(f_c_bottom);
            filterDialog.domNode.appendChild(f_c_container);
            filterDialog.attr('execute',processFilter);
        } else {
            var filterDialog = dijit.byId("filterDialog");
         }
        filterDialog.show();
    }

    function processFilter(){
        dojo.forEach(courseArray,function(course, i){
            course.enabled = dijit.byId("filter_course_" + i).attr('checked');
        });
        dojo.forEach(instrFS.features,function(instructor) {
            dojo.forEach(courseArray,function(course){
                dojo.forEach(instructor.attributes.courses, function(XMLcourse) {
                    if (XMLcourse.id===course.id) {
                        XMLcourse.enabled=course.enabled;
                    }
                });
            });
            var hasAtLeastOneCourse = false;
            dojo.forEach(instructor.attributes.courses, function(XMLcourse) {
                if (XMLcourse.enabled) {
                    hasAtLeastOneCourse = true;
                }
            });
            instructor.attributes.enabled = hasAtLeastOneCourse;
        });
        addPointsToMap(instrFS);
        if (searchComplete) locate();
    }

/*********************************************
 * End filter
**********************************************/
/*********************************************
 * Begin routing
**********************************************/

    /*function getDirections(targetId) {
        destination = dojo.filter(map.getLayer('instructorLayer').graphics,function(graphic){return graphic.attributes.targetId===targetId;})[0];
        showDetailTable(destination);
        if (destination.attributes.street=='') {
            if (!confirm("This destination does not include a street address.\nDirections will be to the geographic center of " + destination.attributes.city + ", " + destination.attributes.state + ".\nAre you sure you want to continue?")) {
                return;
            }
        }
        var start = new esri.geometry.Point(origin.geometry, map.spatialReference);
        var end = new esri.geometry.Point(destination.geometry, map.spatialReference);
        routeParams.stops.features = [];
        map.graphics.clear();
        segmentGraphic = null;

        routeParams.stops.features[0] = map.graphics.add(new esri.Graphic(start, fromSymbol, origin.attributes));
        routeParams.stops.features[1] = map.graphics.add(new esri.Graphic(end, toSymbol, {address:destination.attributes.address}));
        //If both the "to" and the "from" addresses are set, solve the route
        if (routeParams.stops.features.length === 2) {
            if (destination.attributes.distance > 250) {
                longRouteTask.solve(routeParams);
            } else {
                routeTask.solve(routeParams);
            }
        }
    }

    function showRoute(routeResults, barriers, messages) {
        directions = routeResults[0].directions;
        directionFeatures = directions.features;

        //Add route to the map
        map.graphics.add(new esri.Graphic(directions.mergedGeometry, routeSymbol));

        // clear the route section
        var route = dojo.byId('route');
        if (!route) {
            route = document.createElement('div');
            route.id = 'route';
            dojo.byId('details_pane').appendChild(route);
        }
        while (route.hasChildNodes()) route.removeChild(route.firstChild);
        route.style.display = 'block';
        dijit.byId('accordion').selectChild(dijit.byId('details_pane'));

        // set up table
        var routeTable = document.createElement('table');
        routeTable.className = "windlettable";

        //Display the total time and distance of the route
        route.style.border = "1px solid #000";
        var summaryStringHTML = "<br /> &nbsp; Total driving distance: " + formatDistance(directions.totalLength, "miles") + "<br /> &nbsp; Total time: " + formatTime(directions.totalTime);
        var routeSummary = document.createElement('div');
        routeSummary.id = "routesummary";
        routeSummary.onclick = zoomToFullRoute;
        routeSummary.className = "windletbody";
        route.appendChild(routeSummary);
        routeSummary.innerHTML = summaryStringHTML;

        //List the directions and create hyperlinks for each route segment
        var directionsDiv = document.createElement('div');
        route.appendChild(directionsDiv);
        directionsDiv.id = "directions";
        directionsDiv.className = "windletbody";
        var ol = document.createElement('ol');
        directionsDiv.appendChild(ol);
        dojo.forEach(routeResults[0].directions.features, function(feature, i, features) {
            var li = document.createElement('li'); li.onclick = function() { zoomToSegment(i); return false; }; li.className = "segment";
            var link = document.createElement('a'); link.href = ""; link.onclick = function() { return false; }
            link.innerHTML = feature.attributes.text;
            if (i===0) {
                link.innerHTML += '&nbsp;';
                link.appendChild(makeGraphics(GFX_CIRCLE, GFX_GREEN));
            } else if (i===(features.length - 1)) {
                link.innerHTML += '&nbsp;';
                link.appendChild(makeGraphics(GFX_CIRCLE, GFX_RED));
            } else {
                link.innerHTML += " (" + formatDistance(feature.attributes.length ,"miles") + ", " + formatTime(feature.attributes.time) + ")";
            }
            li.appendChild(link);
            ol.appendChild(li);
        });

        dojo.byId("print_link_div").width = directionsDiv.width;
        dojo.byId("print_link_div").style.width = '96%';
        esri.hide(dojo.byId("c_np"));

        zoomToFullRoute();
    }

    //Display any errors that were caught when attempting to solve the route
    function errorHandler(err) {
        alert("An error occured\n" + err.message + "\n" + err.details.join("\n"));
    }

    //Zoom to the appropriate segment when the user clicks a hyperlink in the directions list
    function zoomToSegment(index) {
        var segment = directionFeatures[index];
        map.setExtent(segment.geometry.getExtent(), true);
        if (!segmentGraphic) {
            segmentGraphic = map.graphics.add(new esri.Graphic(segment.geometry, segmentSymbol));
        } else {
            segmentGraphic.setGeometry(segment.geometry);
        }
    }

    function zoomToFullRoute() {
        map.graphics.remove(segmentGraphic);
        segmentGraphic = null;
        map.setExtent(directions.extent, true);
    }

    //Format the time as hours and minutes
    function formatTime(time) {
        var hr = Math.floor(time / 60), //Important to use math.floor with hours
            min = Math.round(time % 60);

        if (hr < 1 && min < 1) {
            return "<1 minute";
        }
        else if (hr < 1) {
            return min + " minute" + ((min==1) ? "" : "s");
        }

        return hr + " hour" + ((hr==1) ? " " : "s ") + min + " minute" + ((min==1) ? "" : "s");
    }

    //Round the distance to the nearest hundredth of a unit
    function formatDistance(dist, units) {
        var d = Math.round(dist * 100) / 100;
        if (d === 0) {
            return "";
        }

        return d + " " + units;
    }*/

/*********************************************
 * End routing
**********************************************/
/*********************************************
 * begin tabular results
**********************************************/

    function makeDetailTableItem(result, table, detail_level, index) {
        var fullDetails = (typeof(fullDetails)!='undefined') ? fullDetails : false;
        var index = (typeof(index)!='undefined') ? index : 0;
        var instructor=result.attributes;
        var row = table.insertRow(index); row.className = "windletbody"; row.style.background = "#FFFFFF"; row.onmouseover=function(){this.style.background="#DDDDDD";}; row.onmouseout=function(){this.style.background="#FFFFFF";}; row.onclick=function(){showInfoWindow(instructor.targetId)}; row.ondblclick=function(){centerTarget(instructor.targetId)};

        // the cells have to be inserted into the row from right to left, because of the index requirement; because this function may not
        // be including the first cell, it's easier just to insert them in reverse order.
//        var cell2 = row.insertCell(0); cell2.className = "windletbody"; cell2.style.verticalAlign = "top"; cell2.style.horizontalAlign = "left"; cell2.style.width = "50%"; var c_ul = document.createElement('ul'); c_ul.className = 'detailli'; cell2.appendChild(c_ul);
//        dojo.forEach(instructor.courses,function(course){
//            var li = document.createElement('li'); li.innerHTML = course.title; c_ul.appendChild(li);
//        });
        var cell1 = row.insertCell(0); cell1.className = "windletbody"; cell1.style.verticalAlign = "top";
            //cell1.innerHTML = instructor.fullname + '<br/>';
			//cell1.innerHTML = cell1.innerHTML += 'click instructor for more info on map';
			cell1.innerHTML = '<b>' + instructor.fullname + '</b><br />';
            //if ((detail_level>=1) && instructor.street!='') cell1.innerHTML += instructor.street + '<br/>';
            cell1.innerHTML += instructor.city + ", " + instructor.state;
            if (detail_level>=1) cell1.innerHTML +=  " " + instructor.postal_code;
            //if (instructor.distance) cell1.innerHTML += ' <span class="noprint">&nbsp;-&nbsp;' + (Math.round(instructor.distance *10)/10)+ ' mi<a href="#distance_fn" style="text-decoration: none;" onclick="hilite(\'distance_fn\');">*</a></span>';
            //cell1.innerHTML += '<br/>';
            //cell1.innerHTML += ((instructor.url.length>1) ? "<a href=\"" + instructor.url + "\" target=\"_blank\">" + instructor.co_name + "</a>" : instructor.co_name) + '<br/>';
            //if (detail_level>=1) cell1.innerHTML += instructor.phone + (((typeof(instructor.extension)!='object') && ((instructor.extension)!='')) ? " ext. " + instructor.extension : "") + '<br/>';
            var c_np = document.createElement('span'); if (detail_level>=1) c_np.id = "c_np"; c_np.className = "noprint"; cell1.appendChild(c_np);
            //if (origin) { var c_np_span1 = document.createElement('span'); c_np.appendChild(c_np_span1); var directionslink = document.createElement('a'); directionslink.href = "javascript:getDirections(" + instructor.targetId + ");"; directionslink.innerHTML = "Directions"; c_np_span1.appendChild(directionslink); c_np_span1.innerHTML += "&nbsp;&bull;&nbsp;";}
            //var c_np_span2 = document.createElement('span'); c_np.appendChild(c_np_span2); var detaillink = document.createElement('a'); detaillink.href = "javascript:showDetailTable(" + instructor.targetId + ");"; detaillink.innerHTML = "Details"; c_np_span2.appendChild(detaillink);
            //var c_np_span3 = document.createElement('span'); c_np.appendChild(c_np_span3); var emaillink = document.createElement('a'); emaillink.href = "mailto:" + instructor.email; emaillink.innerHTML = "Email"; c_np_span3.innerHTML = "&nbsp;&bull;&nbsp;"; c_np_span3.appendChild(emaillink);
        if (detail_level==0) { var cell0 = row.insertCell(0); cell0.className = "windletbody"; cell0.style.verticalAlign = "top"; cell0.innerHTML = (index + 1) + "."; }
        return true;
    }

    /*function showDetailTable(result) {
        var ret = true;
        if (typeof(result)==='number') {
            var targetId = result;
            result = null;
            result = dojo.filter(map.getLayer('instructorLayer').graphics,function(graphic){return graphic.attributes.targetId===targetId;})[0];
            ret = null;
        }
        var detailTable = dojo.byId('detailTable');
        if (!detailTable) {
            // create table if it doesn't already exist
            detailTable = document.createElement('table');
            detailTable.id = 'detailTable';
            detailTable.style.border = 'none';
            detailTable.width = '100%';
            dojo.byId('details_pane').appendChild(detailTable);
            detailTable.setAttribute("targetId",result.attributes.targetId);

            // show the instructor details
            makeDetailTableItem(result, detailTable, 1, 0);

        } else {
            if (detailTable.targetId != result.attributes.targetId) {
                // clear table if it already exists
                while (detailTable.hasChildNodes()) detailTable.removeChild(detailTable.firstChild);
                // clear any existing route
                var route = dojo.byId('route');
                if (route) dojo.byId('details_pane').removeChild(route);
                map.graphics.clear();

                // show the instructor details
                makeDetailTableItem(result, detailTable, 1, 0);
            }
            // if details table and/or route is already displayed, do nothing
        }
        dijit.byId('accordion').selectChild(dijit.byId('details_pane'));
    }*/

    function makeResultsTable(results) {
        var resultsDiv = dojo.byId("results");
        // delete table if it already exists
        while (resultsDiv.hasChildNodes()) resultsDiv.removeChild(resultsDiv.firstChild);

        var resultsTable = document.createElement('table');
        resultsDiv.appendChild(resultsTable);
        resultsTable.className = "windlettable"; resultsTable.width = '100%'; resultsTable.style.borderCollapse = "collapse"; resultsTable.style.border = "none"; resultsTable.cellSpacing = "0px"; resultsTable.cellPadding = "3px";
        dojo.forEach(results,function(result,i){
            // show the instructor details
            makeDetailTableItem(result, resultsTable, 0, i);
        });
        var r_t_header = resultsTable.createTHead().insertRow(0);
//        var r_t_h_1 = r_t_header.insertCell(0); r_t_h_1.className = "windletheader"; r_t_h_1.colSpan = 2; r_t_h_1.innerHTML = "Authorized Instructors Locations";
//        var r_t_h_2 = r_t_header.insertCell(1); r_t_h_2.className = "windletheader"; r_t_h_2.innerHTML = "Courses Offered";

        return true;
    }

/*********************************************
 * end tabular results
**********************************************/
/*********************************************
 * begin search
**********************************************/

    function locate() {
        map.graphics.clear();

        var searchAdd = dojo.byId("address").value;
        var add = searchAdd.split(",");
        var tmpAdd = (add[0]) ? dojo.string.trim(add[0]) : "";
        var tmpCity = (add[1]) ? dojo.string.trim(add[1]) : "";
        var tmpState = (add[2]) ? dojo.string.trim(add[2]) : "";
        var tmpZip = (add[3]) ? dojo.string.trim(add[3]) : "";
        if (add.length < 4) {
            if ((add.length===2) &&
                ((isNaN(parseFloat(tmpCity.substring(0,1))))
                && (isNaN(parseFloat(tmpCity.substring(0,1)))))) {
                // only two non-numerical results were returned; this is most likely to be
                // "city, st"
                tmpState = tmpCity;
                tmpCity = tmpAdd;
                tmpAdd = "";
            } else if ((!isNaN(parseFloat(tmpCity.substring(0,1)))) ||
                ((add.length===1) &&
                (tmpAdd.length===5) &&
                (!isNaN(parseFloat(tmpAdd.substring(0,1)))))) {
                // if the first character of tmpCity is a number, or if only a 5-digit number
                // was entered, then it's probably supposed to be the ZIP
                tmpZip = tmpAdd;
                tmpAdd = "";
            } else if ((add.length===1) &&
                (isNaN(parseFloat(tmpAdd.substring(0,1))))) {
                // only a city or state was entered; rearrange things so that the placesLocator
                // gets called later on
                var states = ["ALABAMA","ALASKA","AMERICAN SAMOA","ARIZONA","ARKANSAS","CALIFORNIA","COLORADO","CONNECTICUT","DELAWARE","DISTRICT OF COLUMBIA","FLORIDA","GEORGIA","GUAM","HAWAII","IDAHO","ILLINOIS","INDIANA","IOWA","KANSAS","KENTUCKY","LOUISIANA","MAINE","MARYLAND","MASSACHUSETTS","MICHIGAN","MINNESOTA","MISSISSIPPI","MISSOURI","MONTANA","NEBRASKA","NEVADA","NEW HAMPSHIRE","NEW JERSEY","NEW MEXICO","NEW YORK","NORTH CAROLINA","NORTH DAKOTA","NORTHERN MARIANAS ISLANDS","OHIO","OKLAHOMA","OREGON","PENNSYLVANIA","PUERTO RICO","RHODE ISLAND","SOUTH CAROLINA","SOUTH DAKOTA","TENNESSEE","TEXAS","UTAH","VERMONT","VIRGINIA","VIRGIN ISLANDS","WASHINGTON","WEST VIRGINIA","WISCONSIN","WYOMING",
                    "AL","AK","AZ","AR","CA","CO","CT","DE","FL","GA","HI","ID","IL","IN","IA","KS","KY","LA","ME","MD","MA","MI","MN","MS","MO","MT","NE","NV","NH","NJ","NM","NY","NC","ND","OH","OK","OR","PA","RI","SC","SD","TN","TX","UT","VT","VA","WA","WV","WI","WY","AS","CD","GU","MP","PR","VI"];
                var isState = dojo.some(states, function(elem) { return elem==tmpAdd.toUpperCase(); });
                if (isState) {
                    tmpState = tmpAdd;
                    tmpCity = "";
                    tmpAdd = "";
                    var address = {
                        PlaceName: (tmpCity + ((tmpState) ? ", " + tmpState : "") + ((tmpZip) ? " " + tmpZip : ""))
                    }
                    placesLocator.addressToLocations(address,["Loc_name", "City", "State_Abbr", "Country"]);
                    searchComplete = true;
                    return(false);
                } else {
                    tmpState = "";
                    tmpCity = tmpAdd;
                    tmpAdd = "";
                }
            }
            // many users will not enter a comma between the state and zip
            if (tmpState.length > 7) {
                var sp = tmpState.split(" ");
                var s = sp[sp.length - 1];
                if (!isNaN(parseFloat(s.substring(0, 1)))) {
                    tmpZip = s;
                    tmpState = sp[0];
                }
            }
            // workaround to specifically use the world places locator (instead of the "cities"
            // locator) for cities that have "Saint", "Ste.", or "St." in the name
            if ((tmpAdd=="") && ((tmpCity.search(/st(\ |\.)|ste(\ |\.)|saint\ /i)>-1))) {
                var address = {
                    PlaceName: (tmpCity + ((tmpState) ? ", " + tmpState : "") + ((tmpZip) ? " " + tmpZip : ""))
                }
                placesLocator.addressToLocations(address,["Loc_name", "City", "State_Abbr", "Country"]);
                searchComplete = true;
                return(false);
            }
        }
        var address = {
            Address: tmpAdd,
            City: tmpCity,
            State: tmpState,
            Zip: tmpZip
        };
        locator.addressToLocations(address,["Loc_name", "City", "State_Abbr", "Country"]);
        searchComplete = true;
        return(false);
    }

    function showResults(candidates) {
        var candidate;

        var max_candidates = 5;
        var actual_max = Math.min(max_candidates, candidates.length);
        var foundAddress = false;
        var lastscore = 0;
        var multipleCandidatesFound = false;
        for (var i=0, il=actual_max; i<il; i++) {
            candidate = candidates[i];
            var isPlace = ((candidate.attributes.Loc_name=="Place_Name") || (candidate.attributes.Loc_name=="Place_Admin") || (candidate.attributes.Loc_name=="Place_City_Admin"));
            var minScore = (isPlace) ? 70 : 70;
            if ((candidate.score > minScore) || (candidates.length===1)) {
                foundAddress = true;
                multipleCandidatesFound = false;
                i=actual_max;
            }
            if ((candidate.score >= lastscore) && (candidate.score < minScore)) {
                multipleCandidatesFound = true;
            }
            lastscore = candidate.score;
        }
        if (multipleCandidatesFound) {
            // multiple addresses found
            dojo.byId("searchError").innerHTML = "<font color=\"red\">Multiple locations found.  Please select one from the list below or correct it and search again.</font><br/>";
            var multCandOut = "";
            for (var i=0, il=actual_max; i<il; i++) {
                candidate = candidates[i];
                multCandOut += "<a href=\"javascript:redoAddress(" + candidate.location.x
                    + ", " + candidate.location.y + ", '" + candidate.address
                    + (((candidate.attributes.Loc_name=="Place_Name") && (candidate.attributes.State_Abbr) && (candidate.attributes.Country=="United States")) ? ", " + candidate.attributes.State_Abbr : "")
                    + (((candidate.attributes.Loc_name=="Place_Name") && (candidate.attributes.Country) && (candidate.attributes.Country!="United States")) ? ", " + candidate.attributes.Country : "")
                    + "', " + candidate.score + ", '" + candidate.attributes.Loc_name
                    + "')\">" + candidate.address
                    + (((candidate.attributes.Loc_name=="Place_Name") && (candidate.attributes.State_Abbr) && (candidate.attributes.Country=="United States")) ? ", " + candidate.attributes.State_Abbr : "")
                    + (((candidate.attributes.Loc_name=="Place_Name") && (candidate.attributes.Country) && (candidate.attributes.Country!="United States")) ? ", " + candidate.attributes.Country : "")
                    + "</a><br/>\n";
            }
            dojo.byId("multCandSelection").innerHTML = multCandOut;
        } else {
            if (foundAddress) {
                // one - and only one - good address was found
                clearLayer('addressLayer', 1);

                showAddress(candidate, true);
            } else {
                // no good addresses were found
                dojo.byId("searchError").innerHTML = "<font color=\"red\">We couldn't find the location you requested.  Please try again.</font>";
                dojo.byId("multCandSelection").innerHTML = "";
            }
        }
    }

    function startOver() {
        var anim = dojo.fx.wipeOut({node: "search_address_pane",duration: 500}); anim.play();
        dijit.byId('accordion').selectChild(dijit.byId('search_pane'));
    }

    function clearLayer(layerId, newIdx) {
        if (map.getLayer(layerId)) {
            //an address has already been located and drawn on the map - it must be cleared
            map.removeLayer(map.getLayer(layerId));
        }
        var newLayer = new esri.layers.GraphicsLayer({id:layerId});
        map.addLayer(newLayer);
        map.reorderLayer(newLayer,newIdx);
        return newLayer;
    }

    function showAddress(loc, isSelected) {
        isSelected = (typeof(isSelected)!=='undefined') ? isSelected : false;

        var symbol = new esri.symbol.SimpleMarkerSymbol().setStyle(esri.symbol.SimpleMarkerSymbol.STYLE_CIRCLE).setColor(GFX_GREEN);
        var candInfoTemplate = new esri.InfoTemplate("Location", "Address: ${address}");

        var attributes = { address: loc.address, score:loc.score, locatorName:loc.attributes.Loc_name };
        var point = new esri.geometry.Point(loc.location.x,loc.location.y);
        var graphic = new esri.Graphic(point, symbol, attributes, candInfoTemplate);
        var addressLayer = clearLayer('addressLayer', 1);
        addressLayer.add(graphic);
        addressLayer.add(new esri.Graphic(point, new esri.symbol.TextSymbol(loc.address).setOffset(0, 18)));

        if (isSelected) {
            dojo.byId("address").value = loc.address;
            dojo.byId("searchError").innerHTML = "";
            dojo.byId("multCandSelection").innerHTML = "";
            dojo.byId("search_address").innerHTML = loc.address + "&nbsp;";
            var addMarker = makeGraphics(GFX_CIRCLE, GFX_GREEN); addMarker.style.position = "static";
            dojo.byId("search_address").appendChild(addMarker);
            var anim = dojo.fx.wipeIn({node: "search_address_pane",duration: 500}); anim.play()
            dijit.byId('accordion').selectChild(dijit.byId('results_pane'));
            origin = new esri.Graphic(point, symbol, attributes, candInfoTemplate);
            findPointsNearby(point);
        }
    }

    function redoAddress(newLocX, newLocY, newAddress, score, loc_name) {
        showAddress({
            location:{
                x:newLocX,
                y:newLocY
            },
            address:newAddress,
            score:score,
            attributes: {
                Loc_name:loc_name
            }
        }, true);
    }

    //find all points within distance from argument
    function findPointsNearby(addresssPoint) {
        var instructorLabelLayer = map.getLayer("instructorLabelLayer");
        if (!instructorLabelLayer) {
            instructorLabelLayer = new esri.layers.GraphicsLayer({id:"instructorLabelLayer"});
            map.addLayer(instructorLabelLayer);
            instructorLabelLayer.enableMouseEvents();
            dojo.connect(instructorLabelLayer, "onMouseMove", function(evt) {
                if (!showInfoClick) {
                    showInfoWindow(evt.graphic.attributes.targetId);
                }
            });
            dojo.connect(instructorLabelLayer, "onMouseOut", function(evt) {
                if (!showInfoClick) {
                    map.infoWindow.hide();
                }
            });
            dojo.connect(instructorLabelLayer, "onClick", function(evt) {
                showInfoClick = true;
                tempTargetId = evt.graphic.attributes.targetId;
                showInfoWindow(tempTargetId);
                //showDetailTable(evt.graphic);
            });
        } else {
            instructorLabelLayer.clear();
        }
        var graphics = map.getLayer('instructorLayer').graphics;

        var results = [];
        var max_distance = -1; // MILES
        var max_points = dojo.byId("nearestLocations").options[dojo.byId("nearestLocations").selectedIndex].value;

        // first apply a distance to every point
        dojo.forEach(graphics,function(graphic,i){
            //if graphic point is near given point, highlight it and add for display in results list
            if (graphic.geometry.type == 'point') {
                var dist = getDistance(addresssPoint.x, addresssPoint.y, graphic.geometry.x, graphic.geometry.y);

                graphic.attributes.distance = dist;
/*
                var distanceString = ' miles<a href="#distance_fn" style="text-decoration: none;" onclick="hilite(\'distance_fn\');">*</a><br/>';
                var newContent = "Distance: " + (Math.round(dist * 10)/10) + distanceString;
                if (graphic.infoTemplate.content.indexOf(distanceString) > 0) {
                    newContent += graphic.infoTemplate.content.substring(graphic.infoTemplate.content.indexOf(distanceString) + distanceString.length,graphic.infoTemplate.content.length);
                } else {
                    newContent += graphic.infoTemplate.content;
                }
                graphic.setInfoTemplate(new esri.InfoTemplate(graphic.infoTemplate.title,newContent));
*/
            }
        });
        map.infoWindow.resize(infoWindowBaseWidth,infoWindowBaseHeight + 20);

        graphics.sort(sortByDistance);

        // next select points that meet specified criteria
        var points = new esri.geometry.Multipoint(map.spatialReference);
        points.addPoint(origin.geometry);

        dojo.forEach(graphics,function(graphic){
            //else if point was previously highlighted, reset its symbology
            if (graphic.symbol == highlightSymbol) {
                graphic.setSymbol(defaultSymbol);
            }
        });
//        var actual_max_points = (graphics.length > max_points) ? max_points : graphics.length;
        var actual_max_points = Math.min(graphics.length, max_points);
        for (var i=0; i<actual_max_points; i++) {
            graphic = graphics[i];

            if (((max_distance > 0) && (graphic.attributes.distance < max_distance)) || (max_distance <= 0)) {
                graphic.setSymbol(highlightSymbol);
                results.push(graphic);
                points.addPoint(graphic.geometry);
                // add text enumerations to each instructor, matching each number
                // with the corresponding tabular entry (1=1, 2=2, etc.)
                var txtS = new esri.symbol.TextSymbol(i + 1).setOffset(0,14).setColor("white");
/*************
IE font issues workaround begin
**************/
                if (!dojo.isIE) {
                    txtS.setFont(new esri.symbol.Font().setFamily("arial").setWeight(esri.symbol.Font.WEIGHT_BOLD).setSize("1.0em"));
                }
/*************
IE font issues workaround end
**************/
                var label = new esri.Graphic(graphic.geometry, txtS, graphic.attributes, graphic.infoTemplate);
                var billboard = new esri.Graphic(graphic.geometry, instrSymbol.setOffset(0,16), graphic.attributes, graphic.infoTemplate);
                var shadow = new esri.Graphic(graphic.geometry, shadowSymbol, graphic.attributes, graphic.infoTemplate);

                instructorLabelLayer.add(shadow);
                instructorLabelLayer.add(billboard);
                instructorLabelLayer.add(label);
            }
        }
        map.infoWindow.hide();
        map.setExtent(points.getExtent().expand(1.8));
        map.reposition();

        //display number of points in extent
//        dojo.byId("inextent").innerHTML = "<strong>Nearest " + results.length + " instructors</strong> <em>(Click to show on map)</em>";

        //display list of points in extent
//        dojo.byId("results").style.border = "1px solid #000";

        makeResultsTable(results);
    }

/*********************************************
 * end search
**********************************************/

    dojo.addOnLoad(init);

