barbitoff programmer`s blog

Здесь я публикую заметки из программерской жизни: грабли, на которые мне случилось наступить, проблемы, для которых было найдено элегантное (или не очень) решение, а также все, с чем мне пришлось столкнуться и чем хотелось бы поделиться =)
PS Если хотите меня поблагодарить - на странице есть 3 места, чтобы это сделать =)

вторник, 31 января 2012 г.

YUI DataTable: определение редактируемой записи и её индекса из функции-валидатора

Функция-валидатор редактора ячеек таблицы вызывается в контексте этой таблицы, которая, следовательно, доступна валидатору через this. Получить редактируемую запись (Record) можно вызовом:
this.getCellEditor().getRecord()
Таким образом, можно из валидатора обращаться к другим полям той записи, к которой относится валидируемая ячейка (если, скажем, нужно сравнить новое значение с значением в другой ячейке той же строки). 
Определить индекс редактируемой записи в RecordSet табицы можно следующим образом:
this.getRecordIndex(this.getCellEditor().getRecord())

понедельник, 30 января 2012 г.

Orbeon xforms-jsp и теги <script> внутри <head> на включаемых jsp-страницах

Наткнулся тут на интересные грабли при размещении jsp-страниц в директории xforms-jsp веб-приложения Orbeon`а: мало того, что генерируемая jsp-шкой html-страница должна быть валидным xml (с чем я ещё готов как-то мириться), так ещё к тому же Orbeon умудряется по своему усмотрению переносить в пределах документа теги <script>. 
А именно: пусть есть некоторая jsp-страница header.inc.jsp, включаемая в другую страницу index.jsp с помощью тега  <jsp:include>. Тогда, если на странице header.inc.jsp в блоке <head> есть теги <script> с inline яваскриптом или со ссылками на внешние скрипты, в итоговом html-коде, генерируемом страницей index.jsp, теги <script> окажутся после закрывающегося тега </body>, а вовсе не в <head>. 
Такое поведение бывает крайней неудобно, например, когда переменные из js-файлов или inline js-кода используются в теле страницы т.к. предполагается, что js уже загружен в <head>. Единственное пока найденное мною решение - перенести теги <script> из <head> в начало тега <body>.
Вся эта неразбериха вызвана тем, что к jsp-страницам в директории xforms-jsp применяется фильтр org.orbeon.oxf.servlet.OrbeonXFormsFilter, выполняющий рендеринг xform`ы из xml в html (благодаря этому jsp-страницы в этой директории могут содержать xforms-код, который будет корректно отрендерен в html). 

среда, 25 января 2012 г.

JavaScript: Копирование и объединение объектов

Для копирования объектов и их объединения можно использовать средства Yahoo UI 2:
YAHOO.lang.augmentObject({},obj)
YAHOO.lang.merge(obj1, obj2)
Проблема этих методов в том, что они не выполняют рекурсивное копирование полей объектов в случае, если они сами являются объектами. Проверяется это просто:
var a = {a:{a:"a",b:"b"}}
alert(a.a.a);// выведет "а"
var b = YAHOO.lang.augmentObject({},a);
alert(b.a.a);// выведет "а"
a.a.a = "b"alert(a.a.a);// выведет "b", что естественноalert(b.a.a);// выведет "b", что говорит о том, что поле b.a ссылается реально на тот же объект, что и a.a
В принципе, можно было и не проверять, я просто посмотреть в исходниках, где явно видно, что поля переносятся простым присваиванием (http://developer.yahoo.com/yui/docs/Lang.js.html). Т.к. из тех же исходников видно, что метод merge вызывается augmentObject, и, следовательно, при его использовании полученный объект также будет ссылаться своими полями на теже объекты, что и поля исходных объектов.

Поэтому пришлось написать подобные функции самому (copyObj имеет дополнительный функционал, копируя массивы не как объекты, а как массивы, а также не копируя функции, оставляя их как есть):
/**
 * Копирует объект (рекурсивно)
 * Аналогична YAHOO.lang.augmentObject({},obj), только выполняет копирование полей
 * рекурсивно, т.е. если поле - объект, то он тоже копируется.
 */

function copyObj(obj)
{
    // если передан не объект - копировать не надо, возвращаем значение. Функции тоже не копируем
    if(!(obj instanceof Object) || obj instanceof Function)
        return obj;
    // иначе - производим рекурсивное копирование всех полей (в т.ч. унаследованных). Объекты возвращаем
    // как объекты, массивы - как массивы  
    var ret;
    if(obj instanceof Array)
        ret = new Array();
    else
        ret = Object();

    for(var key in obj)
        ret[key] = copyObj(obj[key]);
    return ret;  
}

/**
 * Объединяет объекты, возвращая новый объект
 * (все поля нового объекта являются копиями (рекурсивными, т.е. полнями)
 * переданных объектов, т.е. изменение переданных объектов никак не повлияет
 * на результат, полученный с помощью данной функции)
 * Аналогична YAHOO.lang.merge, только копирование полей из переданных объектов выполняется
 * рекурсивно (т.е. если поля переданных в качестве аргументов объектов сами являются объектами,
 * они копируются в выходной объект не присваиванием, а вызовом copyObj, которая рекурсивно копирует
 * поле в новый объект)
 * TODO: распространить на любое число объектов-аргументов
 */
function mergeObjects(obj1, obj2)
{
    var ret = copyObj(obj1);
    for(var key in obj2)
        ret[key] = copyObj(obj2[key]);
    return ret;

вторник, 24 января 2012 г.

Java: преобразование спецсимволов в соответствующие HTML сущности (аналог php-функции htmlentities)

Для этого есть статический метод StringEscapeUtils.escapeHtml()* из библиотеки Apache Commons. Помимо этого метода, данный класс содержит много других полезных методов для экранирования / разэкранирования строк в соответствии с правилами Java, JavaScript, HTML, XML, SQL и CSV.

* - или два метода: escapeHtml4 и escapeHtml3 в библиотеке Apache Commons версии 3.

Регулярные выражения в Java: удаление из строки не-ASCII символов

Удаление из строки всех символов, не входящих в набор ASCII, можно сделать с помощью регулярного выражения, использовав POSIX класс символов \p{ASCII}:
mystr.replaceAll("[^\\p{ASCII}]", "");

пятница, 20 января 2012 г.

Определение в Firefox, что загружено в iframe: html или файл

В Firefox в случае, если в iframe загружается файл, свойство location устанавливается в "about:blank",  что можно использовать для проверки:
if(iframeElement.contentWindow.location == 'about:blank')
     // значит в iframe загрузился файл
else
    // в iframe загружен html-документ (точнее, пользователю не было показано диалоговое окно сохранения файла, возможно, при других mime-типах, отображаемых в браузере, мы тоже попадем в эту ветку, я не проверял)
В IE в обоих случаях в location будет src iframe`а.

Также, как вариант, в Firefox можно воспользоваться свойством iframeElement.contentWindow.document.inputEncoding, которое будет null при загрузке файла и не-null для HTML. В IE этого свойства нет вообще.

четверг, 19 января 2012 г.

Проект под контролем git и svn одновременно

Возникла тут необходимость одновременно использовать для контроля версий проекта и git, и svn. Всё бы хорошо, но служебные файл мешают, пытаясь залезть в коммит конкурирующей VCS. Заигнорить служебные файлы гита просто, добавлением в игнор svn`а диретории .git в корне проекта. С svn сложнее, ведь она раскидывает свои ".svn" по всем подпапкам. Но и тут оказалось всё несложно: открываем (или создаем) файл .gitignore в корне проекта, и записываем туда строчку ".svn" (без кавычек и слеша в начале). Всё, теперь git будет игнорить все директории .svn и их содержимое.