OSDN Git Service

Version 0.6.222, Fix the bug of X.TextRange.
[pettanr/clientJs.git] / 0.6.x / js / 02_dom / 30_XTextRange.js
index a2fc552..b0712e5 100644 (file)
@@ -18,8 +18,8 @@
  */\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
@@ -34,25 +34,26 @@ var X_TextRange = X_Class_create(
        \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
@@ -60,8 +61,8 @@ 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
@@ -98,13 +99,15 @@ function X_TextRange_collectTextNodes( elm, ary ){
        };\r
 };\r
 \r
-function X_TextRange_getRawRange( tr, createFrom ){\r
+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
@@ -112,24 +115,24 @@ function X_TextRange_getRawRange( tr, createFrom ){
                \r
                elm = xnode[ '_rawObject' ];\r
                \r
-               switch( createFrom || tr.createFrom ){\r
+               switch( tr[ 'by' ] ){\r
                        case 'selection' :\r
                                if( X_TextRange_isW3C ){\r
-                                       selection = window.getSelection();\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 = 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
@@ -138,57 +141,100 @@ function X_TextRange_getRawRange( tr, createFrom ){
                                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.duplicate().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
@@ -255,13 +301,13 @@ function X_TextRange_getOffset(){
                                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
+                                               '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
+                                               'from' : this[ 'v1' ] = elm.selectionStart,\r
+                                               'to'   : this[ 'v2' ] = elm.selectionEnd\r
                                        };      \r
                                };\r
                        };\r
@@ -272,17 +318,18 @@ function X_TextRange_getOffset(){
                        range = result.hitRange;\r
                        ret = {\r
                                'offset' : result.offset,\r
-                               'from'   : this.v1 = range.startOffset,\r
-                               'to'     : this.v2 = 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
                        \r
-                       range = X_TextRange_range2;\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
                        X_TextRange_collectXTexts( xnode, xtexts = [] );\r
@@ -298,8 +345,8 @@ function X_TextRange_getOffset(){
 \r
                                ret = {\r
                                        'offset' : n, // elm の何個目の node か?\r
-                                       'from'   : this.v1 = from - n,\r
-                                       'to'     : this.v2 = from - n + result.text.length,\r
+                                       'from'   : this[ 'v1' ] = from - n,\r
+                                       'to'     : this[ 'v2' ] = from - n + result.text.length,\r
                                        'text'   : xtext\r
                                };                              \r
                        };\r
@@ -319,9 +366,9 @@ function X_TextRange_text( v ){
                        elm = xnode[ '_rawObject' ];\r
                        val = X_UA[ 'IE' ] < 9 ? X_Node_Attr_getValueForIE( elm ) : elm.value;\r
                        \r
-                       if( this.createFrom === 'char' ){\r
+                       if( this[ 'by' ] === 'char' ){\r
                                xnode.attr( {\r
-                                       'value' : val.substr( 0, this.v1 ) + v + val.substr( this.v2 )\r
+                                       'value' : val.substr( 0, this[ 'v1' ] ) + v + val.substr( this[ 'v2' ] )\r
                                } );\r
                        } else {\r
                                offset = this[ 'getOffset' ]();\r
@@ -330,7 +377,7 @@ function X_TextRange_text( v ){
                                to     = offset[ 'to' ];\r
 \r
                                if( X_UA[ 'IE' ] < 9 ){\r
-                                       range = document.selection.createRange();\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
@@ -358,18 +405,18 @@ function X_TextRange_move( from, to ){
                len, range;\r
 \r
        if( 0 <= from ){\r
-               this.v1 = from;\r
+               this[ 'v1' ] = from;\r
        } else {\r
-               this.v1 = this.v1 + from;\r
-               this.v1 < 0 && ( this.v1 = 0 );\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
+                       this[ 'v2' ] = to;\r
                } else {\r
-                       this.v2 = this.v2 + to;\r
-                       this.v2 < this.v1 && ( this.v2 = this.v1 );\r
+                       this[ 'v2' ] = this[ 'v2' ] + to;\r
+                       this[ 'v2' ] < this[ 'v1' ] && ( this[ 'v2' ] = this[ 'v1' ] );\r
                };\r
        };\r
        \r
@@ -380,24 +427,22 @@ function X_TextRange_move( from, to ){
                        len = ( X_UA[ 'IE' ] < 9 ? X_Node_Attr_getValueForIE( elm ) : elm.value ).length;\r
                        \r
                        if( X_UA[ 'Opera' ] ){\r
-                               X_EventDispatcher_ignoreActualEvent = 'focus';\r
-                               elm.focus(); // Operaの為(IEでは無くても大丈夫)\r
-                               X_EventDispatcher_ignoreActualEvent = '';\r
+                               FocusUtility_setTemporarilyFocus( elm );\r
                        };\r
 \r
                        range = elm.createTextRange();\r
 \r
-                       if( this.v1 === this.v2 && this.v1 === 0 ){\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
+                               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
+                                       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
+                                               range.moveEnd( 'character', this[ 'v2' ] );\r
+                                               range.moveStart( 'character', this[ 'v1' ] );\r
                                        };\r
                                } else {\r
                                        range.collapse( false ); // 末美に移動\r
@@ -406,7 +451,7 @@ function X_TextRange_move( from, to ){
                        range.select();\r
 \r
                } else if( elm.setSelectionRange ){\r
-                       elm.setSelectionRange( this.v1, this.v2 );\r
+                       elm.setSelectionRange( this[ 'v1' ], this[ 'v2' ] );\r
                };\r
        };\r
 };\r
@@ -422,15 +467,13 @@ function X_TextRange_select( v ){
                                        // https://web.archive.org/web/20090904183807/http://www.dedestruct.com/cursorPosition.html\r
                                        function cursorPosition( textarea ){\r
 \r
-                                               var selection_range = document.selection.createRange().duplicate();\r
+                                               var selection_range = X_TextRange_selection().duplicate();\r
                                                \r
 \r
                                                if (selection_range.parentElement() !== textarea) {\r
                                                        // TODO 正しくはカーソル位置・選択範囲の復帰\r
                                                        \r
-                                                       X_EventDispatcher_ignoreActualEvent = 'focus';\r
-                                                       textarea.focus();\r
-                                                       X_EventDispatcher_ignoreActualEvent = '';\r
+                                                       FocusUtility_setTemporarilyFocus( textarea );\r
                                                                \r
                                                        // BODY要素のテキスト範囲を作成する\r
                                                        selection_range = X_elmBody.createTextRange();\r
@@ -523,8 +566,8 @@ function X_TextRange_select( v ){
                                                        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
+                                                               'from'   : this[ 'v1' ] = startPoint,\r
+                                                               'to'     : this[ 'v2' ] = startPoint + untrimmed_selection_text.split( '\r' ).join( '' ).length\r
                                                        };\r
                                                //}\r
                                        }\r