OSDN Git Service

Fix the bug of X.NodeAnime.
[pettanr/clientJs.git] / 0.6.x / js / 02_dom / 30_XTextRange.js
index 80ddf4c..b0712e5 100644 (file)
@@ -2,21 +2,24 @@
  * tr = X.Doc.createRange('selection')\r
  *      X.Doc.createRange({from : num, to : num})\r
  * tr = xnode.createRange( from, to ),\r
- *        ( 'selection' ) docment の slection のうち xnode の配下のもの\r
- *     ( 'char', from, to )\r
- *     ( 'line', index ),\r
- *     ( 'point', x, y ) | ( 'point', e )\r
- * tr.move( startIndex, endIndex )\r
- * tr.getRect() { width, height, x, y }\r
- * tr.getOffset() { from, to }\r
+ *        ( 'selection' ) docment の slection のうち xnode の配下のもの, textarea, input, iframe[desineMode] の場合、選択範囲またはカーソル位置\r
+ *     ( 'select', from[, to] ) 選択する\r
+ *     ( 'char', from[, to] )\r
+ *     ( 'lineAt', index ),\r
+ *        ( 'line', x, y ), | ( 'line', pointerEvent )\r
+ *     ( 'point', x, y ) | ( 'point', pointerEvent )\r
+ * tr.move( from, to )\r
+ * tr.select( true | false )\r
+ * tr.getRect() { width, height, x, y } -> tr.mesure()\r
+ * tr.getOffset() { from, to } \r
  * tr.text()\r
  * \r
  * naming は mozilla に寄せる\r
  */\r
 \r
 var X_TextRange_range,\r
