function ComparisonData(jsName, primarySubcatId, fieldData, priceRangeData, subcatSpecs, subcatClasses)
{
  this.jsName = jsName;
  this.primarySubcatId = primarySubcatId;
  this.fieldData = [];
  for (var i = 0; i < fieldData.length; i += 3)
    this.fieldData.push(new ComparisonDataField(fieldData[i], fieldData[i + 1], fieldData[i + 2]));
  this.priceRangeData = priceRangeData;
  this.subcatSpecs = subcatSpecs;
  this.subcatClasses = subcatClasses;
  
  if (this.priceRangeData != null)
    for (var i = 0; i < this.subcatSpecs.length; i++)
      this.priceRangeData.subcats.push(this.subcatSpecs[i]);

  for (var i = 0; i < this.subcatClasses.length; i++)
  {
    var curSubcatClass = this.subcatClasses[i];
    for (var j = 0; j < curSubcatClass.subcatIds.length; j++)
    {
      var curSubcat = this.getSubcatById(curSubcatClass.subcatIds[j]);
      if (curSubcat != null)
        curSubcat.subcatClass = curSubcatClass;
      curSubcatClass.subcats.push(curSubcat);
      if (curSubcatClass.priceRangeData != null)
        curSubcatClass.priceRangeData.subcats.push(curSubcat);
    }
  }
}

ComparisonData.prototype.getPrimarySubcat = function()
{
  return this.getSubcatById(this.primarySubcatId);
}

ComparisonData.prototype.getSubcatIndexById = function(subcatId)
{
  for (var i = 0; i < this.subcatSpecs.length; i++)
    if (this.subcatSpecs[i].subcatId == subcatId)
      return i;
  return -1;
}

ComparisonData.prototype.getSubcatById = function(subcatId)
{
  var index = this.getSubcatIndexById(subcatId);
  if (index >= 0)
    return this.subcatSpecs[index];
  return null;
}

ComparisonData.prototype.getSubcatClassById = function(classId)
{
  for (var i = 0; i < this.subcatClasses.length; i++)
    if (this.subcatClasses[i].classId == classId)
      return this.subcatClasses[i];
  return null;
}

ComparisonData.prototype.getNumFields = function()
{
  return this.fieldData.length;
}

ComparisonData.prototype.getFieldByIndex = function(fieldIndex)
{
  return this.fieldData[fieldIndex];
}

ComparisonData.prototype.getFieldIndexById = function(fieldId)
{
  for (var i = 0; i < this.fieldData.length; i++)
    if (this.fieldData[i].fieldId == fieldId)
      return i;
  return -1;
}

ComparisonData.prototype.getFieldIndexByName = function(fieldName)
{
  for (var i = 0; i < this.fieldData.length; i++)
    if (this.fieldData[i].fieldName == fieldName)
      return i;
  return -1;
}

/* ****************************** ComparisonState ****************************** */

function ComparisonState(comparisonData, primaryDisambiguationIndex, comparisonLimitStack, comparisonLimitClassId, comparisonLimitPriceSel, comparisonLimitMfr, subcatIdList, disambiguationIndexList)
{
  this.comparisonData = comparisonData;
  this.primaryDisambiguationIndex = primaryDisambiguationIndex;
  this.comparisonLimitStack = comparisonLimitStack;
  this.comparisonLimitClassId = comparisonLimitClassId;
  this.comparisonLimitPriceSel = comparisonLimitPriceSel;
  this.comparisonLimitMfr = comparisonLimitMfr;
  this.subcatIdList = subcatIdList;
  this.disambiguationIndexList = disambiguationIndexList;
  this.curPageIndex = 0;
  this.isComparisonState = true;
}

ComparisonState.prototype.getBlankState = function(comparisonData)
{
  return new ComparisonState(comparisonData, 0, '', 0, null, '', [], []);
}

