четверг, 8 октября 2009 г.

Неправильная работа в JQuery функции $.browser.version для IE выше 6 версии. Incorrect work JQuery function $.browser.version for IE 6 version above

Совершенно неожиданно для себя, я открыл серьезный глюк в моем любимом Jquery. Если бы мне кто-то сказал, что функция определения версии броузера $.browser.version работает неправильно, я бы спорил на что угодно, что он неправ. Но....

Как известно, все познается на практике. Я совершенно случайно сделал такой финт:

alert('Browser version: '+ $.browser.version);

Каково же было мое удивление, когда я увидел на экране моего броузера IE8 попап: «Browser version: 6.0»!

Я немного погуглял и выяснилось – да, действительно, вражеская бажина... Для всех IE > 6 выводится, что текущая версия експлорера - 6.

Посмотрев множество решений проблемы, перечитав кучу статей, я собрал воедино все лучшее и переопределил для JQuery метод $.browser.version. Чтобы и у вас все работало правильно, необходимо переопределить в $(document).ready обработку таким образом:

(function($) {
  var userAgent = navigator.userAgent.toLowerCase();
  var ie_version = -1;
  if (navigator.appName == 'Microsoft Internet Explorer') {
   var re = new RegExp("msie ([0-9]{1,}[\.0-9]{0,})");
   if (re.exec(userAgent) != null)
   ie_version = parseFloat(RegExp.$1);
  }
  $.browser = {
    version: (ie_version>-1)?ie_version:(userAgent.match( /.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/ ) || [0,'0'])[1],
    safari: /webkit/.test( userAgent ),
    opera: /opera/.test( userAgent ),
    msie: /msie/.test( userAgent ) && !/opera/.test( userAgent ),
    mozilla: /mozilla/.test( userAgent ) && !/(compatible|webkit)/.test( userAgent )
  };
})(jQuery);

Коментарии в коде думаю не понадобятся, тут все предельно ясно: для ИЕ надо использовать отдельное регулярное выражение. Так как дефолтное работает неправильно.

суббота, 3 октября 2009 г.

JQuery siblings VS find

Простенький пример:

$("div.test").find("li")  и $("div.test").siblings("li")

выполнят одну и ту же функцию - выберут из дива с классом test все элементы li, но по скорости siblings будет значительно выигрывать, так как ищет сестринские элементы в наборе, то-есть только элементы li, а find пропускает через себя все, оставляя только нужные.

Если у вас имеются большие куски ХТМЛ для парсинга, то вам следует использовать sibling если выбираются однотипные элементы. Это является неплохой оптимизацией работы поиска, особенно при больших объемах данных.

пятница, 2 октября 2009 г.

Универсальный механизм отправки AJAX запросов

Я хочу поделится своим опытом работы с JQuery для генерации AJAX запросов. Думаю что интересно будет не только новичкам.

Допустим, у нас есть какой-то проект, который находится на такой стадии разработки, что основной серверный функционал отлажен, дизайн существует в виде каркасов страниц. Нам ставят задачу - сделать обработку всех форм (причем для разных контроллеров) с помощью Аякса. Вполне понятно, что хотелось бы реализовать простой, удобный, а самое главное - универсальный механизм обработки AJAX запросов.

Для начала, нам понадобится подключить собственно сам JQuery, а также плагин JQuery Form. Советую создать JScript файл в котором мы и будем хранить наши универсальные методы обработки (удобно назвать например general.js).

В нем создаем такую функцию:

function FormSubmit(id,url,func){
   var params = $("#"+id).formSerialize();
   $.post(url,params, function(response){
    if(StatusOk(response)){
      call_func(func,response);
    }else{
      ShowValidation(response);
    }
   });
}

Функция принимает 3 параметра:
1 - ID формы со страницы, для того чтобы можно было сериализировать данные формы для отправки
2 - URL скрипта который будет обрабатывать запрос
3 - имя JScript функции-обработчика ответа

Добавим соглашение, что результатом обработки запроса, сервер вернет или текст валидационной ошибки, или: $id."Ok!"

Где id - номер обработанной записи в БД, или номер созданной записи и т.п.
StatusOk(response) - проверяет ответ сервера и возвращает TRUE если он успешный.
Код функции ниже:

function StatusOk(str){
   var re = /Ok!/;
   var result = re.test(str) ? true : false;
   return result;
}

Метод call_func(func,response) вызывает функцию по имени func и передает ей ответ сервера как параметр.

Реализацию привожу ниже:

function call_func(func, param){
  if(func!='') window[func](param);
}

Эта функция позволяет динамически вызывать функции по имени с передачей ей параметров.

ShowValidation(response) - стандартный метод в котором вы будете обрабатывать валидационные сообщения, например выводить их в какой-то общий див со специальным стилем.

Такой подход позволяет очень гибко обрабатывать формы. Например, мы в форме на кнопочку вешаем обработчик onclick='FormSubmit("form_id","/users/update", "userCreate")'

Наша функция соберет данные с формы заданной через id, передаст их на url скрипта обработки, после чего в случае успешного ответа - вызовет ф-ю userCreate (ее надо не забыть создать) и передаст туда ответ сервера для дальнейшей обработки. Как правило, эта функция должна обновлять набор данных на странице после их изменения (создание, редактирование). Если в ответе сервера не присутствует признак успешной операции, то значит ответом является валидационное сообщение которое надо отобразить.

Таким образом, все AJAX запросы выполняются в одном месте. Добавлением третьего параметра - вызов call-back функции, мы добавляем невероятную гибкость нашему методу. Он позволит вынести индивидуальную обработку ответа в нужное место в каждом конкретном случае. При этом, в call-back функции нам не надо заботится о Аяксе и о том как туда попали параметры.

Когда все запросы обрабатываются в одном месте, то вероятность возникновения ошибок, стремится к нулю. Необходимо только придерживаться соглашений для ответа сервера.

Вы можете всячески усовершенствовать данный механизм, который здесь приводится в облегченном варианте. Надеюсь что данный подход будет вам интересен и использован в своих приложениях.

Создаем JQuery фильтр для полей для предотвращения XSS атак. Create JQuery filter for XSS attacks avert

Столкнулся с задачей, когда необходимо фильтровать значение полей ввода, на предмет содержания в них HTML тегов, а также вызовов JScript функций.

В моем конкретном случае, дыра образовалась в инпуте, значение которого обрабатывалось JQuery методом.

Проблему решает написание JQuery фунции, которая может применятся к любому тегу который имеет значение val(). Я вырезаю из него все теги, а также содержимое всех скобок, вместе со скобками.

Реализацию функции привожу ниже:


(function($) {
  $.fn.stripTagsFromVal = function() {
    var regexp = /([<|(]("[^"]*"|'[^']*'|[^'">])*[>|)])/gi;
    this.each(function() {
        $(this).val(
          $(this).val().replace(regexp,"")
        );
      });
    return $(this);
  }
})(jQuery);


Приведу пример использования:
Пусть у нас есть input#test, зададим ему значение:


$("#test").val('< script>alert('XSS attack!')< / script>');


Применим нашу функцию и проверим что получим на выходе:


$("#test").stripTagsFromVal();
console.log('text: ',$("#test").val());


В консоле будет: text: alert, что означает что из всей конструкции осталось только безобидное имя функции alert.

Модифицировать даный пример, для конкретного вашего случая, не составит проблем. Для отладки регулярных выражений пользуйтесь замечательным online отладчиком Rubular