/*	SOURCE FILE: markermanager.js	*/
eval(function(p,a,c,k,e,r){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)r[e(c)]=k[c]||e(c);k=[function(e){return r[e]}];e=function(){return'\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('5 4(c,b){2 e=3;e.13=c;e.D=c.1k();e.1h=c.1V().1N();b=b||{};e.N=4.1f;2 g=c.29();2 h=g[0].1o();l(2 i=0;i<g.E;i++){2 f=g[i].1o();7(f>h){h=f}}e.p=b.1J||h;e.18=b.1E;e.m=b.15||C;2 d;7(28 b.1s==="24"){d=b.1s}11{d=4.1q}e.1p=w q(-d,d);e.1j=w q(d,-d);e.1Y=d;e.B=[];e.H=[];e.H[e.p]=[];e.s=[];e.s[e.p]=0;X.1e(c,"1U",e,e.1d);e.r=5(a){c.1L(a);e.G--};e.t=5(a){7(e.m){c.1G(a);e.G++}};e.U();e.G=0;e.8=e.V()}4.1f=1C;4.1q=1z;4.1w=1x;4.6.U=5(){2 a=3;2 c=4.1w;l(2 b=0;b<=a.p;++b){a.H[b]=[];a.s[b]=0;a.B[b]=o.2c(c/a.N);c<<=1}};4.6.27=5(){2 a=3;a.v(a.8,a.r);a.U()};4.6.n=5(a,c,b){2 d=3.1h.25(a,c);9 w 23(o.1r((d.x+b.22)/3.N),o.1r((d.y+b.1Z)/3.N))};4.6.10=5(e,a,f){2 b=e.Z();e.1n=a;7(3.18){X.1e(e,"1m",3,3.1l)}2 d=3.n(b,f,q.A);l(2 c=f;c>=a;c--){2 g=3.Y(d.x,d.y,c);g.1i(e);d.x=d.x>>1;d.y=d.y>>1}};4.6.F=5(e){2 a=3;2 c=a.8.J<=e.y&&e.y<=a.8.I;2 f=a.8.M;2 d=f<=e.x&&e.x<=a.8.K;7(!d&&f<0){2 b=a.B[a.8.z];d=f+b<=e.x&&e.x<=b-1}9 c&&d};4.6.1l=5(e,i,g){2 c=3;2 a=c.p;2 f=O;2 h=c.n(i,a,q.A);2 d=c.n(g,a,q.A);1g(a>=0&&(h.x!==d.x||h.y!==d.y)){2 b=c.L(h.x,h.y,a);7(b){7(c.W(b,e)){c.Y(d.x,d.y,a).1i(e)}}7(a===c.D){7(c.F(h)){7(!c.F(d)){c.r(e);f=C}}11{7(c.F(d)){c.t(e);f=C}}}h.x=h.x>>1;h.y=h.y>>1;d.x=d.x>>1;d.y=d.y>>1;--a}7(f){c.u()}};4.6.1T=5(e){2 c=3;2 b=c.p;2 a=O;2 f=e.Z();2 d=c.n(f,b,q.A);1g(b>=0){2 g=c.L(d.x,d.y,b);7(g){c.W(g,e)}7(b===c.D){7(c.F(d)){c.r(e);a=C}}d.x=d.x>>1;d.y=d.y>>1;--b}7(a){c.u()}c.s[e.1n]--};4.6.1S=5(b,a,c){2 d=3.R(c);l(2 i=b.E-1;i>=0;i--){3.10(b[i],a,d)}3.s[a]+=b.E};4.6.R=5(a){9 a||3.p};4.6.1Q=5(a){2 b=0;l(2 z=0;z<=a;z++){b+=3.s[z]}9 b};4.6.1P=5(e,b,a){2 d=3;2 h=w 1O(e,b);2 g=d.n(h,a,q.A);2 f=w 1M(h);2 c=d.L(g.x,g.y,a);7(c!=1b){l(2 i=0;i<c.E;i++){7(e==c[i].1a().1K()&&b==c[i].1a().T()){f=c[i]}}}9 f};4.6.1I=5(e,a,c){2 b=3;2 f=3.R(c);b.10(e,a,f);2 d=b.n(e.Z(),b.D,q.A);7(b.F(d)&&a<=b.8.z&&b.8.z<=f){b.t(e);b.u()}3.s[a]++};19.6.1H=5(a){2 b=3;9(b.M<=a.x&&b.K>=a.x&&b.J<=a.y&&b.I>=a.y)};4.6.Y=5(x,y,z){2 b=3.H[z];7(x<0){x+=3.B[z]}2 c=b[x];7(!c){c=b[x]=[];9(c[y]=[])}2 a=c[y];7(!a){9(c[y]=[])}9 a};4.6.L=5(x,y,z){2 a=3.H[z];7(x<0){x+=3.B[z]}2 b=a[x];9 b?b[y]:1b};4.6.17=5(j,b,c,e){b=o.S(b,3.p);2 i=j.1F();2 f=j.1D();2 d=3.n(i,b,c);2 g=3.n(f,b,e);2 a=3.B[b];7(f.T()<i.T()||g.x<d.x){d.x-=a}7(g.x-d.x+1>=a){d.x=0;g.x=a-1}2 h=w 19([d,g]);h.z=b;9 h};4.6.V=5(){2 a=3;9 a.17(a.13.1R(),a.D,a.1p,a.1j)};4.6.1d=5(){2 a=3;a.16(3,3.1c,0)};4.6.16=5(b,a,c){9 1B.1A(5(){a.1W(b)},c)};4.6.1X=5(){9 3.m?C:O};4.6.1y=5(){9!3.m};4.6.15=5(){3.m=C;3.P()};4.6.20=5(){3.m=O;3.P()};4.6.21=5(){3.m=!3.m;3.P()};4.6.P=5(){2 a=3;7(a.G>0){a.v(a.8,a.r)}7(a.m){a.v(a.8,a.t)}a.u()};4.6.1c=5(){2 a=3;a.D=3.13.1k();2 b=a.V();7(b.2d(a.8)&&b.z===a.8.z){9}7(b.z!==a.8.z){a.v(a.8,a.r);7(a.m){a.v(b,a.t)}}11{a.14(a.8,b,a.1v);7(a.m){a.14(b,a.8,a.1u)}}a.8=b;a.u()};4.6.u=5(){X.2b(3,"1m",3.8,3.G)};4.6.v=5(b,a){l(2 x=b.M;x<=b.K;x++){l(2 y=b.J;y<=b.I;y++){3.Q(x,y,b.z,a)}}};4.6.Q=5(x,y,z,a){2 b=3.L(x,y,z);7(b){l(2 i=b.E-1;i>=0;i--){a(b[i])}}};4.6.1v=5(x,y,z){3.Q(x,y,z,3.r)};4.6.1u=5(x,y,z){3.Q(x,y,z,3.t)};4.6.14=5(c,d,a){2 b=3;b.1t(c,d,5(x,y){a.2a(b,[x,y,c.z])})};4.6.1t=5(j,k,b){2 f=j.M;2 a=j.J;2 d=j.K;2 h=j.I;2 g=k.M;2 c=k.J;2 e=k.K;2 i=k.I;2 x,y;l(x=f;x<=d;x++){l(y=a;y<=h&&y<c;y++){b(x,y)}l(y=o.12(i+1,a);y<=h;y++){b(x,y)}}l(y=o.12(a,c);y<=o.S(h,i);y++){l(x=o.S(d+1,g)-1;x>=f;x--){b(x,y)}l(x=o.12(f,e+1);x<=d;x++){b(x,y)}}};4.6.W=5(a,c,b){2 d=0;l(2 i=0;i<a.E;++i){7(a[i]===c||(b&&a[i]===c)){a.26(i--,1);d++}}9 d};',62,138,'||var|this|MarkerManager|function|prototype|if|shownBounds_|return||||||||||||for|show_|getTilePoint_|Math|maxZoom_|GSize|removeOverlay_|numMarkers_|addOverlay_|notifyListeners_|processAll_|new||||ZERO|gridWidth_|true|mapZoom_|length|isGridPointVisible_|shownMarkers_|grid_|maxY|minY|maxX|getGridCellNoCreate_|minX|tileSize_|false|refresh|processCellMarkers_|getOptMaxZoom_|min|lng|resetManager_|getMapGridBounds_|removeFromArray_|GEvent|getGridCellCreate_|getPoint|addMarkerBatch_|else|max|map_|rectangleDiff_|show|objectSetTimeout_|getGridBounds_|trackMarkers_|GBounds|getLatLng|undefined|updateMarkers_|onMapMoveEnd_|bind|DEFAULT_TILE_SIZE_|while|projection_|push|nePadding_|getZoom|onMarkerMoved_|changed|MarkerManager_minZoom|getMaximumResolution|swPadding_|DEFAULT_BORDER_PADDING_|floor|borderPadding|rectangleDiffCoords_|addCellMarkers_|removeCellMarkers_|MERCATOR_ZOOM_LEVEL_ZERO_RANGE|256|isHidden|100|setTimeout|window|1024|getNorthEast|trackMarkers|getSouthWest|addOverlay|containsPoint|addMarker|maxZoom|lat|removeOverlay|GMarker|getProjection|GLatLng|getMarker|getMarkerCount|getBounds|addMarkers|removeMarker|moveend|getCurrentMapType|call|visible|borderPadding_|height|hide|toggle|width|GPoint|number|fromLatLngToPixel|splice|clearMarkers|typeof|getMapTypes|apply|trigger|ceil|equals'.split('|'),0,{}))

/*	SOURCE FILE: functions.3.0.3.2.js	*/
/**
 * [File : functions.3.0.3.2.js]
 *
 * Project:         MPS CrimeMapping
 * Version:         3.0.3.2.
 * Last change:     23/03/09
 * Lead Developer:  Diego Lago, WTG
 * Reviewed:        Yaseen Chattun, MPS, 14/08/08
 *
 * [Table of contents]
 *
 *  global variables declaration
 * 
 *  overlayBoroughs()
 *  overlayWards()
 *  overalayLSOA()
 *  infoWindowBuilder()
 *  showLocation()
 *  updateHelp()
 *  updateBreadcrumb()
 *  ajaxRequest()
 *  checkBounds()
 *  tabsBuilder()
 *  removePolygonOverlays()
 *  removeMarkerOverlays()
 *  createMarker()
 *  markerBuilder()
 *  displayResults()
 *  polyBuilder()
 *  showlink()
 *  validateAddress()
 *  textBoxProcessor()
 *  searchSubmit()
 *  refineMap()
 *  getSublevelArea()
 *  ajaxTabs()
 *  setHeight()
 *  showMapsOnLoad()
 *  show searh panels [obj]
 *  autocompleteFindValue()
 *  autocompleteSelectItem()
 *  autocompleteFormatItem()
 *  autocompleteLookupLocal()
 *  Contains()
 *  Map object and Controls [obj]
 *  zoomEndListener()
 *  moveStartListener()
 *  moveEndListener()
 *  default overlay
 *  jQuery DOM manipulation and effects
 */


/**
 * global variables declaration
 */
var currentCodeArea = 'MPS',
    textBoxBoroughCode = 'MPS',
    boroughArray = [['Barking & Dagenham','00AB'],['Barnet','00AC'],['Bexley','00AD'],['Brent','00AE'],['Bromley','00AF'],['Camden','00AG'],['Croydon','00AH'],['Ealing','00AJ'],['Enfield','00AK'],['Greenwich','00AL'],['Hackney','00AM'],['Hammersmith & Fulham','00AN'],['Haringey','00AP'],['Harrow','00AQ'],['Havering','00AR'],['Hillingdon','00AS'],['Hounslow','00AT'],['Islington','00AU'],['Kensington & Chelsea','00AW'],['Kingston-upon-Thames','00AX'],['Lambeth','00AY'],['Lewisham','00AZ'],['Merton','00BA'],['Newham','00BB'],['Redbridge','00BC'],['Richmond-upon-Thames','00BD'],['Southwark','00BE'],['Sutton','00BF'],['Tower Hamlets','00BG'],['Waltham Forest','00BH'],['Wandsworth','00BJ'],['Westminster','00BK']],
    currentTab = 0;


/** 
 * jQuery initialisation
 *
 * check for browser compatibility
 * sets main local variables
 * declare main functions
 */

$(document).ready(function() {

    //set size of map div on load  
    var elements =  $('#header, #search-options, #tabs');
    var elementsHeight = 0;
    var height = 0;
    elements.each(function(index){
      elementsHeight += $(this).outerHeight();
    });
    height = $(window).outerHeight() - elementsHeight + 'px';
    $('#mapDiv').height(height);
    
  // check to see if this browser can run the Google API
  if (GBrowserIsCompatible()) {
  
    // main variables
    // they are locals to the scope of the function init()
    var map,
        usesForm = false,
        empty = true,
        moveStarts = null,
        lastAlertedType = 0,
        moveEnds,
        globalPoint,
        farPoint,
        link,
        tempPcode = 'pcode',
        tempTextBoxBoroughCode = 'MPS',
        breadcrumbIndicator,
        focusBoroughName, 
        polysArray = [],
        areaCodesArray = [],
        addressArray = [],
        searchTermsArray = [],
        latArray = [],
        lngArray = [],
        markerArray = [],
        refineQueryString,
        refineSearchInUse = 0,
   /** 
    * the functions below are also declared as variables
    * this is the case until it reaches the block "Contains()"
    */


   /** 
    * overlayBoroughs()
    *
    * default opening position for the application
    * does an ajax call about the centre point of london, return borough polygon shapes
    */  
    overlayBoroughs = function() {
      var getVars;
      // build query string from refine map form elements
      refineQueryString = $('#refine_map_search :input[type!="image"]').fieldSerialize();
      if ($('#refine_map_search input[name="focus_view"]').fieldValue() == 'on'){
        refineQueryString += '&focusarea=' + currentCodeArea;
      }
      if (refineSearchInUse === 1){
        getVars = 'php/lookup.php?minlat=0&minlng=0&maxlat=0&maxlng=0&level=2&' + refineQueryString;
      } 
      else {
        getVars = 'php/lookup.php?minlat=0&minlng=0&maxlat=0&maxlng=0&level=2&CTID='+CTID;
      } 
      ajaxRequest(getVars);
    },


   /** 
    * overlayWards()
    *
    * does an ajax call to return ward polygon shapes based on bounds (sw, ne)
    * of the window or user double click
    */
    overlayWards = function(pointSW, pointNE) {
      var minLat = pointSW.lat(),
          minLng = pointSW.lng(),
          maxLat = pointNE.lat(),
          maxLng = pointNE.lng(),
          getVars;
      
      // build query string from refine map form elements
      refineQueryString = $('#refine_map_search :input[type!="image"]').fieldSerialize();
      if ($('#refine_map_search input[name="focus_view"]').fieldValue() == 'on'){
        refineQueryString += '&focusarea=' + currentCodeArea;
      }
      if (refineSearchInUse === 1){
        getVars = 'php/lookup.php?minlat=' + minLat.toFixed(3) + '&minlng=' + minLng.toFixed(3) + '&maxlat=' + maxLat.toFixed(3) + '&maxlng=' + maxLng.toFixed(3) + '&level=3&' + refineQueryString;
      } 
      else {
        getVars = 'php/lookup.php?minlat=' + minLat.toFixed(3) + '&minlng=' + minLng.toFixed(3) + '&maxlat=' + maxLat.toFixed(3) + '&maxlng=' + maxLng.toFixed(3) + '&level=3&CTID='+CTID;
      } 
      ajaxRequest(getVars);
    },


   /** 
    * overalayLSOA()
    *
    * does an ajax call to return LSOA polygon shapes based
    * on centrepoint of the window or user double click
    */
    overlayLSOA = function(pointSW, pointNE) {
      var minLat = pointSW.lat(),
          minLng = pointSW.lng(),
          maxLat = pointNE.lat(),
          maxLng = pointNE.lng(),
          getVars;
      
      // build query string from refine map form elements
      refineQueryString = $('#refine_map_search :input[type!="image"]').fieldSerialize();
      if ($('#refine_map_search input[name="focus_view"]').fieldValue() == 'on'){
        refineQueryString += '&focusarea=' + currentCodeArea;
      }
      if (refineSearchInUse === 1){
        getVars = 'php/lookup.php?minlat=' + minLat.toFixed(3) + '&minlng=' + minLng.toFixed(3) + '&maxlat=' + maxLat.toFixed(3) + '&maxlng=' + maxLng.toFixed(3) + '&level=4&' + refineQueryString;
      } 
      else{
        getVars = 'php/lookup.php?minlat=' + minLat.toFixed(3) + '&minlng=' + minLng.toFixed(3) + '&maxlat=' + maxLat.toFixed(3) + '&maxlng=' + maxLng.toFixed(3) + '&level=4&CTID='+CTID;
      }
      ajaxRequest(getVars);
    },


   /** 
    * infoWindowBuilder()
    *
    * function that builds the infoWindow
    * when called, passed a point as parameter
    */
    infoWindowBuilder = function(point) {
      for (var j = 0; j < polysArray.length; j++) {
        if (polysArray[j].getBounds().contains(point)) {
          if (polysArray[j].Contains(point)) { 
            var getVars;
            globalPoint = point;
            currentCodeArea= areaCodesArray[j];

            // current call in project
            if(refineSearchInUse === 1){
              //return CTID selected in form
              var formCTID = $('#refine_map_search input[name="CTID"]').fieldValue();
              getVars = 'php/info_lookup.php?area=' + areaCodesArray[j] + '&CTID=' + formCTID;
            } 
            else{
              
                getVars = 'php/info_lookup.php?area=' + areaCodesArray[j] + '&CTID=' + CTID;
            }
            ajaxRequest(getVars);
          }
        }
      }
    },


   /** 
    * updateHelp()
    *
    * message alert handling for search box
    * it appears around it when triggered 
    */
    updateHelp = function(message, style){
      $('.update-msg').remove();
      $('#search').addClass(style).append('<div class="' + style + ' update-msg"><h3>Warning</h3><p>' + message + '.</p></div>');
      // Hides the error message after 10 secs.
      $('#search div.update-msg')
        .animate({
          height: '1%'// to fix IE problem
          }, 
          10000,
          function(){
            $('#search').removeClass('notice');
            $('#search div.update-msg').remove();
          }
        );
    },


   /** 
    * updateBreadcrumb()
    *
    * shows current level
    */
    updateBreadcrumb = function(level){
      // reset previous state
      $('#view-location li span').removeClass('selected');
      $('#view-location li span').css('background-position','0 0').css('color','#696969');
      
      // get new state based on level
      switch (level){
        case 2: {
          $('#level-borough span').addClass('selected').css('background-position','0 -28px').css('color','#fff');
          $('#level-ward span').css('background-position','0 -28px');
          break;
        }
        case 3: {
          $('#level-ward span').addClass('selected').css('background-position','0 -56px').css('color','#fff');
          break;
        }
        case 4: {
          $('#level-subward span').addClass('selected').css('background-position','0 -84px').css('color','#fff');
          $('#level-ward span').css('background-position','0 -84px');
          break;
        }
        default : { 
          $('#level-borough span').addClass('selected').css('background-position','0 -28px').css('color','#fff');
          $('#level-ward span').css('background-position','0 -28px');
        }
      }
    },


   /** 
    * ajaxRequest()
    *
    * main function that decides to show the user polygons or an infowindow
    * checks to see if a response is given from the server before making a request
    * To ensure against HTTP errors that result in null or bad data,
    * always check status code is equal to 200 before processing the data
    */
    ajaxRequest = function(getVars) {
      GDownloadUrl(getVars, function(data, responseCode) {
        if (responseCode === 200 || responseCode === 304) {
          // request tabs or polygons accordingly
          var match = /info_lookup/.test(getVars);
          if (match === true) {
            tabsBuilder(data);
          }
          else {
            polyBuilder(data);
          }
        } else if (responseCode === -1) {
          $('#loading').hide();
          updateHelp('Sorry, there is an error with the crime mapping website. We are currently investigating this issue. Please try again later.', 'fail');
        } else { 
          $('#loading').hide();
          updateHelp('Sorry, there is an error with the crime mapping website. We are currently investigating this issue. Please try again later.', 'fail');
        }
      });
    },


    /** 
    * checkBounds()
    *
    * this function is called from the 'move' event listener and
    * re-centres the window about a user double click or centre point.
    * if the map position is out of range, move it back
    */
    checkBounds = function() {
      // perform the check and return if OK
      // check to see if the move occured in the default window position
      if (allowedBounds.contains(map.getCenter())) {
        return;
      }
      // it's not OK, so find the nearest allowed point and move there
      // else find the new point, recalculate the new window and put the point in the centre of the map.
      var C = map.getCenter(),
          X = C.lng(),
          Y = C.lat(),
          AmaxX = allowedBounds.getNorthEast().lng(),
          AmaxY = allowedBounds.getNorthEast().lat(),
          AminX = allowedBounds.getSouthWest().lng(),
          AminY = allowedBounds.getSouthWest().lat();

      if (X < AminX) {X = AminX;}
      if (X > AmaxX) {X = AmaxX;}
      if (Y < AminY) {Y = AminY;}
      if (Y > AmaxY) {Y = AmaxY;}
      map.setCenter(new GLatLng(Y,X));
    },


   /** 
    * tabsBuilder()
    *
    * function that builds the infoWindow tabs
    * data is retrieve from a JSON file requested by AJAX
    */
    tabsBuilder = function(tabs) {
      // parse the JSON document
      var jsonData = eval('(' + tabs + ')');

      // if city of london then have only one tab
      var custom = jsonData.data.custom;
      if (custom === true) {
        summary = '<div style="width: 310px;">' + jsonData.data.summary + '</div>';
        var infoTabs = [
          new GInfoWindowTab('Summary', summary)
        ];
      }
      // default: build three tabs for met police info windows
      else {
        // build content for tabs
        var helpLink = '<p class="help-link"><a href="text/info-help.html" class="help">What do these numbers mean?</a></p>',        
            summary = jsonData.data.summary + helpLink,
            comparison = jsonData.data.comparison + helpLink,
            trend = jsonData.data.trend + helpLink;

        // Set pair of label and content for each tab
         var infoTabs = [
          new GInfoWindowTab('Summary', summary),
          new GInfoWindowTab('Compare', comparison),
          new GInfoWindowTab('Trend', trend)
        ];
      }

      // adjust the width so that the info window is large enough for this many tabs
      summary = '<div style="width: 310px;">' + summary + '</div>';

      // attach info window to map
      map.openInfoWindowTabsHtml(globalPoint, infoTabs);
      
      // Clear the globalPoint var
      globalPoint = null;
    },


   /** 
    * removePolygonOverlays()
    *
    * remove polygon overlays and 
    * empty global array
    */
    removePolygonOverlays = function(){
      for (var i = 0; i < polysArray.length; i += 1){
        map.removeOverlay(polysArray[i]);
      }
      // reset the polys and area codes array
      polysArray.length = 0;
      areaCodesArray.length = 0;
    },


   /** 
    * removeMarkerOverlays()
    *
    * remove marker overlays and 
    * empty global array
    */
    removeMarkerOverlays = function(){
      for (var i = 0; i < markerArray.length; i += 1){
        map.removeOverlay(markerArray[i]);
      }
      // reset the marker array
      markerArray.length = 0;
    },


   /** 
    * createMarker()
    *
    * Create instance of marker
    */
    createMarker = function(point,html,markerOptions) {
      var marker = new GMarker(point, markerOptions);
      GEvent.addListener(marker, 'click', function() {
        marker.openInfoWindowHtml(html);
      });
      return marker;
    },


   /** 
    * markerBuilder()
    *
    * When a search for street is triggered
    * it deals with JSON file from Google
    */
    markerBuilder = function(data){
      if (!data || data.Status.code != 200) {
		updateHelp('Sorry, we were unable to find that address, please check your spelling and try again','notice');
      }
      else {
		var placecounter=0,
            pointsArray = [],
            tempAddressArray = [];
            // reset the array
            addressArray.length = 0;
		//  check if placemarks exist
		try {
	        for (var i = 0; i < data.Placemark.length; i += 1) {
				var place = data.Placemark[i],
					point = new GLatLng(place.Point.coordinates[1], 
					place.Point.coordinates[0]),
					sAddress = new String(place.address);		
					// search for uk in the address field
					sAddress= sAddress.toLowerCase();
		        // process to filter through results. Some results returned are not relevant.
				var indexUK = -1,				
					sSearchTermsArray = [],
					ukCounter,
					indexLondon = -1,
					londonCounter;
					// split the address field into an array
					sSearchTermsArray = sAddress.split(' ');
				// check if response contains term 'uk'
				for(ukCounter = sSearchTermsArray.length; ukCounter > 0; ukCounter -= 1){
					if(sSearchTermsArray[ukCounter]==='uk'){
						indexUK = 0;
						break;
					}
				}
				if(indexUK!=-1){
					// check if response contains term 'london or lon'
					for(londonCounter = sSearchTermsArray.length; londonCounter > 0; londonCounter -= 1){
						if(sSearchTermsArray[londonCounter]==='london'){
							indexLondon = 0;
							break;
						}
						if(sSearchTermsArray[londonCounter]==='london,'){
							indexLondon = 0;
							break;
						}
						if(sSearchTermsArray[londonCounter]==='lon'){
							indexLondon = 0;
							break;
						}
					}
						
				}
				else {
					// find landmarks in london
					// if uk is not present, then there should be united kingdom
					for(ukCounter = sSearchTermsArray.length; ukCounter > 0; ukCounter -= 1){
						if(sSearchTermsArray[ukCounter]==='kingdom'){
							indexLondon = 0;
						}
					}
				}
				// find the intial part of the address - used for the title
				sAddress = sAddress.substr(0,sAddress.lastIndexOf(',')); 
				//if address is in london
				if(indexLondon!=-1){
					// check the length of the search terms used. If only 1 then check the results to see if the term is part of the result.
					if(searchTermsArray.length===1){
					  // special case: get the first 6 characters at the address response to remove matches for london rd
					  var sTemp = sAddress.substr(0,6);
					  sTemp = sTemp.toLowerCase();
					  if(sTemp!='london'){
						// save the address
						  latArray[placecounter] = place.Point.coordinates[1];
						  lngArray[placecounter] = place.Point.coordinates[0];
						  addressArray[placecounter] = sAddress;
						  pointsArray[placecounter] = point;
						  marker = new GMarker(pointsArray[placecounter]);
						  tempAddressArray.push(marker);
						  var markerListener = GEvent.addListener(marker, "click", function(point) {
							infoWindowBuilder(point);
						  });
						  placecounter = placecounter+1;
					  }
					}
					else{
						
						latArray[placecounter] = place.Point.coordinates[1];
						lngArray[placecounter] = place.Point.coordinates[0];
						addressArray[placecounter] = sAddress;
						pointsArray[placecounter] = point;
						marker = new GMarker(pointsArray[placecounter]);
						tempAddressArray.push(marker);
						var markerListener = GEvent.addListener(marker, "click", function(point) {
						  infoWindowBuilder(point);
						});
						placecounter = placecounter+1;				  
					}
				}
	        }
	        if (addressArray.length > 0) {
	          // remove the old markers
	          
	          removeMarkerOverlays();
	          // reset the markerArray and the tempAddressArrays
	          markerArray = tempAddressArray;
	          tempAddressArray = 0;
	          // overlay the markers 
	          for(var j=0; j<addressArray.length; j+=1){
	            map.addOverlay(markerArray[j]);
	          }
	          // set the map at the first result
	          map.setCenter(pointsArray[0],15);
	          // clear polygons and old markers
	          removePolygonOverlays();
	          var mapBounds = map.getBounds();
	          var pointSW = mapBounds.getSouthWest();
	          var pointNE = mapBounds.getNorthEast();
	          //overlayLSOA(latArray[0],lngArray[0]);
	          overlayLSOA(pointSW, pointNE);
	          
	          //open an infowindow at the first result
	          globalPoint = pointsArray[0];
	          // Set time out on infowindow pop up to execute after polygons have loaded
	          var t=setTimeout(function(){infoWindowBuilder(globalPoint)},4000);
	          
	          if (addressArray.length > 1) {
	            var tt=setTimeout(function(){displayResults(0,addressArray,latArray,lngArray)},7000);
	          }
	          else {
	            // If there is only one result, don't diasplay the results Table
	            document.getElementById('results-table').style.display="none";
	          }
	        }
	        else {
	          document.getElementById('results-table').style.display="none";
	          updateHelp('Sorry, we were unable to find that address at this time, please check your spelling and try again later','notice');
	        }
	      
			}catch (error){
				updateHelp('Sorry, we were unable to find that address at this time, please check your spelling and try again later','notice');
		}
	  }  
      $('#close-results').click(function () { 
        $('#results-table').hide();
      });
    },

	
	/** 
    * displayResults()
    *
    * displays the list of results from a street search, if more than 1
    */
    displayResults = function(indicator,addressArray,latArray,lngArray) {
      var displayResults = '<h2>Search Results <a href="#" onClick="javascript:document.getElementById('+"'results-table'"+').style.display='+"'none'"+';" ><img src="images/ico-close-s.gif" alt="" id="close-results" /></a></h2>';
      for (var i = 0; i < addressArray.length; i += 1) {
        if (i === indicator) {
          displayResults = displayResults +'<ul>';
          displayResults = displayResults+'<li><a style="background-color:#D9D2D3" href="javascript:showLocation('+i+','+latArray[i]+','+lngArray[i]+');" >'+addressArray[i]+'</a></li>';
          displayResults = displayResults +'</ul>';
        }
        else {
          displayResults = displayResults +'<ul>';
          displayResults = displayResults+'<li><a href="javascript:showLocation('+i+','+latArray[i]+','+lngArray[i]+');" >'+addressArray[i]+'</a></li>';
          displayResults = displayResults +'</ul>';
        }
      }
      document.getElementById('results-table').innerHTML = displayResults;
      // display output box
      document.getElementById('results-table').style.display = 'block';
    },

   /** 
    * polyBuilder()
    *
    * define the function that is going to process the JSON file
    * converts JSON text to JSON object
    * error handler to check if an errorred JSON has been passed back
    */
    polyBuilder = function(data) {
			//if data is returned empty, do nothing
			//May no be called as this is a secondary catch, 'no data' calls are handled earlier by not sending them to php
			if (data==""){
				$('#loading').hide();
			}
      // parse the JSON document			
			else{
				var jsonData = eval('(' + data + ')');
				// db error handling
				if (jsonData.data.error) {
					var msg = jsonData.data.error.errorMessage,
							zoomLevel = map.getZoom();

					$('#loading').hide();
					// Special case for error 24 (restricted data level)
					if (jsonData.data.error.errorCode == 24) {
						if (refineSearchInUse = 1){
							var arrCurrentCTID = $('#refine_map_search input[name="CTID"]').fieldValue();
							var currentCTID = arrCurrentCTID[0];
						}
						else {
							var currentCTID = CTID;
						}

						if (jsonData.data.error.errorMessage == 'borough') {
							msg = 'Data for this crime type can only be shown down to borough level.';
						}
						else if (jsonData.data.error.errorMessage == 'ward') {
							msg = 'Data for this crime type can only be shown down to ward level.';
						}
						else {
							msg = 'No data could be found for the selected crime type at this level of detail. Please zoom out or select another crime type.';
						}
					}
					if (currentCTID!=lastAlertedType) {
						updateHelp(msg,'notice');
						lastAlertedType = currentCTID;
					}
				}
				
					// get centre point (this is the 1st request)
					if (jsonData.data.centrePoint) { 
						var lat = jsonData.data.centrePoint[0].lat,
								lng = jsonData.data.centrePoint[0].lng,
								point = new GLatLng(lat, lng),
								tooltip = jsonData.data.centrePoint[0].BalloonContent,
								marker = new GMarker(point),
								zoomLevel = jsonData.data.centrePoint[0].zoom,
								mapBounds,
								pointSW,
								pointNE;

						map.setCenter(point, zoomLevel);
						// once in right zoom level (16 for pcode) get the bounds so they can be used for the second request (overlayLSOA)
						mapBounds = map.getBounds();
						pointSW = mapBounds.getSouthWest();
						pointNE = mapBounds.getNorthEast();

						// add marker
						markerArray.push(marker);
						map.addOverlay(marker);
						map.panTo(point);
						map.setCenter(point, zoomLevel);

						// plot right set of polygons
						if      (zoomLevel == 15 || zoomLevel == 16 || zoomLevel == 17) { overlayLSOA(pointSW, pointNE); }
						else if (zoomLevel == 13 || zoomLevel == 14)                    { overlayWards(pointSW, pointNE); }
						else                                                            { overlayBoroughs(pointSW, pointNE); }

						//open info window
						var infoTime = setTimeout(function(){infoWindowBuilder(point)},8000);
						var markerListener = GEvent.addListener(marker, "click", function(point) {
							infoWindowBuilder(point);
						});

					}
					// if jsonData.data.areas instead (this is the 2nd request)
					else {
						// grab detail level from JSON to update the breadcrumb
						// check if received from JSON
						if(jsonData.data.areaType){
							// An areaType has been included in the JSON response. Set the breadcrumb accordingly.
							detailLevel = parseInt(jsonData.data.areaType);
							updateBreadcrumb(detailLevel);
						}
						else {
							// set the default detail level
							updateBreadcrumb(2);
						}
					
						// remove polygon overlays and empty global arrays
						removePolygonOverlays();

						// loop through jsonData and add overlay of all polygons
						for (var i = 0; i < jsonData.data.areas.length; i += 1) {
							// get right color for area
							var areaColor,
									borderColor;

							switch (jsonData.data.areas[i].color){
								case '0': { areaColor = '#4c56dc'; borderColor = '#1b249d'; break; }
								case '1': { areaColor = '#4c56dc'; borderColor = '#1b249d'; break; }
								case '2': { areaColor = '#65c4ec'; borderColor = '#006f9d'; break; }
								case '3': { areaColor = '#f4e600'; borderColor = '#80790a'; break; }
								case '4': { areaColor = '#ff8100'; borderColor = '#9a4b16'; break; }
								case '5': { areaColor = '#c32900'; borderColor = '#772f1b'; break; }
								default : { areaColor = '#EDEDED'; borderColor = '#555555'; }
							}

							// build polygon from points
							var polygon = new GPolygon.fromEncoded({
								polylines: [
								{points: jsonData.data.areas[i].points,
								 levels: jsonData.data.areas[i].levels,
								 color: borderColor,
								 opacity: 1,
								 weight: 1,
								 numLevels: 18,
								 zoomFactor: 2}],
								fill: true,
								color: areaColor,
								opacity: 0.6,
								outline: true
							});
							// push data to arrays
							polysArray.push(polygon);
							areaCodesArray.push(jsonData.data.areas[i].areaCode);
							// finally add area on map
							map.addOverlay(polysArray[i]);
							// stop double click from requesting data twice
							var click1 = 0,
									click2 = 0;

							var clickListener = GEvent.addListener(polygon, 'click', function(point) {
								click2 = new Date().getTime();
								if ((click2 - click1) > 400) {
									infoWindowBuilder(point);
								}
								click1 = click2;
							});
						}
					}
					
					// after drawing polys check if markers are also in the json object and add them if so.
						if (jsonData.data.pois) {
						
							var stationIcon = new GIcon();
						 //Icon attributes
							stationIcon.image = "img/ico-station2.png";
							stationIcon.shadow = "http://labs.google.com/ridefinder/images/mm_20_shadow.png";
							stationIcon.iconSize = new GSize(12, 27);
							stationIcon.shadowSize = new GSize(22, 20);
							stationIcon.iconAnchor = new GPoint(6, 20);
							stationIcon.infoWindowAnchor = new GPoint(5, 1);

							var markers =[];
							// loop through jsonData and add poi markers
							for (var i = 0; i < jsonData.data.pois.length; i += 1) {
								var lat = jsonData.data.pois[i].PoiLat,
										lng = jsonData.data.pois[i].PoiLng,
										point = new GLatLng(lat, lng),
										markerOptions = { icon:stationIcon, title:jsonData.data.pois[i].PoiName},
										balloonText = jsonData.data.pois[i].BalloonContent;
										var marker = createMarker(point,balloonText,markerOptions);
										markers.push(marker);
							}

							// POI marker manager
							mgr.addMarkers(markers, 9);
							mgr.refresh();
						}

						// Update crime type display in Map Info using the last selected crime type in form
						var displayCrimeTypeID = $('#refine_map_search input[name="CTID"]').fieldValue(),
								displayCrimeTypeIDLabel = '#CTID_' + displayCrimeTypeID,
								displayCrimeType = $(displayCrimeTypeIDLabel).next().html();
						$('.displayCrimeTypeMsg').html(displayCrimeType);
						
						// Update focus view messge in Map Info
						if(jsonData.data.focusArea){
							focusBoroughName = jsonData.data.focusArea.focusName,
									displayFocus =  focusBoroughName;
							$('#focusBorough').html(displayFocus);
							$('#displayFocusMsg').css('display', 'block');
							
								//remove focus view via map info
							$('#focusBorough').click(function () { 
								$('input[name=focus_view]').attr('checked', false);
								$('#focus_view_level_3').removeAttr('checked');
								$('#focus_view_level_4').removeAttr('checked');
								$('#options_extreme').slideUp("slow").addClass('hidden');
								var zoomLevel = map.getZoom();
								// listens to zoom, a level before getting into ward level (13-15) from both directions (from 12 or 16)
								if ((zoomLevel > 11) && (zoomLevel < 17)) {
									var mapBounds = map.getBounds(),
											pointSW = mapBounds.getSouthWest(),
											pointNE = mapBounds.getNorthEast();
								}   
								if (zoomLevel == 15 || zoomLevel == 16 || zoomLevel == 17) { overlayLSOA(pointSW, pointNE); }
								else if (zoomLevel == 13 || zoomLevel == 14)               { overlayWards(pointSW, pointNE); }
								else                                                       { overlayBoroughs(pointSW, pointNE); }
							});
						} else {
							$('#focusBorough').html('');
							$('#displayFocusMsg').css('display', 'none');
						}

					// hide spinning wheel animated div
					$('#loading').hide();
				//} 
			}
    },


   /** 
    * showlink()
    *
    * displays current URL when called
    * so visitor can bookmark page
    */
    showlink = function() {
      // By default, use the most recently-clicked area
      if(currentCodeArea!='MPS'){
        link = 'http://'+host+'/index.php?areacode='+currentCodeArea+'&crimetype='+CTID;
      }
      
      // checks the querystring for an areacode
      else if (qryStrAreaCode!='MPS'){
        link = 'http://'+host+'/index.php?areacode='+currentCodeArea+'&crimetype='+CTID;
        // reset variable
        qryStrAreaCode = 'MPS';
      }
      // checks the drop down if a borough's been entered
      else if (tempTextBoxBoroughCode!='MPS'){
        link = 'http://'+host+'/index.php?areacode='+tempTextBoxBoroughCode+'&crimetype='+CTID;
      }
      // tempPcode - is used updated in the textBoxProcessor() before it is passed here.
      // it is a local variable of init, but a copy of the global myPostCode
      else if(tempPcode!='pcode'){
        var regExp = /^ *[B-EHIKNR-UW][A-EGL-NRTW1-9][A-DF-HJKMNPR-Y0-9]?[ABEHMNPRVWXY0-9]?( ?[0-9]([ABD-HJLN-UW-Z]{2})?)? *$/;
        // if it's a postcode
        if (regExp.test(tempPcode)) {
          link = 'http://'+host+'/index.php?pcode='+tempPcode+'&crimetype='+CTID;
        }
        // if it's a search to be sent to the search web service
        else {
          link = 'http://'+host+'/index.php?pcode='+tempPcode+'&crimetype='+CTID;
        }
      }
      else if(CTID!=8){
        link = 'http://'+host+'/index.php?crimetype='+CTID;
      }
      else {
        // default case
        link = window.location.href;
      }
      return link;
    },


   /** 
    * validateAddress()
    *
    * it modifies the string that goes to the Google web search
    * to make the search focused in London
    */
    validateAddress = function(address) {
      var sAddress = new String(address);
      sAddress = sAddress.toLowerCase();
     
      searchTermsArray = sAddress.split(' ');
      /*
       -- YC added 20.03.09, decsion needed by SG on whether to go ahead
       -- commented out but left in case we need to turn on.
      if(focusBoroughName!=''){
          sAddress = sAddress +' '+focusBoroughName;
        }
      */
      var londonCount = 0;
      for(var i = 0; i < searchTermsArray.length; i += 1) {
        if(searchTermsArray[i] === 'london'){
          londonCount = londonCount+1;
        }
      }
      if (londonCount === 0){
        sAddress = sAddress + ' london uk';
      } 
      else {
        // case london road add london to the string
        if(searchTermsArray[0] === 'london'){
          if(searchTermsArray.length > 1){
            sAddress = sAddress + ' london uk';
          }
        }
        // dont do anything if the user has already input london in the querystring
      }
      
      return sAddress;
    },


   /** 
    * textBoxProcessor()
    *
    * this function checks whether the data entered in the 
    * search box is a postcode, street or borough and, 
    * after validation, precess accordingly
    */
    textBoxProcessor = function(){
        $('#loading span').empty();
        $('#loading').show();
        // jQuery remove error message (element) & remove error class from form
        $('.update-msg').remove();
        $('#search').removeClass();
        // reomve the results table once a new query is sent
        $('#results-table').css('display','none');
        
        // check to see value of myPostCode
        if(myPostCode !='pcode'){
          // check if the param value passed is a borough
          for(var i=0; i<boroughArray.length; i++){
            if(myPostCode.toLowerCase() === boroughArray[i][0].toLowerCase()){
              // update the textBoxBoroughCode with the code
              textBoxBoroughCode = boroughArray[i][1];
              myPostCode = 'pcode';
            }
          }
          // get value from querystring
          $('#pcode').attr('value',myPostCode);
        }
        else{
          // get value from form
          myPostCode = document.getElementById('pcode').value;
          // check if the value entered is a borough
          for(var i=0; i<boroughArray.length; i++){
            if(myPostCode.toLowerCase() === boroughArray[i][0].toLowerCase()){
              // update the textBoxBoroughCode with the code
              textBoxBoroughCode = boroughArray[i][1];
              myPostCode = 'pcode';
            }
          }
        }
        empty = true;
          // postcode validation
          myPostCode = myPostCode.toUpperCase();
          var regExp = /^ *[B-EHIKNR-UW][A-EGL-NRTW1-9][A-DF-HJKMNPR-Y0-9]?[ABEHMNPRVWXY0-9]?( ?[0-9]([ABD-HJLN-UW-Z]{2})?)? *$/;
          
          // case if borough is typed in text box
          if(textBoxBoroughCode!='MPS'){
            removeMarkerOverlays();
            var sendVar = 'php/lookup.php?areacode='+ textBoxBoroughCode + '&CTID='+CTID;
            ajaxRequest(sendVar);
            tempTextBoxBoroughCode = textBoxBoroughCode;
            textBoxBoroughCode = 'MPS';
            tempPcode = 'pcode';
          }
          // case if we want to pass the param to the google web search service
          else if (!regExp.test(myPostCode)) {
            tempPcode = myPostCode.toLowerCase();
            geocoder = new GClientGeocoder();
            geocoder.setViewport(bounds);
            geocoder.setBaseCountryCode('GB');
            // validate input
            myPostCode = validateAddress(myPostCode);
            geocoder.getLocations(myPostCode, markerBuilder);
            // send an update to the crime maps logs
            var getVars = 'php/freesearch.php?q='+myPostCode;
						GDownloadUrl(getVars, function(data, responseCode) {
							//Don't need to send this to ajaxRequest as nothing needs to be returned. An error is returned otherwise as polyBuilder errros on no 'data' 
							//Hide loading image 
							$('#loading').hide();
						});
          }
          // case if postcode is good
          else {
            removeMarkerOverlays();
            var sendVar = 'php/lookup.php?pcode='+ myPostCode + '&CTID='+CTID;
            ajaxRequest(sendVar);
            empty = false;
            tempPcode = myPostCode.toLowerCase();
        }
        
        myPostCode = 'pcode';
        return false;
    },


   /** 
    * searchSubmit()
    *
    * this handle the different situations in
    * which the search box data is submitted,
    * depending on the active tab some filtering
    * controls will be unavailable
    */
    searchSubmit = $('#search').submit(function(event) {
      map.closeInfoWindow();
      if (currentTab === 1 || currentTab === 2){
        //removeMarkerOverlays();
        if (currentTab === 1) {
          searchPanels.init('#refine-map');
          $('#refine-map h2').removeClass();
        }
        if (currentTab === 2) {
          searchPanels.init('#refine-map');
          searchPanels.init('#crime-types');
          $('#refine-map h2').removeClass();
          $('#crime-types h2').removeClass();
        }
        $('#tabs-1').show();
        $('#tabs').find('li').removeClass();
        $('#tabs').find('li:eq(0)').addClass('ui-tabs-selected');
        currentTab = 0;
      }

      // disable the search box action
      event.preventDefault();
      if (document.getElementById('pcode').value!=''){
        map.closeInfoWindow();
        $('#loading').css('display','block');
        textBoxProcessor();
      }
      
    }),


   /** 
    * refineMap()
    *
    * handles the refine map form
    * this form is broken in two separate controls that show one at a time
    */
    refineMap = $('#refine_map_search').submit(function(event) {
      map.closeInfoWindow();
      if(mgr !== undefined){
        mgr.clearMarkers();
        mgr.refresh();
      }
      var zoomLevel = map.getZoom(),
          point = map.getCenter(),
          mapBounds = map.getBounds(),
          pointSW = mapBounds.getSouthWest(),
          pointNE = mapBounds.getNorthEast();
      refineSearchInUse = 1;
      if (zoomLevel == 15 || zoomLevel == 16 || zoomLevel == 17) { overlayLSOA(pointSW, pointNE); }
      else if (zoomLevel == 13 || zoomLevel == 14)               { overlayWards(pointSW, pointNE); }
      else                                                       { overlayBoroughs(pointSW, pointNE); }
      //get CTID value when form is submitted
      CTID =  $('#refine_map_search input[name="CTID"]').fieldValue();
      //var relatedLinks = ajaxRequest('php/getlinks.php?ct='+CTID);
      GDownloadUrl('php/getlinks.php?ct='+CTID, function(data) {
        $('.last ul').html(data);
      });
	  if (currentTab === 1) {
		var url ='php/dataview.php?area=' 
		CTID =  $('#refine_map_search input[name="CTID"]').fieldValue();
		url += currentCodeArea + '&ct=' + CTID;
		$.ajax({
		  url: url,
		  //cache: false,
		  beforeSend: function(){
			 $('#loading').show();
		   },
		  success: function(data){
			$('#loading').hide();
			$("#tabs-2").html(data);
			getSublevelArea();
		  }
		});
	  }
      event.preventDefault();
    }),


   /** 
    * getSublevelArea()
    *
    * Get wards or subwards when user clicks
    * on area name on text version
    */
    getSublevelArea = function(){
      //make table sortable
      var tableHeaders = $('#areas-table th a, #nearby-areas th a');
      tableHeaders.each(function(){
        var header = $(this);
        header.click(function(event){
          event.preventDefault();
          
          var query = $(header).attr('href');
          query = query.substring(query.indexOf('?'), query.length);
          //console.log(query);
          $.ajax({
            url: 'php/dataview.php'+ query,
            //cache: false,
            beforeSend: function(){
               $('#loading').show();
             },
            success: function(data){
              $('#loading').hide();
              $("#tabs-2").html(data);
              getSublevelArea();
            }
          });
        });
      });
    
      //get anchors on the table displaying the areas and the bread-crumb anchors
      var anchors = $('#areas-table td a, #areas-table img, #nearby-areas td a,ul.bread-crumb a, #last-three-months td a,  #last-three-years td a');
      var crimeDataDisplayed = false;
                       
      anchors.each(function(){
        var anchor = $(this);
        anchor.click(function(event){
          var url ='php/dataview.php?area=' ;
          var isALink = (event.target.tagName.toLowerCase() == 'a')? true: false;
          var isAnImage = (event.target.tagName.toLowerCase() == 'img')? true: false;
          var href= '';
          if(isALink){
            href= $(anchor).attr('href');
          }
          else if(isAnImage){
            //it is  an image then
            href= $(anchor).parents('tr').find('a').attr('href');
          }
          
          var start = href.indexOf('=') +1;
          var end = href.indexOf('&');
          
          currentCodeArea = href.substring(start, end);
          if(isALink){
            event.preventDefault();
            url += currentCodeArea + '&ct=' + CTID;
            $.ajax({
              url: url,
             // cache: false,
              beforeSend: function(){
                 $('#loading').show();
               },
              success: function(data){
                $('#loading').hide();
                $("#tabs-2").html(data);
                getSublevelArea();
              }
            });
          }
          else {
            if(isAnImage){
              if($(anchor).parents('tr').next().find('td').hasClass('crime-table-data')){
                $(anchor).attr('src', 'img/ico-plus.gif');
                $(anchor).parents('tr').next().remove();
              }
              else{
                $(anchor).attr('src', 'img/ico-minus.gif');
                url += currentCodeArea + '&limit=types';
                $.ajax({
                  url: url,
                 // cache: false,
                  beforeSend: function(){
                     $('#loading').show();
                   },
                  success: function(data){
                    $('#loading').hide();
                    
                    $(anchor.parents('tr')).after("<tr><td class=\"crime-table-data\" colspan=\"4\">" + data + "</td></tr>");
                    crimeDataDisplayed = true;
                  }
                });
              }
            }
          }
        });
      });
    },


   /** 
    * ajaxTabs()
    *
    * builds site's main tabs and sync Map and Text view tabs
    * Uses tabsselect event from tabs plugin
    * Options:
    * ui.tab     // anchor element of the selected (clicked) tab
    * ui.panel   // element, that contains the selected/clicked tab contents
    * ui.index   // zero-based index of the selected (clicked) tab
    */
    ajaxTabs = $('#tabs-wrapper #tabs').tabs({
      fx: { opacity: 'toggle' },
      load: function(){
        //make table sortable and add click events to retrieve wards or sub-wards 
        getSublevelArea();
      },
      select:  function(event, ui){
        if (ui.index === 1){
          var url ='php/dataview.php?area=' + currentCodeArea +'&ct=' + CTID;
          $(this).tabs('url', 1, url);
          if(currentTab === 2) {
            searchPanels.init('#refine-map');
            searchPanels.init('#crime-types');
            $('#crime-types h2').removeClass();
          }
          setHeight('#tabs-2');
          currentTab = 1;
          $('#refine-map h2').addClass('inactive');
          $('#refine-map h2').unbind('click');
/*		Removed 2009-04-29, SG - was being attached repeatedly to the menu submit. Functionality moved to main menu submit function.
          $('#refine_map_search').submit(function() {
            var url ='php/dataview.php?area=' 
            CTID =  $('#refine_map_search input[name="CTID"]').fieldValue();
            url += currentCodeArea + '&ct=' + CTID;
			alert('Requesting URL ' + url + ' at line 1177');
            $.ajax({
              url: url,
              cache: false,
              beforeSend: function(){
                 $('#loading').show();
               },
              success: function(data){
                $('#loading').hide();
                $("#tabs-2").html(data);
                getSublevelArea();
              }
            });
          });*/
        }
        if(ui.index === 2){
          currentTab = 2;
          $('#refine-map h2').addClass('inactive');
          $('#refine-map h2').unbind('click');
          $('#refine-map h2 a').click(function(event){
            event.preventDefault();
          });
          $('#crime-types h2').addClass('inactive');
          $('#crime-types h2').unbind('click');
          $('#crime-types h2 a').click(function(event){
            event.preventDefault();
          });
          // call sub-tabs for "stuff" tab
          $('#stuff-tabs-wrapper').tabs({
            fx: { opacity: 'toggle' },
            selected: 0,
            load: function(event, ui){
              if(ui.index === 2){
                //console.log("Index : " + ui.index);
                $(".glossary").click(function(event){
                  event.preventDefault();
                  //console.log("Hello");
                  $('#stuff-tabs-wrapper').tabs('select', 4); // switch to fifth tab
                  selected: 0;
                  return false;
                });
              }
              else {
                //console.log("I am : " + ui.index);
              }
            }
          });
        }
      },
      show: function(event, ui){
        if(ui.index === 0){
          
          if(currentTab !== 0) {
            searchPanels.init('#refine-map');
            $('#crime-types h2').removeClass();
            //to show infoWindows again when the user comes from 
            //either Text version of Stuff tab
            //Returning to MPS level - to stop focus on Lewisham 
            if(currentCodeArea =='MPS'){
                //resize map to div size before getting zoom/point/bounds
                map.checkResize();
                var zoomLevel = map.getZoom(),
                    point = map.getCenter(),
                    mapBounds = map.getBounds(),
                    pointSW = mapBounds.getSouthWest(),
                    pointNE = mapBounds.getNorthEast();
                if (zoomLevel === 15 || zoomLevel === 16 || zoomLevel === 17) { overlayLSOA(pointSW, pointNE); }
                else if (zoomLevel === 13 || zoomLevel === 14)               { overlayWards(pointSW, pointNE); }
                else                                                       { overlayBoroughs(pointSW, pointNE); }
            }
            //returning to any other level
            else{
              var getVars='php/lookup.php?areacode='+currentCodeArea;
              removeMarkerOverlays();
              ajaxRequest(getVars);
              map.checkResize();
            }
          }
          if(currentTab === 2) {
            searchPanels.init('#crime-types');
          }
					// Was commented out, but restored 2009-04-29 13:23 to enable check of current view on menu Go (SG)
          currentTab = 0; 
          $('#refine-map h2').removeClass();
        }
        if (ui.index === 1){
          setHeight('#tabs-2');
        }
        if (ui.index === 2){
          setHeight('#tabs-3');
        }
      }
    }),
    
   /**
    * setHeight()
    *
    * set height of map to take all available vertical space
    */
    setHeight = function(ele){
      if(!$('#map-wrapper')) return;
      var elements =  $('#header, #search-options, #tabs');
      var elementsHeight = 0;
      var height = 0;

      elements.each(function(index){
        elementsHeight += $(this).outerHeight();
      });
			
      height = $(window).outerHeight() - elementsHeight + 'px';
      $(ele).height(height);
    },


   /** 
    * showMapsOnLoad()
    *
    * this function decides if a querystring has been passed
    * it checks both qryStrAreaCode and myPostCode
    */
    showMapsOnLoad = function(){ 
      var getVars;
      if(qryStrAreaCode!='MPS'){
        getVars = 'php/lookup.php?areacode='+qryStrAreaCode+'&CTID='+CTID;
        ajaxRequest(getVars);
      }
      // evaluate the myPostCode
      else if(myPostCode!='pcode'){
         textBoxProcessor();
      }
      else{
        //default - no params passed
        overlayBoroughs();
      }
      //reset form elements to default on load
      document.refine_map_search.reset();
    },


   /** 
    * show searh panels
    *
    * this is an object
    * that's why the difference in syntax
    */
    searchPanels = {
      containers: '',
      init: function(parent){
        this.containers = $(parent);
        this.headers = this.containers.find('h2');
        if(!this.containers) return;
        
        //hide elements holding the form elements
        //prepend close anchor
        this.headers.each(function(){
          var header = $(this);
          var parent = header.parent();
          parent.find('div.panel').hide();
          parent.find('div.panel').prepend('<a href="#" class="close">Close</a>');
          
          parent.find('a').each(function(){
            var anchor = $(this);
            anchor.click(function(event){
              event.preventDefault();
            });
          });
          
          header.click(function(){
          //Disable focus view if current area is MPS
            if(currentCodeArea == 'MPS'){
              $('#detail-view').css('color','#ccc');
              $('#focus_view').attr('disabled','disabled');
            }
            else{
              $('#detail-view').css('color','#000');
              $('#focus_view').removeAttr('disabled');
            }
            searchPanels.showPanel(parent);
          });
        });
        //close form by clicking on close anchor
        $('#refine_map_search a.close').each(function(){
          var anchor = $(this);
          anchor.click(function(){
            $('#refine_map_search').find('div.panel').hide('fast');
            $('#refine_map_search').find('div.selected').removeClass('selected');
          });
        });
        // close form on submit
        $('#refine_map_search').submit(function(event) {
          $(this).find('div.panel').hide('fast');
          $(this).find('div.selected').removeClass('selected');
          event.preventDefault();
        });
      },
      showPanel: function(parent){
        if(!parent.hasClass('selected') && !parent.siblings().hasClass('selected')){
          parent.addClass('selected');
          parent.find('div.panel').toggle('slow');
        }
        else if(parent.hasClass('selected') && !parent.siblings().hasClass('selected')){
          parent.find('div.panel').toggle('fast', function(){
            parent.removeClass('selected');
          });
        }
        else if(parent.hasClass('selected') && parent.siblings().hasClass('selected')){
          parent.removeClass('selected');
          parent.find('div.panel').toggle('fast');
          
          parent.siblings().removeClass('selected');
          parent.siblings().find('div.panel').toggle('fast');
          $('div.search').css({'background-image': '../img/bg-vertical-sep.gif'});
        }
        else {
          parent.addClass('selected');
          parent.find('div.panel').toggle('slow');
          parent.siblings().removeClass('selected');
          parent.siblings().find('div.panel').toggle('fast');
        }
      }
    },


   /** 
    * autocomplete for boroughs in search box
    *
    * these functions work all together providing 
    * autocomplete functionality for boroughs in search box
    * the form then sends a value (borough code)
    * associated with the borough name
    *
    * documented at http://www.pengoworks.com/workshop/jquery/autocomplete_docs.txt
    */
    autocompleteFindValue = function(li) {
      if (li == null) {
        return alert("No match!");
      }
      // if coming from an AJAX call, let's use the Id as the value
      if (!!li.extra) {
        textBoxBoroughCode = li.extra[0];
      }
      // otherwise, let's just display the value in the text box
      else { 
        textBoxBoroughCode = li.selectValue;
      }
    },

    autocompleteSelectItem = function(li) {
      document.getElementById('pcode').focus();
      autocompleteFindValue(li);
    },

    autocompleteFormatItem = function(row) {
      return row[0];
    },

    autocompleteLookupLocal = function(){
      var oSuggest = $("#pcode")[0].autocompleter;
      oSuggest.autocompleteFindValue();
      return false;
    };


   /** 
    * Contains()
    *
    * extend GPolygon class: method for testing if a point is inside a polygon
    * returns true if poly contains point
    * algorithm from http://alienryderflex.com/polygon/
    */
    GPolygon.prototype.Contains = function(point) {
      var j = 0,
          oddNodes = false,
          x = point.lng(),
          y = point.lat();
      for (var i = 0; i < this.getVertexCount(); i += 1) {
        j++;
        if (j === this.getVertexCount()) {j = 0;}
        if (((this.getVertex(i).lat() < y) && (this.getVertex(j).lat() >= y))
        || ((this.getVertex(j).lat() < y) && (this.getVertex(i).lat() >= y))) {
          if ( this.getVertex(i).lng() + (y - this.getVertex(i).lat())
          /  (this.getVertex(j).lat()-this.getVertex(i).lat())
          *  (this.getVertex(j).lng() - this.getVertex(i).lng())<x ) {
            oddNodes = !oddNodes
          }
        }
      }
      return oddNodes;
    };


   /** 
    * Map object and Controls
    *
    * display the map with some controls
    * add "fake" custom controls to limit infoWindow movement
    * set the initial location
    *
    * (container block is just used for legibility, no scoping applies here)
    */
    {
      map = new GMap2(document.getElementById('mapDiv'));
      map.addControl(new GMapTypeControl());
      map.addControl(new GSmallMapControl());
      map.addControl(new GOverviewMapControl());
      
      // "fake" custom controls: these act as placeholders where the controls in index.php sit on
      // javaScript interaction and maintenance in general is easier this way
      // each placeholder is an empty GControl. We define the functions first, each function matched a custom control
      function LeftControl() {};
      function RightControl () {};

      // to "subclass" the GControl, we set the prototype object to an instance of the GControl object
      LeftControl.prototype  = new GControl();
      RightControl.prototype = new GControl();
      
      // creates DIV's for our custom controls. We add the controls to
      // to the map container and return the element for the map class to
      // position properly.
      LeftControl.prototype.initialize = function(map) {
        var leftControl = document.createElement('div');
        leftControl.id = 'pholder-left';
        map.getContainer().appendChild(leftControl);
        return leftControl;
      };
      RightControl.prototype.initialize = function(map) {
        var rightBlock = document.createElement('div');
        rightBlock.id = 'pholder-right';
        map.getContainer().appendChild(rightBlock);
        return rightBlock;
      };

      // by default, the controls will appear in the top left corner of the map. We set the position here
      LeftControl.prototype.getDefaultPosition = function() {
        return new GControlPosition(G_ANCHOR_TOP_LEFT, new GSize(8, 140));
      };
      RightControl.prototype.getDefaultPosition = function() {
        return new GControlPosition(G_ANCHOR_TOP_RIGHT, new GSize(7, 63));
      };
     
      // finally add control to map
      map.addControl(new LeftControl());
      map.addControl(new RightControl());

      var sw = new GLatLng(51.286, -0.508), 
          ne = new GLatLng(51.691, 0.335),
          bounds = new GLatLngBounds(sw, ne),
          mapZoomLevel = map.getBoundsZoomLevel(bounds),
          mapCenter = new GLatLng(51.496, -0.046);
          
      // we give ie6 a fixed mapZoomLevel and overwrite mapCenter so it loads the map on the right location
      
      if ((jQuery.browser.msie) && (jQuery.browser.version.substr(0,3) === "6.0")) {
        mapZoomLevel = 10;
        mapCenter = new GLatLng(51.496, -0.046);
        //north centre point used to resolve IE6 problem - not needed at present
        //mapCenter = new GLatLng(51.68, -0.046);
      }
      map.setCenter(mapCenter, mapZoomLevel);
      
      //global marker manager, needs map details (viewpoint and zoome level) thus declared here.
      var mgr = new MarkerManager(map);

      // restricts the range of zoom Levels
      // get the list of map types
      var mt = map.getMapTypes();
      // overwrite the getMinimumResolution() and getMaximumResolution() methods
      for (var i = 0; i < mt.length; i += 1) {
        mt[i].getMinimumResolution = function() { return 9; };
        mt[i].getMaximumResolution = function() { return 17; };
      };

      // add a move listener to restrict the bounds range
      GEvent.addListener(map, 'move', function() {
        checkBounds();
      });

      GEvent.addListener(map, 'infowindowopen', function() {
        $('#trend-tab').tabs({ fx: { opacity: 'toggle' } });
      });

      map.enableScrollWheelZoom();

      // the allowed region which the whole map must be within
      var allowedBounds = new GLatLngBounds(new GLatLng(49.5,-10), new GLatLng(59,2.6));
    }


    /** 
    * zoomEndListener()
    *
    * polygon ajax requests depending on relashinship
    * between zoom level and area type
    */
    var zoomEndListener = GEvent.addListener(map, 'zoomend', function(oldLevel, newLevel) {
      var zoomLevel = map.getZoom();
    
      // listens to zoom, a level before getting into ward level (13-15) from both directions (from 12 or 16)
      if ((zoomLevel > 11) && (zoomLevel < 17)) {
        var mapBounds = map.getBounds(),
            pointSW = mapBounds.getSouthWest(),
            pointNE = mapBounds.getNorthEast();
    
        // Load Boroughs
        if (oldLevel === 13 && newLevel === 12) {
          $('#loading span').empty().html('<strong>Boroughs</strong> are being loaded.');
          $('#loading').show();
          overlayBoroughs();
          map.closeInfoWindow();
        }
        // Load Wards
        else if ((oldLevel === 12 && newLevel === 13) || (oldLevel === 16 && newLevel === 15) || (oldLevel === 15 && newLevel === 14) || (oldLevel === 14 && newLevel === 13)) {
          $('#loading span').empty().html('<strong>Wards</strong> are being loaded.');
          $('#loading').show();
          overlayWards(pointSW, pointNE);
          map.closeInfoWindow();
        }
        // Load Sub Wards
        else if ((oldLevel === 15 && newLevel === 16) || (oldLevel === 17 && newLevel === 16)) {
          $('#loading span').empty().html('<strong>Sub Wards</strong> are being loaded.');
          $('#loading').show();
          overlayLSOA(pointSW, pointNE);
          map.closeInfoWindow();
        }
      }
    });


   /** 
    * moveStartListener()
    *
    * sets the start position for the move event
    */
    var moveStartListener = GEvent.addListener(map, "movestart", function() { 
      var zoomLevel = map.getZoom();
      // at wards and LSOAs levels only
      if (zoomLevel >= 13 && zoomLevel < 18) {
        moveStarts = map.getCenter();
        var bounds = map.getBounds();
        farPoint = bounds.getNorthEast();
      }
    });


   /** 
    * moveEndListener()
    *
    * sets the end position for the move event
    * and loads new data accordingly
    */
    var moveEndListener = GEvent.addListener(map, "moveend", function() {  
      var zoomLevel = map.getZoom(),
          point = map.getCenter(),
          mapBounds = map.getBounds(),
          pointSW = mapBounds.getSouthWest(), 
          pointNE = mapBounds.getNorthEast();
      // check whether the infoWindow source point is off the viewable map and close if it is.
      if(!map.getInfoWindow().isHidden()) {
        var infoPoint = map.getInfoWindow().getPoint();
        var zeroPoint = new GLatLng(0,0);
        if (!infoPoint.equals(zeroPoint)) {
          if(!map.getBounds().contains(infoPoint)) {
            map.closeInfoWindow();
          }
        }
      }

      // at wards and LSOAs levels only. Also infoWindow must be closed and point is inside London bounds
      if (zoomLevel >= 13 && zoomLevel < 18 && map.getInfoWindow().isHidden() && bounds.contains(point)) {
        var pointLat = point.lat(),
            pointLng = point.lng();

        // map moves, it could be by draging, panning, etc
        if (moveStarts !== null) {
          moveEnds = map.getCenter();
          var moveDistance = moveStarts.distanceFrom(moveEnds),
              maxDistance = moveStarts.distanceFrom(farPoint);

          // if there is no data from the form
          // moveend event is also triggered when jumping to a new zoom level i.e. using form
          // then this is needed so requests from form don't run this section
          if (empty === true) {
            // don't allow AJAX request on trivial drag moves - minimum drag set to a tenth of centre - north east distance
            if ( moveDistance > maxDistance * 0.1) {
              if ((zoomLevel >= 15) && (zoomLevel < 18)) {
                overlayLSOA(pointSW, pointNE);
              }
              else {
                //overlayWards(pointLat, pointLng);
                overlayWards(pointSW, pointNE);
              }
            }
          } 
        }

        // needed for pan buttons to load new json data
        empty = true;
        moveStarts = null;
      }
    });

	
   /** 
    * showLocation()
    *
    * the call to this function relies on the map
    * panning to the new location triggering off the move listener
    *
    * Note: on 29-04-09 we found that this function should be global,
    * ommiting "var" attaches the function to the global object
    */
    showLocation = function(indicator,lat, lng){
      $('#results-table').hide();
      $('#loading').hide();
      map.closeInfoWindow();
       
      var location = new GLatLng(lat,lng);
      
      //map.setCenter(location,15);
      map.panTo(location);
      
      var infoTime = setTimeout(function(){infoWindowBuilder(location)},3000);
      var displayTime = setTimeout(function(){displayResults(indicator,addressArray,latArray,lngArray)},4000);
    };

   /** 
    * default overlay
    *
    * fetch the JSON data file for default overlay
    * of Met Police data
    */
    showMapsOnLoad();

   /** 
    * jQuery DOM manipulation and effects
    *
    * A fast, concise, library that simplifies how to traverse 
    * HTML documents, handle events, perform animations, and add AJAX
    * http://jquery.com
    *
    * (container block is just used for legibility, no scoping applies here)
    */
    {
      //calls function to show/hide search panels
      searchPanels.init('#refine-map');
      searchPanels.init('#crime-types');
      
      // autocomplete call
      $('#pcode').autocompleteArray(boroughArray,
      {
        delay:10,
        minChars:1,
        matchSubset:1,
        matchContains:1,
        cacheLength:10,
        onItemSelect:autocompleteSelectItem,
        onFindValue:autocompleteFindValue,
        formatItem:autocompleteFormatItem,
        autoFill:true
      });

      // AJAX forms: Refine Map, Crime Types
      var options = { 
        //beforeSubmit: showRequest // pre-submit callback 

        // other available options: 
        //url:       url           // override for form's 'action' attribute 
        //type:      type          // 'get' or 'post', override for form's 'method' attribute 
        //dataType:  null          // 'xml', 'script', or 'json' (expected server response type) 
        //clearForm: true          // clear all form fields after successful submit 
        //resetForm: true          // reset the form after successful submit
        //target:    '#output1',   // target element(s) to be updated with server response
        //success:   showResponse  // post-submit callback

        // $.ajax options can be used here too, for example: 
        //timeout:   3000 
      }; 

      // bind 'ajaxForm' to the forms that need it
      //$('#refine_map_search').ajaxForm(options);
    
     //calls function to set height of the map
     //This somehow fixes the error of the map only loading partialy in IE6
     //$(window).load(function(){
        //setHeight('#mapDiv');
        //setHeight('#tabs-2');
        //setHeight('#tabs-3');
      //});
      
      // resize when changing tabs too
      $(window).resize(function(){
        setHeight('#mapDiv');
        setHeight('#tabs-2');
        setHeight('#tabs-3');
      });
       
      // show link box
      $("#show-link").click(
        function () {
          $("#show-link-text").attr('value',showlink());		
					$("#show-link-item ul").fadeIn();
					$('#show-link-text').focus();
					$("#show-link-text").select();
					// handler to capture click event
					$("#show-link-text").bind("click", function(e){		
						$("#show-link-text").val(showlink());
						$("#show-link-text").select();
					});
          // return false added to prevent # from appearing in the input text
          return false;
      });
			
      // call for the show-link element
      $('#close-page-link').click(function () { 
        $("#show-link-item ul").fadeOut();
      });

      
      
      // add map key for print style-sheet
      $('body > div.container').append('<img src="img/mapcontrolpanel-key.gif" id="key-img" alt="" />');
      
      // modal view for help in info window - uses livequery and modal plugins
      $('.help').livequery('click', function(event) {
				//console.log('inside infowindow');
        event.preventDefault();
        $.get('text/info-help.html', function (data) {
          $.modal(data);
          $('#faqs').livequery('click', function(event){
						map.closeInfoWindow();
            event.preventDefault();
            $.modal.close();
            map.closeInfoWindow();
            
            $('#tabs').tabs('select', 2); //switch to second tab  ie: info
            $('#stuff-tabs-wrapper').tabs('select', 2); // switch to third tab ie: Help
            currentTab = 2;
          });
        });
      }); 

      $('#search-options').css('display','block');

      // call labelify plugin
      $(':text').labelify();
      
      // toggle the map control div
      $('.control h3').click(function(){
        if($(this).hasClass('close')){
          $(this).removeClass('close');
        }
        else{
          $(this).addClass('close');
        }
        $('#control-sections').slideToggle('slow');
      });

      // adds accordion functionality
      $('#accordion').accordion({
        header: "legend",
        autoHeight: false,
        collapsible: true
      });

      // open related links in another window
      $('#cf_mapinfopanel_links a').click(function() {
        window.open(this.href);
        return false;
      });

      // add states for the search button
      $('#search-options input[type="image"]').hover(function(){
          $(this).attr({src : 'img/btn-go-hover.gif'});
        }
        ,function(){
          $(this).attr({src : 'img/btn-go.gif'});
        }
      );
      $('#search-options input[type="image"]').mousedown(function(){
          $(this).attr({src : 'img/btn-go-pressed.gif'});
        }
      );
      $('#search-options input[type="image"]').focus(function(){
          $(this).attr({src : 'img/btn-go-pressed.gif'});
        }
      );
     
      // extreme options in refine maps block
      $('#options_extreme').addClass('hidden');
      $('#focus_view').click(function () {
        if ($('#options_extreme').hasClass('hidden')) {
        //if ($('#focus_view:checked')) {
          $('#options_extreme').slideDown("slow").removeClass('hidden');
          $('#focus_view_level_3').attr({checked :'checked'});
        }
        else {
          $('#options_extreme').slideUp("slow").addClass('hidden');
          $('#focus_view_level_3').removeAttr('checked');
          $('#focus_view_level_4').removeAttr('checked');
        }
      });
    }

  }
  else {
    updateHelp('Sorry, the Google Maps API is not compatible with this browser', 'fail');
  }
});

/** 
 * GUnload the jQuery way
 *
 * GUnload is called to reduce the
 * potential that the application leaks memory
*/
$(document.body).unload(function() {
  if (GBrowserIsCompatible()) {
    GUnload();
  }
});
