function klApplication() {

  var this_proxy = this;

  /* private */
  function __construct()
  {
    initRoundCorners();
    initOverLabels();

    initFilterForm()

    initSubmitionCallBackForm();
    initCartFunctions();

    initNM();

    //initDragAndDrop();
    //initGallery();
  }

  function getIndexFromId(str, separator)
  {
    if(!separator)
      var separator = '_';

    var tmp = str.split(separator);
    var num = tmp[tmp.length - 1];

    return num;
  }

  function initRoundCorners()
  {
    jQuery('.banners .banner').corner("6px");
  }

  function showMessage(message)
  {
    alert(message);
  }

  function initNM()
  {
    jQuery('.nyroModal').nyroModal({zIndexStart: 999});
  }

  function _skuFilterOnChange()
  {
    var sku = jQuery('#sku').val();

    if(sku == '')
    {
      jQuery('#title').removeAttr('disabled');
      jQuery('#category_id').removeAttr('disabled');
    }
    else
    {
      jQuery('#title').attr('disabled', 'disabled');
      jQuery('#category_id').attr('disabled', 'disabled');
    }
  }
  function _titleFilterOnChange()
  {
    var category_id = jQuery('#category_id').val();
    var title = jQuery('#title').val();
    var sku = jQuery('#title').val();

    jQuery('#sku, #category_id').attr('disabled', 'disabled').addClass('disabled');

    jQuery.get('/catalog/filter_options', {id: category_id, title: title}, function(data)
    {
      var sku_select = jQuery('#sku');
      sku_select.empty();
      var html = '<option value="">--Все--</option>';
      for(var sku in data.skus)
        html += '<option value="' + sku + '">' + sku + '</option>';
      sku_select.append(html).val('').removeAttr('disabled').removeClass('disabled');

      var category_id_select = jQuery('#category_id');
      category_id_select.empty();
      var html = '<option value="">--Все--</option>';
      for(var category_index in data.categories)
      {
        var category = data.categories[category_index];
        html += '<option value="' + category.id + '">' + category.title + '</option>';
      }
      category_id_select.append(html).val(category_id).removeAttr('disabled').removeClass('disabled');
    });
  }
  function _categoryFilterOnChange()
  {
    var category_id = jQuery('#category_id').val();
    var title = jQuery('#title').val();
    var sku = jQuery('#title').val();

    jQuery('#sku, #title').attr('disabled', 'disabled').addClass('disabled');

    jQuery.get('/catalog/filter_options', {id: category_id, title: title}, function(data)
    {
      var sku_select = jQuery('#sku');
      sku_select.empty();
      var html = '<option value="">--Все--</option>';
      for(var sku in data.skus)
        html += '<option value="' + sku + '">' + sku + '</option>';
      sku_select.append(html).val('').removeAttr('disabled').removeClass('disabled');

      var title_select = jQuery('#title');
      title_select.empty();
      var html = '<option value="">--Все--</option>';
      for(var title in data.titles)
        html += '<option value="' + title + '">' + title + '</option>';
      title_select.append(html).val('').removeAttr('disabled').removeClass('disabled');
    });
  }
  function initFilterForm()
  {
    jQuery('#sku').change(function(){ _skuFilterOnChange() });
    _skuFilterOnChange();

    jQuery('#title').change(function(){ _titleFilterOnChange() });

    jQuery('#category_id').change(function(){ _categoryFilterOnChange() });
  }

  function initSubmitionCallBackForm()
  {
    jQuery('#callback_form').submit(function(){
      var phone = jQuery('#phone').val();
      var time = jQuery('#time').val();
      return loadContent('#' + this.id + '_container', this.action, { phone: phone, time: time }, null, this.method);
    });
  }

  function initCartFunctions()
  {
    jQuery('.remove_from_cart').click(function(){
      var id = jQuery(this).attr('rel');
      jQuery.ajax({
        url: this.href,
        type: 'GET',
        data: { type: 'ajax', back_url: window.location.href },
        dataType: 'json',
        success: function(data)
        {
          if(data.status == 'success')
            jQuery('#'+id).remove();

          if(data.total_amount == 0)
          {
            jQuery('#cart_product_list').html('<p>Ваша корзина пуста.</p>');
            jQuery('.cart').html( '' );
          }
          else
          {
            jQuery('.cart').html( data.total_amount );
          }

          if(data.message)
            showMessage(data.message);
        }
      });
      return false;
    });

    jQuery('.add_to_cart_form').submit(function(){
      var id = jQuery(this).find('[name=id]').val();
      var amount = jQuery(this).find('[name=amount]').val();
      jQuery.ajax({
        url: this.action,
        type: 'POST',
        data: { type: 'ajax', id: id, amount: amount, back_url: window.location.href },
        dataType: 'json',
        success: function(data)
        {
          if(data.total_amount == 0)
          {
            jQuery('.cart').html( '' );
          }
          else
          {
            jQuery('.cart').html( data.total_amount );
          }

          if(data.message)
            showMessage(data.message);
        }
      });
      return false;
    });
  }

  function initDragAndDrop()
  {
    if( typeof DropTarget == 'undefined' )
      return;

    jQuery('#product-list .refItem').click(function(){ return false; });
    var dragObjects = jQuery('#product-list .refItem img');
    for(var i=0; i<dragObjects.length; i++) {
        new DragObject(dragObjects[i]);
    }

    new DropTarget(document.getElementById('product_block_1'));
    new DropTarget(document.getElementById('product_block_2'));
  }

  function initGallery()
  {
    var galleries = $('.ad-gallery').adGallery({
      loader_image: 'images/loader.gif',
      width: 540, // Width of the image, set to false and it will read the CSS width
      height: 400, // Height of the image, set to false and it will read the CSS height
      thumb_opacity: 0.7, // Opacity that the thumbs fades to/from, (1 removes fade effect)
                          // Note that this effect combined with other effects might be resource intensive
                          // and make animations lag
      start_at_index: 0, // Which image should be displayed at first? 0 is the first image
      description_wrapper: $('#descriptions'), // Either false or a jQuery object, if you want the image descriptions
                                               // to be placed somewhere else than on top of the image
      animate_first_image: false, // Should first image just be displayed, or animated in?
      animation_speed: 400, // Which ever effect is used to switch images, how long should it take?
      display_next_and_prev: true, // Can you navigate by clicking on the left/right on the image?
      display_back_and_forward: true, // Are you allowed to scroll the thumb list?
      scroll_jump: 0, // If 0, it jumps the width of the container
      effect: 'slide-hori', // or 'slide-vert', 'resize', 'fade', 'none' or false
      enable_keyboard_move: true, // Move to next/previous image with keyboard arrows?
      // All callbacks has the AdGallery objects as 'this' reference
      callbacks: {
        // Executes right after the internal init, can be used to choose which images
        // you want to preload
        init: function() {
          // preloadAll uses recursion to preload each image right after one another
          this.preloadAll();
          // Or, just preload the first three
          this.preloadImage(0);
          this.preloadImage(1);
          this.preloadImage(2);
        }
      }
    });
  }

  /* public */
  klApplication.prototype.loadContent = function(elId, url, data, success, method, verbose)
  {
    if(window.submition_lock == true)
      return false;

    var options = {
      type: method || "POST",
      url: url,
      data: data || '',
      dataType: 'html',
      error: function(XMLHttpRequest, textStatus, errorThrown)
      {
        window.submition_lock = false;

        //unlockPage();

        if( XMLHttpRequest.status == 404 )
        {
          if(elId && data)
            jQuery(elId).html(XMLHttpRequest.responseText);
        }
        else
        {
          if(verbose)
            showMessage('Запрос не может быть вополнен. Попробуйте позже или обратитесь к администратору сайта.');
        }
      },
      success: function(data, textStatus)
      {
        window.submition_lock = false;

        //unlockPage();

        if(elId && data)
          jQuery(elId).html(data);

        if(success)
          success(data, textStatus);
      }
    };

    window.submition_lock = true;

    //lockPage();

    jQuery.ajax(options);

    return false;
  }

  klApplication.prototype.loadProductInfo = function(drop_target, drop_object)
  {
    var panel_id = getIndexFromId( drop_target.toString() );
    var product_id = getIndexFromId( drop_object.toString() );
    this_proxy.loadContent( '#' + drop_target.toString(),
                            '/catalog/product_info_ajax',
                            { id: product_id, panel_id: panel_id },
                            function() {
                              initNM();
                              initCartFunctions();
                            }
                          );
  }

  klApplication.prototype.buildUrl = function(url, rand, parameters)
  {
    if(rand)
    {
      var indexOfSign = url.indexOf("?");
      if( indexOfSign > 0 ) { url += '&'; } else { url += '?'; }

      url += Math.random();
    }

    if(parameters)
      url = appendParamsToUrl(url, parameters);
    else if(api)
      url = appendParamsToUrl(url, api.params);

    return url;
  }

  klApplication.prototype.setURIAnchor = function(url)
  {
    var parsed_url = this_proxy.parseUrl(url);
    var hash_url = '#' + parsed_url.path.substring(1);
    if(parsed_url.query)
      hash_url += '?' + parsed_url.query;

    window.location.hash = hash_url;
  }

  klApplication.prototype.parseUrl = function(str, component)
  {
    var  o   = {
        strictMode: false,
        key: ["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"],
        q:   {
            name:   "queryKey",
            parser: /(?:^|&)([^&=]*)=?([^&]*)/g
        },
        parser: {
            strict: /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/,
            loose:  /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/\/?)?((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/ // Added one optional slash to post-protocol to catch file:/// (should restrict this)
        }
    };

    var m   = o.parser[o.strictMode ? "strict" : "loose"].exec(str),
    uri = {},
    i   = 14;
    while (i--) {uri[o.key[i]] = m[i] || "";}
    // Uncomment the following to use the original more detailed (non-PHP) script
    /*
        uri[o.q.name] = {};
        uri[o.key[12]].replace(o.q.parser, function ($0, $1, $2) {
        if ($1) uri[o.q.name][$1] = $2;
        });
        return uri;
    */

    switch (component) {
        case 'PHP_URL_SCHEME':
            return uri.protocol;
        case 'PHP_URL_HOST':
            return uri.host;
        case 'PHP_URL_PORT':
            return uri.port;
        case 'PHP_URL_USER':
            return uri.user;
        case 'PHP_URL_PASS':
            return uri.password;
        case 'PHP_URL_PATH':
            return uri.path;
        case 'PHP_URL_QUERY':
            return uri.query;
        case 'PHP_URL_FRAGMENT':
            return uri.anchor;
        default:
            var retArr = {};
            if (uri.protocol !== '') {retArr.scheme=uri.protocol;}
            if (uri.host !== '') {retArr.host=uri.host;}
            if (uri.port !== '') {retArr.port=uri.port;}
            if (uri.user !== '') {retArr.user=uri.user;}
            if (uri.password !== '') {retArr.pass=uri.password;}
            if (uri.path !== '') {retArr.path=uri.path;}
            if (uri.query !== '') {retArr.query=uri.query;}
            if (uri.anchor !== '') {retArr.fragment=uri.anchor;}
            return retArr;
    }
  }

  /* */
  __construct();
};


/* */
function initApp()
{
  klApp = new klApplication();
}











function initOverLabels()
{
  var labels, id, field;

  labels = document.getElementsByTagName('label');
  for (var i = 0; i < labels.length; i++) {
    if (labels[i].className == 'overlabel') {

      id = labels[i].htmlFor || labels[i].getAttribute('for');
      if (!id || !(field = document.getElementById(id))) {
        continue;
      }

      labels[i].className = 'overlabel-apply';

      if (field.value !== '') {
        hideLabel(field.getAttribute('id'), true);
      }

      field.onfocus = function () {
        hideLabel(this.getAttribute('id'), true);
      };
      field.onblur = function () {
        if (this.value === '') {
          hideLabel(this.getAttribute('id'), false);
        }
      };

      labels[i].onclick = function () {
        var id, field;
        id = this.getAttribute('for');
        if (id && (field = document.getElementById(id))) {
          field.focus();
        }
      };
    }
  }
}
function hideLabel(field_id, hide)
{
  var field_for;
  var labels = document.getElementsByTagName('label');
  for (var i = 0; i < labels.length; i++) {
    field_for = labels[i].htmlFor || labels[i].getAttribute('for');
    if (field_for == field_id) {
      labels[i].style.textIndent = (hide) ? '-1000px' : '0px';
      return true;
    }
  }
}


