/**
 * Needs diamond-sliders.js and ui.js
 *
 * known issues:
 *  see searchMode vs mode, and comments surrounding hypenated vs. camelcased, tabNames vs mode-names.
 *  useful pattern to keep in mind is most function wanting a _type can be satisfied like, for example, search_page.currentSelection(search_page.searchMode())
 */ 
var search_page = {
  _cache : {},
  _debug : true,
  extra : {}, // for manager pages.  Allows you to add data for search that's not represented with a control on the page (like the category links).
  version : 0, // stops late ajax responses from changing page.
  never_searched : true,
  types : {},
  _currentBasket : {}, /* hold choosen bakset info, not just temparay "compare" type selection */
  _currentTab : 'diamondSearch',
  _setTab : function(t) { // clumsy little function, but made this work quick.
    this._currentTab = t; 
     switch(t) {
      case 'compare':
        search_page.formatCompare();
        break;
     }
    // do a search for this tab automatically, if not already done.
    //if(this.cache(t) && !this.cache(t).last_post) { this.debug('Automatic search for tab load.'); this.search(t);}
    this.showInfoBox();
  },
  // Search types for a attribute and returns type name
  // llok at simliar elementId functions
  typesSearch: function(attr,val) {
    for(_type in this.types) {
      if(this.types[_type][attr] == val) return _type; 
    }
  },
  basketSelection: function(_type,sku /* or options object */) {
    if(_type == 'settingSearch' && this.mode() == 'search') {
      this.debug('WARNING: Clearing setting selection because this is a regular serch');  
      this._currentBasket['settingSearch'] = {};
      return {};
    }
    if(sku) {
      // get sub-type (inventory model type) for this _type... prefer last_post, otherwise assume same type as last item in currentBasket (like on page load)
      var last_post = this.cache(_type, 'last_post');
      var dbtype = (_type=='diamondSearch') ? 
            'diamond' :  // yeah, diamond is set here.  It's an exception, ok? Geez.
            ( last_post ? last_post['type'] : this._currentBasket[_type]) ;

      var out = {type:dbtype};
      if(typeof(sku)=='object') {
        for(opt in sku) {
          out[opt] = sku[opt];
        }
      } else {
        out['sku'] = sku;
      }
      var c = this._currentBasket[_type];
      if(!c || !out['sku'] || (c['sku'] != out['sku'])) {
        this._currentBasket[_type] = out;
        this.saveState();
      }
    }
    return _type ? this._currentBasket[_type] || {} : this._currentBasket;
  },
  loadSelection: function(selection) {
    this.debug('LOADING SELECTION',selection);
    for(t in selection) {
      if(selection[t]) {
       var p = selection[t].price;
       if(p || p === 0) selection[t].price = sprintf('$%.2f', parseInt(p)); 
      }
    }
    if(selection['Diamond']) this._currentBasket['diamondSearch'] = selection['Diamond'];
    if(selection['Setting']) this._currentBasket['settingSearch'] = selection['Setting'];
    if(selection['Search'] && selection['Search']['compare']) this.setCompare(selection['Search']['compare']);
    this.showInfoBox();
  },
  setCompare : function (skuList) {
    this.debug('Setting compare', skuList);
    this._currentCompareList = skuList; // kludge to load later when diamond results need to load first.
    var l = skuList.split(',');
    for(var i = 0; i < l.length; i++) {
      var tr = $('input[value="'+l[i]+'"]');
      if(tr) {
        tr.closest('tr').addClass('ui-selected ui-compare').find('input:checkbox').attr('checked','checked');
      }
    }
  },
  saveState : function () {
    // Current Selection ("basket", before it really a basket item)
    var trans = {'diamondSearch':"data[Diamond]", 'settingSearch':"data[Setting]"};
    var basket = this.basketSelection();
    var data = {};
    var m = this.mode();
    for(typeName in basket) {
      for(itemName in basket[typeName]) {
        // this ternary is a kludge for stud search
        data[trans[typeName]+'['+itemName+']'] = (itemName == 'type'  && typeName=='diamondSearch' && m == 'cyo-stud') ? 'stud' :  basket[typeName][itemName];
      }
    }
    // search params
    data['data[Search][pageParams]'] = this.stateLink();
    data['data[Search][mode]'] = this.mode();
    var primary_name = this._currentTab == 'manageSearch' ? 'manageSearch' : 'diamondSearch'; // todo: to make more generic, need a way to say one tab is the "primary" tab (like diamondSearch is on the customer search pages).  The convention for tabs was camelCase, while modes was hyphen.  Because we're faking tabs for manager search (the "main tab" on that page is simply , you see the inconsitency here.
    data['data[Search][compare]'] = this.currentSelection(primary_name).join(',');
    // we load the response into the search to get good descrptions, and beacuse the item might no longer be on the page anyway.
    (function(ver) {
      $.post('/search/save',data, function(data, _status){if(ver==search_page.version) search_page.loadSelection(data); else search_page.debug('Late arriving data for basket ignored');},'json');
    })(this.version);
  },
  // intended for page links to call.  Resets all page controls to widest.
  allDiamonds : function() {
    var d = {carat:'any',clarity:'any',color:'any',cut:'any',price:'any',shape:'any'};
    var name = this._currentTab=='manageSearch' ? 'manageSearch' : 'diamondSearch'; // todo: to make more generic, need a way to say one tab is the "primary" tab (like diamondSearch is on the customer search pages)
    this.setParams(name, d);
    this.setControls(name);
    this.search(name, {data:d}); // we know the data, we don't need it to gather from the controls
  },
  // reads page params, sets on object, sets on correct controls.
  parsePageParams : function(params,preventSearch) {
    this.debug('--parsePageParams: Search params found', params);
    var ready = {};
    // break into search/tab groups by prefix
    var mode = null;
    for(param in params) {
      // these "params" aren't item type params, but whole page parameters
      if(param=='mode') {
        this.debug('parsePageParams: setting mode', params[param]);
        mode = params[param];
        continue;
      } else if(param=='tab') {
        this.debug('Reading tab parameter:' + params[param]);
        this.selectTab(params[param]);
        continue;
      }
      var parts = param.split('_');
      var p = parts.pop();
      var t = parts.shift();
      t = t || 'diamond'; // todo: hard-code default here
      t = this.typesSearch('paramPrefix',t);
      if(!t) continue;
      if(!ready[t]) ready[t] = {};
      ready[t][p] = params[param];
    }
    // set values found
    for(_type in ready) {
      this.debug('parsePageParams, setting params:', _type, ready[_type]);
      this.setParams(_type, ready[_type]);
      this.setControls(_type);
    }
    if(mode) this.mode(mode);
  },
  selectTab : function(index) {
    $('#search_tabs').tabs('select',parseInt(index));
  },
  titleHelp: function(title,mode,tab_title) {
    $('#search_page_title').html(title);
    //$('#search_page_help').html($('#help-'+mode).html());
    if(tab_title) $('[href="#cys"]').html(tab_title);
  },
  /**
   * handles content unique to each type
   */ 
  modeFormat : function(mode) {
    var modes = {
      'search' : {
        banner : 'diamond-search-banner',
        title : 'Loose Diamond Search'
      },
      'cyo-stud' : {
        banner : 'stud-search-banner',
        title : 'Fine Diamond Pairs',
        tab_title : 'Earring Options'
      },
      'cyo-pair' : {
        banner : 'stud-search-banner',
        title : 'Find Fine Diamond Pairs',
        tab_title : 'Earring Options'
      },
      'cyo-ring' : {
        banner : 'ring-search-banner',
        title : 'Create Your Own Engagment Ring',
        tab_title : 'Choose Your Setting'
      },
      'cyo-pendant' : {
        banner : 'pendant-search-banner',
        title : 'Create Your Own Pendant',
        tab_title : 'Pendant Options'
      },
      'manage-search' : {
        banner : 'manage-search-banner',
        title : 'Manager Diamond Search'
      }
    };

    // remove all banners and add the one we want.
    var kill = '';
    for(t in modes) {
      if(t.banner) kill += t.banner + ' '; 
      $('.diamond-selections .default .mode-'+t).hide();
      $('.setting-selections .default .mode-'+t).hide();
    }
    var m = modes[mode];
    $('.main-banner').removeClass(kill).addClass(m.banner);
    $('.setting-selections .default .mode-'+mode).show();
    $('.diamond-selections .default .mode-'+mode).show();

    $('#search_page_title').html(m.title);
    //$('#search_page_help').html($('#help-'+mode).html());
    if(m.tab_title) $('[href="#cys"]').html(m.tab_title);
  },
  // *warning* modes and tabs are odd.  See searchMode, and comments there as well. 
  mode : function(set) {
    if(!set) return this._mode;
    this.debug('SETTTING MODE', set);
    set = set.toLowerCase();
    var tab; // a stupid name.  only refers to a possible settingSeach tab.
    if(set!='manage-search') {  // just prevents the warning tab(...) would create when I know there will never be a settingSearch tab.
      tab = this.tab('settingSearch');
      tab = tab.length ? tab : null;
    }
    this._mode = set;
    this.modeFormat(set);
    switch(set) {
      case 'search':
        this.search('diamondSearch');
        $('#search_tabs').tabs('disable',1);
        break; 
      case 'manage-search':
        this.search('manageSearch'); 
        break;
      case 'cyo-ring':
        $('#search_tabs').tabs('enable',1);
        //tab.html(this._cache_settingTab); // restores "search" style markup originally in container.  Sort of a kludge.
        if(tab) tab.find('.search-controls')
          .html($('#controls-cyo-ring').html()).addClass('controls-cyo-ring') // yes, this is a little crazy...
          .find('.banner-ring-click')
            .click(function() {
               $('.banner-ring-click').removeClass('ui-selected');
               var a = $(this).addClass('ui-selected').find('.action-link').attr('href');
               //var a = $($(t.selected).closest('.banner,.inside-banner,.banner-rings')).find('.action-link').attr('href');          
               $.callLinkAction(a, search_page);
            });
            /*
            .selectable({
                'cancel':'a,.banner-rings-instruction',
                selected:function(u,t) {
                  var a = $($(t.selected).closest('.banner,.inside-banner,.banner-rings')).find('.action-link').attr('href');          
                  $.callLinkAction(a, search_page);
                }
            });*/
        if(this.never_searched) this.search('diamondSearch');
        this.search('settingSearch'/*, {data:{'type':'solitaire-settings'}}*/);
        // if we already have a diamond, make the settings tab active (todo: doesn't work for "incoming links..." this happens too soon?)
        if(!$.isEmpty(this.basketSelection('diamondSearch'))) $('#search_tabs').tabs('select',1);
        break;
      case 'cyo-stud': // ie. stud *search* syo-stud actually is coming still.
        $('#search_tabs').tabs('disable',1);
        this.tab('diamondSearch').find('[name="shape"]').hide().end()
                                 .find('[name="carat"] .cui-label').html('Total Carat Weight');

        if(tab) tab.find('.search-controls')
          .html($('#controls-cyo-stud').html()).addClass('controls-cyo-stud')
          .find('input[type="radio"]').click(function() {search_page.refreshSelection(); search_page.showInfoBox() });

        // because this isn't a search, we set these now:
        this.cache('settingSearch', {'last_post' : {'type':'setting-stud'}});
        if(this.never_searched) this.search('diamondSearch');
        //else if(this.basketSelection('diamondSearch')) $('#search_tabs').tabs('select',1); // if we already have a diamond, make the settings tab active

        break;
      /*
      case 'cyo': // this mode is untested
        $('#search_tabs').tabs('enable',1);
        if(!this._cache_settingTab) this._cache_settingTab = tab.html(); // just cache's the intial look to the CYO tab before show the "default/intro" screen
        tab.html($('#cys_default').html());
        break; */

      case 'cyo-pair':
        this.debug('loose diamond studs mode');
        $('#search_tabs').tabs('disable',1);

        this.tab('diamondSearch').find('[name="carat"]').attr('name','tw').find('.cui-label').html('Total Carat Weight');

        if(this.never_searched) this.search('pairSearch');
        break;
      case 'cyo-pendant':
        $('#search_tabs').tabs('disable',1);
        //this.tab('diamondSearch','.cui-search-slider')
        this.tab('diamondSearch').find('[name="shape"]').hide();

        if(tab) tab.find('.search-controls')
          .html($('#controls-cyo-pendant').html()).addClass('controls-cyo-pendant')
          .find('input[type="radio"]').click(function() {search_page.refreshSelection(); search_page.showInfoBox() });

        // because this isn't a search, we set these now:
        this.cache('settingSearch', {'last_post' : {'type':'setting-pendant'}});
        if(this.never_searched) this.search('diamondSearch');
        // if we already have a diamond, make the settings tab active
        else if(this.basketSelection('diamondSearch')) $('#search_tabs').tabs('select',1);
        break;
    }
    return set;
  },
  // todo: remove this
  viewDetails: function(sku) {
    alert('Depreciated!');
    var d = this.cache('settingSearch', 'last_post');
    RAOS.log(this._cache, d, 'View details');
    $('#details_dialog').load('/details/'+d['type']+'/'+sku+'?mode='+this.mode(), {}, function() {$('#details_dialog').dialog('open');});
  },
  // new style details links, should be used in all places.
  diamondDetailsLink: function(sku) {
    var m = this.mode();
    var dtype = {'cyo-stud':'stud', 'cyo-pair':'pair'}[m] || 'diamond';
    return '/details/'+dtype+'/'+sku+'?mode='+m;
  },
  pairDetailsLink: function(id) {
    return '/details/pair/'+id+'?mode=cyo-pair';
  },
  /**
   * Returns the parameters that, ideally, should re-create this page state
   */ 
  getPageParams : function() {
    var out = {};
    for(_type in this.types) {
      var data = this.cache(_type, 'last_post');
      for(d in data) {
        var prefix = this.types[_type].paramPrefix;
        if(prefix=='diamond') prefix = ''; // todo: hard-coded default here
        if(prefix) prefix  += '_';
        out[prefix+d] = data[d];
      }
    }
    out['mode'] = this.mode();
    return out;
  },
  stateLink : function(includeAny) {
    var params = this.getPageParams();
    out = [];
    this.debug('Saving State:', params);
    for(param in params) {
      if(includeAny || params[param] != 'any') 
        if(params[param]) out.push(param + '=' + $.URLEncode(params[param]));
    }
    return '?'+out.join('&');
  },
  updateStateLink : function() {
    $('.cui-state-link').attr('href', this.stateLink());
  },
  init : function(options) {
    options = $.extend( {
      'types' : {}, 
      'params' : null // params that can be given to the page, generally search terms
    }, options);

    this.types = options.types;
    this.load_blind = $('#load_blind').css({'opacity':'0.6','position':'absolute','display':'none'});
    this.info_box = $('#info_box')
      .css({'z-index':99999,'position':'absolute', 'display':'none'}) 
      .find('.exit').click(function() {
       search_page.clearSelection('diamondSearch'); 
      }).end();
    // paginate display and events
    $('.cui-paginate:not(.template)').html($('.cui-paginate.template').html());
    $('.cui-paginate-next').click(search_page.nextPageClick);
    $('.cui-paginate-prev').click(search_page.prevPageClick);
    // temp. "page 1" link
    $('.cui-paginate-text').click(function(e) {
      search_page.repeatSearch(search_page.searchMode(), {'page':1});
    });
    $('.cui-state-link').mouseenter(function() {search_page.updateStateLink()});
    // todo: temp hack to make older functions from first tab (Diamond Search) still work
    this.tabs_box = $('#search_tabs');

    //this.mode('cyo');

    this.results_box =  $('#cyd .results-display');
    this.last_data = {};
    this.hover_select = true; // mouse hover selects elements until one is clicked to "hold" selection
    $(window).scroll(function() {search_page.scroll()});

    // -- Display the Search Page Controls, hidden until script can run --
    $('#search_container').show();

    // -- should happen once visible:
    /* NOTE: If you are thinking of setting default here, don't! Use the controller! */
    if(options.params) this.parsePageParams(options.params); //<-- ie, string or get-style params
    if(options.selection) this.loadSelection(options.selection);
    if(!this.mode()) this.mode('search'); // if no mode is set after parsing params, set as regular diamond search.
    this._setTab(this._currentTab);
    this.positionInfoBox();
  },
  tab : function(name,fragment) {
    if(!this.types[name]) {
      this.error('Tried to find unknown tab area "'+name+'".');
      return null;
    }
   
    var s = this.types[name]['sel'];
    if(fragment) s += ' ' + fragment;
    s = $(s);
    if(fragment && fragment.indexOf('.template')==-1) s = s.not('.template');
    return s; 
  },
  currentTab : function(fragment) {
    return this.tab(this._currentTab,fragment);
  },
  /**
   * Set parameters on object to later be used in a search, or collect them from the controls
   */ 
  setParams: function(_type,data) {
    // collect
    if(!data) {
      data = $.extend({},this.extra);
      this.tab(_type,'.cui-search-slider').each(function() {
        if($(this).find('cui-slider').hasClass('ui-state-disabled')) return;
        data[$(this).attr('name') || $(this).attr('id')] = $(this).cadiSlider('currentRange');
      });
      this.tab(_type,'[name="shape"]').each(function() {data['shape'] = $(this).shapeSelector('currentRange');});
      // generic controls.  Just must use ui-selected to indicate what div to look for an input in.
      this.tab(_type,'.cui-control').each(function() {
        $(this).find('.ui-selected').each(function() {
          var d = $(this).find('input:first');
          data[d.attr('name')] = d.val();
        });
      });
      if(_type=='manageSearch') {
        // todo: 1) with checkboxes, can't search for FALSE (treated as any) 2) we only have one checkbox (fancy) right now anyway.
        $('#diamond_extras input[type=checkbox]').each(function(){
          data[$(this).attr('name')] = $(this).attr('checked') ? 1 : 'any';
        });
        data['lab'] =  $('[name=lab]').val() || 'any';
      }
    }
    this.debug('Set search parameters on object for type:',_type,data);
    this.cache(_type, {'pending_post':data});
    return data;
  },
  /**
   * Set controls with param values (or params set with setParams)
   * 'data' is params
   */ 
  setControls : function(_type, data) {
    if(!data) data = this.cache(_type, 'pending_post');
    this.debug('Set search parameters on display controls for type:',_type,data);
    this.tab(_type,'.cui-search-slider').each(function() {
      var d = data[$(this).attr('name') || $(this).attr('id')];
      if(d) $(this).cadiSlider('setValueList',d);
    });
    if(data['shape']) {
      this.tab(_type,'[name="shape"]').each(function() {
        $(this).shapeSelector('setValueList', data['shape']);
      });
    }
    return data;
  },
  /**
   * With data passed as option, (or will set with setParams), performs a search.
   *
   * Calls _formatResult or given formatting callback
   * Can also provide with additional 'success' callback for other purposes.
   */ 
  search: function(_type,options) {
    options = $.extend( {
      repeat : false,
      data : null,
      success : function() {}
    }, options);

    // Intial search, or ( Collect params from page  or do repeat search merging new data)
    this.debug('Type for search is:', _type,this.never_searched);
    if(this.never_searched) {
      options.data = this.cache(_type, 'pending_post');
      this.debug('Doing an initial search with data:', options.data);
    } else {
      if(options.repeat) {
        this.debug('Doing a repeat.');
        options.data = $.extend(this.cache(_type,'last_post'), options.data);
      } else { // otherwise, with no data passed, collect from controls.
        if(!options.data) {
          options.data = this.setParams(_type);
        }
      }
    }

    this.clearSelection(_type);
    this.showLoading();
    this.last_search_type = _type;
    // set data for posting
    this.cache(_type, 'clear results');
    this.cache(_type, {last_post : options.data });
    this.never_searched = false;
    
    // studs search
    if(this.mode() == 'cyo-stud') { options.data.shape = 'Studs'; options.data.category = 'studs'}
    // pendants search
    //if(this.mode() == 'cyo-pendant') { options.data.shape = 'Pendants'; options.data.category = 'diamond-pendants'} // note: wasn't sure if 'diamond-pendants' is right category in new system.
    if(this.mode() == 'cyo-pendant') { options.data.shape = 'round'; }
    // pair search
    if(this.mode() == 'cyo-pair') {
      if (!options.data || !options.data.sort) { if(!options.data) options.data = {category:'cyo-pair'}; options.data.sort = 'tw', options.data.direction = 'asc'}
      else options.data.category= 'cyo-pair';
    }
    
    // "freeze" type in case multiple searched are being performed
    this.debug('Do Search:', _type, options.data);
    this.debug(this.mode());
    (function(_type,options) {
      $.ajax({
      'type' : 'GET',
      'url' : search_page.types[_type].url,
      'data': options.data,
      'success' : function(data) {
        data = search_page.parseJSON(data); 
        search_page.loadResult(_type, data);
        search_page.saveState();
        options.success();
      }
      });
    })(_type,options);
  },
  /**
   * Read , merge or clears the data cache or just the results and pagination
   */ 
  cache : function(_type,data,clear_results) {
    this.debug('Retrieving data for ',_type,data,clear_results); 
    if(data=='clear cache') {
      this._cache[_type] = {};
      return;
    }
    if(data=='clear results' && this._cache[_type]) {
        delete this._cache[_type]['paginate'];
        delete this._cache[_type]['results'];
    }
    if(!data) { // read
      return this._cache[_type];
    } 
    if(typeof(data)=='object') { // write (merge)
      if(!this._cache[_type]) this._cache[_type] = {}
      $.extend(this._cache[_type], data);
      return this._cache[_type];
    } else { // read
      if(!this._cache[_type]) {this._cache[_type] = {}; return {}};
      return this._cache[_type][data];
    }
  },
  repeatSearch : function(_type,newData) {
    if(!_type) _type = this.last_search_type;
    return this.search(_type, {'data':newData,'repeat':true});
  },
  /**
   * Two helpers to figure out the 'types' based on an element being inside a 'tab' with a certain 'id'
   * Or just based on that id (see types array)
   */ 
  elementFindType : function(el) {
    var t_id =  $(el).closest('.tab').attr('id')
    if(t_id == 'cyd') return this.mode() == 'cyo-pair' ? 'pairSearch' : 'diamondSearch';
    return this.elementIdFindType(t_id);
  },
  elementIdFindType: function(elid) {
    var all = search_page.types;
    elid = '#' + elid.replace('#','');
    for(_type in all) {
      if(all[_type].sel == elid) return _type;
    }
    search_page.error('elementFindType could not determine _type based on "'+elid+'".');
  },
  // called as event handlers
  nextPageClick : function(e) {
    e.preventDefault();
    var _type = search_page.elementFindType(this);
    //if(!this.cache[_type]) return;
    var p = search_page.cache(_type,'paginate');
    if(p['page'] < p['pageCount']) search_page.repeatSearch(_type,{'page':p['page']+1});
  },
  sortPageClick: function(e) {
    e.preventDefault();
    var _type = search_page.elementFindType(this);
    //if(!this.cache[_type]) return;
    var order = $(this).attr('title');
    //if(order=='cut') order = 'diamondCut';
    var asc = $(this).hasClass('cui-order-asc');
    var way =  asc ? 'asc' : 'desc'; 
    search_page.repeatSearch(_type,{'sort':order,'direction':way});
  },
  prevPageClick : function(e) {
    e.preventDefault();
    var _type = search_page.elementFindType(this);
    var p = search_page.cache(_type,'paginate');
    if(p['page'] > 1) search_page.repeatSearch(_type,{'page':p['page']-1});
  },
  clearSelection : function(_type) {
    this.debug('CLEAR SELECTION',_type)
    this.tab(_type, '.results-display .ui-selectee').removeClass('ui-selected');
    this.tab(_type, '.results-display input[type="checkbox"]').removeAttr('checked');
    this.hideInfoBox();
    this.hover_select = true;
  },
  /** 
   * Gets an array of Skus for based on the selection in a certain type
   */ 
  currentSelection : function(_type) {
    var out = [];
    this.tab(_type, '.results-display .ui-selected').each(function() {
        out.push($(this).find('.cui-id').val());
    });
    return out;
  },
  /** 
   * Gets the sku of current highlight.
   */ 
  currentHighlight : function(_type) {
      return this.tab(_type, '.results-display .ui-highlight').find('.cui-id').val();
  },
  /**
   * Returns the "choice"  (selection for basket) for a given type (only should be one choice).  
   */ 
  currentChoice : function(_type) {
    var m = this.mode();
    if(_type == 'settingSearch' && (m == 'cyo-stud' || m == 'cyo-pendant')) {
      // in this specail case, returns an object of options, not just a sku value.
      out = {};
      this.tab(_type, '.search-controls input:checked').each(function() {
        out[$(this).attr('name')] = $(this).val();
      });
      return out;
    }
    return this.tab(_type, '.results-display .cui-choosen').find('.cui-id').val();
  },
  positionInfoBox : function() {
    //var rb = this.currentTab('.results-display');
    var ib = $('#info_banner');
    if(!ib.length) ib = $('#search_container'); // because manage pages don't have the sider bar. todo: chagne to right selector.
    var pos = ib.offset();
    pos.top += ib.height()+10;
    this.info_box_min = pos.top; 
    this.info_box.css({'top':pos.top,'left':pos.left});
  },
  scroll : function() {
   var s = $(window).scrollTop()+20; 
   if(s > this.info_box_min) {
    this.info_box.css({'position':'fixed','top':20});
   } else {
    this.info_box.css({'position':'absolute', 'top':this.info_box_min});
   }
  },
  // doesn't follow convention because it's not a "magic" callback like other format_ functions
  formatCompare : function() {
    var m = this.mode();
    this.debug('MODE IS CURRENTLY...', m);
    var mSearch = (m == 'cyo-pair') ? 'pairSearch': 'diamondSearch';
    var skus = this.currentSelection(mSearch);
    this.debug('ok');
    if(!skus || !skus.length) {this.tab('compare').html('<center><h2>To compare multiple diamonds use the compare column from the "Choose Your Diamond" tab.</h2></center>'); return;}
    var compare_fields = ['carat','cut','color','depthPercent','tablePercent','girdle','polish','symmetry','culet','fluor','measurements','price'];
    var alias = {'tablePercent' : 'table %','depthPercent' : 'depth %'};

    this.debug('ok');
    skuDetails = [];
    for(var i = 0; i < skus.length; i++) {
     var ds = this.skuSearch(mSearch, skus[i]);
     if(m == 'cyo-pair') skus[i] = skus[i].replace('~','&nbsp;&nbsp;');
     this.debug('sku search restults:', ds);
     skuDetails.push(ds);
    }

    this.debug('ok');
    var pair_hack = (m == 'cyo-pair') ? ' colspan="2"' : '';
    var out = '<tr class="table-center"><th>Sku</th>'+$.tagData(null,skus,'<th'+pair_hack+'></th>')+'</tr>';
    for(var y = 0; y < compare_fields.length; y++) {
      var cls = (y%2) ? ' class="even"' : '';
      var title = compare_fields[y] == 'carat' ? this.carat() : compare_fields[y]; 
      if(alias[title]) title = alias[title];
      out += '<tr'+cls+'><td class="first">'+title+'</td>';
      var price_hack = (title == 'price') ? '$' : '';
      if(m=='cyo-pair') {
        for(var i = 0; i < skuDetails.length; i++) {
         out += '<td class="pair-left">'+price_hack+skuDetails[i]['d1_'+compare_fields[y]]+'</td><td class="pair-right">'+price_hack+skuDetails[i]['d2_'+compare_fields[y]] || '&nbsp;'+'</td>';
        }
      } else {
        for(var i = 0; i < skuDetails.length; i++) {
         out += '<td>'+price_hack+(skuDetails[i][compare_fields[y]] || skuDetails[i]['details'][compare_fields[y]] || '&nbsp;')+'</td>';
        }
      }
      out += '</tr>';
    }
    out += '<tr class="table-center"><td></td>';
    for(var i = 0; i < skuDetails.length; i++) {
     out += '<td'+pair_hack+'><a href="'+this.diamondDetailsLink(skuDetails[i]['sku'])+'"><img class="rollover" src="/img/btn_view_off.jpg" alt="view" /></a></td>';
    }
    out += '</tr>';

    this.debug('ok');

    out = '<table id="compare_table" class="standard">'+out+'</table>';

    this.tab('compare').html(out);
  },
  refreshSelection : function() {
    if(this.mode()=='manage-search') {
      var dsel = this.currentChoice('manageSearch');
      this.basketSelection('manageSearch', dsel); 
    } else {
      var dsel = this.currentChoice('diamondSearch');
      var ssel = this.currentChoice('settingSearch'); // could be sku string *or* object!  Watch out!
      this.basketSelection('diamondSearch', dsel); //todo: this sort of selection
      this.basketSelection('settingSearch', ssel); // refresh probally shuold be *here*
    }
  },
  // totally stupid function, but needed it for the kludge
  carat : function() {
    return this.mode()=='cyo-stud' ? 'total cw.' : 'carat';
  },
  /**
   * Shows/refreshes the Info Box -- which is now two areas: the pop-up on right AND the "current selection" area on top.
   * *But Also* refreshes the basket selection
   * todo: this "refresh" function is probably getting called more than it need to be!
   */ 
  showInfoBox : function() {
    // find selection for hover-like display:
    if(this.mode()=='cyo-pair')  { // no pop-up box for pair mode, so we do all the hiding here.  Calling this function all the time is particularly stupid in this mode...
      this.info_box.find('#info_box_hover').slideUp();
      $('#info_box_selection').find('.real').hide().end()
          .find('.cs-start-over').hide().end()
          .find('.cs-links').hide();
      return;
    }
    var out ='';
    //var sel = this.currentSelection('diamondSearch');
    //var sel = this.currentHighlight('diamondSearch');
    var sel = this.currentHighlight(this._currentTab);
    sel = sel ? [sel] : [];

    if(sel.length) {
      var compare_fields = ['carat','cut','color','price'];
      var details_fields = ['sku','depthPercent','tablePercent','girdle','polish','symmetry','culet','fluor','measurements'];
      var aliases = {
        'fluor':'fluor.',
        'tablePercent':'table',
        'depthPercent':'depth'
      };


      var primary_name = this._currentTab=='manageSearch' ? 'manageSearch' : 'diamondSearch'; // todo: to make more generic, need a way to say one tab is the "primary" tab (like diamondSearch is on the customer search pages)
      // load info box, show info box
      if(sel.length > 1 ) { // compare style
        this.info_box.find('h1').html('<a class="action-link" href="#selectTab/2">Compare '+sel.length+' Diamonds</a>');
        for(var i = 0; i < sel.length; i++) {
         var ds = this.skuSearch(primary_name, sel[i]);
         if(!ds) {this.debug('showInfoBox: ds was null'); continue;}
         out += '<tr>';
         $(compare_fields).each(function() {
           out += '<td>'+ds[this]+'</td>';
         });
         out += '</tr>';
        }
        out = '<table>'+out+'</table>';
      }
      else { // details style
        this.info_box.find('h1').html('Diamond Details');
        var ds = this.skuSearch(primary_name, sel[0]);
        if(!ds) return;
        if(ds) {
          for(var i = 0; i < (details_fields.length-1); i++) {
            var title = details_fields[i]; 
            if(aliases[title]) title = aliases[title];
            out += '<dt>'+title+'</dt><dd>'+(ds[details_fields[i]] || ds['details'][details_fields[i]] ||'&nbsp;')+'</dd>';
          }
          out = '<dl>'+out+'</dl>';
          out = out+'<dl>'+'<dt>'+details_fields[8]+'</dt><br /><dd class="measure">'+(ds[details_fields[8]] || ds['details'][details_fields[8]] ||'&nbsp;')+'</dd>'+'</dl>';
          //out += '<div class="help"><br /><b>Hint:</b> Use your "Ctrl" key to compare multiple diamonds.</div>';
        }
      }
      this.info_box.find('#info_box_hover .content').html( out ).end().show().find('#info_box_hover').slideDown();
    } else {
      //search_page.hover_select = true;
      this.info_box.find('#info_box_hover').slideUp();
    }
    //// end hover selection area
    //
    //  Current Selection Box
    // find selectio for "choosen" box:
    //var sel = this.tab('settingSearch','.results-display .cui-choosen');
    var dsel = this.basketSelection('diamondSearch')['sku'];
    var ssel = this.basketSelection('settingSearch');
    //console.log('%%%',ssel);
    ssel_desc = ssel['title'] || ssel['description'];
    if(ssel['sku']) { // because sometimes this is an object, sometimes not.  It's stupid, I know...
      ssel = ssel['sku'];
    }

    out = '<i>Choose your diamond from the tab below.</i>';
      var dout = '<i>Choose your diamond from the tab below.</i>';
      var sout;
      var m = this.mode();
      /*
      switch(m) {
        case 'cyo-ring':
          sout = '<i>Choose your setting from the tab below.</i>';
          break;
        case 'cyo-pendant':
          sout = '<i>Choose your pendant options from the tab below.</i>';
          break;
        case 'cyo-stud':
          sout = '<i>Choose your earring options the tab below.</i>';
          break;
        default:
          sout = '<p><i>Create your own <a class="action-link" href="#mode/cyo-ring">Ring</a> or <a class="action-link" href="#mode/cyo-pendant">Pendant</a></i></p>'
      }*/

      $ib = $('#info_box_selection');

      if(dsel || ssel_desc ) {
        if((m=='cyo-stud' || m=='cyo-pendant' || m=='cyo-ring') && !(dsel && ssel_desc)){ // don't let settings only be added!
          $ib.find('.cs-start-over').show();
          $ib.find('.cs-links').hide();
        } else {
          $ib.find('.cs-start-over,.cs-links').show();
        }
      } else {
        $ib.find('.cs-start-over,.cs-links').hide();
      }

      if(dsel) {
        // Diamond items try to hit the result's data before the basket selection (why?)
        var ds =  this.skuSearch('diamondSearch',dsel);
        var dbsel = this.basketSelection('diamondSearch');
        var dout = ds['title'] || dbsel['description']|| ds['fullDescShort'];
        var dprice = dbsel['price'] || ds['price'];
        var sku = dbsel['sku'] || ds['sku'];
        var image = dbsel['images'] ? dbsel['images']['thumbnail'] || '/img/shapes/detail_'+dbsel['shape'].toLowerCase()+'_top_real_thumb.jpg' : null;

        var dtype = (this.mode() == 'cyo-stud') ? 'stud' : 'diamond';
        dout += ' - <a href="'+this.diamondDetailsLink(sku)+'">Details</a>';
        $ib.find('.diamond-selections')
          .find('.default').hide().end()
          .find('.real').show()
          .find('.content').html(dout).end()
          .find('.price').html(dprice);
        var dimg = $ib.find('.diamond-picture');
        if(image) dimg.css({'background' : 'url("'+image+'") no-repeat center center'});
        else dimg.css({'backgroundImage' : 'none'});

      } else {
        $ib.find('.diamond-selections')
          .find('.real').hide().end()
          .find('.default').show();
      }


      if(!$.isEmpty(ssel)) {
        var sbask = this.basketSelection('settingSearch');
        if(!ssel_desc) {
          if(typeof(ssel)=='object') { // will be an object for setting options type tabs.
              // todo: temporary.
              sout = '';
              // could display a message based on what we know before repsone from sever, but nah
              // this.debug(ssel);
          } else {
            var ds = this.skuSearch('settingSearch',ssel);
            sout = 'Set in '+ds['title'];
          }
        } else sout = 'Set in '+ssel_desc;

       // won't do anything if there isn't a message.
       if(sout) {
         if(sbask['sku'] && sbask['type']) 
            sout += ' - <a href="/details/'+sbask['type']+'/'+sbask['sku']+'?mode='+this.mode()+'">Details</a>';
          
          $ib.find('.setting-selections')
            .find('.default').hide().end()
            .find('.real').show()
            .find('.content').html(sout).end()
            .find('.price').html(sbask['price']);

          var simg = $ib.find('.selection-picture');
          if(sbask.images && sbask.images.thumbnail) {
            simg.css({'background' : 'url("'+sbask.images.thumbnail+'") no-repeat center center'}).show();
          } else {
            simg.hide();
          }
       }

      } else { // no selection, go back to default.
        $ib.find('.setting-selections')
          .find('.real').hide().end()
          .find('.default').show();
      }
  },
  startOver : function() {
    $.post('/search/clear');
    this._currentBasket = {};
    this.version += 1;
    this.cache('clear cache');
    $('.results-display .ui-selected').removeClass('ui-selected');
    $('.results-display .cui-choosen').removeClass('cui-choosen');
    $('.results-display [checked]').removeClass('checked');
    $('.search-controls [checked]').removeAttr('checked');
    this.mode(this.mode()); 
    this.showInfoBox();
    /*
    var m = this.mode();
    if(m == 'cyo-pendant' || m == 'cyo-ring') this.mode('search');
    */
  },
  addToCart : function() {
    this.addToBasket('cart');
  },
  addToWishList : function() {
    this.addToBasket('wishList');
  },
  addToBasket : function(name) {
    this.refreshSelection();
    var d = this.basketSelection();
    var data = {};
    //copy basket info into pseudo-cakephp convention
    for(i in d['diamondSearch']) {
     data['data[Diamond]['+i+']'] = d['diamondSearch'][i]; 
    }
    for(i in d['settingSearch']) {
     data['data[Setting]['+i+']'] = d['settingSearch'][i]; 
    }

    var msg = (name=='wishList') ? 'Items added to your wishlist.' : 'Items added to your shopping cart.';
    var elsel = (name=='wishList') ? '#wish_list_count' : '#shopping_cart_count';

    $.ajax({
     url: '/shop/'+name+'/addGroup',
     type: 'post',
     'data' : data,
     error: function(req) {
       var d = this._debug ? '<br><i>'+req.responseText+'</i>' : '';
       feedback.say('Sorry, there was an error adding your item.<br/>Please try again or contact us for assistance.'+d);
     },
     success: function(data) {
       var cart = eval('['+data+']').pop(); 
       if(cart.length) {
         feedback.say(msg);
         $(elsel).html(cart.length);
         search_page.startOver();
       } else {
         feedback.say('Sorry, there was an error retriving your information.<br/>Please try again or contact us for assistance.');
       }
     } 
    })

  },
  /*
  setMode : function(mode, on, type) {
._mode[mode] = on;

    switch(new_mode) {
      case 'search':
        if(on) {

        } else {}
        break;
      case 'create-your-own':
        if(on) {
          this._mode[mode].
          this._cyo_mode = type;
        } else {}
        this.tabs_box.tabs('enable',1);
        break;
      case 'compare':
        this.tabs_box.tabs('enable',2);
        break;
      case 'no-compare':
        this.tabs_box.tab('disable',2);
        break;
    }
  },
  */
  hideInfoBox : function() {
    this.info_box.hide();
  },
  parseJSON : function(data) {
    var d = eval('['+data+']');
    d = d.pop();
    return d;
  },
  showLoading : function() {
    // match size and position of the current tab's results area
    var r = this.currentTab('.results-display');
    if(!r.height()) {
      r.height(300);
    } 
    this.load_blind.matchAll(r).show();
  },
  // load diamond search results
  // new 'loadResults' function
  /**
   * New Load Result function.
   * `type` : diamondSearch, settingSearch, etc
   * Loads from cache is 'result' is omitted.
   */ 
  loadResult : function(_type, result) {
    // this will set or read cache if `result`
    this.load_blind.hide();
    this.currentTab('.results-display').css({'height':'auto'});
    
    data = this.cache(_type, result,true);
    this.debug('loadResult : looking for format_'+_type);
    format_cb =  this['format_'+_type] || this.format_generic;
    wire_cb =  this['wire_'+_type] || function() {};
    var none = false;

    if(!data['results'] ||  !data['results'].length) {
      if(_type=='diamondSearch' || _type=='pairSearch' || _type=='manageSearch') this.noResult(); // only show this message on diamond pages.  Other pages are SOL.
      none = true;
    } else {
      var target = this.tab(_type, '.results-display');
      format_cb.call(this,_type,target,data['results']);
      wire_cb.call(this,_type,target);
    }

    // show/hide pagination
    var p = data['paginate'];
    if(!none && p['pageCount'] > 0) {
      this.currentTab('.cui-paginate-text').html(
       'Page ' + p['page'] + ' of ' + p['pageCount'] 
      ).css({visibility:'visible'});
    } else this.currentTab('.cui-paginate-text').css({visibility:'hidden'});

    if(!none && p['page'] > 1) this.currentTab('.cui-paginate-prev').css({visibility:'visible'});
    else this.currentTab('.cui-paginate-prev').css({visibility:'hidden'});
    if(!none && p['page'] < p['pageCount']) this.currentTab('.cui-paginate-next').css({visibility:'visible'});
    else this.currentTab('.cui-paginate-next').css({visibility:'hidden'});
  },
  noResult : function() {
    this.currentTab('.results-display').html('<br/><center>Start by selecting a diamond shape above,<br/><a class="action-link" href="#allDiamonds"><u>Or, search our entire inventory</u></a></center>');
  },
  skuSearch : function(_type, sku) {
    this.debug('Need to looks at cache for skuSearch:', _type, this.cache(_type));
    var d = this.cache(_type, 'results');
    var model = (this.mode() == 'cyo-pair') ? 'Pair' : 'Inventory';
    this.debug('sku search on model', model);
    if(!d) {this.debug('WARNING: Sku search failed'); d = [];}
    for(var i = 0; i < d.length; i++) {
      if(d[i][model]['sku'] == sku) return d[i][model];
    }
    return {};
  },
  /** wire_ and format_ functions are magic functions.  format_generic will be called if a function with the right name is not. _wrire is called right after _format **/
  /* if you have your own format_ function, you don't need a wire_ function too, but if you want to use the default formatting, _wire is useful */
  // "Results" are diamond searches, "Inventory" are inventory such as rings with images.
  wire_settingSearch: function(_type, target) {
    target.find('.cui-select-setting').click(function() {
      target.find('.cui-choosen').removeClass('cui-choosen');
      $(this).closest('.inventory-display').addClass('cui-choosen');
      search_page.refreshSelection();
      search_page.showInfoBox();
    });
  },
  format_pairSearch: function(_type, target, data) {
    // format  --- 
    var show_buttons = true;
    var out = '<table>';

    var fields = { // hint: it'd be easy to add an "alias" memeber here if we need to.
      shape : {},
      cut : {},
      sku : {'split':true},
      tw : {'alias' : 'total cw.'},
     // carat: {'split' : true},
      color : {'split':true},
      clarity : {'split':true},
      certified : {'value' : 'Yes'},
      price : {}
    };
    
    var last_params = search_page.cache(_type,'paginate'), asc = false;
    out += '<tr><th>Compare</th>';
    for(field in fields) {
      asc = false;
      if(field.toLowerCase() == last_params['sort']) {
        asc = !(last_params['direction'] == 'asc');
      }
      asc = asc ? 'cui-order-asc' : 'cui-order-desc'

      var title = field, sortable = field;
      if(fields[field]['split']) {
        title = field;
        sortable = 'd1_'+field;
      } 
      if(fields[field]['alias']) {
        title = fields[field]['alias'];
      }
      out += '<th><a title="'+sortable+'" class="cui-sort '+asc+'">'+title+'</a></th>'; 
    }
    if(show_buttons) out += '<th>Details</th>';
    out += '</tr>';

    for(var i = 0; i < data.length; i++) {
      var cl = i%2 ? ' class="even"' : '';
      var d = data[i]['Pair'];
      out += '<tr'+cl+'><td><input type="hidden" class="cui-id" value="'+d['sku']+'"/><input type="checkbox"/></td>';
      for(field in fields) {
        if(fields[field]['split']) {
          out += '<td>'+ d['d1_'+field] + '<br/>' + d['d2_'+field] + '</td>';
        } else {
          var v = fields[field]['value'] || d[field];
          out += '<td>' + v + '</td>';
        }
      }
      
      // Diamonds and studs are closely realted... almost the same thing!
      if(show_buttons) out += '<td><a href="'+this.pairDetailsLink(d['sku'])+'"><img class="rollover" src="/img/btn_view_off.jpg" alt="view" /></a></td>';
      out += '</tr>';
    }
    out += '</table>';
    
    target.html(out);
  },
  wire_pairSearch : function(_type, target) {
    this.wire_diamondSearch(_type, target);
  },
  wire_manageSearch: function(_type,target) {
    return this.wire_diamondSearch(_type,target);
  },
  // orginally I had this all under format (cause you can) , but  I split it so I can share it with pairs
  wire_diamondSearch: function(_type, target) {
    // add and wire
    target
      .commonEvents()
      .find('input[type="checkbox"]').click(function(){
        if($(this).attr('checked')) { // select from checkbox
          var p = $(this).closest('tr');
          search_page.debug(p);
          //if(p.hasClass('cui-choosen')) return;

          /*
          target.find('.cui-choosen').each(function() {
            $(this).removeClass('cui-choosen');
            $(this).find('input[type="checkbox"]').removeAttr('checked');
          });*/
          //p.addClass('cui-choosen');
          p.addClass('ui-selected ui-compare');
          //search_page.hover_select = false;
          //search_page.showInfoBox();
          search_page.saveState();
          search_page.refreshSelection();
          search_page.showInfoBox();
        } else {// unselct from check box
          //$(this).closest('tr').removeClass('cui-choosen');
          $(this).closest('tr').removeClass('ui-selected');
          //search_page.showInfoBox();
          search_page.refreshSelection();
          search_page.showInfoBox();
        }
      }).end()
      .find('tr').hover(
        function() {
          if(search_page.hover_select) {
            $(this).addClass('ui-highlight');    
            search_page.showInfoBox();
          }
        },
        function() {
          $(this).removeClass('ui-highlight');    
        }
      ).end();
      //find('.cui-sort').click(search_page.sortPageClick);
      target.find('.cui-sort').click(search_page.sortPageClick);
      if(this._currentCompareList) this.setCompare(this._currentCompareList);
  },
  /**
   * OK, this get hairier and hairier...
   *
   * diamondSearch was intended originally to refers to that tab (no so much a mode)
   * but as we tacked on pairSearch... wel... we'll refactor later...
   *   ... AND 1 year later, we still didn't.  Now tacking on manageSearch in a similiar fashion.  Tab reference are camelCase, pure mode references are hyphenated.  Obviously, this function don't care.
   */ 
  searchMode : function() {
    var m = this.mode();
    return (m == 'manage-search') ? 'manageSearch':
      (m == 'cyo-pair') ? 'pairSearch': 'diamondSearch';
  },
  format_manageSearch : function(_type,target,data) {
    var show_buttons = true;

    var fields = ['sku','title','price'];
    
    var last_params = search_page.cache(_type,'paginate'), asc = false;
    this.format__Table(fields,target,data,last_params,show_buttons);
  },
  format_diamondSearch : function(_type, target, data) {
    // format  --- 
    var show_buttons = true;

    var fields = ['sku']; 
    if($.inArray(this.mode(), ['cyo-stud', 'cyo-pendant'])==-1) fields.push('shape');
    if(this.mode() == 'cyo-stud') fields.push('metalType');
    fields.push('carat', 'cut','color','clarity','price');
    
    var last_params = search_page.cache(_type,'paginate'), asc = false;
    this.format__Table(fields,target,data,last_params,show_buttons);
  },
  format__Table : function(fields, target, data,last_params,show_buttons) {
    var out = '<table>'; var m = this.mode();
    out += '<tr><th>Compare</th>';
    for(var i = 0; i < fields.length; i++) {
      asc = false;
      if(fields[i].toLowerCase() == last_params['sort']) {
        asc = !(last_params['direction'] == 'asc');
      }
      asc = asc ? 'cui-order-asc' : 'cui-order-desc'
      var title = fields[i] == 'carat' ? this.carat() :
                               (fields[i] == 'metalType' ? 'metal' : fields[i]); 
      var sortable = fields[i] == 'price' ? 'priceDiscounted' : fields[i]; 
      out += '<th><a title="'+sortable+'" class="cui-sort '+asc+'">'+title+'</a></th>'; 
    }
    if(show_buttons) out += '<th>Details</th>';
    out += '</tr>';

    for(var i = 0; i < data.length; i++) {
      var cl = i%2 ? ' class="even"' : '';
      var d = data[i]['Inventory'];
      out += '<tr'+cl+'><td><input type="hidden" class="cui-id" value="'+d['sku']+'"/><input type="checkbox"/></td>';
      if(m=='manage-search' && d._price && d._price != d.price) d.price = d._price+' ('+d.price+')';
      out += $.tagData(fields, d);
      
      // Diamonds and studs are closely realted... almost the same thing!
      if(show_buttons) {
        if(m=='manage-search') {
          out += '<td><a href="/manage/inventory/'+d['id']+'"><img class="rollover" src="/img/btn_view_off.jpg" alt="view" /></a></td>';
        } else out += '<td><a href="'+this.diamondDetailsLink(d['sku'])+'"><img class="rollover" src="/img/btn_view_off.jpg" alt="view" /></a></td>';
      }
      out += '</tr>';
    }
    out += '</table>';
    
    target.html(out);
  },
  // new: intended to be generic, but tied to inventory search right now.
  format_generic : function(_type,target,data) {
    // format ---
    this.debug('format_generic', _type);
    var text = this.tab(_type,'.template.results-display').html();
    var d = data;
    var rec;
    var out = '';
    var replace = {}; // set new variables below 
    for(var i = 0; i < d.length; i++) {
      var oout = text;
      rec = d[i]['Inventory'];
      var _t =  this.cache(_type, 'last_post');
      if(!_t) this.debug('[[WARNING]] No "last post" found when formatting new data');
      else _t = _t['type'];

      // todo: many of these possible sources are old.  There is probally only one way now that is used.
      replace = {
        '-image-source-' : rec['images'] ? rec['images'].pop()['src'] : 
            (d[i]['images'] ?  d[i]['images'].pop()['listing']['url'] : null),
        '{short desc}' : rec['descriptionShort'] || rec['title'],
        '{price}' : rec['price'],
        '-type-' : _t,
        '-mode-' : this.mode()
      };
      oout = oout.replace(/-sku-/g, rec['sku']);
      for(e in replace) {
        oout = oout.replace(e,replace[e]);
      }
      out += oout;
    }
    // add and wire ---
    target.html(out);
  },
  error: function(msg) {
    if(window['console'] && window['console']['log']) console.log('[CADI Search Page Error] ' + msg);
  },
  debug: function() {
    //apply is not available to the ie console!
    if(this._debug && window.console && console.log && !$.browser.msie) console.log.apply(console, arguments);
    else if(this._debug && window.jash && window.jash.show) jash.show.apply(jash,arguments);
  }
};

