function klApplication() {

  var this_proxy = this;

  /* private */
  function __construct()
  {
    initSubmitionCallBackForm();
    initCartFunctions();

    initPopupBanner();
    initNavigation();
    initNM();
    //initGallery();
  }

  function getIndexFromId(str, separator)
  {
    if(!separator)
      var separator = '_';

    var tmp = str.split(separator);
    var num = tmp[tmp.length - 1];

    return num;
  }

  function showMessage(message, is_modal)
  {
    if(!is_modal)
      message += '<p class="a_c"><input type="button" class="unblock_ui" value="Закрыть" /></p>';

    jQuery.blockUI({
      message: message,
      css: {
        width: '35%',
        top: '25%',
        padding: '8px'
      },
      centerY: true
    });

    jQuery('.unblock_ui').click(function() {
      jQuery.unblockUI();
      return false;
    });
  }

  function onPopupOpen(elts, settings)
  {
    jQuery('#nyroModalFull').append('<!--[if lte IE 6.5]><iframe class="select-free"></iframe><![endif]-->');
  }

  function initNM()
  {
    jQuery('a.nyroModal, .gallery a').nyroModal({zIndexStart: 999});
  }

  function setCookie(name, value, expires, path, domain, secure)
  {
    document.cookie = name + "=" + escape(value) +
      ((expires) ? "; expires=" + expires : "") +
      ((path) ? "; path=" + path : "") +
      ((domain) ? "; domain=" + domain : "") +
      ((secure) ? "; secure" : "");
  }
  function getCookie(name)
  {
    var cookie = " " + document.cookie;
    var search = " " + name + "=";
    var setStr = null;
    var offset = 0;
    var end = 0;
    if (cookie.length > 0) {
      offset = cookie.indexOf(search);
      if (offset != -1) {
        offset += search.length;
        end = cookie.indexOf(";", offset)
        if (end == -1) {
          end = cookie.length;
        }
        setStr = unescape(cookie.substring(offset, end));
      }
    }

    return(setStr);
  }

  function showBanner(id, message)
  {
    var ban_name = 'banner_' + id + '_showed';

    if( !getCookie(ban_name) )
    {
      setCookie(ban_name, 1, '', '/');

      showMessage(message);
    }
  }

  function initPopupBanner()
  {
    jQuery('#mp_banner').each(function(){
      showBanner(this.id, this.innerHTML);
    });
  }

  function initNavigation()
  {
    $("#jstree").jstree({
      "html_data" : {
        "ajax" : {
          "url" : "/category/navigation",
          "correct_state" : true,
          "data" : function (n) {
            return { id : n.attr ? getIndexFromId( n.attr("id") ) : 0 };
          }
        }
      },
      "plugins" : [ "themes", "html_data", "cookies" ]
    });
  }

  function initSubmitionCallBackForm()
  {
    jQuery('#callback_form').submit(function(){
      var phone = jQuery('#phone').val();
      var time = jQuery('#time').val();
      var direction = jQuery('#direction').val();
      return this_proxy.loadContent('#' + this.id + '_container', this.action, { direction: direction, 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>');

          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.message)
            showMessage(data.message);
        }
      });
      return false;
    });
  }

  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.showImagePopup = function(from)
  {
    jQuery.nyroModalManual({
      from: from,
      modal: false,
      zIndexStart: 1001,
      minWidth: 100,
      minHeight: 100,
      padding: 50,
      cssOpt: {
        wrapper: {
          backgroundColor: 'transparent',
          padding: '0px'
        }
      },
      closeButton: '<a href="#" class="gl_close nyroModalClose" title="Закрыть">Закрыть</a>',
      showBackground: function(elts, settings, callback) {
        elts.bg.css({ 'backgroundColor': 'transparent' }).show();
        callback();
      },
      showContent: function(elts, settings, callback) {
        elts.contentWrapper
          .css({
            width: settings.width+'px',
            height: settings.height+'px',
            marginTop: settings.marginTop+'px',
            marginLeft: settings.marginLeft+'px'
          })
          .show();
        elts.loading.hide();
        callback();
      },
      hideContent: function(elts, settings, callback){
        elts.contentWrapper
          .animate({
            opacity: 0
          }, {
            duration: 350,
            complete: function() {
              elts.contentWrapper.hide();
              callback();
          }
          });
      },
      endShowContent: function(elts, settings) {
        onPopupOpen(elts, settings);
      }
    });
  }

  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();
};


