$(document).ready(function(){
  TWF.init();
});

var TWF = {


  init: function() {

    this._setUpConsole();

    this.fbox = $('div.block.twf div.filter'); // Filter block
    
    // Display loader image
    $('.loading', this.fbox).show();

    // Load filter block
    this._ajxLoadFilter();

    // Balloon mouse events
    $('div.block.twf .balloon').bind('mouseenter', function(){
      if(TWF.timerHideBalloon) {
        clearTimeout(TWF.timerHideBalloon);
        TWF.timerHideBalloon = 0;
      }
    }).bind('mouseleave', function(){
      clearTimeout(TWF.timerHideBalloon);
      TWF.timerHideBalloon = setTimeout(function() { $('.block.twf .balloon').hide(); }, TWF.TIMEOUT_SHOW_BALLOON_MOUSELEAVE);
    });
    
    // Result products links events
    $('a.twProductLink').bind('mousedown click', function (event) {
      var currentHref = $(this).attr('href');
      if(-1 == currentHref.search(/query=/)) {
        var queryStr = TWF._getFilterQuery();
        if(queryStr.length && ('/' !== queryStr)) {
          var sep = (currentHref.search(/\?/) >= 0) ? '&amp;' : '?';
          $(this).attr('href', currentHref + sep + 'query=' + queryStr);
        }
      }
      return true;
    });

    this.timerSlider = {};

  },
  
  
  _setUpConsole: function() {
    // Init console
    if(!window['console']) {
      // Enable console
      /*if(window['loadFirebugConsole']) {
        window.loadFirebugConsole();
      } else*/ {

        // No console, use Firebug Lite
        /*var firebugLite = function(F,i,r,e,b,u,g,L,I,T,E){if(F.getElementById(b))return;E=F[i+'NS']&&F.documentElement.namespaceURI;E=E?F[i+'NS'](E,'script'):F[i]('script');E[r]('id',b);E[r]('src',I+g+T);E[r](b,u);(F[e]('head')[0]||F[e]('body')[0]).appendChild(E);E=new Image;E[r]('src',I+L);};
        firebugLite(document,'createElement','setAttribute','getElementsByTagName','FirebugLite','4','firebug-lite.js','releases/lite/latest/skin/xp/sprite.png','https://getfirebug.com/','#startOpened');
        */

        // Stub console object
        window['console'] = {
          log: function (message) {
            //$('body').prepend('<p><i>LOG:</i> ' + message + '</p>');
          }
        };

      }
    } else {
      // console is already available, no action needed
    }
  },


  _ajxLoadFilter: function() {
    var catSlug = this._getCategoryFromUri(this._getQueryStrFromLocation());
    var query = catSlug ? ('&query=/' + catSlug) : '';

    $.ajax({
      type: "GET",
      url: this.API_URI + '?action=get_filter' + query,
      dataType: "json",
      timeout: this.AJAX_TIMEOUT,
      cache: this.ENABLE_HTTP_CACHE,
      success: function(data){
        $('.loading', TWF.fbox).hide();
        TWF._getUrlOrdering(data.fields);
        TWF._getTypesHash(data.fields);
        TWF._generateFilter(data.fields);
        TWF._setState();
        TWF.ready = true;
        TWF.showBalloon = false;
        TWF._ajxLoadAvailableValues();
        $('div.block.twf button.action').show();
      },
      error: function(jqXHR, textStatus, errorThrown){
        $('.loading', this.fbox).hide();
        console.log('TWF::_ajxLoadFilter AJAX callback says: error - ' + textStatus);
      }
    });
  },


  _ajxLoadAvailableValues: function() {
    var queryStr = this._getFilterQuery();
    var query = queryStr ? ('&query=' + queryStr) : '';

    if(queryStr && this.showBalloon)
      this.custParams({twfQuery: queryStr}); // register filter use

    $.ajax({
      type: "GET",
      url: this.API_URI + '?action=get_available_values' + query,
      dataType: "json",
      timeout: this.AJAX_TIMEOUT,
      cache: this.ENABLE_HTTP_CACHE,
      success: function(data){
        if(data.fields && !TWF.timerLoadAvailableValues) { // Only if no actions was performed by user while waited for response
          //var start = new Date().getTime();
          TWF._setAvailableValues(data.fields, data.foundRows);
          //console.log((new Date().getTime()) - start);
        }
      }
    });
  },


  _setAvailableValues: function(fields, countItemsFound) {
  
    // Set up ENUM fields
    
    // Make all values grayed
    //$('div.field.enum li.value span.cbLabel', this.fbox).addClass('gray');
    if('undefined' != typeof(fields['enum']))
      for(var fName in fields['enum']) {
        var valuesHash = {};
        for(var value in fields['enum'][fName]) {
          valuesHash[value] = true;
        }
        $('div.field.enum#fid_' + fName + ' li.value').each(function () {
          var inputValue = $(this).attr('id').match(/^lvid_(.+)$/)[1];
          var labelObj = $('span.cbLabel', $(this));
          if(labelObj.hasClass('gray')) {
            if(valuesHash[inputValue]) {
              labelObj.removeClass('gray');
            }
          } else {
            if(!valuesHash[inputValue]) {
              labelObj.addClass('gray');
            }
          }
        });
      }

    // Set up RANGE fields

    if('undefined' != typeof(fields['range']))
      for(var fName in fields['range']) {
        if('undefined' !== typeof(fields['range'][fName])) {

          // Set range mask inputs
          $('div#fid_' + fName + ' input.min_mask', this.fbox).attr('value', fields['range'][fName].min);
          $('div#fid_' + fName + ' input.max_mask', this.fbox).attr('value', fields['range'][fName].max);

          // Calculate range mask parameters
          var sliderParams = TWF._getSliderParams(fName);
          var maskWidth = ((fields['range'][fName].max - fields['range'][fName].min) * 100) / (sliderParams.maxVal - sliderParams.minVal);
          maskWidth = Math.round(maskWidth);
          var leftShift = 0;
          if(100 > maskWidth) { // Mask is not whole width

            // Get slider width
            var sliderObj = $('div.field#fid_' + fName + ' .twSlider');
            if(!sliderObj.width()) {
              // Trick to get width of unvisible element
              $('div.field#fid_' + fName + ' div.values').show();
              var sliderWidth = sliderObj.width();
              $('div.field#fid_' + fName + ' div.values').hide();
            } else
              var sliderWidth = sliderObj.width();

            leftShift = sliderWidth * (fields['range'][fName].min - sliderParams.minVal) / (sliderParams.maxVal - sliderParams.minVal);
            leftShift = Math.round(leftShift / 10) * 10;
            if(maskWidth < 3) { // If mask width is too small, increase it to make more noticeable
              maskWidth = 3;
            }
            if(maskWidth + leftShift >= sliderWidth) { // If mask is too shifted
              leftShift = sliderWidth - maskWidth - 1;
            }
          } else {
            if(maskWidth > 100) { // Mask width can't be more than 100%
              console.log('Error: "' + fName + '" mask width is ' + maskWidth + '% - more than 100%!');
              maskWidth = 100;
            }
          }
          if(!leftShift)
            leftShift = 1;
          else {
            if(leftShift < 0) { // Mask shift can't be negative
              console.log('Error: "' + fName + '" mask shift is ' + leftShift + ' - negative!');
              leftShift = 0;
            }
          }

          //console.log(fName+': minMask='+fields['range'][fName].min+' maxMask='+fields['range'][fName].max+' minVal='+sliderParams.minVal+' maxVal='+sliderParams.maxVal);
          //console.log(fName+': width='+maskWidth+'% shift='+leftShift+'px');

          // Set mask size & position
          $('div.field#fid_' + fName + ' .twSlider .twSliderRange2').css('width', maskWidth + '%');
          $('div.field#fid_' + fName + ' .twSlider .twSliderRange2').css('left', leftShift + 'px');

        }
      }


      // Set filter button text
      $('div.block.twf button.apply').html(this.LANG_SHOW + ' (' + countItemsFound + ')');
      
      // Show balloon
      if(this.showBalloon) {
        $('.block.twf .balloon .countItemsFound').html(countItemsFound);
        $('.block.twf .balloon').fadeIn();
        // Reset hide balloon timer
        if(this.timerHideBalloon) {
          clearTimeout(this.timerHideBalloon);
          this.timerHideBalloon = 0;
        }
        this.timerHideBalloon = setTimeout(function() { $('.block.twf .balloon').hide(); }, TWF.TIMEOUT_SHOW_BALLOON);
      }

  },


  _getQueryStrFromLocation: function() {
    var queryParameter = location.search.match(/(\?|&amp;|&)query=([^&]+)/);
    return queryParameter ? queryParameter[2] : location.pathname;
  },
  

  /*
    Set filter state (parse it from current URL):
      - expanded/minimized fields
      - selected values
  */
  _setState: function() {


    var filtersFromQuery = this._parseFilterQuery(this._getQueryStrFromLocation());
    var fieldsToExpand = [];
    
    // Set values
    for(var fName in filtersFromQuery) {
      this._setFieldValues(fName, filtersFromQuery[fName]);
      fieldsToExpand.push(fName);
    }

    // Merge expanded field set in URL with ones set in cookie
    if('string' == typeof($.cookie('twfExpandedFields'))) {
      var exFieldsFromCookie = $.cookie('twfExpandedFields').split('|');
      for(var i in exFieldsFromCookie)
        if(-1 == $.inArray(exFieldsFromCookie[i], fieldsToExpand)) // do not include twice
          fieldsToExpand.push(exFieldsFromCookie[i]);
    }

    // Expand fields
    this.toggleField(fieldsToExpand, true);

  },
  
  
  _parseFilterQuery: function(fQuery) {

    // Parse URL - get fields
    var parts = fQuery.split('/');

    var currentFilters = {};

    for(var i in parts)
      if('' !== parts[i]) {

        // Parse field - get field name and values
        var fieldData = parts[i].split('-');
        if(fieldData.length >= 2) { // ok, we have field name and at least one value
        
          var fName = fieldData[0];
          currentFilters[fName] = {};
        
          for(var j in fieldData)
            if(j > 0) { // Skip field name, get values only
              currentFilters[fName][j-1] = fieldData[j];
            }
        }

      }

    return currentFilters;
  },
  
  
  /*
    Get array of filter slugs, sorted by `urlOrdering` parameter
  */
  _getUrlOrdering: function(fields) {

    var urlOrderingHash = [];

    // Get array of hashes
    for(var i in fields) {
      urlOrderingHash.push({slug: fields[i].slug, urlOrdering: fields[i].urlOrdering});
    }
    
    // Do sort
    urlOrderingHash.sort(this._sortByUrlOrdering);
    
    // Convert array of hashes to flat sorted array
    this.urlOrdering = [];
    for(var i in urlOrderingHash)
      this.urlOrdering.push(urlOrderingHash[i].slug);

  },
  

  /*
    Get fields hash: slug=>type
  */
  _getTypesHash: function(fields) {
    this.fieldTypes = {};
    for(var i in fields)
      this.fieldTypes[fields[i].slug] = fields[i].type;
  },
  
  
  _sortByUrlOrdering: function(a,b) {
    return a.urlOrdering - b.urlOrdering;
  },
  
  
  _generateFilter: function(fields) {

    for(var i in fields) {

      // Generate field contents
      var fieldContents = '';
      var linkShowAll = '';

      switch(fields[i].type) {

        case 'enum':
          if(fields[i].values.length) {
            for(var j in fields[i].values) {
              var val = fields[i].values[j];
              var oldValueClass = valueClass;
              var valueClass = fields[i].topValuesLimit && (j >= fields[i].topValuesLimit)
                    ? 'extra' : 'top';
              if(oldValueClass !== valueClass)
                fieldContents += '\
                </ul>\
                <ul class="values ' + valueClass + '">\
                ';
              fieldContents += '\
          <!-- Field::Values::Value -->\
          <li class="value" id="lvid_' + val.slug + '">\
            <label>\
              <input type="checkbox" id="vid_' + val.slug + '" value="' + val.value + '" onclick="TWF.onFilterChange(this)">\
              <span class="cbLabel">' + val.label + '</span>\
            </label>\
          </li>\
          <!-- /Field::Values::Value -->';
            }

            if(fields[i].values.length > fields[i].topValuesLimit)
              linkShowAll = '<a href="#" class="toggleShowAll" onclick="TWF.toggleShowAll(\'' + fields[i].slug + '\'); return false;"><span class="all">' + this.LANG_ALL + ' (' + fields[i].values.length + ')</span><span class="popular">' + this.LANG_POPULAR + ' (' + fields[i].topValuesLimit + ')</span></a>';

          }
          break;

        case 'range':
          var minVal = ('undefined' !== typeof(fields[i].values.min)) ? fields[i].values.min : 0;
          var maxVal = ('undefined' !== typeof(fields[i].values.max)) ? fields[i].values.max : 0;
          var units = (fields[i].units !== '') ? '<td class="label units">' + fields[i].units + '</td>' : '';
          fieldContents = '<div class="range">\
  <table>\
    <tr>\
      <td class="label from">'+this.LANG_RANGE_FROM+'</td><td class="input from"><input type="text" id="fid_' + fields[i].slug + '_min" class="minmax min" value="' + minVal + '"></td>\
      <td class="label to">'+this.LANG_RANGE_TO+'</td><td class="input to"><input type="text" id="fid_' + fields[i].slug + '_max" class="minmax max" value="' + maxVal + '"></td>'+units+'\
    </tr>\
  </table>\
</div>\
<input type="hidden" id="fid_' + fields[i].slug + '_min" class="min" value="' + minVal + '"><input type="hidden" id="fid_' + fields[i].slug + '_max" class="max" value="' + maxVal + '"><input type="hidden" id="fid_' + fields[i].slug + '_min_mask" class="min_mask"><input type="hidden" id="fid_' + fields[i].slug + '_max_mask" class="max_mask"><input type="hidden" id="fid_' + fields[i].slug + '_slug" class="slug" value="' + fields[i].slug + '">\
<div id="slider_' + fields[i].slug + '" class="twSlider"></div>';
          break;

      }

      var fClass = fields[i].type;
      fClass += 1 == fields[i].expanded ? ' expanded' : ' minimized';
      //fClass += ' urlOrdering_' + fields[i].urlOrdering;

      // Show field
      this.fbox.append('    <!-- Field -->\
    <div class="field ' + fClass + '" id="fid_' + fields[i].slug + '">\
\
      <!-- Field::Title -->\
      <div class="title">\
        <a href="#" class="toggleField" onclick="TWF.toggleField(\'' + fields[i].slug + '\'); return false;">' + fields[i].name + '</a>\
        ' + linkShowAll + '\
      </div>\
      <!-- /Field::Title -->\
\
      <!-- Field::Values -->\
      <div class="values">\
        <ul class="values popular">\
\
' + fieldContents + '\
\
        </ul>\
      </div>\
      <!-- /Field::Values -->\
\
    </div>\
    <!-- /Field -->');

    }
    
    // Init RANGE fields
    $('div.field.range', this.fbox).each(function () {

      // Get slider parameters
      var fName = $('input.slug', $(this)).attr('value');
      var sliderParams = TWF._getSliderParams(fName);

      // Init slider
      $('.twSlider', $(this)).slider({
        range: true,
        min: sliderParams.minVal,
        max: sliderParams.maxVal,
        values: [sliderParams.minVal, sliderParams.maxVal],
      });

      // Bind slider event handlers (placing handlers right into slider constructor causes a bug with bind/unbind)
      $('div#fid_' + fName + ' div.twSlider', this.fbox).bind("slidechange", TWF.onSliderChange);
      $('div#fid_' + fName + ' div.twSlider', this.fbox).bind("slide", TWF.onSliderSlide);

      // Bind range inputs event handlers
      $('div#fid_' + fName + ' input[type="text"]', this.fbox)
        .bind('input propertychange',function(event){
          var elemId = event.target.id;
          var fieldSlug = elemId.match(/fid_([^_]+)_([^_]+)/)[1];
          TWF.onRangeInputChange(fieldSlug, event.target);
        });

    });
    
    // Add 2nd range ("range mask") to sliders
    $('div.field.range .twSlider', this.fbox).append('<div class="twSliderRange2 top"></div>');

  },
  
  
  onSliderChange: function(event) {
    var fName = $(event.target).attr('id').match(/^slider_(.+)$/)[1];
    if(TWF.timerSlider[fName]) {
      clearTimeout(TWF.timerSlider[fName]);
      TWF.timerSlider[fName] = 0;
    }
    TWF.onFilterChange(event.target);
  },


  /*
    Slider 'slide' event. Timeouted due to performance problems on IE8
  */
  onSliderSlide: function(event, ui, timeouted) {

    // Get field name
    var fName = $(event.target).attr('id').match(/^slider_(.+)$/);
    fName = fName[1];

    if(timeouted) { // Update fields only if timeout passed

      // Get field object
      var fieldObj = $('div#fid_' + fName, TWF.fbox);

      // Update text inputs
      $('input[type="text"].min', fieldObj).attr('value', ui.values[0]);
      $('input[type="text"].max', fieldObj).attr('value', ui.values[1]);

      // Register filter change
      TWF.onFilterChange(event.target);
      
      // Unset timer id
      TWF.timerSlider[fName] = 0;

    } else {
      if(!TWF.timerSlider[fName]) {
        TWF.timerSlider[fName] = setTimeout(function () {
        TWF.onSliderSlide(event, ui, true);
        }, 80);
      }
    }

  },


  onRangeInputChange: function(fName, element) {

    // Get field object
    var fieldObj = $('div#fid_' + fName, this.fbox);
    
    var sliderParams = TWF._getSliderParams(fName);
    
    // Validate range
    var minCurrent = sliderParams.minCurrent;
    if(minCurrent >= sliderParams.minVal) {
      if(minCurrent > sliderParams.maxVal) // Apply MAX limit
        minCurrent = sliderParams.maxVal;
    } else { // Apply MIN limit
      minCurrent = sliderParams.minVal;
    }
    var maxCurrent = sliderParams.maxCurrent;
    if(maxCurrent <= sliderParams.maxVal) {
      if(maxCurrent < sliderParams.minVal) // Apply MIN limit
        maxCurrent = sliderParams.minVal;
    } else { // Apply MAX limit
      maxCurrent = sliderParams.maxVal;
    }
    if(minCurrent > maxCurrent)
      minCurrent = maxCurrent;
    
    // Update inputs
    //$('input[type="text"].min', fieldObj).attr('value', minCurrent);
    //$('input[type="text"].max', fieldObj).attr('value', maxCurrent);

    // Update slider. This also causes AJAX update of all fields
    $('div.twSlider', fieldObj).slider('option', 'values', [minCurrent, maxCurrent]);
    this.onFilterChange(element);

  },
  
  
  onFilterChange: function(element, afterTimeout) {
  
    if(!this.ready) return;

    if(this.timerLoadAvailableValues) {
      clearTimeout(this.timerLoadAvailableValues);
      this.timerLoadAvailableValues = 0;
    }
    $('.block.twf .balloon').hide();

    if(afterTimeout) {
      // Set baloon's position
      var x = Math.round($(this.fbox).position().left + $(this.fbox).width() - 10);
      var y = Math.round($(element).position().top + 3);
      $('.block.twf .balloon').css({'left': x + 'px', 'top': y + 'px'});
      this.timerLoadAvailableValues = false;
      this.showBalloon = true;
      this._ajxLoadAvailableValues();
    } else {
      // Wait a little for next user action
      this.timerLoadAvailableValues = setTimeout(function () { TWF.onFilterChange(element, true) }, this.TIMEOUT_USER_ACTION)
    }
    
  },


  _getSliderParams: function(fieldSlug) {

    var fieldObj = $('div#fid_' + fieldSlug, this.fbox);
    var params = {};
    params.minVal = parseInt($('input[type="hidden"].min', fieldObj).attr('value'));
    params.maxVal = parseInt($('input[type="hidden"].max', fieldObj).attr('value'));
    params.minMask = parseInt($('input[type="hidden"].min_mask', fieldObj).attr('value'));
    params.maxMask = parseInt($('input[type="hidden"].max_mask', fieldObj).attr('value'));
    params.minCurrent = parseInt($('input[type="text"].min', fieldObj).attr('value'));
    params.maxCurrent = parseInt($('input[type="text"].max', fieldObj).attr('value'));

    if(isNaN(params.minMask))
      params.minMask = params.minVal;
    if(isNaN(params.maxMask))
      params.maxMask = params.maxVal;
    if(isNaN(params.minCurrent))
      params.minCurrent = params.minVal;
    if(isNaN(params.maxCurrent))
      params.maxCurrent = params.maxVal;
    
    return params;
  },


  toggleField: function(fieldSlug, doExpand) {

    if('undefined' == typeof(doExpand)) {
      doExpand = ($('#fid_' + fieldSlug + ' div.values:visible', this.fbox).length == 0);
    }

    if('string' == typeof(fieldSlug)) { // Toggle single field

      // Toggle "All/Popular" link visibility
      // Toggle field values visibility
      if(doExpand) {
        $('#fid_' + fieldSlug + ' a.toggleShowAll', this.fbox).fadeIn();
        $('#fid_' + fieldSlug + ' div.values', this.fbox).slideDown();
        $('#fid_' + fieldSlug, this.fbox)
          .removeClass('minimized')
          .addClass('expanded');
      } else {
        $('#fid_' + fieldSlug + ' a.toggleShowAll', this.fbox).fadeOut();
        $('#fid_' + fieldSlug + ' div.values', this.fbox).slideUp();
        $('#fid_' + fieldSlug, this.fbox)
          .removeClass('expanded')
          .addClass('minimized');
      }

    } else { // Toggle multiple fields at once

      // Generate selectors
      var selToggleShowAll = [];
      var selValues = [];
      var selField = [];
      for(var i in fieldSlug) {
        selToggleShowAll.push('#fid_' + fieldSlug[i] + ' a.toggleShowAll');
        selValues.push('#fid_' + fieldSlug[i] + ' div.values');
        selField.push('#fid_' + fieldSlug[i]);
      }

      // Toggle "All/Popular" link visibility
      // Toggle field values visibility
      if(doExpand) {
        $(selToggleShowAll.join(','), this.fbox).fadeIn();
        $(selValues.join(','), this.fbox).slideDown();
        $(selField.join(','), this.fbox)
          .removeClass('minimized')
          .addClass('expanded');
      } else {
        $(selToggleShowAll.join(','), this.fbox).fadeOut();
        $(selValues.join(','), this.fbox).slideUp();
        $(selField.join(','), this.fbox)
          .removeClass('expanded')
          .addClass('minimized');
      }

    }


  },


  toggleShowAll: function(fieldSlug) {

    var doShow = ($('#fid_' + fieldSlug + ' ul.values.extra:visible', this.fbox).length == 0);
    if(doShow) {
      $('#fid_' + fieldSlug + ' a.toggleShowAll span.all', this.fbox).hide();
      $('#fid_' + fieldSlug + ' a.toggleShowAll span.popular', this.fbox).show();
    } else {
      $('#fid_' + fieldSlug + ' a.toggleShowAll span.all', this.fbox).show();
      $('#fid_' + fieldSlug + ' a.toggleShowAll span.popular', this.fbox).hide();
    }

    // Toggle extra field values visibility
    $('#fid_' + fieldSlug + ' ul.values.extra', this.fbox).toggle();

  },
  
  
  _setFieldValues: function(fieldSlug, values) {

    var fType = this.fieldTypes[fieldSlug];

    switch(fType) {

      case 'enum':
        var valSelectors = [];
        for(var i in values)
          valSelectors.push('#fid_' + fieldSlug + ' input[type="checkbox"]#vid_' + values[i]);
        $(valSelectors.join(','), this.fbox).attr('checked', 'checked');
        break;

      case 'range':

        if('undefined' !== typeof(values[0])) {
          var sliderParams = TWF._getSliderParams(fieldSlug);

          // Prepare min-max values pair
          var minMaxPair = values[0].split('~', 2);
          if(('' === minMaxPair[0]) || ('undefined' == typeof(minMaxPair[0])))  minMaxPair[0] = sliderParams.minVal;
          if(('' === minMaxPair[1]) || ('undefined' == typeof(minMaxPair[1]))) minMaxPair[1] = sliderParams.maxVal;
          minMaxPair[0] = parseInt(minMaxPair[0]);
          minMaxPair[1] = parseInt(minMaxPair[1]);

          // Unbind slider event handlers
          var sliderObj = $('div#fid_' + fieldSlug + ' div.twSlider');
          sliderObj
            .unbind("slidechange")
            .unbind("slide");

          // Set range inputs
          $('div#fid_' + fieldSlug + ' input.minmax').each(function () {
            $(this).attr('value', $(this).hasClass('min') ? minMaxPair[0] : minMaxPair[1]);
          });

          // Set slider handles
          sliderObj.slider('option', 'values', [minMaxPair[0], minMaxPair[1]]);

          // Bind slider event handlers back
          sliderObj
            .bind("slidechange", this.onSliderChange)
            .bind("slide", this.onSliderSlide);
        }

        break;

    }

  },


  /*
    Apply current filter
  */
  applyFilter: function() {
    
    $('div.block.twf button.action').attr('disabled',true).addClass('disabled');
    $('.block.twf .balloon').hide();

    // Go to new page
    var filterQuery = this._getFilterQuery();

    // Get eapanded fields list and store in cookie
    var expandedFields = [];
    $('.field.expanded', this.fbox).each(function () {
      // Get field slug from id
      var fieldSlug = $(this).attr('id').match(/^fid_(.+)$/);
      expandedFields.push(fieldSlug[1]);
    });
    $.cookie('twfExpandedFields', expandedFields.join('|'), {expires: this.COOKIE_EXPIRE});

    window.location = '/' == filterQuery ? '/' + this.FILTER_PATH_FLAG[0] + '/' : filterQuery;
    
  },
  
  
  _getFilterQuery: function() {

    var newQuery = '';
    
    for(var i in this.urlOrdering) {

      var fieldSlug = this.urlOrdering[i];
      var fType = this.fieldTypes[fieldSlug];

      switch(fType) {

        case 'enum':
          var values = [];
          $('#fid_' + fieldSlug + ' input[type="checkbox"]:checked', this.fbox).each(function (index) {
            var valueFromId = $(this).attr('id').match(/vid_(.+)/);
            values.push(valueFromId[1]);
          });
          if(values.length) {
            newQuery += fieldSlug + '-' + values.join('-') + '/';
          }
          break;

        case 'range':
          var sParams = this._getSliderParams(fieldSlug);
          if(!isNaN(sParams.minCurrent) || !isNaN(sParams.maxCurrent)) {
            if(!isNaN(sParams.minCurrent) && !isNaN(sParams.maxCurrent)) {
              // If filter differs from default value
              if((sParams.minCurrent != sParams.minVal) || (sParams.maxCurrent != sParams.maxVal)) {
                newQuery += fieldSlug + '-' + sParams.minCurrent + '~' + sParams.maxCurrent + '/';
              }
            } else {
              if(!isNaN(sParams.minCurrent))
                newQuery += fieldSlug + '-' + sParams.minCurrent + '~/';
              else
                newQuery += fieldSlug + '-~' + sParams.maxCurrent + '/';
            }
          }
          break;

      }

    }
    
    var catSlug = this._getCategoryFromUri(this._getQueryStrFromLocation());
    if(catSlug.length)
      catSlug += '/';
    
    return '/' + catSlug + newQuery;
  },


  resetFilter: function() {

    // Reset `enum` fields
    $('div.field.enum input[type="checkbox"]', this.fbox).removeAttr('checked');

    this._setState();

  },


  _getCategoryFromUri: function(uri) {
  
    catSlug = '';

    // Check if category set in current URL
    var urlPrefix = uri.match(/\/([^\/]+)/);
    if(urlPrefix) {

      // Check if URL prefix can be a category identifier
      var isCategory = false;
      var prefixHeadname = urlPrefix[1].match(/([^\-]+)\-/);
      if(prefixHeadname) {
        // If URL starts from an unrecognized string - we think that it's a category identifier
        if(-1 == $.inArray(prefixHeadname[1], this.FILTER_PATH_FLAG))
          isCategory = true;
      } else
        isCategory = true;
      
      if(isCategory)
        catSlug = urlPrefix[1];

    }

    if($.inArray(catSlug, this.URL_PREFIX_BLACKLIST) > -1)
      catSlug = '';

    return catSlug;
  }


};


