// Удаляем строки состоящие из пробелов.
str = str.replace(/^\s+$/gm, "");
// Формируем абзацы.
str = ('<p>'+str.replace(/\n\n+/g,'</p><p>')+'</p>');
// Вытаскиваем из них блочные элементы.
block = /\<(h1|h2|h3|h4|h5|h6|pre|div|ul|)\>(.*?)\<\/\1\>/gi;
str = str.replace(block, '</p>$&<p>');
// Формируем преводы строк (<br />).
str = str.replace(/\n/g, "<br />");
// Чистка от "<p><br />".
str = str.replace(/\<p\>\<br \/\>/gi, "<p>");
// Чистка от "<br /></p>".
str = str.replace(/\<br \/\>\<\/p\>/gi, "</p>");
// Чистка от "<p></p>".
str = str.replace(/\<p\>\<\/p\>/gi, "");
Теперь нужно исключить обработку содержимого списков и pre.
До сих пор я решал проблемы добавляя фильтры. Но с pre так не получится — внутри него уже могут быть <br /> и фильтр удалит и их.
В голову приходит мысль о получении всех оригинальных <pre>...</pre> в переменную и замена ими обработанных парсером вариантов. Но это как-то громоздко.
Как сделать проще?
upd:
Это можно сделать с помощью чего-то вроде (/(?!\<pre\>)\n(?!\1)/gi, "<br />"); отпишу как что-нибудь получится.