OSDN Git Service

Fix the bug of X.NodeAnime.
[pettanr/clientJs.git] / 0.6.x / js / 02_dom / 30_XTextRange.js
1 /*\r
2  * tr = X.Doc.createRange('selection')\r
3  *      X.Doc.createRange({from : num, to : num})\r
4  * tr = xnode.createRange( from, to ),\r
5  *         ( 'selection' ) docment の slection のうち xnode の配下のもの, textarea, input, iframe[desineMode] の場合、選択範囲またはカーソル位置\r
6  *     ( 'select', from[, to] ) 選択する\r
7  *     ( 'char', from[, to] )\r
8  *     ( 'lineAt', index ),\r
9  *         ( 'line', x, y ), | ( 'line', pointerEvent )\r
10  *     ( 'point', x, y ) | ( 'point', pointerEvent )\r
11  * tr.move( from, to )\r
12  * tr.select( true | false )\r
13  * tr.getRect() { width, height, x, y } -> tr.mesure()\r
14  * tr.getOffset() { from, to } \r
15  * tr.text()\r
16  * \r
17  * naming は mozilla に寄せる\r
18  */\r
19 \r
20 var X_TextRange_range,\r
21         X_TextRange_selection,\r
22         X_TextRange_isW3C = !document.selection || 9 <= X_UA[ 'IE' ] || X_UA[ 'Edge' ];\r
23 \r
24 /**\r
25  * ユーザーによって選択されたテキストへの参照や文字の座標の取得\r
26  * @alias X.TextRange\r
27  * @class TextRange テキストレンジ\r
28  * @extends {__ClassBase__}\r
29  */\r
30 var X_TextRange = X_Class_create(\r
31         'X.TextRange',\r
32         \r
33         // TODO コールバックの最後に破棄されるクラス 1刻みの間存在するクラス. X.XML も\r
34         \r
35         /** @lends X.TextRange.prototype */\r
36         {\r
37                 'xnode' : null,\r
38                 'by'    : '',\r
39                 'v1'    : 0,\r
40                 'v2'    : 0,\r
41                 \r
42                 'Constructor' : function( xnode, arg2, arg3, arg4 ){\r
43                         if( !X_TextRange_range ){\r
44                                 X_TextRange_range = X_TextRange_isW3C ? document.createRange() : X_elmBody.createTextRange();\r
45                         };\r
46                         \r
47                         this.xnode = xnode;\r
48                         \r
49                         switch( arg2 ){\r
50                                 case 'selection' :\r
51                                         if( !X_TextRange_selection ){\r
52                                                 X_TextRange_selection = X_TextRange_isW3C ? window.getSelection() : document.selection.createRange;\r
53                                         };\r
54                                 case 'point' :\r
55                                 case 'char' :\r
56                                         this[ 'by' ] = arg2;\r
57                                         break;\r
58                                 default :\r
59                                         arg4 = arg3;\r
60                                         arg3 = arg2;\r
61                         };\r
62                         \r
63                         if( arg2 !== 'selection' ){\r
64                                 this[ 'v1' ] = arg3 || 0;\r
65                                 this[ 'v2' ] = arg4 || 0;\r
66                         } else {\r
67                                 this[ 'getOffset' ]();\r
68                         };\r
69                 },\r
70                 \r
71                 'move'      : X_TextRange_move,\r
72                 \r
73                 'select'    : X_TextRange_select,\r
74                 \r
75                 'getRect'   : X_TextRange_getRect,\r
76                 \r
77                 'getOffset' : X_TextRange_getOffset,\r
78                 \r
79                 'text'      : X_TextRange_text\r
80         }\r
81 );\r
82 \r
83 // TextNode を探して flat な配列に格納する\r
84 function X_TextRange_collectTextNodes( elm, ary ){\r
85         var kids = elm.childNodes,\r
86                 i, e;\r
87         \r
88         if( !kids || !kids.length ) return;\r
89         \r
90         for( i = 0; e = kids[ i ]; ++i ){\r
91                 switch( e.nodeType ){\r
92                         case 1 :\r
93                                 X_TextRange_collectTextNodes( e, ary );\r
94                                 break;\r
95                         case 3 :\r
96                                 ary[ ary.length ] = e;\r
97                                 break;\r
98                 };\r
99         };\r
100 };\r
101 \r
102 function X_TextRange_getRawRange( tr ){\r
103         var xnode = tr.xnode,\r
104                                 //\r
105                 range = //10 <= X_UA[ 'IE' ] /* || X_UA[ 'iOS' ] */ ? document.createRange() :\r
106                                 //8 <= X_UA[ 'IE' ] ? X_elmBody.createTextRange() :\r
107                                 X_TextRange_range,\r
108                 selection = X_TextRange_selection, \r
109                 elm, isPoint,\r
110                 texts, i, offset, j, l, x, y, rect, top, btm, left;\r
111         \r
112         if( xnode[ '_flags' ] & X_NodeFlags_IN_TREE ){\r
113                 \r
114                 X_Node_updateTimerID && X_Node_startUpdate();\r
115                 \r
116                 elm = xnode[ '_rawObject' ];\r
117                 \r
118                 switch( tr[ 'by' ] ){\r
119                         case 'selection' :\r
120                                 if( X_TextRange_isW3C ){\r
121                                         //selection = window.getSelection();\r
122                                         \r
123                             if( selection.getRangeAt ){\r
124                                 return selection.rangeCount && selection.getRangeAt( 0 );\r
125                             };\r
126                             // http://d.hatena.ne.jp/dayflower/20080423/1208941641\r
127                             // for Safari 1.3\r
128                             //range = document.createRange();\r
129                             range.setStart( selection.anchorNode, selection.anchorOffset );\r
130                             range.setEnd( selection.focusNode, selection.focusOffset );\r
131                                         return range;\r
132                                 } else {\r
133                                         switch( document.selection.type ){\r
134                                                 case 'text' :\r
135                                                         return selection();\r
136                                                 case 'Control' :\r
137                                                         // TODO\r
138                                                 case 'none' :\r
139                                         };\r
140                                 };\r
141                                 break;\r
142 \r
143                         case 'point' :\r
144                                 if( X_TextRange_isW3C ){\r
145                                         // textarea で異なる\r
146                                         // TextNode をフラットな配列に回収\r
147                                         X_TextRange_collectTextNodes( elm, texts = [] );                                                \r
148                                         \r
149                                         x = tr[ 'v1' ];\r
150                                         y = tr[ 'v2' ];\r
151                                         \r
152                                         for( i = offset = 0; text = texts[ i ]; ++i ){\r
153                                                 range.selectNodeContents( text ); // selectNodeContents は TextNode のみ?? Firefox\r
154                                                 l = text.data.length;\r
155 \r
156                                         for( j = 0; j < l; ++j ){\r
157                                             if( X_UA[ 'IE' ] || X_UA[ 'Edge' ] ){\r
158                                                 // 改行の直前の文字を選択すると rect が巨大になってしまう\r
159                                                         range.setEnd( text, j );\r
160                                                     range.setStart( text, j );\r
161                                                     rect = range.getBoundingClientRect();\r
162                                                     top  = rect.top;\r
163                                                     btm  = rect.bottom;\r
164                                                     left = rect.left;\r
165                                                 range.setEnd( text, j + 1 );\r
166                                                 rect = range.getBoundingClientRect();\r
167                                                 \r
168                                                 if( rect.left < left ){\r
169                                                         //console.log( '= ', text.data.charAt( j ), ' x:', x, ' y:', y, ' top:', top | 0, ' left:', left | 0, ' bottom:', btm | 0, ' right:', rect.right | 0 );\r
170                                                             if( left <= x && x <= rect.right && top <= y && y <= btm ){\r
171                                                                 return {\r
172                                                                         'hitRange' : range, // TODO startContainer, endContainer\r
173                                                                         'rect'     : rect,\r
174                                                                         'offset'   : offset,\r
175                                                                         'text'     : text // TODO xtext じゃないの?\r
176                                                                 };\r
177                                                             };\r
178                                                             continue;\r
179                                                 };\r
180                                             } else {\r
181                                                         range.setEnd( text, j + 1 );\r
182                                                     range.setStart( text, j );\r
183                                                     rect = range.getBoundingClientRect();\r
184                                             };\r
185                                             \r
186                                                 //console.log( text.data.charAt( j ), ' x:', x, ' y:', y, ' top:', rect.top | 0, ' left:', rect.left | 0, ' bottom:', rect.bottom | 0, ' right:', rect.right | 0 );\r
187                                             if( rect.left <= x && x <= rect.right && rect.top <= y && y <= rect.bottom ){\r
188                                                 return {\r
189                                                         'hitRange' : range, // TODO startContainer, endContainer\r
190                                                         'rect'     : rect,\r
191                                                         'offset'   : offset,\r
192                                                         'text'     : text // TODO xtext じゃないの?\r
193                                                 };\r
194                                             };\r
195                                         };\r
196                                         offset += l;\r
197                                         };\r
198                                         range = null;\r
199                                 } else {\r
200                                         // ie11 の ie10モード で moveToPoint がないといわれる. よって isW3C:false で動作するのは ie9 以下\r
201                                         // 行の最後の文字の端をクリックすると次の行の文字が選択されてしまう ie8, ie7\r
202                                         // 選択を移動して補正する https://msdn.microsoft.com/ja-jp/library/ms535872(v=vs.85).aspx\r
203                                         range.moveToPoint( x = tr[ 'v1' ], y = tr[ 'v2' ] );\r
204                                         \r
205                                         // if( range.parentElement() !== elm  || elm.contains( range.parentElement() ) ){\r
206                                         \r
207                                         if( range.expand( 'character' ) ){\r
208                                                 left = range.boundingLeft;\r
209                                                 top  = range.boundingTop;\r
210                                                 if( x < left || left + range.boundingWidth  < x || y < top || top  + range.boundingHeight < y ){\r
211                                                         range.moveStart( 'character', -1 );\r
212                                                         range.moveEnd( 'character', -1 );\r
213                                                         left = range.boundingLeft;\r
214                                                         top  = range.boundingTop;\r
215                                                         if( x < left || left + range.boundingWidth  < x || y < top || top  + range.boundingHeight < y ){\r
216                                                                 range = null;\r
217                                                         };\r
218                                                 };\r
219                                         } else {\r
220                                                 range = null;\r
221                                         };\r
222                                 };\r
223                                 return range;\r
224 \r
225                         case 'char' :\r
226                                 if( X_TextRange_isW3C ){\r
227                                         // 未チェック!\r
228                                         range.setEnd( elm, l < tr[ 'v2' ] ? l : tr[ 'v2' ] );\r
229                             range.setStart( elm, tr[ 'v1' ] );\r
230                             return { 'hitRange' : range };\r
231                                 } else {\r
232                                         range.moveToElementText( elm );\r
233                                         //range.collapse( true );                                               \r
234                                         range.moveEnd( 'character', l < tr[ 'v2' ] ? l : tr[ 'v2' ] );\r
235                                         range.moveStart( 'character', tr[ 'v1' ] );\r
236                                 };\r
237                                 return range;\r
238                 };\r
239         };\r
240 };\r
241 \r
242 function X_TextRange_getRect(){\r
243         var result = X_TextRange_getRawRange( this ),\r
244                 rect, ret;\r
245         \r
246         if( result ){\r
247                 if( X_TextRange_isW3C ){\r
248                         if( result.hitRange ){\r
249                                 rect = result.hitRange.getBoundingClientRect();\r
250                                 ret = {\r
251                                         'x'      : rect.left,\r
252                                         'y'      : rect.top,\r
253                                         'width'  : rect.width,\r
254                                         'height' : rect.height\r
255                                 };\r
256                                 //range.detach && range.detach();\r
257                         };\r
258                 } else {\r
259                         ret = {\r
260                                 'x'      : result.boundingLeft,\r
261                                 'y'      : result.boundingTop,\r
262                                 'width'  : result.boundingWidth,\r
263                                 'height' : result.boundingHeight // ie は right, bottom を持たない...\r
264                         };\r
265                 };\r
266         };\r
267         return ret || { 'x' : 0, 'y' : 0, 'width' : 0, 'height' : 0 };\r
268 };\r
269 \r
270 // X.Text を探して flat な配列に格納する\r
271 function X_TextRange_collectXTexts( xnode, ary ){\r
272         var kids = xnode[ '_xnodes' ],\r
273                 i;\r
274         \r
275         if( !kids || !kids.length ) return;\r
276         \r
277         for( i = -1; xnode = kids[ ++i ]; ){\r
278                 if( xnode[ '_tag' ] ){\r
279                         X_TextRange_collectXTexts( xnode, ary );\r
280                 } else {\r
281                         ary[ ary.length ] = xnode;\r
282                 };\r
283         };\r
284 };\r
285 \r
286 function X_TextRange_getOffset(){\r
287         var xnode = this.xnode,\r
288                 elm, result, range,\r
289                 ret, from, xtexts, n, i, l, xtext;\r
290         \r
291         if( xnode[ '_tag' ] === 'TEXTAREA' ){\r
292                 elm = xnode[ '_rawObject' ];\r
293                 \r
294                 if( elm && xnode[ '_flags' ] & X_NodeFlags_IN_TREE ){\r
295                         if( X_UA[ 'IE' ] < 9 ){\r
296                                 \r
297 \r
298                                 return cursorPosition.call( this, elm );\r
299 \r
300                         } else if( elm.setSelectionRange ){\r
301                                 if( X_UA[ 'IE' ] < 12 ){\r
302                                         l = elm.value.length;\r
303                                         ret = {\r
304                                                 'from' : this[ 'v1' ] = elm.selectionStart < l ? elm.selectionStart : l,\r
305                                                 'to'   : this[ 'v2' ] = elm.selectionEnd   < l ? elm.selectionEnd   : l\r
306                                         };\r
307                                 } else {\r
308                                         ret = {\r
309                                                 'from' : this[ 'v1' ] = elm.selectionStart,\r
310                                                 'to'   : this[ 'v2' ] = elm.selectionEnd\r
311                                         };      \r
312                                 };\r
313                         };\r
314                 };\r
315         } else\r
316         if( result = X_TextRange_getRawRange( this ) ){\r
317                 if( X_TextRange_isW3C ){\r
318                         range = result.hitRange;\r
319                         ret = {\r
320                                 'offset' : result.offset,\r
321                                 'from'   : this[ 'v1' ] = range.startOffset,\r
322                                 'to'     : this[ 'v2' ] = range.endOffset,\r
323                                 'text'   : X_Node_getXNode( result.text )\r
324                         };\r
325                         // range.detach && range.detach();              \r
326                 } else {\r
327                         // http://www.studio-freesky.net/programming/javascript/3/\r
328                         \r
329                         range = X_TextRange_range.duplicate();\r
330                         range.moveToElementText( xnode[ '_rawObject' ] );\r
331                         range.setEndPoint( 'EndToStart', result );\r
332                         //range.text && range.moveEnd( 'character', -1 );\r
333                         from  = range.text.length;\r
334                         \r
335                         X_TextRange_collectXTexts( xnode, xtexts = [] );\r
336                         \r
337                         if( xtexts.length ){\r
338                                 for( n = 0, i = -1; xtext = xtexts[ ++i ]; ){\r
339                                         l = xtext[ '_rawObject' ].data.length;\r
340                                         if( from < n + l ){\r
341                                                 break;\r
342                                         };\r
343                                         n += l;\r
344                                 };\r
345 \r
346                                 ret = {\r
347                                         'offset' : n, // elm の何個目の node か?\r
348                                         'from'   : this[ 'v1' ] = from - n,\r
349                                         'to'     : this[ 'v2' ] = from - n + result.text.length,\r
350                                         'text'   : xtext\r
351                                 };                              \r
352                         };\r
353                 };\r
354         };\r
355         \r
356         return ret || { 'from' : -1, 'to' : -1 };\r
357 };\r
358 \r
359 function X_TextRange_text( v ){\r
360         var xnode = this.xnode, elm, val, offset, from, to;\r
361         \r
362         if( v === undefined ){\r
363                 \r
364         } else {\r
365                 if( xnode[ '_tag' ] === 'TEXTAREA' ){\r
366                         elm = xnode[ '_rawObject' ];\r
367                         val = X_UA[ 'IE' ] < 9 ? X_Node_Attr_getValueForIE( elm ) : elm.value;\r
368                         \r
369                         if( this[ 'by' ] === 'char' ){\r
370                                 xnode.attr( {\r
371                                         'value' : val.substr( 0, this[ 'v1' ] ) + v + val.substr( this[ 'v2' ] )\r
372                                 } );\r
373                         } else {\r
374                                 offset = this[ 'getOffset' ]();\r
375                                 \r
376                                 from   = offset[ 'from' ];\r
377                                 to     = offset[ 'to' ];\r
378 \r
379                                 if( X_UA[ 'IE' ] < 9 ){\r
380                                         range = X_TextRange_selection();\r
381                                         // TODO check textarea\r
382                                         range.text = v;\r
383                                         // ここには range.text がいない https://msdn.microsoft.com/ja-jp/library/cc427934.aspx\r
384                                 } else {\r
385                                         val = val.substr( 0, from ) + v + val.substr( to );\r
386                                         elm.value = val;\r
387                                 };\r
388                                         \r
389                                 if( to !== from ){\r
390                                         // カーソル位置を挿入した文字列の最後へ\r
391                                         to = from + v.length;\r
392                                 } else {\r
393                                         // カーソル位置を挿入した文字列の後ろへ\r
394                                         to += v.length;\r
395                                 };\r
396                                 this.move( to, to );\r
397                         };\r
398                 };\r
399         };\r
400 };\r
401 \r
402 function X_TextRange_move( from, to ){\r
403         var xnode  = this.xnode,\r
404                 elm    = xnode[ '_rawObject' ],\r
405                 len, range;\r
406 \r
407         if( 0 <= from ){\r
408                 this[ 'v1' ] = from;\r
409         } else {\r
410                 this[ 'v1' ] = this[ 'v1' ] + from;\r
411                 this[ 'v1' ] < 0 && ( this[ 'v1' ] = 0 );\r
412         };\r
413 \r
414         if( X_Type_isNumber( to ) ){\r
415                 if( 0 <= to ){\r
416                         this[ 'v2' ] = to;\r
417                 } else {\r
418                         this[ 'v2' ] = this[ 'v2' ] + to;\r
419                         this[ 'v2' ] < this[ 'v1' ] && ( this[ 'v2' ] = this[ 'v1' ] );\r
420                 };\r
421         };\r
422         \r
423         if( xnode[ '_tag' ] === 'TEXTAREA' ){\r
424                 // http://blog.enjoyxstudy.com/entry/20060305/p1\r
425                 \r
426                 if( X_UA[ 'IE' ] < 9 || X_UA[ 'Opera' ] ){\r
427                         len = ( X_UA[ 'IE' ] < 9 ? X_Node_Attr_getValueForIE( elm ) : elm.value ).length;\r
428                         \r
429                         if( X_UA[ 'Opera' ] ){\r
430                                 FocusUtility_setTemporarilyFocus( elm );\r
431                         };\r
432 \r
433                         range = elm.createTextRange();\r
434 \r
435                         if( this[ 'v1' ] === this[ 'v2' ] && this[ 'v1' ] === 0 ){\r
436                                 range.collapse( true ); // 先頭に移動\r
437                         } else {\r
438                                 if( this[ 'v1' ] !== this[ 'v2' ] || this[ 'v1' ] < len ){\r
439                                         range.collapse(); // おまじない?\r
440 \r
441                                         if( this[ 'v1' ] === this[ 'v2' ] ){\r
442                                                 range.move( 'character', this[ 'v1' ] );\r
443                                         } else {\r
444                                                 range.moveEnd( 'character', this[ 'v2' ] );\r
445                                                 range.moveStart( 'character', this[ 'v1' ] );\r
446                                         };\r
447                                 } else {\r
448                                         range.collapse( false ); // 末美に移動\r
449                                 };\r
450                         };\r
451                         range.select();\r
452 \r
453                 } else if( elm.setSelectionRange ){\r
454                         elm.setSelectionRange( this[ 'v1' ], this[ 'v2' ] );\r
455                 };\r
456         };\r
457 };\r
458 \r
459 function X_TextRange_select( v ){\r
460         \r
461 };\r
462 \r
463 // http://www.studio-freesky.net/programming/javascript/3/\r
464 // それは、IEのTextRangeオブジェクトで取得した範囲にもしラストに改行コード¥r¥nがあった場合それが含まれないのです。(視覚的な選択範囲には含まれています)\r
465 \r
466 // https://web.archive.org/web/20090904134938/http://www.dedestruct.com/2008/03/22/howto-cross-browser-cursor-position-in-textareas/\r
467                                         // https://web.archive.org/web/20090904183807/http://www.dedestruct.com/cursorPosition.html\r
468                                         function cursorPosition( textarea ){\r
469 \r
470                                                 var selection_range = X_TextRange_selection().duplicate();\r
471                                                 \r
472 \r
473                                                 if (selection_range.parentElement() !== textarea) {\r
474                                                         // TODO 正しくはカーソル位置・選択範囲の復帰\r
475                                                         \r
476                                                         FocusUtility_setTemporarilyFocus( textarea );\r
477                                                                 \r
478                                                         // BODY要素のテキスト範囲を作成する\r
479                                                         selection_range = X_elmBody.createTextRange();\r
480                                                 \r
481                                                         // BODY要素のテキスト範囲をeのテキスト範囲に移動する\r
482                                                         // これはe.createTextRange()とほぼ同等\r
483                                                         selection_range.moveToElementText( textarea );\r
484                                                         \r
485                                                         selection_range.collapse( true ); // 末美に移動\r
486                                                         selection_range.select();\r
487                                                 };\r
488 \r
489                                                 //if (selection_range.parentElement() == textarea) {// Check that the selection is actually in our textarea\r
490                                                         // Create three ranges, one containing all the text before the selection,\r
491                                                         // one containing all the text in the selection (this already exists), and one containing all\r
492                                                         // the text after the selection.\r
493                                                         var before_range = X_elmBody.createTextRange();\r
494                                                         before_range.moveToElementText(textarea);\r
495                                                         // Selects all the text\r
496                                                         before_range.setEndPoint('EndToStart', selection_range);\r
497                                                         // Moves the end where we need it\r
498                                                 \r
499                                                         var after_range = X_elmBody.createTextRange();\r
500                                                         after_range.moveToElementText(textarea);\r
501                                                         // Selects all the text\r
502                                                         after_range.setEndPoint('StartToEnd', selection_range);\r
503                                                         // Moves the start where we need it\r
504                                                 \r
505                                                         var before_finished = false, selection_finished = false, after_finished = false;\r
506                                                         var before_text, untrimmed_before_text, selection_text, untrimmed_selection_text, after_text, untrimmed_after_text;\r
507                                                 \r
508                                                         // Load the text values we need to compare\r
509                                                         before_text = untrimmed_before_text = before_range.text;\r
510                                                         selection_text = untrimmed_selection_text = selection_range.text;\r
511                                                         after_text = untrimmed_after_text = after_range.text;\r
512                                                 \r
513                                                         // Check each range for trimmed newlines by shrinking the range by 1 character and seeing\r
514                                                         // if the text property has changed.  If it has not changed then we know that IE has trimmed\r
515                                                         // a \r\n from the end.\r
516                                                         do {\r
517                                                                 if (!before_finished) {\r
518                                                                         if (before_range.compareEndPoints('StartToEnd', before_range) == 0) {\r
519                                                                                 before_finished = true;\r
520                                                                         } else {\r
521                                                                                 before_range.moveEnd('character', -1);\r
522                                                                                 if (before_range.text == before_text) {\r
523                                                                                         untrimmed_before_text += '\r\n';\r
524                                                                                 } else {\r
525                                                                                         before_finished = true;\r
526                                                                                 }\r
527                                                                         }\r
528                                                                 }\r
529                                                                 if (!selection_finished) {\r
530                                                                         if (selection_range.compareEndPoints('StartToEnd', selection_range) == 0) {\r
531                                                                                 selection_finished = true;\r
532                                                                         } else {\r
533                                                                                 selection_range.moveEnd('character', -1);\r
534                                                                                 if (selection_range.text == selection_text) {\r
535                                                                                         untrimmed_selection_text += '\r\n';\r
536                                                                                 } else {\r
537                                                                                         selection_finished = true;\r
538                                                                                 }\r
539                                                                         }\r
540                                                                 }\r
541                                                                 if (!after_finished) {\r
542                                                                         if (after_range.compareEndPoints('StartToEnd', after_range) == 0) {\r
543                                                                                 after_finished = true;\r
544                                                                         } else {\r
545                                                                                 after_range.moveEnd('character', -1);\r
546                                                                                 if (after_range.text == after_text) {\r
547                                                                                         untrimmed_after_text += '\r\n';\r
548                                                                                 } else {\r
549                                                                                         after_finished = true;\r
550                                                                                 }\r
551                                                                         }\r
552                                                                 }\r
553                                                 \r
554                                                         } while ((!before_finished || !selection_finished || !after_finished));\r
555                                                 \r
556                                                         // Untrimmed success test to make sure our results match what is actually in the textarea\r
557                                                         // This can be removed once you're confident it's working correctly\r
558                                                         /*\r
559                                                         var untrimmed_text = untrimmed_before_text + untrimmed_selection_text + untrimmed_after_text;\r
560                                                         var untrimmed_successful = false;\r
561                                                         if (textarea.value == untrimmed_text) {\r
562                                                                 untrimmed_successful = true;\r
563                                                         } */\r
564                                                         // ** END Untrimmed success test\r
565                                                 \r
566                                                         var startPoint = untrimmed_before_text.split( '\r' ).join( '' ).length;\r
567                                                         // alert(startPoint);\r
568                                                         return {\r
569                                                                 'from'   : this[ 'v1' ] = startPoint,\r
570                                                                 'to'     : this[ 'v2' ] = startPoint + untrimmed_selection_text.split( '\r' ).join( '' ).length\r
571                                                         };\r
572                                                 //}\r
573                                         }\r
574                                         \r