OSDN Git Service

Version 0.6.221, Update the X.TextRange.
[pettanr/clientJs.git] / 0.6.x / js / 02_dom / 30_XTextRange.js
index d6f8837..a2fc552 100644 (file)
@@ -2,13 +2,16 @@
  * 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
@@ -27,6 +30,8 @@ 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
@@ -57,9 +62,15 @@ var X_TextRange = X_Class_create(
                        if( arg2 !== 'selection' ){\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
@@ -87,7 +98,7 @@ function X_TextRange_collectTextNodes( elm, ary ){
        };\r
 };\r
 \r
-function X_TextRange_getRawRange( tr ){\r
+function X_TextRange_getRawRange( tr, createFrom ){\r
        var xnode = tr.xnode,\r
                                //\r
                range = 10 <= X_UA[ 'IE' ] /* || X_UA[ 'iOS' ] */ ? document.createRange() :\r
@@ -101,11 +112,20 @@ function X_TextRange_getRawRange( tr ){
                \r
                elm = xnode[ '_rawObject' ];\r
                \r
-               switch( tr.createFrom ){\r
+               switch( createFrom || tr.createFrom ){\r
                        case 'selection' :\r
                                if( X_TextRange_isW3C ){\r
                                        selection = window.getSelection();\r
-                                       return selection.rangeCount && selection.getRangeAt( 0 );\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
@@ -139,10 +159,10 @@ function X_TextRange_getRawRange( tr ){
                                                        };\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
+                                                               'hitRange' : range,\r
+                                                               'rect'     : rect,\r
+                                                               'offset'   : offset,\r
+                                                               'text'     : text\r
                                                        };\r
                                                    };\r
                                                };\r
@@ -153,14 +173,14 @@ function X_TextRange_getRawRange( tr ){
                                                // 未チェック!\r
                                                range.setEnd( elm, l < tr.v2 ? l : tr.v2 );\r
                                    range.setStart( elm, tr.v1 );\r
-                                   return { hitRange : range };\r
+                                   return { 'hitRange' : range };\r
                                        };\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
+                                               if( !range.duplicate().expand( 'character' ) ) range = null;\r
                                        } else {\r
                                                range.moveToElementText( elm );\r
                                                //range.collapse( true );                                               \r
@@ -179,14 +199,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 +238,68 @@ 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_range2;\r
+                       range.moveToElementText( xnode[ '_rawObject' ] );\r
+                       range.setEndPoint( 'EndToStart', result );\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 +310,222 @@ 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.createFrom === '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 = document.selection.createRange();\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
+                               X_EventDispatcher_ignoreActualEvent = 'focus';\r
+                               elm.focus(); // Operaの為(IEでは無くても大丈夫)\r
+                               X_EventDispatcher_ignoreActualEvent = '';\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 = document.selection.createRange().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
+                                                               \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