ComparisonState.prototype.createComparisonStateFromString = function(comparisonData, comparisonStateString)
{
  var pieces = comparisonStateString.split('.');
  // 0 - comparisonLimitStack
  // 1 - comparisonLimitClassId
  // 2 - comparisonLimitPriceSel
  // 3 - escape(comparisonLimitMfr)
  // 4 - primaryDisambiguationIndex
  // 5 - subcatIdList/disambiguationIndexList
  if (pieces.length < 6)
    return null;
  var comparisonLimitStack = pieces[0];
  if (!ComparisonState.prototype.verifyStack(comparisonLimitStack))
    return null;

  var comparisonLimitClassId = parseInt(pieces[1]);
  var comparisonLimitMfr = unescape(pieces[3]);

  // Verify disambiguation indexes
  var primaryDisambiguationIndex = parseInt(pieces[4]);
  if (primaryDisambiguationIndex > 0)
  {
    var primarySubcat = comparisonData.getPrimarySubcat();
    if (!primarySubcat.hasDisambiguations() || (primarySubcat.specData.getNumDisambiguations() >= primaryDisambiguationIndex))
      primaryDisambiguationIndex = 0;
  }
  var idListStr = pieces[5].split(',');
  var subcatIdList = [];
  var disambiguationIndexList = [];
  
  for (var i = 1; i < idListStr.length; i += 2)
  {
    var subcat = comparisonData.getSubcatById(parseInt(idListStr[i - 1]));
    if (subcat == null)
      continue;
    var disambigIndex = parseInt(idListStr[i]);
    if ((disambigIndex > 0) && (!subcat.hasDisambiguations() || (subcat.specData.getNumDisambiguations() >= disambigIndex)))
      disambigIndex = 0;
    subcatIdList.push(subcat.subcatId);
    disambiguationIndexList.push(disambigIndex);
  }
  var comparisonLimitPriceSel = pieces[2];
  if (comparisonLimitPriceSel.length == 0)
    comparisonLimitPriceSel = null;
  else
  {
    if (comparisonLimitPriceSel != 'ON_SALE')
    {
      // Price range
      var priceCharIndex = comparisonLimitStack.indexOf('P');
      if (priceCharIndex < 0)
        return null;
      var classCharIndex = comparisonLimitStack.indexOf('C');
      var prd = null;
      if ((classCharIndex < 0) || (classCharIndex > priceCharIndex))
        prd = comparisonData.priceRangeData;
      else
      {
        var subcatClass = comparisonData.getSubcatClassById(comparisonLimitClassId);
        if (subcatClass != null)
          prd = subcatClass.priceRangeData;
      }
      if (prd == null)
        comparisonLimitPriceSel = null;
      else
      {
        var ranges = comparisonLimitPriceSel.split(',');
        if (ranges.length == 2)
          comparisonLimitPriceSel = prd.getMatchingSelection(parseInt(ranges[0]), parseInt(ranges[1]));
        else
          comparisonLimitPriceSel = null;
      }
    }
  }
  return new ComparisonState(comparisonData, primaryDisambiguationIndex, comparisonLimitStack, comparisonLimitClassId, comparisonLimitPriceSel, comparisonLimitMfr, subcatIdList, disambiguationIndexList);
}

ComparisonState.prototype.resetPageIndex = function()
{
  this.curPageIndex = 0;
  return;
}

ComparisonState.prototype.getNumSubcats = function()
{
  return this.subcatIdList.length;
}

ComparisonState.prototype.setDisambiguationIndex = function(subcatId, disambiguationIndex)
{
  if (subcatId == this.comparisonData.primarySubcatId)
  {
    if (this.primaryDisambiguationIndex == disambiguationIndex)
      return false;
    this.primaryDisambiguationIndex = disambiguationIndex;
    return true;
  }
  for (var i = 0; i < this.subcatIdList.length; i++)
  {
    if (this.subcatIdList[i] == subcatId)
    {
      if (this.disambiguationIndexList[i] == disambiguationIndex)
        return false;
      this.disambiguationIndexList[i] = disambiguationIndex;
      return true;
    }
  }
  return false;
}

ComparisonState.prototype.addSubcatId = function(subcatId)
{
  if (subcatId == this.comparisonData.primarySubcatId)
    return false;
  for (var i = 0; i < this.subcatIdList.length; i++)
    if (this.subcatIdList[i] == subcatId)
      return false;
  if (this.comparisonData.getSubcatById(subcatId) == null)
    return false;
  this.subcatIdList.push(subcatId);
  this.disambiguationIndexList.push(0);
  return true;
}

