  
  tabs = {
    tabEvents   : {},
    current_tab : '',
    
    // Event handling
    register_event:function(tab, eventName, func){
      var eventLabel = '__'+eventName+tab;
      if (this.tabEvents[eventLabel] === undefined){
        this.tabEvents[eventLabel] = [];
      }
      this.tabEvents[eventLabel].push(func);
    },

    do_event:function(tab, eventName){
      var eventLabel     = '__'+eventName+tab;
      var eventOnceLabel = '__single_'+eventName+tab;
      if (this.tabEvents[eventLabel] !== undefined){
        $J.each(this.tabEvents[eventLabel], function(i, func){ func(); });
      } else { this.tabEvents[eventLabel] = []; }
      
      if (this.tabEvents[eventOnceLabel] !== undefined){
        $J.each(this.tabEvents[eventOnceLabel], function(i, func){ func(); });
      }
      this.tabEvents[eventOnceLabel] = [];
    },
    
    // Helpers
    loading:function(){
      $J('#content_pages').html('<div id="loading_page"><big>Loading property information</big><br /><small>One moment please...</small></div>');
    },

    is_listing:function(){
      if (this.current_tab.match(/listings|showing_request/) !== null){ return true; }
      return false;
    },

    is_map:function(){
      if (this.current_tab.match(/map/) !== null){ return true; }
      return false;
    },

    build_hash:function(tabId, obj){
      if (obj === undefined){ obj = {}; }
      // url_templates defined in acts_as_tab_helper.rb:89
      return tabs_url_templates[tabId].evaluate(obj).gsub(/\/+/, '/');
    },
    
    // Getters
    get_tab:function(){ return this.current_tab; },
    get_hash:function(){
      if ($J('#ajax_nav').length === 0){
        var hash = window.location.hash;
        if (hash.indexOf('#') === 0){ hash=hash.substr(1); }
        return hash;
      } else {
        var location = document.frames.ajaxnav.getLocation();
        location = loc.split('hash=');
        return loc[loc.length-1];
      }
    }
  };
  
  function update_hash(hash){
    if(document.iframesfix){
        document.getElementById('ajaxnav').setAttribute('src', '/map/mock_page/?hash='+hash);
    }
    else{ window.location.hash=hash; }
  }
  
  function changeContent(link, opts) {
    if(!opts){ opts = {}; }
    
    var t = (($(link) && $(link).pathname) ? $(link).pathname : link);

    if (t.charAt(0)=='/') {
      t=t.slice(1);
    }
    //if(!check_tab(hash)){return false}
    if (opts.after_open) {
      tabs.register_event(opts.tab_name, 'single_open', opts.after_open);
    }
  
    //if the page isn't changing, check if they want to force a reload otherwise just 
    //do the open event
    if (opts.tab_name && (!(t == tabs.get_hash()) || opts.force_reload)) { 
      tabs.loading();
      show_tab(opts.tab_name);
    }

    if (t == tabs.get_hash()) {
      if (opts.force_reload) {
        doPageUpdate(t);
      } else {
        tabs.do_event(opts.tab_name, 'open');
      };
    } else {
      update_hash(t);
    };
    return false;
  }
  
  function doPageUpdate(hash) {
    if (hash.charAt(0) != '/') {
      hash = '/'+hash;
    }

    if (hash=='/map'){
      show_tab('map');
    } else {
      tabs.loading();
      show_tab('content_pages');
    
      //without ?ajax=true to differentiate it some browsers get confused between these and the permalinkable version and break 
      //the back button
      $J.get(hash, {ajax: true}, function (content) {
        $J("#content_pages").html(content);
      });

      try {
        pageTracker._trackPageview(hash);
        bounceTracker._trackPageview(hash);
        EstatelyLog('Tracked pageview: "'+hash+'"');
      } catch (e) {}

      return hash;
    }
  }
  
  function show_map(options) {
    options = options || {};
    if(options.after_open){
      tabs.register_event('map', 'single_open', options.after_open);
    }
    $J("#content_pages").addClass("hidden");
    changeContent('map', {tab_name: 'map'});
  }
  
  function show_tab(tab) {
    
    if (tab=='map' || tab=='map_index') {
      $J('#content_pages').addClass('hidden');
      $J('#content_map').removeClass('hidden').css('position', 'relative');
      tab = 'map';
    } else {
      $J('#content_map').addClass('hidden').css('position', 'absolute');
      $J('#content_pages').removeClass('hidden');
    };
  
    if ((tab === 'map') && $J('#schedule_showing_corner').length > 0) {
      $J('#schedule_showing_corner').hide();
      //resizePage.resize();
    }
    
    if (tab === tabs.current_tab || tab === 'content_pages') {
      EstatelyLog('not firing events since were not changing tab: ' + tab + '==' + tabs.current_tab);
      return false;
    }
  
    EstatelyLog('firing events for:' + tab);
  
    tabs.do_event(tabs.current_tab, 'close');
  
    tabs.current_tab = tab;
    tabs.do_event(tab, 'open');
  
    if (tabs.is_listing()) { tabs.do_event('listing', 'open'); }
  }
  
  //changes all sibling (?) elements to "" and selected element to class="selected"
  //Not specific to element type
  function change_tabs(tab_id) {
    var parent_div = $(tab_id).parentNode;
    var children = $A(parent_div.childNodes);
    /* This pulls the ULs too, but Firefox doesn't seem to mind' */
    var f = function gimme(each_option) {each_option.className = "";};
    children.each(f);
    $(tab_id).className = "current";
  };
  
  /*--------------------------------------------------------------------*/
    /* PAGELOCATOR */
      function PageLocator(propertyToUse, dividingCharacter) {
        this.propertyToUse = propertyToUse;
        var t = window.location.pathname;
        if(t.charAt(0)=='/'){t=t.slice(1);}
        this.defaultQS = t;
        this.dividingCharacter = dividingCharacter;
      };
      PageLocator.prototype.getLocation = function() {
        return eval(this.propertyToUse);
      };
      PageLocator.prototype.getHash = function() {
        var url = this.getLocation();
        if(url.indexOf(this.dividingCharacter)>-1) {
          var url_elements = url.split(this.dividingCharacter);
          return url_elements[url_elements.length-1];
        } else {
          return this.defaultQS;
        };
      };
      PageLocator.prototype.getHref = function() {
        var url = this.getLocation();
        var url_elements = url.split(this.dividingCharacter);
        return url_elements[0];
      };
      PageLocator.prototype.makeNewLocation = function(new_qs) {
        return this.getHref() + this.dividingCharacter + new_qs;
      };
    /* END PAGELOCATOR */
    
    
    /* AjaxIframesFixer */
      function AjaxIframesFixer(iframeid) {
        this.iframeid = iframeid;
        if (document.getElementById(this.iframeid)) {
          this.locator = new PageLocator("document.frames['"+this.iframeid+"'].getLocation()", "?hash=");
          this.windowlocator = new PageLocator("window.location.href", "#");
          this.timer = new Timer(this);
          
          this.delayInit(); // required or IE doesn't fire
        };
      };
      AjaxIframesFixer.prototype.delayInit = function(){
        this.timer.setTimeout("checkBookmark", 100, "");
      };
      AjaxIframesFixer.prototype.checkBookmark = function(){
        window.location = this.windowlocator.makeNewLocation(this.locator.getHash());
        this.checkWhetherChanged(this.locator.defaultQS);
      };
      AjaxIframesFixer.prototype.checkWhetherChanged = function(location){
        if(this.locator.getHash() != location) {
          doPageUpdate(this.locator.getHash());
          window.location = this.windowlocator.makeNewLocation(this.locator.getHash());
        };
        this.timer.setTimeout("checkWhetherChanged", 200, this.locator.getHash());
      };
    /* END AjaxIframesFixer */
    
    
    /* AjaxUrlFixer */
      function AjaxUrlFixer() {			
        this.locator = new PageLocator("window.location.href", "#");
        this.timer = new Timer(this);
        this.checkWhetherChanged(this.locator.defaultQS);
      }
      AjaxUrlFixer.prototype.checkWhetherChanged = function(location){
        var tab_name;
        if(this.locator.getHash() != location) {
          doPageUpdate(this.locator.getHash());
        }
        this.timer.setTimeout("checkWhetherChanged", 200, this.locator.getHash());
      };
    /* END AjaxUrlFixer */
    
    function FixBackAndBookmarking() {
      if(window.location.hash=='' || window.location.hash=='#'){
        var t = window.location.pathname;
        if(t.charAt(0)=='/'){t=t.slice(1);}
      //if the url starts with a state abbreviation, it's the map...
      if(['AL','AK','AZ','AR','CA','CO','CT','DE','DC','FL','GA','HI','ID','IL','IN','IA','KS','KY','LA','ME','MD','MA','MI','MN','MS','MO','MT','NE','NV','NH','NJ','NC','ND','OH','OK','OR','PA','RI','SC','SD','TN','TX','UT','VT','VA', 'NY','WA','WV','WI','WY', 'BC'].find(function(a){ 
        return t.startsWith(a);
      }) || t.match(/^-?\d+\.\d{1,4}/)) {
        t = 'map';
      }
        update_hash(t);
      }
      show_tab(tab_id);
      if(document.iframesfix) {
        fix = new AjaxIframesFixer('ajaxnav');
      } else {
        fix = new AjaxUrlFixer();
      }
    }

    // This is only missing in IE 6 and 7
    // P.S. I hate myself - love ~ Anders
    var ua = window.navigator.userAgent;
    if ((ua.indexOf('MSIE 6') > -1 || ua.indexOf('MSIE 7') > -1) && ua.indexOf('Opera') == -1) {
      document.iframesfix = true;
    }
  
  
  /* Register default event handlers below */
  
  tabs.register_event('map', 'open', function () {
    //map.enableScrollWheelZoom();
  });
  
  tabs.register_event('map', 'close', function () {
    //map.disableScrollWheelZoom();
  });

