Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > JavaScript: Общие вопросы > 4 пробела для Python-а (продолжение) + баги IE


Автор: 12345c 10.10.2006, 07:32
Попытался сделать операцию средствами скриптов Винграда и наткнулся на баг TextRange в IE, не позволяющий это сделать (простыми методами, в 2-10 строчек).

Суть в том, что если выделение текста в Textarea начинается с 1-го символа не 1-й строки, то выделение (selection) "проглатывает" символ переноса строки, и анализом состыкованных объектов Textrange нельзя узнать, стоит ли между ними перенос. Из этого немедленно следует, что таким способом нельзя выполнить сдвиг группы строк - мы не будем знать, сделано выделение в середине или в начале строки. Конечно, другим способом анализа (пересчётом суммы символов) узнать это можно. Но глюк интересен теоретически сам по себе, поэтому вынес его в новую тему. Заодно, несколько развита тема сдвига строк.

В настоящий момент в архиве лежит скрипт, имеющий 2 кнопки - Ctrl+[, Ctrl+] над Textarea, срабатывающий по клавишам, т.е. совместимый по управлению с Винградом. Но действие наблюдается только в IE, и то неправильное, но уже более-менее локализованное, имеющее наглядность. Вся функция лежит в MyLib.js архива под именем TextAreaSelectionHelper.prototype.move4p . Часть, относящаяся к Опере и FF не затрагивалась вследствие таких интересных событий с IE. Если кто желает переписать или поотладить - архив в http://js2.ru/files/vingradScripts.rar (29 К). 