ComparisonState.prototype.removeSubcatId = function(subcatId)
{
  if (subcatId == this.comparisonData.primarySubcatId)
    return false;
  for (var i = 0; i < this.subcatIdList.length; i++)
  {
    if (this.subcatIdList[i] == subcatId)
    {
      for (var j = i; j < this.subcatIdList.length - 1; j++)
      {
        this.subcatIdList[j] = this.subcatIdList[j + 1];
        this.disambiguationIndexList[j] = this.disambiguationIndexList[j + 1];
      }
      this.subcatIdList.length--;
      this.disambiguationIndexList.length--;
      return true;
    }
  }
  return false;
}

ComparisonState.prototype.stateChangeLimitClassId = function(classId)
{
  if (this.comparisonLimitStack.indexOf('C') >= 0)
    return false;
  if (this.comparisonData.getSubcatClassById(classId) == null)
    return false;
  this.comparisonLimitClassId = classId;
  this.comparisonLimitStack += 'C';
  return true;
}

ComparisonState.prototype.stateChangeLimitMfr = function(mfrName)
{
  if (this.comparisonLimitStack.indexOf('M') >= 0)
    return false;
  this.comparisonLimitMfr = mfrName;
  this.comparisonLimitStack += 'M';
  return true;
}

ComparisonState.prototype.stateChangeLimitPriceOnSale = function()
{
  return this.stateChangeLimitPrice('ON_SALE');
}

ComparisonState.prototype.stateChangeLimitPrice = function(priceSel)
{
  if (this.comparisonLimitStack.indexOf('P') >= 0)
    return false;
  this.comparisonLimitPriceSel = priceSel;
  this.comparisonLimitStack += 'P';
  return true;
}

ComparisonState.prototype.popStack = function()
{
  var stackLen = this.comparisonLimitStack.length;
  if (stackLen == 0)
    return false;
  var c = this.comparisonLimitStack.charAt(--stackLen);
  this.comparisonLimitStack = this.comparisonLimitStack.substring(0, stackLen);
  switch (c)
  {
  case 'C':
    this.comparisonLimitClassId = 0;
    break;
  
  case 'P':
    this.comparisonLimitPriceSel = null;
    break;
  
  case 'M':
    this.comparisonLimitMfr = '';
    break;
  }
  return true;
}

ComparisonState.prototype.clearStack = function()
{
  if (this.comparisonLimitStack.length == 0)
    return false;
  this.comparisonLimitStack = '';
  this.comparisonLimitClassId = 0;
  this.comparisonLimitPriceSel = null;
  this.comparisonLimitMfr = '';
  return true;
}

ComparisonState.prototype.getPrimarySpecData = function()
{
  return this.getPrimarySubcat().getSpecData(this.primaryDisambiguationIndex);
}

ComparisonState.prototype.getPrimarySubcat = function()
{
  return this.comparisonData.getPrimarySubcat();
}

ComparisonState.prototype.getComparisonSpecData = function(index)
{
  return this.getComparisonSubcat(index).getSpecData(this.disambiguationIndexList[index]);
}

ComparisonState.prototype.getComparisonSubcat = function(index)
{
  return this.comparisonData.getSubcatById(this.subcatIdList[index]);
}

ComparisonState.prototype.getLimitedSubcatList = function()
{
  if (this.comparisonData.subcatSpecs.length == 0)
    return [];
  var allSubcats = new Array(this.comparisonData.subcatSpecs.length - 1);
  var n = 0;
  for (var i = 0; i < this.comparisonData.subcatSpecs.length; i++)
    if (this.comparisonData.subcatSpecs[i].subcatId != this.comparisonData.primarySubcatId)
      allSubcats[n++] = this.comparisonData.subcatSpecs[i];

  var limitedByClass = null;
  for (var i = 0; i < this.comparisonLimitStack.length; i++)
  {
    var c = this.comparisonLimitStack.charAt(i);
    switch (c)
    {
    case 'C':
      limitedByClass = this.comparisonData.getSubcatClassById(this.comparisonLimitClassId);
      if (limitedByClass == null)
        return [];
      allSubcats = this.limitByClass(allSubcats, this.comparisonLimitClassId);
      break;
      
    case 'P':
      allSubcats = this.limitByPrice(allSubcats, this.comparisonLimitPriceSel);
      break;
      
    case 'M':
      allSubcats = this.limitByMfrName(allSubcats, this.comparisonLimitMfr);
      break;
    }
  }
  return allSubcats;
}

