Javascript.RU

Создать новую тему Ответ
 
Опции темы Искать в теме
  #1 (permalink)  
Старый 18.07.2010, 10:22
Аватар для балерун
Аспирант
Отправить личное сообщение для балерун Посмотреть профиль Найти все сообщения от балерун
 
Регистрация: 16.11.2009
Сообщений: 79

Алгоритм работы текстового редактора
Привет всем, название темы, наверно, немного непонятное.
Вот что мне нужно, всем известен MS Word, и там если мы выделим текст жирным, то он станет жирным, а если потом в этом тексте выделим подтекст и опять нажмем сделать жирным, то он наоборот уберет жирность с него.
Как добиться того же самого, но с помощью js? Т.е. с помощью range я обрамляю выделенный кусок текста тегами <b> - он становится жирным, потом в этом жирном тексте выделяю еще кусок текста и он должен стать нежирным.
'hi there doc'                // начальная строка
'hi <b>there</b> doc'  // сделать часть текста жирной
'hi <b>th</b>e<b>re</b> doc' // часть жирного текста сделал нежирной


И ладно если б только в этом была проблема, а то ведь теги могут и пересекаться, например, есть строка
<i>курсив жирный курсив</i> жирный

и если выделить "жирный курсив жирный" и сделать его жирным, то должна получиться строка
<i>курсив </i><b><i>жирный курсив</i> жирный</b>

вот как-то так.
И это ж только простейшие функции, я как представлю сколько всяких вариантов может быть меня аж в дрожь бросает. Но ведь уже столько готовых редакторов, должен же быть алгоритм, по какому принципу с тегами работать? Например, та же комманда execCommand('bold', false, '') адекватно делает жирным и нежирным текст, а что б сделать такое же с range надо весь алгоритм вручную сделать.
Не то что б, я не смогу сделать хотя бы простейшую реализацию, просто не хочется по-новой изобретать велосипед.

Все примеры выше делались (и должны делаться) в iframe с включенным designMode.
Если нет готовых алгоритмов, подскажите как сделать "жирный/нежирный" и пересекающиеся теги.

Последний раз редактировалось балерун, 18.07.2010 в 10:25.
Ответить с цитированием
  #2 (permalink)  
Старый 18.07.2010, 11:31
Отправить личное сообщение для Octane Посмотреть профиль Найти все сообщения от Octane  
Регистрация: 10.07.2008
Сообщений: 3,873

Я писал инлайн-форматирование для WYSIWYG-редактора следующим образом:
1. Получаем root = range.commonAncestroContainer || textRange.parentElement
2. Находим внутри root все текстовые узлы (nodeType == 3), формируя двумерный массив следующим образом:
<div>
    text1
    <ul>
        <li>text2</li>
    </ul>
    text3<span>text4</span>text5
</div>

var text = [
    [#text1],
    [#text2],
    [#text3, #text4, #text5]
]
То есть каждый блочный элемент начинает и заканчивает новый подмассив.
3. Проходим в цикле по text и собираем при помощи getComputedStyle/currentStyle все стили, которые собираемся поддерживать, с родителя каждого текстового узла и формируем массив следующего вида:
text = [
    {
        nodes: [#text1],
        CSSStyles: {fontWeight: "bold", …}
    },
    {
        nodes: [#text2],
        CSSStyles: {…}
    },
    {
        nodes: [#text3, #text4, #text5],
        CSSStyles: {…}
    },
]

4. Теперь нужно написать конечный автомат, который сформирует из этих данных куски HTML-кода:
text = [
    {
        nodes: [#text1],
        html: "<b>text1</b>"
    },
    {
        nodes: [text2]
        html: "<b>text2</b>"
    },
    {
        nodes: [#text3, #text4, #text5],
        html: "<b>text3text4text5<b>"
    },
]

5. Преобразуем HTML-код в дом узлы:
function parse(html) {
    var node = document.createElement("div"), fragment = document.createDocumentFragment();
    node.innerHTML = html;
    while (node.firstChild) fragment.appendChild(node.firstChild);
    return fragment;
}

text = [
    {
        oldNodes: [#text1],
        newNodes: parse("<b>text1</b>") 
    },
    {
        oldNodes: [#text2],
        newNodes: parse("<b>text2</b>") 
    },
    {
        oldNodes: [#text3, #text4, #text5],
        newNodes: parse("<b>text3text4text5</b>") 
    },
]

6. Для каждого набора текстовых узлов находим блочного родителя и заменяем старые текстовые узлы на новые (не innerHTML!), проходя в цикле по childNodes, пока не встретится блочный элемент.


Теперь подробнее о том, как заменить стили:
используем только одну команду для любого инлайн-форматрирования:
document.execCommand("fontName", false, "fake")

Браузер будет автоматически разрывать инлайн-теги и создавать <font face="fake">. А когда мы будем собирать стили с родителей текстовых узлов, то можем дополнительно проверить наличие этого element.getAttribute("face") == "fake" и изменить объект с информацией о стилях.




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


Код показать не могу, проект коммерческий.

Последний раз редактировалось Octane, 18.07.2010 в 11:39.
Ответить с цитированием
Ответ



Опции темы Искать в теме
Искать в теме:

Расширенный поиск


Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Поиск работы JavaScript staff-base Работа 9 12.03.2010 19:06
Модуль для работы с модулями JSprog Ваши сайты и скрипты 29 02.09.2009 13:31