Показать сообщение отдельно
  #8 (permalink)  
Старый 24.12.2009, 14:45
Отправить личное сообщение для Octane Посмотреть профиль Найти все сообщения от Octane  
Регистрация: 10.07.2008
Сообщений: 3,873

Эх… достать бы коды старой версии Etherpad, там в реальном времени подсветка синтаксиса в WYSIWYG работала и не тормозила.

Я писал модуль форматирование для такого редактора. Было условие, чтобы на выходе получался красивый валидный код, делал я это следующим образом:

1. Рекурсивно проходил по всему дереву или отдельной его ветке и запоминал ссылки на все непустые текстовые узлы, собирал со всех их inline-родителей computedStyle, вытаскивал из него только поддерживаемые редактором стили, получался двумерный массив объектов:
[
    [
        {node: textNode, style: {color: "#f00", "font-weight": bold}},
        {node: textNode, style: {…}},
        {node: textNode, style: {…}},
    ],
    [
        {node: textNode, style: {…}},
        …
    ]
]

из такого кода:
<div>
    text1
    <ul>
        <li>text2</li>
        <li>text3</li>
    </ul>
    text4<b>text5</b>text6
</div>

получался вот такой массив:
[
    [{node:  text1, style: {}}],
    [{node:  text2, style: {}}],
    [{node:  text3, style: {}}],
    [{node:  text4, style: {}}, {node:  text5, style: {}}, {node:  text6, style: {}}]

]

При первом проходе блочные элементы трогать не будем, оптимизация затрагивает только текстовые узлы и inline-элементы, поэтому и получаем двумерный массив, как бы уменьшаем многомерность задачи.
Внутри какого блока проводить оптимизацию, определяем с помощью range.getAncestorContainer/parentElement, чтобы всегда весь document.body не перестраивать.
Там правда еще много возни было с приведением значений стилей к единому виду, например font-weight может быть bold, 400 или 900, Opera возвращает некоторые значения в кавычках…

2. С помощью конечного автомата, на основе собранных стилей, собираем новый HTML-код (сводим многомерный алгоритм к линейному), проводя объединение одинаковых стилей, создавая <span style="…"> вместо невалидных <U> и т.д. Должен получится одномерный массив кусков HTML-кода.

3. Заменяем старые узлы на новые, полученные из кусочков HTML-кода. Использовать innerHTML не получится, так как, например, для верхнего div, заменив 1-й участок (text1), мы потеряем все остальные, поэтому создаем функцию:
var parseHTML = function () {
    var node = document.createElement("div"); // тут дорога каждая миллисекунда
    var fragment = document.createDocumentFragment(); // узлы будут переносится в дерево
    return function (htmlCode) {
        node.innerHTML = htmlCode;
        while (node.firstChild) fragment.appendChild(node.firstChild);
        return fragment;
    };
}();

Например, заменяем отрезок, полученный из text4–text6: для этого нужно получить самого верхнего inline-родителя узла text4 (если такого нет, то берем сам узел text4), вставить перед ним новый fragment, а затем удаляем все ненужные старые узлы text4–text6 и их inline-родителей.

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

Возвращать курсор (каретку) в нужное место лучше всего следующим образом:
1. Так как мы уничтожаем старые узлы, то никакой связи с range-объетакми не остается и они нам не помогут, поэтому просто при помощи execCommand вставляем на месте каретки <font face="fake"> (потому что работает везде).
2. Заменяем <font face="fake"> на какой-нибудь элемент с классом.
3. В алгоритме, описанном выше, делаем специальные условия, чтобы этот узел со специальным классом остался в нужном месте.
4. После преобразования кода, находим этот элемент, ставим курсор в него selectNodeContent/moveToElementText.
5. Сдвигаем курсор setStart/moveStart.
6. Удаляем служебный элемент.

В твоей задаче конечный автомат, собирающий новый HTML-код, должен еще и подсвечивать необходимые слова. Но ведь они могут быть "разорваны" на несколько текстовых узлов. Вот с этим хз что делать. Наверное, тогда не в конечном автомате это делать, а сначала оптимизировать код, потом найти эти слова и обернуть в теги для подсветки. Хотя тоже не факт, что пол слова как-нибудь не оформлено по другому было… В общем писать модуль поиска слов мне не досталось, но знаю, что там ппц)))

В IE множественные выделения не работают, поэтому только тегами получится подсвечивать. Разрывать части слов (текстовых узлов) опять же удобнее с помощью execCommand("fontFace", "fake"), предварительно выделив область.

Исходников не осталось, да показывать я их не в праве бы был, проект коммерческий был со всеми вытекающими, хотя он вроде так и не вышел)

Последний раз редактировалось Octane, 24.12.2009 в 14:54.
Ответить с цитированием