ComparisonState.prototype.hasSelections = function()
{  return (this.comparisonLimitStack.length > 0);  }

ComparisonState.prototype.verifyStack = function(stackStr)
{
  if (stackStr == null)
    return false;
  if (stackStr.length == 0)
    return true;
  if (stackStr.length > 3)
    return false;

  var flags = 0;
  for (var i = 0; i < stackStr.length; i++)
  {
    var c = stackStr.charAt(i);
    var index = 0;
    switch (c)
    {
    case 'C':
      break;
      
    case 'P':
      index = 1;
      break;
      
    case 'M':
      index = 2;
      break;
      
    default:
      return false;
    }
    if (flags > (flags ^= (1 << index)))
      return false;
  }
  return true;
}

ComparisonState.prototype.getStateString = function(subcatId)
{
  if (this.comparisonLimitStack.length == 0)
    return null;
  var t = this.comparisonLimitStack +
          '.' + this.comparisonLimitClassId +
          '.';
  if (this.comparisonLimitPriceSel != null)
  {
    var cmpStatePrice = this.comparisonLimitPriceSel;
    if (this.comparisonLimitPriceSel != 'ON_SALE')
      cmpStatePrice = this.comparisonLimitPriceSel.getRangeLow() + ',' + this.comparisonLimitPriceSel.getRangeHigh();
    t += cmpStatePrice;
  }
  t += '.' + escape(this.comparisonLimitMfr);

  var idList = this.comparisonData.primarySubcatId + ',' + this.primaryDisambiguationIndex;
  var selectedDisambigIndex = 0;
  for (var i = 0; i < this.subcatIdList.length; i++)
  {
    if (this.subcatIdList[i] != subcatId)
      idList += ',' + this.subcatIdList[i] + ',' + this.disambiguationIndexList[i];
    else
      selectedDisambigIndex = this.disambiguationIndexList[i];
  }
  t += '.' + selectedDisambigIndex + '.' + idList;
  return t;
}

ComparisonState.prototype.limitByClass = function(subcatList, classId)
{
  var newSubcatList = [];
  for (var i = 0; i < subcatList.length; i++)
    if (subcatList[i].subcatClassId == classId)
      newSubcatList.push(subcatList[i]);
  return newSubcatList;
}

ComparisonState.prototype.limitByPrice = function(subcatList, priceSel)
{
  var newSubcatList = [];
  if (priceSel == null)
    return newSubcatList;
  for (var i = 0; i < subcatList.length; i++)
  {
    var addSubcat;
    if (priceSel == 'ON_SALE')
      addSubcat = subcatList[i].isOnSale();
    else
      addSubcat = priceSel.isSubcatIdInRange(subcatList[i].subcatId);
    if (addSubcat)
      newSubcatList.push(subcatList[i]);
  }
  return newSubcatList;
}

ComparisonState.prototype.limitByMfrName = function(subcatList, mfrName)
{
  var newSubcatList = [];
  for (var i = 0; i < subcatList.length; i++)
    if (subcatList[i].mfrName == mfrName)
      newSubcatList.push(subcatList[i]);
  return newSubcatList;
}

ComparisonState.prototype.getMfrNameListToDisplay = function(subcatsList)
{
  if (this.comparisonLimitMfr.length > 0)
    return null;
  var mfrNameList = this.getMfrNameList(subcatsList);
  if (mfrNameList.length < 2)
    return null;
  return mfrNameList;
}

ComparisonState.prototype.getMfrNameList = function(subcatsList)
{
  var mfrNameList = [];
subcatsListLoop:
  for (var i = 0; i < subcatsList.length; i++)
  {
    var mfrName = subcatsList[i].mfrName;
    for (var j = 0; j < mfrNameList.length; j++)
      if (mfrNameList[j] == mfrName)
        continue subcatsListLoop;
    mfrNameList.push(mfrName);
  }
  mfrNameList.sort(function(firstName, secondName) {
      firstName = firstName.toLowerCase();
      secondName = secondName.toLowerCase();
      if (firstName < secondName)
        return -1;
      if (firstName > secondName)
        return 1;
      return 0;
    }
  );
  return mfrNameList;
}

ComparisonState.prototype.getSubcatClassListToDisplay = function(subcatsList)
{
  if (this.comparisonLimitClassId > 0)
    return null;
  var subcatClassList = this.getSubcatClassList(subcatsList);
  if (subcatClassList.length < 2)
    return null;
  return subcatClassList;
}