Правила, которые достигались и достигаются пока что - 
* Если курсора в поле нет, реакции на Ctrl+[ нет;
* Если курсор есть, но нет выделения текста или выделение в пределах строки, ставится/удаляется до 4 пробелов в начале строки;
* Если есть выделение, охватывающее переносы строк, ставится/удаляется группа пробелов в каждом начале строки в выделении и в строке в начале выделения;
* позиции выделения или курсора сохраняются относительно существовавшего текста.

Добавлено @ 07:34 
Исходная тема: http://forum.vingrad.ru/index.php?showtopic=113416 - "вставка/удаление 4-х пробелов при нажатии "Ctrl+]"/"Ctrl+[" "

Автор: 12345c 10.10.2006, 20:37
Код
IE6-Глюк-демо-код с Textarea+TextRange.<br><br>

<textarea cols=70 rows=20 id=t1>
123
A 3
BB3
</textarea><br><button onmousedown=cl1()>Check selection</button>
<script>t11=(d=document).getElementById('t1')
cl1=function(){if(d.all){
  sel=d.selection.createRange()
  tR=d.body.createTextRange();
  tR2=d.body.createTextRange(); //контрольный объект
  tR.moveToElementText(t11);
  tR2.moveToElementText(t11);
  tR.setEndPoint('EndToStart',sel); //делаем слеплённый textRange
  tR2.setEndPoint('EndToEnd',sel);
alert([sel.text.length,'+',tR.text.length,'=',tR2.text.length,
'\nЧисло должно быть равно сумме двух других',
'\nНо при выделении первого символа во 2-й строке'
+'\nили при курсоре в 1-й позиции сумма на 2 меньше'])
}else{alert("For IE only!");}
}</script>

Хуже того, как узнать, сколько мы выделили пустых переносов строки, как в следующем примере? (Длина выделения равна 0.) Проползанием с помощью moveStart()? А если переносов будет тысячи?
Код
IE6-Глюк-демо-код-2 с Textarea+TextRange.<br><br>
<textarea cols=70 rows=20 id=t1>123



A 3
BB3</textarea><br><button onmousedown=cl1()>Check selection</button>
<script>onload=function(){
  t11=(d=document).getElementById('t1');
  tR=d.body.createTextRange();
  tR.moveToElementText(t11);
  tR.collapse();
  tR.moveEnd('character',7);
  tR.moveStart('character',4);
tR.select();}
cl1=function(){if(d.all){  sel=d.selection.createRange();
  tR2=d.body.createTextRange(); //контрольный объект
  tR.moveToElementText(t11);
  tR2.moveToElementText(t11);
  tR.setEndPoint('EndToStart',sel); //делаем слеплённый textRange
  tR2.setEndPoint('EndToEnd',sel);
alert([sel.text.length,'+',tR.text.length,'=',tR2.text.length,
'\nДлина выделенной пустой строки оказывается равной 0'])
}else{alert("For IE only!");}}</script>

(Ещё не пропало желание сделать отличный редактор для Питона в IE? Интересно, какую длину займёт каждая из предположенных Girder-om пары строк кода? Правильно, настоящим джедаям любая длина не помеха.)

То-то я регулярно замечал, что хорошо отлаженные скрипты вставки BB-кодов иногда выдают ошибку, выражающуюся в "слипании" двух соседних строк после вставки. Смотришь, думаешь, что неудачно выделил, вставляешь перенос и идёшь дальше. Вот где собака зарыта - IE не замечает ("проглатывает") любое количество переносов строк, стоящее на краях области выделения. И это остаётся после действия замены document.selection - перенос строки или несколько переносов перестают существовать.

Задача, стало быть, заключается в том, чтобы вычислить (нащупать) то, сколько переносов строк находится на краях области выделения. Метод имеется единственный - moveStart/moveEnd. (При этом надо учитывать, что он понимает перенос как 1 символ, а выделение считает его внутри себя - как 2 (\d\n).)

(Наверняка, задача с другими бр-рами в этом плане будет неизмеримо проще.)

Автор: Zeroglif 10.10.2006, 22:01
Цитата(12345c @  10.10.2006,  07:32 Найти цитируемый пост)
Суть в том, что если выделение текста в Textarea начинается с 1-го символа не 1-й строки, то выделение (selection) "проглатывает" символ переноса строки

Если перенос (\r\n) находится на правом краю текстового диапазона, то он не виден, не считается IE. Мне пришлось это вычислять, если ты видел, в своём скрипте. 

У тебя почти такая же байда, но под другим соусом. Когда курсор стоит в начале второй строки (перед A), то tR (также как и tR2) должны показать длину = 5 (1 плюс 2 плюс 3 плюс \r плюс \n), а мы имеем 3, перенос не учтён, т.к. он у обоих диапазонов с краю. 

Если же мы выделим A, то tR так и останется равным 3, а tR2 уже учтёт перенос правильно, т.к. он у этого диапазона уже не с краю, а вложен внутрь, и учтён IE. Получается разница в 2.

Автор: 12345c 11.10.2006, 01:59
Zeroglif, 
Цитата(Zeroglif @  10.10.2006,  22:01 Найти цитируемый пост)
Если перенос (\r\n) находится на правом краю текстового диапазона, то он не виден, 
 - это не вся правда - вся правда в том, как говорится в сказаниях, что не перенос, а переносЫ не видны и игнорируются на правом краю TextRange. Это поразительное явление в самом популярном браузере иллюстрируется не менее поразительным скриптом, который я сделал на базе верхних примеров: http://javascript.aho.ru/example/JsPrimeryCode-97.htm . Берём сумму длин областей, а результаты частенько плавают так, как им заблагорассудится - такое впечатление smile.

Автор: Zeroglif 11.10.2006, 09:36
Цитата(12345c @  11.10.2006,  01:59 Найти цитируемый пост)
это не вся правда - вся правда в том, как говорится в сказаниях, что не перенос, а переносЫ не видны и игнорируются на правом краю TextRange


Диапазон отсекает правый крайний перенос, то есть условная правая граница диапазона с точки зрения TextRange.text стоит перед этим ближним переносом. Естесственно, не будет виден ни 2-ой, ни 3-ий, ни энный перенос справа, граница же уже установлена перед самым первым. Результаты твоего теста имхо не плавают, а как раз показывают это весёлое поведение IE. smile

Автор: Sardar 11.10.2006, 16:00
Метод Zeroglif'а с "щупом" для начала и конца строки ИМХО самый действенный, вчера часа два убил что бы перенести на форумские скрипты smile

Споткнулся о селекцию, я её сохраняю как на была. При unindent'е если от курсора до начала строки меньше tab'а, то курсор должен остаться на начале строки и не прыгать на предидущую. Сейчас добью фишку и выложу с обильными коментариями.

Автор: 12345c 11.10.2006, 20:19
Я обошёл нащупывание в большинстве случаев через взятие разности длин двух TextRange, когда работает правильная левая граница. (Для примера, когда нужно взять длину выделенной строки.) В малом числе случаев, когда выделение происходит в сплошных переносах в конце поля ввода, такое простое решение перестаёт работать, и нужно нащупывание символа, отличного от переноса, через moveStart|moveEnd('character',-1). Это реализовано в примере http://javascript.aho.ru/example/JsPrimeryCode-97.htm (дополнил его сейчас) для выполнения вроде бы простенькой задачи - взять длину строки выделенных символов в Textarea. И оно стало работать. Теперь Ctrl+A не даёт чудеса в результате.

Подковавшись в методах определения длин строк, можно возвращаться к основной задаче :). Правда, тебе, Sardar, это сподручнее, а то я там наделаю своего кода.