-       X_TextRange_range2,\r
-       X_TextRange_isW3C = !document.selection || 10 <= X_UA[ 'IE' ];\r
+       X_TextRange_selection,\r
+       X_TextRange_isW3C = !document.selection || 9 <= X_UA[ 'IE' ] || X_UA[ 'Edge' ];\r
 \r
 /**\r
  * ユーザーによって選択されたテキストへの参照や文字の座標の取得\r
@@ -27,27 +30,30 @@ var X_TextRange_range,
 var X_TextRange = X_Class_create(\r
        'X.TextRange',\r
        \r
+       // TODO コールバックの最後に破棄されるクラス 1刻みの間存在するクラス. X.XML も\r
+       \r
        /** @lends X.TextRange.prototype */\r
        {\r
-               xnode      : null,\r
-               createFrom : '',\r
-               v1         : 0,\r
-               v2         : 0,\r
+               'xnode' : null,\r
+               'by'    : '',\r
+               'v1'    : 0,\r
+               'v2'    : 0,\r
                \r
                'Constructor' : function( xnode, arg2, arg3, arg4 ){\r
                        if( !X_TextRange_range ){\r
                                X_TextRange_range = X_TextRange_isW3C ? document.createRange() : X_elmBody.createTextRange();\r
-                               if( !X_TextRange_isW3C ) X_TextRange_range2 = X_elmBody.createTextRange();\r
                        };\r
                        \r
                        this.xnode = xnode;\r
                        \r
                        switch( arg2 ){\r
                                case 'selection' :\r
-                                       //break;\r
+                                       if( !X_TextRange_selection ){\r
+                                               X_TextRange_selection = X_TextRange_isW3C ? window.getSelection() : document.selection.createRange;\r
+                                       };\r
                                case 'point' :\r
                                case 'char' :\r
-                                       this.createFrom = arg2;\r
+                                       this[ 'by' ] = arg2;\r
                                        break;\r
                                default :\r
                                        arg4 = arg3;\r
@@ -55,11 +61,17 @@ var X_TextRange = X_Class_create(
                        };\r
                        \r
                        if( arg2 !== 'selection' ){\r
-                               this.v1 = arg3 || 0;\r
-                               this.v2 = arg4 || 0;\r
+                               this[ 'v1' ] = arg3 || 0;\r
+                               this[ 'v2' ] = arg4 || 0;\r
+                       } else {\r
+                               this[ 'getOffset' ]();\r
                        };\r
                },\r
                \r
+               'move'      : X_TextRange_move,\r
+               \r
+               'select'    : X_TextRange_select,\r
+               \r
                'getRect'   : X_TextRange_getRect,\r
                \r
                'getOffset' : X_TextRange_getOffset,\r
@@ -90,10 +102,12 @@ function X_TextRange_collectTextNodes( elm, ary ){
 function X_TextRange_getRawRange( tr ){\r
        var xnode = tr.xnode,\r
                                //\r
-               range = 10 <= X_UA[ 'IE' ] /* || X_UA[ 'iOS' ] */ ? document.createRange() :\r
-                               8 <= X_UA[ 'IE' ] ? X_elmBody.createTextRange() : X_TextRange_range,\r
-               elm, selection, isPoint,\r
-               texts, i, offset, j, l, x, y, rect;\r
+               range = //10 <= X_UA[ 'IE' ] /* || X_UA[ 'iOS' ] */ ? document.createRange() :\r
+                               //8 <= X_UA[ 'IE' ] ? X_elmBody.createTextRange() :\r
+                               X_TextRange_range,\r
+               selection = X_TextRange_selection, \r
+               elm, isPoint,\r
+               texts, i, offset, j, l, x, y, rect, top, btm, left;\r
        \r
        if( xnode[ '_flags' ] & X_NodeFlags_IN_TREE ){\r
                \r
@@ -101,15 +115,24 @@ function X_TextRange_getRawRange( tr ){
                \r
                elm = xnode[ '_rawObject' ];\r
                \r
-               switch( tr.createFrom ){\r
+               switch( tr[ 'by' ] ){\r
                        case 'selection' :\r
                                if( X_TextRange_isW3C ){\r
-                                       selection = window.getSelection();\r
-                                       return selection.rangeCount && selection.getRangeAt( 0 );\r
+                                       //selection = window.getSelection();\r
+                                       \r
+                           if( selection.getRangeAt ){\r
+                               return selection.rangeCount && selection.getRangeAt( 0 );\r
+                           };\r
+                           // http://d.hatena.ne.jp/dayflower/20080423/1208941641\r
+                           // for Safari 1.3\r
+                           //range = document.createRange();\r
+                           range.setStart( selection.anchorNode, selection.anchorOffset );\r
+                           range.setEnd( selection.focusNode, selection.focusOffset );\r
+                                       return range;\r
                                } else {\r
                                        switch( document.selection.type ){\r
                                                case 'text' :\r
-                                                       return document.selection.createRange();\r
+                                                       return selection();\r
                                                case 'Control' :\r
                                                        // TODO\r
                                                case 'none' :\r
@@ -118,57 +141,100 @@ function X_TextRange_getRawRange( tr ){
                                break;\r
 \r
                        case 'point' :\r
-                               isPoint = true;\r
-                       case 'char' :\r
                                if( X_TextRange_isW3C ){\r
                                        // textarea で異なる\r
+                                       // TextNode をフラットな配列に回収\r
+                                       X_TextRange_collectTextNodes( elm, texts = [] );                                                \r
+                                       \r
+                                       x = tr[ 'v1' ];\r
+                                       y = tr[ 'v2' ];\r
+                                       \r
+                                       for( i = offset = 0; text = texts[ i ]; ++i ){\r
+                                               range.selectNodeContents( text ); // selectNodeContents は TextNode のみ?? Firefox\r
+                                               l = text.data.length;\r
 \r
-                                       if( isPoint ){\r
-                                               // TextNode をフラットな配列に回収\r
-                                               X_TextRange_collectTextNodes( elm, texts = [] );                                                \r
-                                               \r
-                                               for( i = offset = 0; text = texts[ i ]; ++i ){\r
-                                                       range.selectNodeContents( text ); // selectNodeContents は TextNode のみ?? Firefox\r
-                                                       l = text.data.length;\r
-\r
-                                               for( j = 0, x = tr.v1, y = tr.v2; j < l; ++j ) {\r
-                                                       if( range ){\r
-                                                           range.setStart( text, j );\r
-                                                           range.setEnd( text, j + 1 );\r
-                                                           rect = range.getBoundingClientRect();                                               \r
-                                                       };\r
-                                                   if( rect.left <= x && x <= rect.right && rect.top <= y && y <= rect.bottom ){\r
-                                                       return {\r
-                                                               hitRange : range,\r
-                                                               rect     : rect,\r
-                                                               offset   : offset,\r
-                                                               text     : text\r
-                                                       };\r
-                                                   };\r
-                                               };\r
-                                               offset += l;                                                    \r
-                                               };\r
-                                               range = null;\r
-                                       } else {\r
-                                               // 未チェック!\r
-                                               range.setEnd( elm, l < tr.v2 ? l : tr.v2 );\r
-                                   range.setStart( elm, tr.v1 );\r
-                                   return { hitRange : range };\r
+                                       for( j = 0; j < l; ++j ){\r
+                                           if( X_UA[ 'IE' ] || X_UA[ 'Edge' ] ){\r
+                                               // 改行の直前の文字を選択すると rect が巨大になってしまう\r
+                                                       range.setEnd( text, j );\r
+                                                   range.setStart( text, j );\r
+                                                   rect = range.getBoundingClientRect();\r
+                                                   top  = rect.top;\r
+                                                   btm  = rect.bottom;\r
+                                                   left = rect.left;\r
+                                               range.setEnd( text, j + 1 );\r
+                                               rect = range.getBoundingClientRect();\r
+                                               \r
+                                               if( rect.left < left ){\r
+                                                       //console.log( '= ', text.data.charAt( j ), ' x:', x, ' y:', y, ' top:', top | 0, ' left:', left | 0, ' bottom:', btm | 0, ' right:', rect.right | 0 );\r
+                                                           if( left <= x && x <= rect.right && top <= y && y <= btm ){\r
+                                                               return {\r
+                                                                       'hitRange' : range, // TODO startContainer, endContainer\r
+                                                                       'rect'     : rect,\r
+                                                                       'offset'   : offset,\r
+                                                                       'text'     : text // TODO xtext じゃないの?\r
+                                                               };\r
+                                                           };\r
+                                                           continue;\r
+                                               };\r
+                                           } else {\r
+                                                       range.setEnd( text, j + 1 );\r
+                                                   range.setStart( text, j );\r
+                                                   rect = range.getBoundingClientRect();\r
+                                           };\r
+                                           \r
+                                               //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
+                                           if( rect.left <= x && x <= rect.right && rect.top <= y && y <= rect.bottom ){\r
+                                               return {\r
+                                                       'hitRange' : range, // TODO startContainer, endContainer\r
+                                                       'rect'     : rect,\r
+                                                       'offset'   : offset,\r
+                                                       'text'     : text // TODO xtext じゃないの?\r
+                                               };\r
+                                           };\r
+                                       };\r
+                                       offset += l;\r
                                        };\r
+                                       range = null;\r
                                } else {\r
-                                       // !save && ( text = text.split( '\r\n' ).join( '\n' ) ); textarea用\r
-                                       if( isPoint ){\r
-                                               // ie11 の ie10モード で moveToPoint がないといわれる. よって isW3C:false で動作するのは ie9 以下\r
-                                               range.moveToPoint( tr.v1, tr.v2 );\r
-                                               if( !range.expand( 'character' ) ) range = null;\r
+                                       // ie11 の ie10モード で moveToPoint がないといわれる. よって isW3C:false で動作するのは ie9 以下\r
+                                       // 行の最後の文字の端をクリックすると次の行の文字が選択されてしまう ie8, ie7\r
+                                       // 選択を移動して補正する https://msdn.microsoft.com/ja-jp/library/ms535872(v=vs.85).aspx\r
+                                       range.moveToPoint( x = tr[ 'v1' ], y = tr[ 'v2' ] );\r
+                                       \r
+                                       // if( range.parentElement() !== elm  || elm.contains( range.parentElement() ) ){\r
+                                       \r
+                                       if( range.expand( 'character' ) ){\r
+                                               left = range.boundingLeft;\r
+                                               top  = range.boundingTop;\r
+                                               if( x < left || left + range.boundingWidth  < x || y < top || top  + range.boundingHeight < y ){\r
+                                                       range.moveStart( 'character', -1 );\r
+                                                       range.moveEnd( 'character', -1 );\r
+                                                       left = range.boundingLeft;\r
+                                                       top  = range.boundingTop;\r
+                                                       if( x < left || left + range.boundingWidth  < x || y < top || top  + range.boundingHeight < y ){\r
+                                                               range = null;\r
+                                                       };\r
+                                               };\r
                                        } else {\r
-                                               range.moveToElementText( elm );\r
-                                               //range.collapse( true );                                               \r
-                                               range.moveEnd( 'character', l < tr.v2 ? l : tr.v2 );\r
-                                               range.moveStart( 'character', tr.v1 );\r
+                                               range = null;\r
                                        };\r
                                };\r
                                return range;\r
+\r
+                       case 'char' :\r
+                               if( X_TextRange_isW3C ){\r
+                                       // 未チェック!\r
+                                       range.setEnd( elm, l < tr[ 'v2' ] ? l : tr[ 'v2' ] );\r
+                           range.setStart( elm, tr[ 'v1' ] );\r
+                           return { 'hitRange' : range };\r
+                               } else {\r
+                                       range.moveToElementText( elm );\r
+                                       //range.collapse( true );                                               \r
+                                       range.moveEnd( 'character', l < tr[ 'v2' ] ? l : tr[ 'v2' ] );\r
+                                       range.moveStart( 'character', tr[ 'v1' ] );\r
+                               };\r
+                               return range;\r
                };\r
        };\r
 };\r
@@ -179,14 +245,16 @@ function X_TextRange_getRect(){
        \r
        if( result ){\r
                if( X_TextRange_isW3C ){\r
-                       rect = result.hitRange.getBoundingClientRect();\r
-                       ret = {\r
-                               'x'      : rect.left,\r
-                               'y'      : rect.top,\r
-                               'width'  : rect.width,\r
-                               'height' : rect.height\r
+                       if( result.hitRange ){\r
+                               rect = result.hitRange.getBoundingClientRect();\r
+                               ret = {\r
+                                       'x'      : rect.left,\r
+                                       'y'      : rect.top,\r
+                                       'width'  : rect.width,\r
+                                       'height' : rect.height\r
+                               };\r
+                               //range.detach && range.detach();\r
                        };\r
-                       //range.detach && range.detach();                       \r
                } else {\r
                        ret = {\r
                                'x'      : result.boundingLeft,\r
@@ -216,46 +284,69 @@ function X_TextRange_collectXTexts( xnode, ary ){
 };\r
 \r
 function X_TextRange_getOffset(){\r
-       var result = X_TextRange_getRawRange( this ),\r
-               range, ret, all, from, xtexts, n, i, l, xtext;\r
+       var xnode = this.xnode,\r
+               elm, result, range,\r
+               ret, from, xtexts, n, i, l, xtext;\r
        \r
-       if( result ){\r
+       if( xnode[ '_tag' ] === 'TEXTAREA' ){\r
+               elm = xnode[ '_rawObject' ];\r
+               \r
+               if( elm && xnode[ '_flags' ] & X_NodeFlags_IN_TREE ){\r
+                       if( X_UA[ 'IE' ] < 9 ){\r
+                               \r
+\r
+                               return cursorPosition.call( this, elm );\r
+\r
+                       } else if( elm.setSelectionRange ){\r
+                               if( X_UA[ 'IE' ] < 12 ){\r
+                                       l = elm.value.length;\r
+                                       ret = {\r
+                                               'from' : this[ 'v1' ] = elm.selectionStart < l ? elm.selectionStart : l,\r
+                                               'to'   : this[ 'v2' ] = elm.selectionEnd   < l ? elm.selectionEnd   : l\r
+                                       };\r
+                               } else {\r
+                                       ret = {\r
+                                               'from' : this[ 'v1' ] = elm.selectionStart,\r
+                                               'to'   : this[ 'v2' ] = elm.selectionEnd\r
+                                       };      \r
+                               };\r
+                       };\r
+               };\r
+       } else\r
+       if( result = X_TextRange_getRawRange( this ) ){\r
                if( X_TextRange_isW3C ){\r
                        range = result.hitRange;\r
                        ret = {\r
                                'offset' : result.offset,\r
-                               'from'   : range.startOffset,\r
-                               'to'     : range.endOffset,\r
+                               'from'   : this[ 'v1' ] = range.startOffset,\r
+                               'to'     : this[ 'v2' ] = range.endOffset,\r
                                'text'   : X_Node_getXNode( result.text )\r
                        };\r
                        // range.detach && range.detach();              \r
                } else {\r
                        // http://www.studio-freesky.net/programming/javascript/3/\r
-                       range = X_TextRange_range2;\r
-                       //var _rang = X_elmBody.createTextRange();\r
                        \r
+                       range = X_TextRange_range.duplicate();\r
+                       range.moveToElementText( xnode[ '_rawObject' ] );\r
+                       range.setEndPoint( 'EndToStart', result );\r
+                       //range.text && range.moveEnd( 'character', -1 );\r
+                       from  = range.text.length;\r
                        \r
-                       //_rang.moveToElementText( this.xnode.getChildAt(1)[ '_rawObject' ] );\r
-                       range.moveToElementText( this.xnode[ '_rawObject' ] );\r
-                       range.setEndPoint( 'EndToStart', result ); //_rang )//\r
-                       from  = range.text.length;// - result.text.length;\r
-\r
-                       X_TextRange_collectXTexts( this.xnode, xtexts = [] );\r
+                       X_TextRange_collectXTexts( xnode, xtexts = [] );\r
                        \r
                        if( xtexts.length ){\r
-                               // 改行が入ると正しく startIndex を取ることができない...\r
-                               for( n = l = 0, i = -1; xtext = xtexts[ ++i ]; ){\r
-                                       l = xtext[ '_text' ].length;\r
+                               for( n = 0, i = -1; xtext = xtexts[ ++i ]; ){\r
+                                       l = xtext[ '_rawObject' ].data.length;\r
                                        if( from < n + l ){\r
                                                break;\r
                                        };\r
                                        n += l;\r
                                };\r
-                               \r
+\r
                                ret = {\r
-                                       'offset' : n,\r
-                                       'from'   : from - n,\r
-                                       'to'     : from - n + result.text.length,\r
+                                       'offset' : n, // elm の何個目の node か?\r
+                                       'from'   : this[ 'v1' ] = from - n,\r
+                                       'to'     : this[ 'v2' ] = from - n + result.text.length,\r
                                        'text'   : xtext\r
                                };                              \r
                        };\r
@@ -266,10 +357,218 @@ function X_TextRange_getOffset(){
 };\r
 \r
 function X_TextRange_text( v ){\r
+       var xnode = this.xnode, elm, val, offset, from, to;\r
+       \r
        if( v === undefined ){\r
                \r
        } else {\r
+               if( xnode[ '_tag' ] === 'TEXTAREA' ){\r
+                       elm = xnode[ '_rawObject' ];\r
+                       val = X_UA[ 'IE' ] < 9 ? X_Node_Attr_getValueForIE( elm ) : elm.value;\r
+                       \r
+                       if( this[ 'by' ] === 'char' ){\r
+                               xnode.attr( {\r
+                                       'value' : val.substr( 0, this[ 'v1' ] ) + v + val.substr( this[ 'v2' ] )\r
+                               } );\r
+                       } else {\r
+                               offset = this[ 'getOffset' ]();\r
+                               \r
+                               from   = offset[ 'from' ];\r
+                               to     = offset[ 'to' ];\r
+\r
+                               if( X_UA[ 'IE' ] < 9 ){\r
+                                       range = X_TextRange_selection();\r
+                                       // TODO check textarea\r
+                                       range.text = v;\r
+                                       // ここには range.text がいない https://msdn.microsoft.com/ja-jp/library/cc427934.aspx\r
+                               } else {\r
+                                       val = val.substr( 0, from ) + v + val.substr( to );\r
+                                       elm.value = val;\r
+                               };\r
+                                       \r
+                               if( to !== from ){\r
+                                       // カーソル位置を挿入した文字列の最後へ\r
+                                       to = from + v.length;\r
+                               } else {\r
+                                       // カーソル位置を挿入した文字列の後ろへ\r
+                                       to += v.length;\r
+                               };\r
+                               this.move( to, to );\r
+                       };\r
+               };\r
+       };\r
+};\r
+\r
+function X_TextRange_move( from, to ){\r
+       var xnode  = this.xnode,\r
+               elm    = xnode[ '_rawObject' ],\r
+               len, range;\r
+\r
+       if( 0 <= from ){\r
+               this[ 'v1' ] = from;\r
+       } else {\r
+               this[ 'v1' ] = this[ 'v1' ] + from;\r
+               this[ 'v1' ] < 0 && ( this[ 'v1' ] = 0 );\r
+       };\r
+\r
+       if( X_Type_isNumber( to ) ){\r
+               if( 0 <= to ){\r
+                       this[ 'v2' ] = to;\r
+               } else {\r
+                       this[ 'v2' ] = this[ 'v2' ] + to;\r
+                       this[ 'v2' ] < this[ 'v1' ] && ( this[ 'v2' ] = this[ 'v1' ] );\r
+               };\r
+       };\r
+       \r
+       if( xnode[ '_tag' ] === 'TEXTAREA' ){\r
+               // http://blog.enjoyxstudy.com/entry/20060305/p1\r
                \r
+               if( X_UA[ 'IE' ] < 9 || X_UA[ 'Opera' ] ){\r
+                       len = ( X_UA[ 'IE' ] < 9 ? X_Node_Attr_getValueForIE( elm ) : elm.value ).length;\r
+                       \r
+                       if( X_UA[ 'Opera' ] ){\r
+                               FocusUtility_setTemporarilyFocus( elm );\r
+                       };\r
+\r
+                       range = elm.createTextRange();\r
+\r
+                       if( this[ 'v1' ] === this[ 'v2' ] && this[ 'v1' ] === 0 ){\r
+                               range.collapse( true ); // 先頭に移動\r
+                       } else {\r
+                               if( this[ 'v1' ] !== this[ 'v2' ] || this[ 'v1' ] < len ){\r
+                                       range.collapse(); // おまじない?\r
+\r
+                                       if( this[ 'v1' ] === this[ 'v2' ] ){\r
+                                               range.move( 'character', this[ 'v1' ] );\r
+                                       } else {\r
+                                               range.moveEnd( 'character', this[ 'v2' ] );\r
+                                               range.moveStart( 'character', this[ 'v1' ] );\r
+                                       };\r
+                               } else {\r
+                                       range.collapse( false ); // 末美に移動\r
+                               };\r
+                       };\r
+                       range.select();\r
+\r
+               } else if( elm.setSelectionRange ){\r
+                       elm.setSelectionRange( this[ 'v1' ], this[ 'v2' ] );\r
+               };\r
        };\r
 };\r
 \r
+function X_TextRange_select( v ){\r
+       \r
+};\r
+\r
+// http://www.studio-freesky.net/programming/javascript/3/\r
+// それは、IEのTextRangeオブジェクトで取得した範囲にもしラストに改行コード¥r¥nがあった場合それが含まれないのです。(視覚的な選択範囲には含まれています)\r
+\r
+// https://web.archive.org/web/20090904134938/http://www.dedestruct.com/2008/03/22/howto-cross-browser-cursor-position-in-textareas/\r
+                                       // https://web.archive.org/web/20090904183807/http://www.dedestruct.com/cursorPosition.html\r
+                                       function cursorPosition( textarea ){\r
+\r
+                                               var selection_range = X_TextRange_selection().duplicate();\r
+                                               \r
+\r
+                                               if (selection_range.parentElement() !== textarea) {\r
+                                                       // TODO 正しくはカーソル位置・選択範囲の復帰\r
+                                                       \r
+                                                       FocusUtility_setTemporarilyFocus( textarea );\r
+                                                               \r
+                                                       // BODY要素のテキスト範囲を作成する\r
+                                                       selection_range = X_elmBody.createTextRange();\r
+                                               \r
+                                                       // BODY要素のテキスト範囲をeのテキスト範囲に移動する\r
+                                                       // これはe.createTextRange()とほぼ同等\r
+                                                       selection_range.moveToElementText( textarea );\r
+                                                       \r
+                                                       selection_range.collapse( true ); // 末美に移動\r
+                                                       selection_range.select();\r
+                                               };\r
+\r
+                                               //if (selection_range.parentElement() == textarea) {// Check that the selection is actually in our textarea\r
+                                                       // Create three ranges, one containing all the text before the selection,\r
+                                                       // one containing all the text in the selection (this already exists), and one containing all\r
+                                                       // the text after the selection.\r
+                                                       var before_range = X_elmBody.createTextRange();\r
+                                                       before_range.moveToElementText(textarea);\r
+                                                       // Selects all the text\r
+                                                       before_range.setEndPoint('EndToStart', selection_range);\r
+                                                       // Moves the end where we need it\r
+                                               \r
+                                                       var after_range = X_elmBody.createTextRange();\r
+                                                       after_range.moveToElementText(textarea);\r
+                                                       // Selects all the text\r
+                                                       after_range.setEndPoint('StartToEnd', selection_range);\r
+                                                       // Moves the start where we need it\r
+                                               \r
+                                                       var before_finished = false, selection_finished = false, after_finished = false;\r
+                                                       var before_text, untrimmed_before_text, selection_text, untrimmed_selection_text, after_text, untrimmed_after_text;\r
+                                               \r
+                                                       // Load the text values we need to compare\r
+                                                       before_text = untrimmed_before_text = before_range.text;\r
+                                                       selection_text = untrimmed_selection_text = selection_range.text;\r
+                                                       after_text = untrimmed_after_text = after_range.text;\r
+                                               \r
+                                                       // Check each range for trimmed newlines by shrinking the range by 1 character and seeing\r
+                                                       // if the text property has changed.  If it has not changed then we know that IE has trimmed\r
+                                                       // a \r\n from the end.\r
+                                                       do {\r
+                                                               if (!before_finished) {\r
+                                                                       if (before_range.compareEndPoints('StartToEnd', before_range) == 0) {\r
+                                                                               before_finished = true;\r
+                                                                       } else {\r
+                                                                               before_range.moveEnd('character', -1);\r
+                                                                               if (before_range.text == before_text) {\r
+                                                                                       untrimmed_before_text += '\r\n';\r
+                                                                               } else {\r
+                                                                                       before_finished = true;\r
+                                                                               }\r
+                                                                       }\r
+                                                               }\r
+                                                               if (!selection_finished) {\r
+                                                                       if (selection_range.compareEndPoints('StartToEnd', selection_range) == 0) {\r
+                                                                               selection_finished = true;\r
+                                                                       } else {\r
+                                                                               selection_range.moveEnd('character', -1);\r
+                                                                               if (selection_range.text == selection_text) {\r
+                                                                                       untrimmed_selection_text += '\r\n';\r
+                                                                               } else {\r
+                                                                                       selection_finished = true;\r
+                                                                               }\r
+                                                                       }\r
+                                                               }\r
+                                                               if (!after_finished) {\r
+                                                                       if (after_range.compareEndPoints('StartToEnd', after_range) == 0) {\r
+                                                                               after_finished = true;\r
+                                                                       } else {\r
+                                                                               after_range.moveEnd('character', -1);\r
+                                                                               if (after_range.text == after_text) {\r
+                                                                                       untrimmed_after_text += '\r\n';\r
+                                                                               } else {\r
+                                                                                       after_finished = true;\r
+                                                                               }\r
+                                                                       }\r
+                                                               }\r
+                                               \r
+                                                       } while ((!before_finished || !selection_finished || !after_finished));\r
+                                               \r
+                                                       // Untrimmed success test to make sure our results match what is actually in the textarea\r
+                                                       // This can be removed once you're confident it's working correctly\r
+                                                       /*\r
+                                                       var untrimmed_text = untrimmed_before_text + untrimmed_selection_text + untrimmed_after_text;\r
+                                                       var untrimmed_successful = false;\r
+                                                       if (textarea.value == untrimmed_text) {\r
+                                                               untrimmed_successful = true;\r
+                                                       } */\r
+                                                       // ** END Untrimmed success test\r
+                                               \r
+                                                       var startPoint = untrimmed_before_text.split( '\r' ).join( '' ).length;\r
+                                                       // alert(startPoint);\r
+                                                       return {\r
+                                                               'from'   : this[ 'v1' ] = startPoint,\r
+                                                               'to'     : this[ 'v2' ] = startPoint + untrimmed_selection_text.split( '\r' ).join( '' ).length\r
+                                                       };\r
+                                               //}\r
+                                       }\r
+                                       \r