ComparisonState.prototype.getSubcatClassList = function(subcatsList)
{
  var subcatClassList = [];
subcatsListLoop:
  for (var i = 0; i < subcatsList.length; i++)
  {
    var subcatClass = subcatsList[i].subcatClass;
    if (subcatClass == null)
      continue;
    for (var j = 0; j < subcatClassList.length; j++)
      if (subcatClassList[j] == subcatClass)
        continue subcatsListLoop;
    subcatClassList.push(subcatClass);
  }
  subcatClassList.sort(function(firstClass, secondClass) {
      var firstName = firstClass.getDisplayName().toLowerCase();
      var secondName = secondClass.getDisplayName().toLowerCase();
      if (firstName < secondName)
        return -1;
      if (firstName > secondName)
        return 1;
      return 0;
    }
  );
  return subcatClassList;
}

ComparisonState.prototype.getPriceRangeData = function()
{
  var comparisonLimitClass = (this.comparisonLimitClassId > 0) ? this.comparisonData.getSubcatClassById(this.comparisonLimitClassId) : null;
  if (comparisonLimitClass == null)
    return this.comparisonData.priceRangeData;
  var priceCharIndex = this.comparisonLimitStack.indexOf('P');
  var classCharIndex = this.comparisonLimitStack.indexOf('C');
  if ((classCharIndex < 0) || (classCharIndex > priceCharIndex))
    return this.comparisonData.priceRangeData;
  return comparisonLimitClass.priceRangeData;
}

ComparisonState.prototype.shouldDisplayOnSale = function(subcatsList)
{
  if (this.comparisonLimitPriceSel != null)
    return false;
  var foundOnSale = false;
  var foundNotOnSale = false;
  for (var i = 0; i < subcatsList.length; i++)
  {
    var subcat = subcatsList[i];
    if (subcat.isOnSale())
      foundOnSale = true;
    else
      foundNotOnSale = true;
  }
  return (foundOnSale && foundNotOnSale);
}

ComparisonState.prototype.getPriceIndexListToDisplay = function(subcatsList)
{
  if (this.comparisonLimitPriceSel != null)
    return null;
  var priceRangeData = this.getPriceRangeData();
  var priceIndexList = this.getPriceIndexList(subcatsList, priceRangeData);
  if (priceIndexList.length < 2)
    return null;
  return priceIndexList;
}

ComparisonState.prototype.getPriceIndexList = function(subcatsList, priceRangeData)
{
  var priceIndexList = [];
  if (priceRangeData == null)
    return priceIndexList;
  for (var i = 0; i < priceRangeData.getNumRanges(); i++)
  {
    for (var j = 0; j < subcatsList.length; j++)
    {
      if (priceRangeData.isSubcatIdInRange(subcatsList[j].subcatId, i))
      {
        priceIndexList.push(i);
        break;
      }
    }
  }
  return priceIndexList;
}

/* ****************************** ComparisonDataField ****************************** */

function ComparisonDataField(fieldId, fieldName, fieldDisplayName)
{
  this.fieldId = fieldId;
  this.fieldName = fieldName;
  this.fieldDisplayName = fieldDisplayName;
}

ComparisonDataField.prototype.getDisplayName = function()
{
  if (this.fieldDisplayName.length > 0)
    return this.fieldDisplayName;
  return this.fieldName;
}

/* ****************************** SubcatClass ****************************** */

function SubcatClass(classId, subcatClassName, displayName, subcatIds, priceRangeData)
{
  this.classId = classId;
  this.subcatClassName = subcatClassName;
  this.displayName = displayName;
  this.subcatIds = subcatIds;
  this.priceRangeData = priceRangeData;
  this.subcats = [];
}

SubcatClass.prototype.getDisplayName = function()
{
  if (this.displayName.length > 0)
    return this.displayName;
  return this.subcatClassName;
}

SubcatClass.prototype.getSubcatIndexById = function(subcatId)
{
  for (var i = 0; i < this.subcatSpecs.length; i++)
    if ((this.subcats[i] != null) && (this.subcats[i].subcatId == subcatId))
      return i;
  return -1;
}

SubcatClass.prototype.getSubcatById = function(subcatId)
{
  var index = this.getSubcatIndexById(subcatId);
  if (index >= 0)
    return this.subcats[index];
  return null;
}

