jQuery.fn.feedback = function(options){
  //настройки по умолчанию
  var options = jQuery.extend({
      check_class : "fb_check", //класс полей, которые нужно проверить
      attr_check_type : "data-type_check", //атрибут хранящий тип проверки
      error_class : "fb_error", //класс для поля с ошибкой
      ok_class : "fb_ok", //класс для верно заполненного поля
      empty_error_mess : "fiels is empty",  //сообщение о том, что поле пустое
      mail_error_mess : "Поле Email заполнено некорректно.<br>Введите адрес в формате name@domain.com",  //сообщение о том, что email введен неверно
      messages_id : "fb_messages",  //id обертки стека сообщений
      message_class : "fb_message", //класс для обертки одного сообщения
      message_class_admin : "fb_message_admin", //класс для одминской мессаги
      message_mask_id : "fb_message_",  //маска id для обертки сообщения
      message_mask_elm : "fb_elm_", //маска для классов элементов 
      post_address : "feedback.php",  //адрес для отправки сообщений
      class_separator : "fb_separator", //класс разделителя сообщений
      class_message_time : "fb_time", //класс для времени сообщения
      class_message_like : "fb_like", //класс для лайков
      class_message_dislike : "fb_dislike", //класс для дизлайков
      class_message_rate : "fb_rate", //класс для бока рейтинга
      class_message_return : "fb_message_return", //класс для ссылки отмены удаления
      delete_link_class : "fb_delete_link", //класс для ссылки удаления сообщения в режиме админа
      answer_link_class : "fb_answer_link", //класс для ссылки ответа на сообщения в режиме админа
      cansel_answer_class : "fb_cansel_answer", //класс для отмены ответа на сообщение
      fb_answer_id : "feedback_answer_id", //ид поля для идентификатора ответа на сообщение
      loading_layer_class : "fb_loading_layer", //класс для слоя, на котором будет сообщение о загрузке
      error_layer_class : "fb_error_layer", //класс для слоя, на котором будет сообщение об ошибке
      images_cat : "/modules/feedback/images/",//каталог с картинками
      use_userpic : true, //использовать юзерпик?
      id_submit_button : "submit_button", //id кнопки отправки сообщения
      date_stamp : "dd.mm.yyyy hh:MM", //формат даты
      class_link_to_form : "link_to_form", //класс плашечки переходи к форме
      text_link_to_form : "Оставить свой отзыв", //текст на плашечке перхода к форме
      rate : false, //голосовалка за отзывы. по умолчанию отключена
      answer : false, //можно ли админу отвечать на сообщения
      order : false //сортировка. false - прямая, true - обратная
    },options);
      options.close_pic = options.images_cat+"close.png"; //картинка закрытия
      options.loading_pic = options.images_cat+"159.gif"; //индикатор загрузки
      options.userpic = options.images_cat+"fb_user.png"; //адрес картинки
      options.adminpic = options.images_cat+"fb_admin.png"; //адрес картинки 
    //флаг перехвата хоткеев
    var keyboard_is_mine = false;
    //функции валидации
    var tests = {
            'not_empty' : function(obj){
                    if(obj.val()==""){
                        return error(obj,0);
                    }else{
                        return ok(obj);
                    }
                },
            'mail' : function(obj){
                    var filter = /^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/;
                    if (!filter.test(obj.val())) {
                        return error(obj,options.mail_error_mess);
                    }else{
                        return ok(obj);
                    }
                },
            'radio' : function(obj){}
        };
        var form = jQuery("<div>Loading. Please, wait.<br><img src='"+options.loading_pic+"'></div>");  //форма. сначала надо разместить здесь картинку загрузки
        
        
        
        var parent = jQuery(this); //родительский элемент формы
        
        var title_form = jQuery("<div/>").attr("id","fb_form_title").text("Оставить отзыв");  //заголовок для формы
        
        var wrap = jQuery("<div/>").attr("id","fb_block");  //обертка для формы

        wrap.append(form);   //запихиваем форму в обертку
        
        parent.append(wrap);  // и обертку с формой с родительский элемент
        
        wrap.prepend(title_form); //вставляем заголовок
        jQuery.post(options.post_address,{"action":"get_form"},function(data){
          var response = jQuery.parseJSON(data);
          if(response.error){
            alert(response.error);
          }else{
            form.remove();
            form = jQuery(response.content);   //запихиваем форму в обертку
              //отправка формы по ctrl+enter
              form.find("textarea").keypress(function(e){
                if(e.keyCode == 10 && e.ctrlKey===true ) {form.submit();}
              });
            
            wrap.append(form);
            
            
            form.submit(function(){
              return send_form(form);
            });
            
          }
        });
        
        
        var messages_conteiner = jQuery("<div id='"+options.messages_id+"'></div>");  //создаем контейнер для сообщений
        
        wrap.before(messages_conteiner);  //помещаем его перед формой
        //подгружаем сообщения
        jQuery.post(options.post_address,{"action" : "get_messages"},function(data){
          //получили сообщения в формате JSON и сделали из этого объект 
          var response = jQuery.parseJSON(data);
          if(response.error){
            alert(response.error);
          }else{
            //добавляем сообщения
            add_message_to_stack(response.content, messages_conteiner,response.admin);
            
          }
          
        });
        
        //привязываем обработчики проверки
        check_form(form);
        
        
        //функция говорит,что поле не так заполенено как надо
        function error(obj,mess){
            obj.removeClass(options.ok_class).addClass(options.error_class);
            if(mess!=0){
              form.append(create_error(mess));
            }
            //alert(mess);
            return false;
        }
        
        //функция грит что с полем все гуд
        function ok(obj){
            obj.removeClass(options.error_class).addClass(options.ok_class);
            return true;
        }
        //проверка поля
        function check_field(field){
                var type_check = field.attr(options.attr_check_type);
                if(type_check!=""){
                    return tests[type_check](field);
                }else{
                    return true;
                }
                
        }
        //навешивание обработчиков проверки полей на события change and blur
        function check_form(form){
            form.find("."+options.check_class).each(function(){
                jQuery(this).bind("change blur",function(){check_field(jQuery(this));});
            });
        }
        //проверка полей формы
        function check_form_now(form){
          var result = true;
          var all_result  = true;
          form.find("."+options.check_class).each(function(){
                result = check_field(jQuery(this));
                if(!result) all_result = false;
          });
          return all_result;
        }
        
        function add_message_to_stack(obj, conteiner,admin){
            //делаем видимым последний сепаратор
          jQuery("."+options.class_separator+":last").height(1);
           jQuery.each(obj, function(i,val){  //обходим полученный объект для вырывания отдельных сообщений
            
            
            //TODO:
            //учесть админ мод
            //создаем обертку для сообщения
            var message = jQuery("<div id='"+options.message_mask_id+val.fb_id+"'></div>").addClass(options.message_class);
            if(val.fb_admin_mode){
              message.addClass(options.message_class_admin);
            }
            //создаем юзерпик для красоты
            var r = getRandomInt(58,253);
            var g = getRandomInt(16,211);
            var b = getRandomInt(2,50);
            if(options.use_userpic){
              var userpic = jQuery("<img/>").attr("src",options.userpic).addClass("fb_userpic").css("background-color","rgb("+r+","+g+","+b+")");
              if(val.fb_admin_mode) userpic.attr("src",options.adminpic);
            }else{
              var userpic = jQuery("<div/>").addClass("fb_userpic").css("background-color","rgb("+r+","+g+","+b+")");
            }
            //выдергиваем имя оствившего
            var name = jQuery("<div>"+val.fb_name+"</div>").addClass(options.message_mask_elm+"name");
            //выдергиваем текст сообщения
            var message_text = jQuery("<div>"+val.fb_message+"</div>").addClass(options.message_mask_elm+"message");
            
            
            //выдергиваем и форматируем время. оно приходит в unix format
            var message_time = jQuery("<div>"+dateFormat(val.fb_time, options.date_stamp)+"</div>").addClass(options.class_message_time);
            
            //добавить рейтинг
            if(options.rate){
            var like = jQuery("<a href='#'>Согласен</a>").addClass(options.class_message_like);
            var dislike = jQuery("<a href='#'>Не согласен</a>").addClass(options.class_message_dislike);
            var rate = jQuery("<div/>").addClass(options.class_message_rate);
            rate.append(like).append(" | "+val.fb_rate+" | ").append(dislike);
            }else{
              var rate = jQuery("<div/>").addClass(options.class_message_rate);
            }
            
            //создаем сепаратор
            var separator = jQuery("<div/>").addClass(options.class_separator);
            //запихиваем все в обертку сообщения
            message.append(message_time)    //время
              .append(userpic)              //картинку
                .append(name).              //имя юзверя
                  append(message_text)      //текст сообщения
                    .append(rate)           //рейтинг
                      .append(separator);   //разделитель
                      
            //создаем инструменты управления, если пришел админ
            if(admin){
              var delete_link = jQuery("<a/>").attr("href","#").addClass(options.delete_link_class).html("<img src='"+options.close_pic+"' title='Удалить сообщение'>");
              delete_link.bind("click",function(){
                $.post(options.post_address,{'action':'remove_message','fb_id':val.fb_id},function(data){
                  var json = jQuery.parseJSON(data);
                  if(json.error){
                    message.append(create_error(json.error));
                  }else{
                    //создаем отменялку
                    var delete_link_return = delete_link.detach();
                    var return_message_link = jQuery("<a href='#'></a>").text("[ отменить удаление ]").addClass(options.class_message_return);
                    return_message_link.click(function(){
                      $.post(options.post_address,{"action":"return_message","content":json.content},function(data){
                        var response = jQuery.parseJSON(data);
                        if(response.error){
                          message.append(create_error(response.error));
                        }else{
                          return_message_link.replaceWith(delete_link_return);
                        }
                      });
                      return false;
                    });
                    message.append(return_message_link);
                  }
                });
                
                //делаем невидимым последний сепаратор
                jQuery("."+options.class_separator+":last").height(0);
                return false;
              });
              message.append(delete_link);
              
              //создание ссылки для ответа
              if(options.answer){
                  var answer_link = jQuery("<a/>").attr("href","#").addClass(options.answer_link_class).text("Ответить");
                  answer_link.click(function(){//привязываем обработчик клика
                    jQuery("#"+options.fb_answer_id).val(val.fb_id);  //запоминаем ид сообщения на которое отвечаем
                    //создаем ссылку отмены ответа
                    var cansel_answer = jQuery("<a/>").attr("href","#").addClass(options.cansel_answer_class).text("Отменить");
                    cansel_answer.click(function(){ //привязываем к ней обработчик
                      jQuery("#fb_form_title").html("Оставить отзыв");
                      jQuery("#"+options.fb_answer_id).val(''); //зануляем запомненное ид сообщения
                    });
                    //записываем в титл формы, что хотим ответить
                    jQuery("#fb_form_title").html("Ответить пользователю &#171"+name.text()+"&#187").append(cansel_answer);
                    //ставим фокус в текстареа
                    form.find("textarea:first").focus();
                  });
                  //добавляем ссылочку ответа к мессаге
                  message.append(answer_link);
              }
            }
            //и каждое сформированое сообщение отправляем к остальным
            if(val.fb_answer_id!="-1"){
              message.addClass("answer_message");
              jQuery("#"+options.message_mask_id+val.fb_answer_id).after(message);
            }else{
              if(options.order){
                conteiner.prepend(message);
              }else{
                conteiner.append(message);
              }
            }
            
            //добавляем ссылку на форму, если надо
            if(form.offset().top > jQuery(window).height()){
              create_link_to_form();
            }
          });
          //делаем невидимым последний сепаратор
          jQuery("."+options.class_separator+":last").height(0);
        }
        
        function send_form(obj){
          if(check_form_now(obj)){
            var form_data = obj.serialize();
            var loading_layer = jQuery("<div/>").html("Loading. Please, wait.<br><img src='"+options.loading_pic+"'>").addClass(options.loading_layer_class);
            obj.append(loading_layer);
            obj.find("input[type=submit]").attr("disabled","disabled");
            jQuery.post(options.post_address,{"action" : "add" ,"form_data" : form_data},function(data){
                var json = jQuery.parseJSON(data);
                if(json.error){ //если сервер вернул ошибку
                    
                    loading_layer.remove();
                    obj.find("input[type=submit]").removeAttr("disabled");
                    
                    var error_mess = create_error(json.error);
                    
                    obj.append(error_mess);
                    
                }else{
                    //вставляем в стек только что добавленное сообщение
                    add_message_to_stack(json.content,messages_conteiner,json.admin);
                    loading_layer.remove();
                    obj.find("input[type=submit]").removeAttr("disabled");
                    //очищаем сообщение
                    obj.find("textarea").val('');
                }
                
                
            });
            return false;
          }else{
            return false;
          }
        }
        
        // использование Math.round() даст неравномерное распределение!
        function getRandomInt(min, max)
        {
          return Math.floor(Math.random() * (max - min + 1)) + min;
        }

        function create_error(text){
          remove_error();
          keyboard_is_mine = true;
          form.find(":submit").attr("disabled","disabled");          
          var error_layer = jQuery("<div/>").addClass(options.error_layer_class);
          var close_element = jQuery("<img/>").attr("src",options.close_pic);
              close_element.click(function(){
                remove_error();
              });
              
          var error_data = jQuery("<div/>").html(text);          
          return error_layer.append(close_element).append(error_data);
        }
        
        function remove_error(){
          form.find(":submit").removeAttr("disabled");
          jQuery("."+options.error_layer_class).remove();
          keyboard_is_mine = false;
        }
        
        //обработчик нажатия Esc для скрытия ошибки
        jQuery(document).keyup(function(e){
          if(e.keyCode == 27 ) {remove_error();}
        });
        
        
        
        
        function dateFormat(unix_format,template){
          
          var js_unix_stamp = new Date(unix_format*1000);
          
          var templates = {
            'dd':function(){
              var result = String(js_unix_stamp.getDate());
              if(result.length == 1) result = "0"+result;
              return result;
            },
            'mm':function(){
              var result = String(js_unix_stamp.getMonth());
              if(result.length == 1) result = "0"+result;
              return result;
            },
            'yyyy':function(){
              return String(js_unix_stamp.getFullYear());
            },
            'hh':function(){
              var result = String(js_unix_stamp.getHours());
              if(result.length == 1) result = "0"+result;
              return result;
            },
            'MM':function(){
              var result = String(js_unix_stamp.getMinutes());
              if(result.length == 1) result = "0"+result;
              return result;
            }
            //тут можно расширять, вернее нужно
          }
          
          for(property in templates){
            var replace = new RegExp(property,"g");
            
           template =  template.replace(replace,templates[property]());
          }
          
          
          return template;
        }
        
        function create_link_to_form(){
          //втавляем кнопочку перехода к форме, если она шибко низко
            if(jQuery("."+options.class_link_to_form).size() == 0){
              var to_form = jQuery("<a href='#' />").text(options.text_link_to_form).addClass(options.class_link_to_form);
              to_form.click(function(){
                form.find("input[type=text]:first").focus();
                return false;
              });
              messages_conteiner.before(to_form);
            }
        }
        
        
        
        
        return this;
};
