пятница, сентября 07, 2007

выделение, визивиг и изменение контента

при реализации визивига некоторые вещи приходится вытворять по схеме: сериализация->обработка->десериализация

в качестве примера - реализация динамической подсветки кода. одними только dom-функциями это реализовывать - страшный геморрой, а ещё и очень медленно. гораздо быстрее и проще пройтись парой регулярок по сериализованному документу.

но тут нас подстерегает одна гадость - после обработки хорошо было бы восстановить выделение или хотябы позицию курсора. очевидно, пока выделение ещё существует, нужно сохранить его позиции в dom-е, обработать и по оставленным меткам восстановить выделение.

очевидно, что метки начала и конца выделения должны быть неиспользуемой последовательностью символов юникода. также они должны иметь нулевую ширину, дабы при вводе текста не было скачков. я остановился на парах диактрических знаков: '\u0300\u0301' и '\u0302\u0303'. вроде как маловероятно, чтобы в тексте потребовалось их применить последовательно. ширины они точно не имеют. единственный недостаток: они имеют визуализацию, но с учётом того, что обработка должна быть "мгновенной" (ибо рилтаймовая подсветка) - кратковременное появление маленьких значков не должно быть заметно на глаз.

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

в ИЕ всё оказалось весьма шоколадно...

сохранение меток выглядит так:

var range= document.selection.createRange();
range.pasteHTML( '\u0300\u0301' + range.htmlText + '\u0300\u0301' );

восстановление несколько менее тривиально:

var ran= document.body.createRange();
var tr= document.body.createRange();
tr.findText( '\u0300\u0301' );
tr.text= '';
ran.setEndPoint( 'StartToStart', tr );
tr.findText( '\u0302\u0303' );
tr.text= '';
ran.setEndPoint( 'EndToEnd', tr );
ran.select( );

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

сохранение меток:

var ran= window.getSelection().getRangeAt(0);
var rane= ran.cloneRange( );
rane.collapse( false );
var node= document.createTextNode( '\u0302\u0303' );
rane.insertNode( node );
var node= document.createTextNode( '\u0300\u0301' );
ran.insertNode( node );

а для восстановления соответственно приходится бежать по всему дому в поисках открывающей и закрывающей пар node+offset, после чего наведение выделения с помощью функций setStart и setEnd. почему w3c не соизволила узаконить findText остаётся загадкой 8(o_0)8

8 комментариев:

malik комментирует...

Скажи пожалуйста, как ты сделал поиск по DOM открывающихся и закрывающихся node + offset для восстановления спанов? А то я замучился с рекурсивным перебором элементов DOM... Не помешает и ссылка на твою реализацию. :)

Dark-Demon комментирует...

если проблема только в пробежке по дому, то можешь подправить этот код: http://fastcoder.org/javascript/21.html

у меня реализация без рекурсии, но сильно завязана на фреймворк (хотя, алгоритм вырвать не сложно, наверное).
http://dollar-script.googlecode.com/svn/trunk/$node.js
функция $descwalk
колбэк функция должна возвращать firstChild, чтобы идти вглубь, nextSibling, чтобы идти к следующему узлу и false, чтобы остановиться.

malik комментирует...

С рекурсивной пробежкой по DOM-у я справился - смотрю регуляркой в node.textContents() на наличие метки*. Далее выделяю метку и удаляю ее через range.deleteContents().

Но вот как теперь поставить каретку на законное место? setStart и setEnd() применимы только в случае выделения участка текста, начало и конец тут вроде не могут совпадать. =(

* Метка у меня всего одна - ставится в позицию каретки. Дальше я передаю управление парсеру. Парсер вставляет результат в редактор, после чего я пробегаюсь по DOM и ищу метку.

malik комментирует...

Вопрос снят - воспользовался collapse().

Спасибо большое! Respect за идею.

Dark-Demon комментирует...

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

Анонимный комментирует...

Кардеры, продаем банковские карты дампы кредитных карт, дампы карт,дамп с пин кардинг, кардинг взлом кредитных карт, кардер, продам дампы карт. Продаем кредитные карты, банковские карточки Mastercard, кредитки с пин кодом Visa с балансами 800$-5000$, доставка в любой город, гарантия надежности, ищем постоянных партнеров, пишите нам на емайл kredstore@gmail.com

Анонимный комментирует...

Может обсудим на форуме ?

Анонимный комментирует...

Is it possible to contact administration?
By the way, anybody home?!