/* ****************************** SubcatSpecs ****************************** */

function SubcatSpecs(subcatId, subcatName, mfrName, thumbImageUrl, topTag, doNotShowPrice, modelList, subcatClassId, disambiguationAttributeName, specData)
{
  this.subcatId = subcatId;
  this.subcatName = subcatName;
  this.mfrName = mfrName;
  this.thumbImageUrl = thumbImageUrl;
  this.topTag = topTag;
  this.doNotShowPrice = doNotShowPrice;
  this.modelList = modelList;
  this.subcatClassId = subcatClassId;
  this.subcatClass = null;
  this.disambiguationAttributeName = disambiguationAttributeName;
  this.specData = specData;
}

SubcatSpecs.prototype.isOnSale = function()
{
  return (this.topTag.length > 0);
}

SubcatSpecs.prototype.hasDisambiguations = function()
{
  return ((this.disambiguationAttributeName != null) && (this.disambiguationAttributeName.length > 0));
}

SubcatSpecs.prototype.hasField = function(field)
{
  if (this.hasDisambiguations())
  {
    for (var i = 0; i < this.specData.getNumDisambiguations(); i++)
      if (this.specData.getDisambiguationSpecData(i).getSpecDataByFieldFiltered(field) != null)
        return true;
    return false;
  }
  return (this.specData.getSpecDataByFieldFiltered(field) != null);
}

SubcatSpecs.prototype.getSpecData = function(disambiguationIndex)
{
  if (this.hasDisambiguations())
    return this.specData.getDisambiguationSpecData(disambiguationIndex);
  return this.specData;
}

/* ****************************** DisambiguationSpecData ****************************** */

function DisambiguationSpecData(disambiguationSpecData)
{
  this.disambiguationSpecData = disambiguationSpecData;
}

DisambiguationSpecData.prototype.getNumDisambiguations = function()
{
  return this.disambiguationSpecData.length / 3;
}

DisambiguationSpecData.prototype.getDisambiguationName = function(index)
{
  if (index < 0)
    return null;
  return this.disambiguationSpecData[index * 3];
}

DisambiguationSpecData.prototype.getDisambiguationDisplayName = function(index)
{
  if (index < 0)
    return null;
  var t = this.disambiguationSpecData[(index * 3) + 1];
  if (t.length > 0)
    return t;
  return this.getDisambiguationName(index);
}

DisambiguationSpecData.prototype.getDisambiguationSpecData = function(index)
{
  if (index < 0)
    return null;
  return this.disambiguationSpecData[(index * 3) + 2];
}

DisambiguationSpecData.prototype.getDisambiguationSpecIndexByName = function(value)
{
  for (var i = 0; i < this.disambiguationSpecData.length; i += 3)
    if (this.disambiguationSpecData[i] == value)
      return (i / 3);
  return -1;
}

DisambiguationSpecData.prototype.getDisambiguationSpecDataByName = function(value)
{
  return this.getDisambiguationSpecData(this.getDisambiguationSpecIndexByName(value));
}

/* ****************************** SpecData ****************************** */

function SpecData(priceCentsLow, priceCentsHigh, srpCentsLow, srpCentsHigh, specs)
{
  this.priceCentsLow = priceCentsLow;
  this.priceCentsHigh = priceCentsHigh;
  this.srpCentsLow = srpCentsLow;
  this.srpCentsHigh = srpCentsHigh;
  this.specs = new Object();
  for (var i = 0; i < specs.length; i += 2)
    this.specs['FID' + specs[i]] = specs[i + 1];
}

SpecData.prototype.getSpecDataByFieldFiltered = function(fieldObj)
{
  return this.nullIfNoData(this.getSpecDataByFieldId(fieldObj.fieldId));
}

SpecData.prototype.getSpecDataByField = function(fieldObj)
{
  return this.getSpecDataByFieldId(fieldObj.fieldId);
}

SpecData.prototype.getSpecDataByFieldIdFiltered = function(fieldId)
{
  return this.nullIfNoData(this.getSpecDataByFieldId(fieldId));
}

SpecData.prototype.getSpecDataByFieldId = function(fieldId)
{
  var v = this.specs['FID' + fieldId];
  if (v == undefined)
    return null;
  return v;
}

