﻿//Author:   rebecca.mccay
//Created:  11/16/2009 10:41:41 AM


var Locator = {

    InitialZoom: 5,
    //InitialCentre: new MapDS.LatLng(-28, 135),
    InitialCentre: new MapDS.LatLng(-40.9, 174),
    PoiSearchBuffer: 30000, // metres from the location.
    PoiSearchRecordLimit: 10, // max number of POIs to find.

    Strings: {
        InvalidSearch: 'Please enter a Town/Suburb.',
        InvalidSuburb: 'The value of Suburb is not valid.',
        InvalidPostcode: 'The value of Postcode is not valid.',
        MultipleAddressDialogTitle: 'Multiple Address Matches Found.',
        MultipleAddressPrompt: 'Choose an address.',
        Searching: 'Searching...',
        //POIsFound: '{0} result{1} found'
        POIsFound: '{0} closest programmes found within 30km of {1}'
    },

    Images: {
        Loading: './images/loading-circle-black.gif'
    },

    ElementIDs: {
        Map: 'map',
        MultipleAddressDialog: 'multipleAddressDialog',
        MultipleAddresses: 'ddlMultipleAddresses',
        ExceptionDialog: 'exceptionDialog',
        NoResultDialog: 'noLocationsFound',
        Search: 'search',
        SearchStreet: 'txtStreet',
        SearchSuburb: 'txtSuburb',
        SearchState: 'ddlState',
        SearchPostcode: 'txtPostcode',
        SearchButton: 'btnSearch',
        ResetButton: 'btnReset',
        ApplyFiltersButton: 'btnApply',
        //DrivingDirections: 'drivingDirections',
        PrintLinkButton: 'print_link',
        DirectionsButton: 'btnDirections',
        DrivingDirectionsList: 'drivingDirectionsList',
        DrivingDirectionsWrapper: 'drivingDirectionsWrapper',
        StartTitle: 'startTitle',
        EndTitle: 'endTitle',
        POIsButton: 'btnPointsOfInterest',
        ResultsButton: 'ResultButtons',
        POIsWrapper: 'pointsOfInterestWrapper',
        POIsList: 'pointsOfInterestList',
        POIsFound: 'pointsOfInterestFound',
        FiltersButton: 'btnFilters',
        FiltersList: 'filtersList'
    },

    /// Reset the search fields back to default values.
    /// Here we set the textfields to the value in the title attribute and then call the blur method to
    /// trigger jQuery hint code.
    resetSearchFields: function() {
        $(this.getElementID(this.ElementIDs.Search) + ' input[title!=""]').attr("value", "").blur();
        $(this.getElementID(this.ElementIDs.Search) + ' input[type="checkbox"]').attr("checked", "");
        $(this.getElementID(this.ElementIDs.SearchState)).attr("selectedIndex", 0);
        $(this.getElementID(this.ElementIDs.POIsButton)).css({ 'display': 'none', 'height': '0px' });
        $(this.getElementID(this.ElementIDs.POIsButton)).css({ 'display': 'none', 'height': '0px' });

        $(this.getElementID(this.ElementIDs.DirectionsButton)).css({ 'display': 'none', 'height': '20px' });
    },

    /// This method is used to validate the search fields. The following function has code
    /// to perform validation on the standard address fields. Use it as a guide for your
    /// custom search fields.
    validateSearchFields: function() {
        var haveValidSuburb = false;
        var haveValidPostcode = false;
        var txtSuburb = $(this.getElementID(this.ElementIDs.SearchSuburb));
        //var txtPostcode = $(this.getElementID(this.ElementIDs.SearchPostcode));

        if (txtSuburb.attr("value") != '' && txtSuburb.attr("value").toLowerCase() != txtSuburb.attr("title").toLowerCase()) {
            haveValidSuburb = true;
        }

        //else if (txtPostcode.attr("value") != '' &&
        //        txtPostcode.attr("value").toLowerCase() != txtPostcode.attr("title").toLowerCase() &&
        //        this.isNumeric(txtPostcode.attr("value"))) {
        //    haveValidPostcode = true;
        //}

        if (!haveValidSuburb) {
            this.displayException(this.Strings.InvalidSearch);
            return false;
        }

        return true;
    },

    /// Builds a MapDS.Address representation of the address to be geocoded.
    /// This method is automatically called.
    buildAddressSearch: function() {
        if (this.DebugMode) { Logger.info('buildAddressSearch'); }

        // load street information
        var txtStreet = $(this.getElementID(this.ElementIDs.SearchStreet));
        var street = txtStreet.attr("value");
        if (txtStreet.attr("value").toLowerCase() == txtStreet.attr("title").toLowerCase()) {
            street = '';
        }

        // load suburb information
        var txtSuburb = $(this.getElementID(this.ElementIDs.SearchSuburb));
        var suburb = txtSuburb.attr("value");
        if (txtSuburb.attr("value").toLowerCase() == txtSuburb.attr("title").toLowerCase()) {
            suburb = '';
        }

        // load postcode information
        //var txtPostcode = $(this.getElementID(this.ElementIDs.SearchPostcode));
        //var postcode = txtPostcode.attr("value");
        //if (txtPostcode.attr("value").toLowerCase() == txtPostcode.attr("title").toLowerCase()) {
        //    postcode = '';
        //}

        // load selected state value
        var state = '';
        var ddlState = $(this.getElementID(this.ElementIDs.SearchState));
        if (ddlState.attr("selectedIndex") != 0) {
            state = ddlState.attr("value");
        }

        return new MapDS.Address(street, suburb, '', null, state);
    },

    /// Builds a string representation of the search filters available.
    /// MUST return either an object or null. Last line should always be "return null;"
    /// instead of removing, return your object before this line.
    getSearchFiltersString: function() {
        var filters = [];

        $.each($(this.getElementID(this.ElementIDs.FiltersList) + ' input:checkbox'), function(i, checkbox) {
            if (!checkbox.checked) { return; }
            // here we replace " with \" so that the features service can use it.
            //var company = checkbox.value.replace('"', '\"');
            //filters.push('company="{0}"'.format(company));

            var colName = checkbox.value.replace('"', '\"');
            //alert(' Name : ' + colName);
            filters.push('{0}="1"'.format(colName));
        });

        if (filters.length > 0) {
            return "[(" + filters.join(" OR ") + ")]";
        }

        // the following return should always remain and be the last line.
        return null;
    },

    /// This method is called from within findNearest. The purpose of this function is to allow
    /// for control of how the marker is created and how the extra POI information is displayed.
    processPOI: function(id, poi) {
        if (this.DebugMode) { Logger.debug(poi); }
        var _SELF = this;
        var size = new MapDS.Size(26, 26);
        var offset = new MapDS.Pixel(-13, -26);
        var icn = new MapDS.Maps.Icon('./images/markers/{0}.png'.format(id), size, offset);
        var mkr = new MapDS.Maps.Marker(poi.position, { icon: icn });
        Logger.log(poi.properties.Shop_Unit_No);

        mkr.setPopupContent('<div class="popup-content"><img id="popIcon" src="{0}"/><h6 style="color:#4988BE;">{1}</h6><p>&nbsp</p></div><table class="popup-content-table" ><td align="left">{2}<p style="margin-bottom:6px;font-size:11px;">{3}{4}{5}</p>{6}{7}{8}{9}{10}{11}{12}{13}</td></table>'.format(
            icn.URL(),
            (poi.properties.DisplayName ? poi.properties.DisplayName : ''),
            (poi.properties.Shop_Unit_No ? "<p>" + poi.properties.Shop_Unit_No + "</p>" : ''),
            (poi.properties.Street != '' ? poi.properties.Street + ', ' : ''),
            (poi.properties.Suburb != '' ? poi.properties.Suburb + ', ' : ''),
            (poi.properties.State != '' ? poi.properties.State + '' : ''),
            ((poi.properties.Phone_Detail != '' && !_SELF.isUndefined(poi.properties.Phone_Detail)) ? '<p><strong>Ph:&nbsp&nbsp&nbsp&nbsp&nbsp </strong>' + poi.properties.Phone_Detail + '</p>' : ''),
            ((poi.properties.Email != '' && !_SELF.isUndefined(poi.properties.Email)) ? '<p><strong>Email: </strong>' + '<a href="mailto:' + poi.properties.Email + '">' + poi.properties.Email + '</a></p>' : ''),
        //((poi.properties.Website != '' && !_SELF.isUndefined(poi.properties.Website)) ? '<p><strong>Website: </strong>' + '<a href="' + poi.properties.Website + '">' + poi.properties.Website + '</a></p>' : ''),
            _SELF.buildValidURL(poi),
            ((poi.properties.StoreName != '' && !_SELF.isUndefined(poi.properties.StoreName)) ? '<br/><p><strong>Programme:</strong></p><p>' + poi.properties.StoreName + '</p>' : ''),
            _SELF.buildSchoolServices(poi),
            ((poi.properties.Oscar_SchoolsServiced != '' && !_SELF.isUndefined(poi.properties.Oscar_SchoolsServiced)) ? '<br/><p><strong>School Serviced: </strong></p><p>' + poi.properties.Oscar_SchoolsServiced + '</p>' : ''),
            ((poi.properties.Oscar_Comments != '' && !_SELF.isUndefined(poi.properties.Oscar_Comments)) ? '<br><p>' + poi.properties.Oscar_Comments + '</p>' : ''),
            '<br><a href="javascript:Locator.getDirections(' + id + ')">Driving Directions...</a>'
         ));


        var listItem = $('<li style="background: #ffffff url(./images/markers/{0}.png) no-repeat 3px 3px;" />'.format(id)).html('<h6>{0}</h6><p>{1}</p><p>{2}{3}{4}</p><p>{5}</p><p>{6}</p>'.format(
                (poi.properties.DisplayName ? '<b>' + poi.properties.DisplayName + '</b>' : 'undefined'),
                ((poi.properties.Shop_Unit_No != '' && !_SELF.isUndefined(poi.properties.Shop_Unit_No)) ? poi.properties.Shop_Unit_No : ''),
                ((poi.properties.Street != '' && !_SELF.isUndefined(poi.properties.Street)) ? poi.properties.Street + ', ' : ''),
                ((poi.properties.Suburb != '' && !_SELF.isUndefined(poi.properties.Suburb)) ? poi.properties.Suburb + ', ' : ''),
                poi.properties.State,
                ((poi.properties.Phone_Detail != '' && !_SELF.isUndefined(poi.properties.Phone_Detail)) ? 'Ph: ' + poi.properties.Phone_Detail + ' ' : ''),
                '<a href="#">Programme details...</a>'
             )).click(function() {
                 _SELF.map.setCentre(mkr.getPoint(), _SELF.map.getZoom()); mkr.showPopup();
                 if (!$(_SELF.getElementID(_SELF.ElementIDs.POIsWrapper)).is(":hidden")) { _SELF.toggleDropdown(2); }
             }).appendTo(_SELF.getElementID(_SELF.ElementIDs.POIsList));

        $('<img src="./images/car.png" class="drivingLink" height="16px" width="16px" alt="Driving Directions" title="Driving Directions" />').click(function() {
            _SELF.getDirections(id); return false;
        }).appendTo(listItem);

        return mkr;
    },

    buildSchoolServices: function(poi) {
        var _SELF = this;

        var listItem = '{0}{1}{2}{3}'.format(
        ((poi.properties.BeforeSchoolCare != 0 && !_SELF.isUndefined(poi.properties.BeforeSchoolCare)) ? '<p>Before school programme</P>' : ''),
        ((poi.properties.AfterSchoolCare != 0 && !_SELF.isUndefined(poi.properties.AfterSchoolCare)) ? '<p>After school programme</p>' : ''),
        ((poi.properties.HolidayProgramme != 0 && !_SELF.isUndefined(poi.properties.HolidayProgramme)) ? '<p>Holiday programme</p>' : ''),
        ((poi.properties.Camp != 0 && !_SELF.isUndefined(poi.properties.HolidayProgramme)) ? '<p>Camp</p>' : '')
        );
        if (listItem) return '<br/><p><strong>Services: </strong></p>' + listItem;
        else return '';
    },

    buildValidURL: function(poi) {

        var _SELF = this;
        var vurl = ((poi.properties.ExternalUri != 0 && !_SELF.isUndefined(poi.properties.ExternalUri)) ? poi.properties.ExternalUri : '');
        if (vurl != '') {
            if (vurl.indexOf('http://') < 0) {
                vurl = '{0}{1}{2}'.format(
                '<p><strong>Website: </strong>',
                '<a href="http://' + poi.properties.ExternalUri,
                '" target="_blank">' + poi.properties.ExternalUri + '</a></p>'
                );
            }
            else {
                vurl = '{0}{1}{2}'.format(
                '<p><strong>Website: </strong>',
                '<a href="' + vurl + '" target="_blank">',
                vurl + '</a></p>'
                );
            }
        }

        return vurl;

    },

    /// This method is called from within findNearest. The purpose of this function is to allow
    /// for control of how the searched location marker is created and how the extra information is displayed.
    processLocationPOI: function(poi) {
        if (this.DebugMode) { Logger.debug(poi); }

        var size = new MapDS.Size(26, 26);
        var offset = new MapDS.Pixel(-13, -26);
        var locicn = new MapDS.Maps.Icon('./images/markers/green-callout.png', size, offset);

        poi.position = new MapDS.LatLng(poi.Position.Latitude, poi.Position.Longitude);



        var mkr = new MapDS.Maps.Marker(poi.position, { icon: locicn });
        mkr.setPopupContent('<div class="popup-content"><h5>Your searched location</h5><p>{0}{1}{2}</p></div>'.format(
                (poi.AddressLine != '' ? poi.AddressLine + ', ' : ''),
                (poi.Locality != '' ? poi.Locality + ', ' : ''),
                poi.Region
            )
        );
        this.map.addOverlay(mkr);

        // DO NOT REMOVE THE FOLLOWING LINE - It is needed for routing purposes.
        this.locationMarker = mkr;
    },


    /// Start of core Locator code

    map: null, // object that contains the QuickMap instance
    geocoder: new MapDS.Maps.Geocoder(),
    directionsHelper: null,
    RouteMode: 1, // eventually this will be a setting that can change therefore it will move to the top of the script file.
    locationMarker: null,
    DebugMode: false,

    _foundLocations: null, // private
    _lastSearchedLocation: null, // private
    _pois: [], // private
    _lastFieldFocussed: null, // private, the last search field to have focus

    /// Returns true if object is undefined
    isUndefined: function(object) { return typeof object === "undefined"; },

    //Check IE version
    BrowserVersion: function() {
        var version = 999; // we assume a sane browser    
        if (navigator.appVersion.indexOf("MSIE") != -1)
        // bah, IE again, lets downgrade version number      
            version = parseFloat(navigator.appVersion.split("MSIE")[1]);
        return version;
    },

    /// Private method
    /// Returns true if object is an Image
    /// IE doesn't support "instanceof Image" so instead we test that the object is in the DOM then test the tagName is 'IMG'
    isImage: function(object) { return this.isHTMLElement(object, 'IMG'); },

    /// Returns true if object is an HTML Element
    /// {type} is a string that represents the type of HTML element that the object should be.
    ///      for example. if [type] is "div" then the element should be a div.
    isHTMLElement: function(object, type) { if (!type || !(typeof type === "string")) { return false; } if (object && object.nodeType) { return (object.tagName === type.toUpperCase() ? true : false); } return false; },

    /// Returns true if input is a number
    isNumeric: function(input) { return (input - 0) == input && input.length > 0; },

    /// Returns true if object is a function
    isFunction: function(object) { return typeof object === "function"; },

    /// Returns the id string asked for, will prepend # unless the second parameter is false.
    getElementID: function(id, withHash) { if (this.isUndefined(withHash)) { withHash = true; } return (withHash ? "#" : "") + id; },

    /// Will fire a call to load up the exception dialog, the HTML will be set using the message parameter.
    displayException: function(errorMessage) {
        $(this.getElementID(this.ElementIDs.ExceptionDialog)).html(errorMessage).dialog('open');
        var ver = this.BrowserVersion();
        //alert(ver + " ver");
        if (ver > 6) this.reset();
    },

    /// Will save a POI as the last location.
    saveLocationPOI: function(poi) { this._lastSearchedLocation = poi; },

    /// Formats a distance into a string representation.
    formatDistance: function(d) {
        d = parseInt(d, 10);
        if (d < 1000) { return d + ' metres'; }
        d = d / 1000;
        return d + ' kilometres';
    },

    /// Resets everything! 
    reset: function() {
        this._foundLocations = null;
        this._lastSearchedLocation = null;
        this.locationMarker = null;
        this._pois = [];
        this.resetDropdowns();
        if (this.map instanceof MapDS.Maps.Map) {
            this.map.clearOverlays();
            this.map.setCentre(this.InitialCentre, this.InitialZoom);
        }
        // User defined method
        this.resetSearchFields();
    },

    /// Resets the content of the POIs and Direction dropdowns.
    resetDropdowns: function() {
    //$(this.getElementID(this.ElementIDs.DrivingDirectionsList)).html('<p>Please perform a search.</p>');
        $(this.getElementID(this.ElementIDs.DrivingDirectionsList)).html('');
        $(this.getElementID(this.ElementIDs.POIsFound)).html('');
        $(this.getElementID(this.ElementIDs.POIsList)).html('<p>Please perform a search.</p>');
        if (!$(this.getElementID(this.ElementIDs.DrivingDirectionsList)).is(":hidden")) { this.toggleDropdown(3); }
        if (!$(this.getElementID(this.ElementIDs.POIsWrapper)).is(":hidden")) { this.toggleDropdown(2); }
        if (!$(this.getElementID(this.ElementIDs.FiltersList)).is(":hidden")) { this.toggleDropdown(1); }
        $(this.getElementID(this.ElementIDs.StartTitle)).css({ 'display': 'none' });
        $(this.getElementID(this.ElementIDs.EndTitle)).css({ 'display': 'none' });
        $(this.getElementID(this.ElementIDs.PrintLinkButton)).css({ 'display': 'none' });
    },

    /// Performs a call to geocode in order to geocode an address. 
    findAddress: function() {
        if (!this.validateSearchFields()) { return; }

        // lock down the find button
        //$( this.getElementID( this.ElementIDs.SearchButton ) ).attr('disabled', 'true');

        var _SELF = this;


        this.geocoder.setCountryCode('nz');
        this.geocode(this.buildAddressSearch(), function(address) { _SELF.findNearest(address); });
    },

    /// Performs a call to Geocode an address. If a single match is found then the method 
    /// will automatically call findNearest. Multiple matches are displayed within a 
    /// dialog for the user to select.
    geocode: function(addressToGeocode, loadAddressCallBack) {
        if (this.DebugMode) { Logger.info('geocode'); }

        // make jQuery Ajax call...
        var _SELF = this;
        _SELF.geocoder.getLocations(addressToGeocode, function(result) {
            if (this.DebugMode) { Logger.log(result); }

            if (result.Code === 1) {
                loadAddressCallBack(result.Locations[0]);
            } else if (result.Code === 100) {
                _SELF._foundLocations = result.Locations;

                $(_SELF.getElementID(_SELF.ElementIDs.MultipleAddressDialog)).find(
                        _SELF.getElementID(_SELF.ElementIDs.MultipleAddresses)).html('');

                $("<option/>").attr('value', '0').html(_SELF.Strings.MultipleAddressPrompt).appendTo(
                        $(_SELF.getElementID(_SELF.ElementIDs.MultipleAddressDialog)).find(
                        _SELF.getElementID(_SELF.ElementIDs.MultipleAddresses)));

                $.each(result.Locations, function(i, address) {
                    $("<option/>").html((address.AddressLine != '' ? address.AddressLine + ', ' : '') +
                        address.Locality + ', ' + address.Region + ', ' + address.PostalCode).appendTo(
                        $(_SELF.getElementID(_SELF.ElementIDs.MultipleAddressDialog)).find(
                        _SELF.getElementID(_SELF.ElementIDs.MultipleAddresses)));
                });

                // unlock down the find button
                //$(_SELF.getElementID(_SELF.ElementIDs.SearchButton)).attr('disabled', '');

                //For IE6 it has to hidden
                $(_SELF.getElementID(_SELF.ElementIDs.SearchState)).css({ 'visibility': 'hidden' });
                $(_SELF.getElementID(_SELF.ElementIDs.MultipleAddressDialog)).dialog('open');



            } else if (result.Code > 100) {
                var emsg = result.Message;
                if (result.Message.indexOf('Could not find address') > -1) emsg = 'We were unable to find the location specified. Please try new information.';
                _SELF.displayException(emsg);
            }

        });
    },

    /// Calls findnearest.ashx passing X/Y and search filters. Results are displayed.
    findNearest: function(address) {
        if (this.DebugMode) { Logger.info('findNearest'); }

        var _SELF = this;
        var pt = new MapDS.LatLng(address.Position.Latitude, address.Position.Longitude);

        var fo = new MapDS.Maps.FindOptions(pt, _SELF.PoiSearchBuffer, _SELF.getSearchFiltersString(), _SELF.PoiSearchRecordLimit);
        _SELF.map.Find(fo, function(results) {
            //if(this.DebugMode){Logger.log(results);}

            // TODO: handle any errors from the service
            if (results.features.length < 1) {

                var dd = $(_SELF.getElementID(_SELF.ElementIDs.NoResultDialog));
                if (_SELF.getSearchFiltersString()) {
                    dd.html('<p class="question">There are no programmes within 30km of your address. Please try another search.</p>');
                }
                else {
                    dd.html('<p class="question">There are no programmes within 30km of your address.</p>');
                }
                $(_SELF.getElementID(_SELF.ElementIDs.NoResultDialog)).dialog('open');

            } else {


                _SELF._pois = [];
                _SELF.resetDropdowns();
                _SELF.map.clearOverlays();

                // build the search caption
                var searchedAddress = new String(
                (address.Locality != '' ? address.Locality.toProperCase() + ', ' : '') +
                (address.Region != '' ? address.Region + ', ' : '')
                //(address.PostalCode != '' ? address.PostalCode + ', ' : '')
            );
                searchedAddress = searchedAddress.substr(0, searchedAddress.lastIndexOf(', '));



                $(_SELF.getElementID(_SELF.ElementIDs.POIsFound)).html(
                //_SELF.Strings.POIsFound.format(results.features.length,
                //    (results.features.length > 1 ? 's' : '')) + ' for ' +
                //    _SELF.getFriendlySearchFiltersString() + searchedAddress
//                _SELF.Strings.POIsFound.format('<b>' + results.features.length,
//                    (results.features.length > 1 ? 's' : '')) + ' for ' +
//                    searchedAddress + '</b>'
                _SELF.Strings.POIsFound.format('<b>' + results.features.length,
                    searchedAddress + '</b>')
                );

                if (results.features.length > 0) {
                    //$(_SELF.getElementID(_SELF.ElementIDs.DrivingDirectionsList)).html('<p>Please choose a destination.</p>');
                    $(_SELF.getElementID(_SELF.ElementIDs.DrivingDirectionsList)).html('');
                    $(_SELF.getElementID(_SELF.ElementIDs.POIsList)).html('');
                    $(_SELF.getElementID(_SELF.ElementIDs.StartTitle)).html('');
                    $(_SELF.getElementID(_SELF.ElementIDs.EndTitle)).html('');
                    $.each(results.features, function(i, poi) {
                        var id = i + 1;
                        poi.Marker = _SELF.processPOI(id, poi)
                        _SELF._pois.push(poi);
                        _SELF.map.addOverlay(poi.Marker);
                    });

                    _SELF.processLocationPOI(address);
                    _SELF.saveLocationPOI(address);
                    _SELF.map.loadBestView(pt);
                } /* else {
                $( _SELF.getElementID( _SELF.ElementIDs.POIsList ) ).html('');
            }*/

                if ($(_SELF.getElementID(_SELF.ElementIDs.POIsWrapper)).is(":hidden")) {
                    _SELF.toggleDropdown(2); $(_SELF.getElementID(_SELF.ElementIDs.POIsButton)).css({ 'display': 'inline-block', 'height': '20px' });
                    $(_SELF.getElementID(_SELF.ElementIDs.ResultsButton)).css({ 'display': 'inline-block', 'height': '20px', 'margin-top': '5px' });
                    $(_SELF.getElementID(_SELF.ElementIDs.PrintLinkButton)).css({ 'display': 'block' });
                }


            }
        });
    },

    /// Gets a string summary representation of the selected filters.
    getFriendlySearchFiltersString: function() {
        var filters = [];

        $.each($(this.getElementID(this.ElementIDs.FiltersList) + ' input:checkbox'), function(i, checkbox) {
            if (!checkbox.checked) { return; }
            var company = checkbox.value;
            filters.push(company);
        });

        if (filters.length > 0) {
            var lastFilter = '';
            if (filters.length > 1) {
                lastFilter = ' and ' + filters.pop();
            }
            return filters.join(", ") + lastFilter + ' in ';
        }

        // the following return should always remain and be the last line.
        return '';
    },

    /// Executes a request for driving directions for the given POI id.
    getDirections: function(id) {
        var _SELF = this;


        if (_SELF.map.hasVisiblePopup()) { _SELF.map.removePopups(); }

        if (!_SELF.isUndefined(_SELF._pois) && _SELF._pois.length >= id) {
            id -= 1; // decrement to get array position
            _SELF.directionsHelper.clear();
            var st = _SELF._lastSearchedLocation.position;
            var ed = (_SELF._pois[id].position instanceof MapDS.LatLng ? _SELF._pois[id].position : new MapDS.LatLng(_SELF._pois[id].Position.Latitude, _SELF._pois[id].Position.Longitude));
            if (this.RouteMode === 1) {
                var dd = $(_SELF.getElementID(_SELF.ElementIDs.DrivingDirectionsList));
                dd.html('<p><img src="./images/loading-circle-black.gif" alt="Loading" height="16px" width="16px" /> Loading</p>');
                _SELF.directionsHelper.buildFromWaypoints([st, ed]);

                //**************
                var stTitle = $(_SELF.getElementID(_SELF.ElementIDs.StartTitle));

                stTitle.html($('<h4 />'.format(id)).html('Start: {0}{1}{2}'.format(
                            ((_SELF._lastSearchedLocation.AddressLine != '' && !_SELF.isUndefined(_SELF._lastSearchedLocation.AddressLine)) ? _SELF._lastSearchedLocation.AddressLine.toProperCase() + ', ' : ''),
                            ((_SELF._lastSearchedLocation.Locality != '' && !_SELF.isUndefined(_SELF._lastSearchedLocation.Locality)) ? _SELF._lastSearchedLocation.Locality.toProperCase() + ', ' : ''),
                            ((_SELF._lastSearchedLocation.Region != '' && !_SELF.isUndefined(_SELF._lastSearchedLocation.Region)) ? _SELF._lastSearchedLocation.Region + '' : '')
                        )));

                var edTitle = $(_SELF.getElementID(_SELF.ElementIDs.EndTitle));
                edTitle.html($('<h4 />'.format(id)).html('End: {0}{1}{2}{3}'.format(
                            ((_SELF._pois[id].properties.DisplayName != '' && !_SELF.isUndefined(_SELF._pois[id].properties.DisplayName)) ? _SELF._pois[id].properties.DisplayName.toProperCase() + ', ' : ''),
                            ((_SELF._pois[id].properties.Street != '' && !_SELF.isUndefined(_SELF._pois[id].properties.Street)) ? _SELF._pois[id].properties.Street.toProperCase() + ', ' : ''),
                            ((_SELF._pois[id].properties.Suburb != '' && !_SELF.isUndefined(_SELF._pois[id].properties.Suburb)) ? _SELF._pois[id].properties.Suburb + ', ' : ''),
                            ((_SELF._pois[id].properties.State != '' && !_SELF.isUndefined(_SELF._pois[id].properties.State)) ? _SELF._pois[id].properties.State + '' : '')
                        )));



                //************


                if (dd.is(":hidden")) { _SELF.toggleDropdown(3); }

            }
        }
    },

    /// Toggles the directions dropdown
    toggleDropdown: function(dropDown) {
        var _SELF = this;
        var A = null;
        var B = null;
        if (dropDown === 1) {
            // Filters
            if (!$(_SELF.getElementID(_SELF.ElementIDs.DrivingDirectionsList)).is(":hidden")) { _SELF.toggleDropdown(3); }
            //if (!$(_SELF.getElementID(_SELF.ElementIDs.DrivingDirectionsWrapper)).is(":hidden")) { _SELF.toggleDropdown(3); }
            if (!$(_SELF.getElementID(_SELF.ElementIDs.POIsWrapper)).is(":hidden")) { _SELF.toggleDropdown(2); }
            //A = $(_SELF.getElementID(_SELF.ElementIDs.FiltersList));
            //B = $(_SELF.getElementID(_SELF.ElementIDs.FiltersButton));
        } else if (dropDown === 2) {           // POIs
            if (!$(_SELF.getElementID(_SELF.ElementIDs.DrivingDirectionsList)).is(":hidden")) { _SELF.toggleDropdown(3); }
            //if (!$(_SELF.getElementID(_SELF.ElementIDs.DrivingDirectionsWrapper)).is(":hidden")) { _SELF.toggleDropdown(3); }
            //if (!$(_SELF.getElementID(_SELF.ElementIDs.FiltersList)).is(":hidden")) { _SELF.toggleDropdown(1); }
            A = $(_SELF.getElementID(_SELF.ElementIDs.POIsWrapper));
            B = $(_SELF.getElementID(_SELF.ElementIDs.POIsButton));
            $(_SELF.getElementID(_SELF.ElementIDs.POIsButton)).css({ 'display': 'inline-block' });
            $(_SELF.getElementID(_SELF.ElementIDs.Map)).toggleClass('mapResultsOpen');

            _SELF.map.setCentre(_SELF.map.getCentre(), _SELF.map.getZoom(), true);

        } else if (dropDown === 3) {
            // Directions
            if (!$(_SELF.getElementID(_SELF.ElementIDs.POIsWrapper)).is(":hidden")) { _SELF.toggleDropdown(2); }
            //if (!$(_SELF.getElementID(_SELF.ElementIDs.FiltersList)).is(":hidden")) { _SELF.toggleDropdown(1); }
            A = $(_SELF.getElementID(_SELF.ElementIDs.DrivingDirectionsList));
            //A = $(_SELF.getElementID(_SELF.ElementIDs.DrivingDirectionsWrapper));
            B = $(_SELF.getElementID(_SELF.ElementIDs.DirectionsButton));

            $(_SELF.getElementID(_SELF.ElementIDs.StartTitle)).css({ 'display': 'block' });
            $(_SELF.getElementID(_SELF.ElementIDs.EndTitle)).css({ 'display': 'block' });

            $(_SELF.getElementID(_SELF.ElementIDs.DirectionsButton)).css({ 'display': 'inline-block', 'position': 'relative', 'top': '0px' });
            $(_SELF.getElementID(_SELF.ElementIDs.Map)).toggleClass('mapResultsOpen');
        }
        if (!A || !B) { return; }
        var C = B.offset();
        A.css();
        //B.toggleClass("menu-open"); 
        A.toggle();
    },

    /// This method is hooked into the window.onload within the init method.
    initMap: function(lat, lng, zoom) {
        this.map = new MapDS.Maps.Map(this.getElementID(this.ElementIDs.Map, false), { zoom: this.InitialZoom, centre: this.InitialCentre });
        this.directionsHelper = new MapDS.Maps.Directions(this.map, (this.ElementIDs.DrivingDirectionsList ? document.getElementById(this.ElementIDs.DrivingDirectionsList) : null));
    },

    PrinterFriendly: function() {
        var _SELF = this;
        var newWin;
        newWin = window.open("PrintPage.aspx", "kid", 'resizable=yes,menubar=1,toolbar=1,scrollbars=yes,width=900,height=650,toolbar=no');
    },

    /// The main initialisation method for the Locator object.
    init: function() {
        var _SELF = this;
        $().ready(function() {

            // Grab all anchor elements on the page that have an href and target is external. Set their target to blank.
            $('a[rel="external"]').filter('[href!=""]').each(function() { this.target = "_blank"; });

            $(_SELF.getElementID(_SELF.ElementIDs.SearchButton)).click(function() { _SELF.findAddress(); });
            $(_SELF.getElementID(_SELF.ElementIDs.ApplyFiltersButton)).click(function() { _SELF.findAddress(); });
            $(_SELF.getElementID(_SELF.ElementIDs.ResetButton)).click(function() { _SELF.reset(); });
            $(_SELF.getElementID(_SELF.ElementIDs.DirectionsButton)).click(function() { _SELF.toggleDropdown(3); });
            $(_SELF.getElementID(_SELF.ElementIDs.POIsButton)).click(function() { _SELF.toggleDropdown(2); });
            $(_SELF.getElementID(_SELF.ElementIDs.StartTitle)).css({ 'display': 'none' });
            $(_SELF.getElementID(_SELF.ElementIDs.EndTitle)).css({ 'display': 'none' });
            $(_SELF.getElementID(_SELF.ElementIDs.PrintLinkButton)).css({ 'display': 'none' });
            //$(_SELF.getElementID(_SELF.ElementIDs.FiltersButton)).click(function() { _SELF.toggleDropdown(1); });

            // need to have the current text blanked on focus, reset on blur...
            $(_SELF.getElementID(_SELF.ElementIDs.Search) + ' input[title!=""]').keypress(function(evt) { _SELF._lastFieldFocussed = this; if (evt.keyCode == 13) { $(_SELF.getElementID(_SELF.ElementIDs.SearchButton)).click(); } }).hint();
            // ensure that Enter presses on the form don't cause a submit
            $("form").keypress(function(e) { if (e.keyCode == 13) { return false; } });

            // attach the exception dialog
            $(_SELF.getElementID(_SELF.ElementIDs.ExceptionDialog)).dialog({
                bgiframe: true,
                modal: true,
                draggable: false,
                resizable: false,
                autoOpen: false,
                zIndex: 99999,
                close: function(evt, ui) {
                    if (_SELF._lastFieldFocussed) {
                        _SELF._lastFieldFocussed.focus();
                    }
                },
                buttons: { Ok: function() { $(this).dialog('close'); } }
            });


            // attach the No Locations dialog
            $(_SELF.getElementID(_SELF.ElementIDs.NoResultDialog)).dialog({
                bgiframe: true,
                modal: true,
                draggable: false,
                resizable: false,
                autoOpen: false,
                zIndex: 99999,
                close: function(evt, ui) {
                    if (_SELF._lastFieldFocussed) {
                        _SELF._lastFieldFocussed.focus();
                    }
                },
                buttons: { Ok: function() { $(this).dialog('close'); } }
            });

            // attach the multiple results dialog.
            $(_SELF.getElementID(_SELF.ElementIDs.MultipleAddressDialog)).dialog({
                bgiframe: true,
                height: 300,
                width: 400,
                modal: true,
                draggable: false,
                resizable: false,
                autoOpen: false,
                zIndex: 99999,
                buttons: {
                    'Use Address': function() {
                        var idx = $(this).find(_SELF.getElementID(_SELF.ElementIDs.MultipleAddresses)).attr('selectedIndex');
                        if (idx != 0) {
                            // go back one to get correct array location.
                            idx--;
                            // Call FindNearest....

                            _SELF.findNearest(_SELF._foundLocations[idx]);
                        }
                        $(this).dialog('close');
                        $(_SELF.getElementID(_SELF.ElementIDs.SearchState)).css({ 'visibility': 'visible' });
                    },
                    Cancel: function() {
                        $(this).dialog('close');
                        $(_SELF.getElementID(_SELF.ElementIDs.SearchState)).css({ 'visibility': 'visible' });
                    }
                    // for ie6, release again    

                }
            });

            // handle the resizing of the browser window.
            var currentMapWidth = $(_SELF.getElementID(_SELF.ElementIDs.Map)).width();
            var currentMapHeight = $(_SELF.getElementID(_SELF.ElementIDs.Map)).height();
            $(window).wresize(function() {
                /*var w = $( window );
                var H = w.height();
                var W = w.width();*/
                var mW = $(_SELF.getElementID(_SELF.ElementIDs.Map)).width();
                var mH = $(_SELF.getElementID(_SELF.ElementIDs.Map)).height();
                if (mW !== currentMapWidth || mH !== currentMapHeight) {
                    currentMapWidth = mW;
                    currentMapHeight = mH;
                    if (_SELF.map instanceof MapDS.Maps.Map) {
                        _SELF.map.setCentre(_SELF.map.getCentre(), _SELF.map.getZoom(), true);
                    }
                }
            });

            _SELF.reset();

            // need to wrap the call in a function so we can maintain scope
            window.onload = function() { _SELF.initMap.apply(_SELF); };

        });
    }

};
Locator.init();