Автор: 12345c 12.10.2006, 22:37
Вот она, функция вычисления длины любой области TextRange, лежащей в поле textarea:
Код
lenTR=function(oTR,oTa){var ltr; //длина объекта TextRange oTR в области Textarea oTa (IE only)
  var tR=document.body.createTextRange();  tR.moveToElementText(oTa);
  var tR1=tR.duplicate();  tR1.setEndPoint('StartToEnd',oTR);
  var tR2=tR.duplicate();  tR2.setEndPoint('StartToStart',oTR);
  var lenSel=tR2.text.length-tR1.text.length;
  if(tR1.text.length>0||oTa.value.length<=tR.text.length)return lenSel;
  else{
    var lenCorr=0;
    var lenCorr2=0;
    var tR3=oTR.duplicate();
    var i,i0;  i=i0=tR3.text.length;
    while(i>=i0&&tR1.text.length==0){
      if(i==0){tR3.moveStart('character',-1);lenCorr--;lenCorr2=1;}
      tR3.moveEnd('character',-1);
      i=tR3.text.length;
      tR1.setEndPoint('StartToEnd',tR3);
      lenCorr++; //чис. переносов в конце
  };return lenSel+(lenCorr+lenCorr2-1)*2;}
}


Дополнение: выставление и убирание пробелов сделано в IE с помощью написанной функции (конец строки не очень правильно обрабатывается), осталось выставлять выделение. Поскольку при работе с this.iesel появляется новый букет глюков, подумал, что проще будет работать с правильными вычисленными смещениями краёв выделения, но при выставлении выделения требуется перевести его снова в глючный формат (ожидается ради этого новая страница примера). Промежуточный результат в 
http://javascript.aho.ru/files/vingradScripts.rar , где для наглядности пробелы заменены на "@".

Продолжение:
По скрипту остался "последний вздох" - правильно вычислить положение конца области выделения (скрипт для IE). От идеи менять this.iesel, чтобы не переустанавливать границы выделения, пришлось отказаться, потому что глючность всоприятия им краевых переносов строки привела к тому, что сделать корректное изменение не представилось возможным. Вместо этого продолжил подход, сделанный в скрипте "http://forum.vingrad.ru/index.php?showtopic=107024" (вставка текстов под курсором; её надо поправить тоже в соответствии с этой темой) - вычислить корректные заменители .selectionStart, .selectionEnd в IE и пользоваться реальными строками из .value . При выставлении выделения нужно было учесть очередной глюк (или нестыковку) TextRange, когда он считает, что move() через перенос строки - это 1 символ, когда в коде Textarea их 2 (\r\n).

В http://javascript.aho.ru/files/vingradScripts.rar - обновлённая версия и комметарий того, что функция TextAreaSelectionHelper.prototype.move4p делает. Ещё 1 день - и можно проверять практичность работы.

Окончание для IE: функция для IE работает, можно тестировать юзабельность. Специально проработал начала и концы выделений в разных случаях. Срабатывает по кнопкам в конце списка кнопок над полем ввода под названием Ctrl_[  и Ctrl+], но сочетания клавиш не работают - что-то сломал в скриптах, потому что вначале работали. Вероятно, переустановить в скрипты не должно составить проблемы.

Sardar, доделывать ли дальше или ты сам сделаешь свой вариант? Для себя я задачу решил (могу подробно расписать процесс и полезные выводы здесь), дальше может интересовать, если только будет применяться. Если будет применяться, доделаю скрипт и подробно распишу процесс для всех браузеров.

Автор: pythonwin 7.11.2006, 06:58
потестировал - вроде нормально, но жаль что не работают клавиши (Ctrl+[  и Ctrl+])
+ хорошо бы сделать так чтобы при нажатии Enter в конце строки в новую строку добавлялось столько же пробелов как и у родительской строки. smile

Автор: 12345c 2.12.2006, 20:59
Сделана совместимость с остальными бр-рами, просьба проверить в http://forum.vingrad.ru/topic-124918.html . Имеется как файл с внедрённым в скрипты форума кодом, так и демонстрационный пример, который достаточно скопировать в файл для выполнения.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)