SpecData.prototype.nullIfNoData = function(v)
{
  if ((v == null) || (v.length == 0) || (v == 'N/A'))
    return null;
  return v;
}

SpecData.prototype.getPriceDisplay = function()
{
  if ((this.priceCentsLow > 0) && (this.priceCentsHigh > 0))
  {
    if (this.priceCentsLow == this.priceCentsHigh)
      return '$' + formatPrice(this.priceCentsLow);
    return '$' + formatPrice(this.priceCentsLow) + ' - $' + formatPrice(this.priceCentsHigh);
  }
  return '';
}

SpecData.prototype.getSRPDisplay = function()
{
  if ((this.srpCentsLow > 0) && (this.srpCentsHigh > 0))
  {
    if (this.srpCentsLow == this.srpCentsHigh)
      return '$' + formatPrice(this.srpCentsLow);
    return '$' + formatPrice(this.srpCentsLow) + ' - $' + formatPrice(this.srpCentsHigh);
  }
  return '';
}

/* ****************************** PriceRangeData ****************************** */

function PriceRangeData(rangeData, flags)
{
  this.rangeData = rangeData;
  this.numRanges = rangeData.length / 3;
  this.flags = flags.toUpperCase();
  this.subcats = [];
}

PriceRangeData.prototype.getNumRanges = function()
{
  return this.numRanges;
}

PriceRangeData.prototype.getRangeLow = function(rangeIndex)
{
  return this.rangeData[rangeIndex * 3];
}

PriceRangeData.prototype.getRangeHigh = function(rangeIndex)
{
  return this.rangeData[(rangeIndex * 3) + 1];
}

PriceRangeData.prototype.getRangeDisplayName = function(rangeIndex)
{
  return this.rangeData[(rangeIndex * 3) + 2];
}

PriceRangeData.prototype.getSubcatIndexById = function(subcatId)
{
  for (var i = 0; i < this.subcats.length; i++)
    if ((this.subcats[i] != null) && (this.subcats[i].subcatId == subcatId))
      return i;
  return -1;
}

PriceRangeData.prototype.isSubcatIdInRange = function(subcatId, rangeIndex)
{
  var index = this.getSubcatIndexById(subcatId);
  if (index < 0)
    return false;
  return this.isSubcatIndexInRange(index, rangeIndex);
}

PriceRangeData.prototype.isSubcatIndexInRange = function(scIndex, rangeIndex)
{
  var index = (scIndex * this.numRanges) + rangeIndex;
  var cc = this.flags.charCodeAt(index >>> 2);
  if ((cc >= 48) && (cc < 58))
    cc -= 48;
  else
  {
    if ((cc >= 65) && (cc < 71))
      cc -= 55;
    else
      return false;
  }
  return ((cc >>> (index & 3)) & 1) != 0;
}

PriceRangeData.prototype.getSelection = function(rangeIndex)
{
  return new PriceRangeDataSel(this, rangeIndex);
}

PriceRangeData.prototype.getMatchingSelection = function(rangeLow, rangeHigh)
{
  for (var i = 0; i < this.numRanges; i++)
    if ((this.getRangeLow(i) == rangeLow) && (this.getRangeHigh(i) == rangeHigh))
      return this.getSelection(i);
  return null;
}

/* ****************************** PriceRangeDataSel ****************************** */

function PriceRangeDataSel(priceRangeData, rangeIndex)
{
  this.priceRangeData = priceRangeData;
  this.rangeIndex = rangeIndex;
}

PriceRangeDataSel.prototype.getRangeDisplayName = function()
{
  return this.priceRangeData.getRangeDisplayName(this.rangeIndex);
}

PriceRangeDataSel.prototype.getRangeLow = function()
{
  return this.priceRangeData.getRangeLow(this.rangeIndex);
}

PriceRangeDataSel.prototype.getRangeHigh = function()
{
  return this.priceRangeData.getRangeHigh(this.rangeIndex);
}

PriceRangeDataSel.prototype.isSubcatIdInRange = function(subcatId)
{
  return this.priceRangeData.isSubcatIdInRange(subcatId, this.rangeIndex);
}

PriceRangeDataSel.prototype.isSubcatIndexInRange = function(scIndex)
{
  return this.priceRangeData.isSubcatIndexInRange(scIndex, this.rangeIndex);
}
