X-Git-Url: http://git.osdn.jp/view?p=pettanr%2FclientJs.git;a=blobdiff_plain;f=0.6.x%2Fjs%2F02_dom%2F30_XTextRange.js;fp=0.6.x%2Fjs%2F02_dom%2F30_XTextRange.js;h=b0712e58446e34fd9e4c0ffa375ceef80395f705;hp=a2fc5520798be1256b7b8937897eadae1193465d;hb=604668ba9efa027d4bc77fd8020d6b6be55d03e1;hpb=ef25747bebf1799d49f9bd0d64e339da9ea61d13 diff --git a/0.6.x/js/02_dom/30_XTextRange.js b/0.6.x/js/02_dom/30_XTextRange.js index a2fc552..b0712e5 100644 --- a/0.6.x/js/02_dom/30_XTextRange.js +++ b/0.6.x/js/02_dom/30_XTextRange.js @@ -18,8 +18,8 @@ */ var X_TextRange_range, - X_TextRange_range2, - X_TextRange_isW3C = !document.selection || 10 <= X_UA[ 'IE' ]; + X_TextRange_selection, + X_TextRange_isW3C = !document.selection || 9 <= X_UA[ 'IE' ] || X_UA[ 'Edge' ]; /** * ユーザーによって選択されたテキストへの参照や文字の座標の取得 @@ -34,25 +34,26 @@ var X_TextRange = X_Class_create( /** @lends X.TextRange.prototype */ { - xnode : null, - createFrom : '', - v1 : 0, - v2 : 0, + 'xnode' : null, + 'by' : '', + 'v1' : 0, + 'v2' : 0, 'Constructor' : function( xnode, arg2, arg3, arg4 ){ if( !X_TextRange_range ){ X_TextRange_range = X_TextRange_isW3C ? document.createRange() : X_elmBody.createTextRange(); - if( !X_TextRange_isW3C ) X_TextRange_range2 = X_elmBody.createTextRange(); }; this.xnode = xnode; switch( arg2 ){ case 'selection' : - //break; + if( !X_TextRange_selection ){ + X_TextRange_selection = X_TextRange_isW3C ? window.getSelection() : document.selection.createRange; + }; case 'point' : case 'char' : - this.createFrom = arg2; + this[ 'by' ] = arg2; break; default : arg4 = arg3; @@ -60,8 +61,8 @@ var X_TextRange = X_Class_create( }; if( arg2 !== 'selection' ){ - this.v1 = arg3 || 0; - this.v2 = arg4 || 0; + this[ 'v1' ] = arg3 || 0; + this[ 'v2' ] = arg4 || 0; } else { this[ 'getOffset' ](); }; @@ -98,13 +99,15 @@ function X_TextRange_collectTextNodes( elm, ary ){ }; }; -function X_TextRange_getRawRange( tr, createFrom ){ +function X_TextRange_getRawRange( tr ){ var xnode = tr.xnode, // - range = 10 <= X_UA[ 'IE' ] /* || X_UA[ 'iOS' ] */ ? document.createRange() : - 8 <= X_UA[ 'IE' ] ? X_elmBody.createTextRange() : X_TextRange_range, - elm, selection, isPoint, - texts, i, offset, j, l, x, y, rect; + range = //10 <= X_UA[ 'IE' ] /* || X_UA[ 'iOS' ] */ ? document.createRange() : + //8 <= X_UA[ 'IE' ] ? X_elmBody.createTextRange() : + X_TextRange_range, + selection = X_TextRange_selection, + elm, isPoint, + texts, i, offset, j, l, x, y, rect, top, btm, left; if( xnode[ '_flags' ] & X_NodeFlags_IN_TREE ){ @@ -112,24 +115,24 @@ function X_TextRange_getRawRange( tr, createFrom ){ elm = xnode[ '_rawObject' ]; - switch( createFrom || tr.createFrom ){ + switch( tr[ 'by' ] ){ case 'selection' : if( X_TextRange_isW3C ){ - selection = window.getSelection(); + //selection = window.getSelection(); if( selection.getRangeAt ){ return selection.rangeCount && selection.getRangeAt( 0 ); }; // http://d.hatena.ne.jp/dayflower/20080423/1208941641 // for Safari 1.3 - range = document.createRange(); + //range = document.createRange(); range.setStart( selection.anchorNode, selection.anchorOffset ); range.setEnd( selection.focusNode, selection.focusOffset ); return range; } else { switch( document.selection.type ){ case 'text' : - return document.selection.createRange(); + return selection(); case 'Control' : // TODO case 'none' : @@ -138,57 +141,100 @@ function X_TextRange_getRawRange( tr, createFrom ){ break; case 'point' : - isPoint = true; - case 'char' : if( X_TextRange_isW3C ){ // textarea で異なる + // TextNode をフラットな配列に回収 + X_TextRange_collectTextNodes( elm, texts = [] ); + + x = tr[ 'v1' ]; + y = tr[ 'v2' ]; + + for( i = offset = 0; text = texts[ i ]; ++i ){ + range.selectNodeContents( text ); // selectNodeContents は TextNode のみ?? Firefox + l = text.data.length; - if( isPoint ){ - // TextNode をフラットな配列に回収 - X_TextRange_collectTextNodes( elm, texts = [] ); - - for( i = offset = 0; text = texts[ i ]; ++i ){ - range.selectNodeContents( text ); // selectNodeContents は TextNode のみ?? Firefox - l = text.data.length; - - for( j = 0, x = tr.v1, y = tr.v2; j < l; ++j ){ - if( range ){ - range.setStart( text, j ); - range.setEnd( text, j + 1 ); - rect = range.getBoundingClientRect(); - }; - if( rect.left <= x && x <= rect.right && rect.top <= y && y <= rect.bottom ){ - return { - 'hitRange' : range, - 'rect' : rect, - 'offset' : offset, - 'text' : text - }; - }; - }; - offset += l; - }; - range = null; - } else { - // 未チェック! - range.setEnd( elm, l < tr.v2 ? l : tr.v2 ); - range.setStart( elm, tr.v1 ); - return { 'hitRange' : range }; + for( j = 0; j < l; ++j ){ + if( X_UA[ 'IE' ] || X_UA[ 'Edge' ] ){ + // 改行の直前の文字を選択すると rect が巨大になってしまう + range.setEnd( text, j ); + range.setStart( text, j ); + rect = range.getBoundingClientRect(); + top = rect.top; + btm = rect.bottom; + left = rect.left; + range.setEnd( text, j + 1 ); + rect = range.getBoundingClientRect(); + + if( rect.left < left ){ + //console.log( '= ', text.data.charAt( j ), ' x:', x, ' y:', y, ' top:', top | 0, ' left:', left | 0, ' bottom:', btm | 0, ' right:', rect.right | 0 ); + if( left <= x && x <= rect.right && top <= y && y <= btm ){ + return { + 'hitRange' : range, // TODO startContainer, endContainer + 'rect' : rect, + 'offset' : offset, + 'text' : text // TODO xtext じゃないの? + }; + }; + continue; + }; + } else { + range.setEnd( text, j + 1 ); + range.setStart( text, j ); + rect = range.getBoundingClientRect(); + }; + + //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 ); + if( rect.left <= x && x <= rect.right && rect.top <= y && y <= rect.bottom ){ + return { + 'hitRange' : range, // TODO startContainer, endContainer + 'rect' : rect, + 'offset' : offset, + 'text' : text // TODO xtext じゃないの? + }; + }; + }; + offset += l; }; + range = null; } else { - // !save && ( text = text.split( '\r\n' ).join( '\n' ) ); textarea用 - if( isPoint ){ - // ie11 の ie10モード で moveToPoint がないといわれる. よって isW3C:false で動作するのは ie9 以下 - range.moveToPoint( tr.v1, tr.v2 ); - if( !range.duplicate().expand( 'character' ) ) range = null; + // ie11 の ie10モード で moveToPoint がないといわれる. よって isW3C:false で動作するのは ie9 以下 + // 行の最後の文字の端をクリックすると次の行の文字が選択されてしまう ie8, ie7 + // 選択を移動して補正する https://msdn.microsoft.com/ja-jp/library/ms535872(v=vs.85).aspx + range.moveToPoint( x = tr[ 'v1' ], y = tr[ 'v2' ] ); + + // if( range.parentElement() !== elm  || elm.contains( range.parentElement() ) ){ + + if( range.expand( 'character' ) ){ + left = range.boundingLeft; + top = range.boundingTop; + if( x < left || left + range.boundingWidth < x || y < top || top + range.boundingHeight < y ){ + range.moveStart( 'character', -1 ); + range.moveEnd( 'character', -1 ); + left = range.boundingLeft; + top = range.boundingTop; + if( x < left || left + range.boundingWidth < x || y < top || top + range.boundingHeight < y ){ + range = null; + }; + }; } else { - range.moveToElementText( elm ); - //range.collapse( true ); - range.moveEnd( 'character', l < tr.v2 ? l : tr.v2 ); - range.moveStart( 'character', tr.v1 ); + range = null; }; }; return range; + + case 'char' : + if( X_TextRange_isW3C ){ + // 未チェック! + range.setEnd( elm, l < tr[ 'v2' ] ? l : tr[ 'v2' ] ); + range.setStart( elm, tr[ 'v1' ] ); + return { 'hitRange' : range }; + } else { + range.moveToElementText( elm ); + //range.collapse( true ); + range.moveEnd( 'character', l < tr[ 'v2' ] ? l : tr[ 'v2' ] ); + range.moveStart( 'character', tr[ 'v1' ] ); + }; + return range; }; }; }; @@ -255,13 +301,13 @@ function X_TextRange_getOffset(){ if( X_UA[ 'IE' ] < 12 ){ l = elm.value.length; ret = { - 'from' : this.v1 = elm.selectionStart < l ? elm.selectionStart : l, - 'to' : this.v2 = elm.selectionEnd < l ? elm.selectionEnd : l + 'from' : this[ 'v1' ] = elm.selectionStart < l ? elm.selectionStart : l, + 'to' : this[ 'v2' ] = elm.selectionEnd < l ? elm.selectionEnd : l }; } else { ret = { - 'from' : this.v1 = elm.selectionStart, - 'to' : this.v2 = elm.selectionEnd + 'from' : this[ 'v1' ] = elm.selectionStart, + 'to' : this[ 'v2' ] = elm.selectionEnd }; }; }; @@ -272,17 +318,18 @@ function X_TextRange_getOffset(){ range = result.hitRange; ret = { 'offset' : result.offset, - 'from' : this.v1 = range.startOffset, - 'to' : this.v2 = range.endOffset, + 'from' : this[ 'v1' ] = range.startOffset, + 'to' : this[ 'v2' ] = range.endOffset, 'text' : X_Node_getXNode( result.text ) }; // range.detach && range.detach(); } else { // http://www.studio-freesky.net/programming/javascript/3/ - range = X_TextRange_range2; + range = X_TextRange_range.duplicate(); range.moveToElementText( xnode[ '_rawObject' ] ); range.setEndPoint( 'EndToStart', result ); + //range.text && range.moveEnd( 'character', -1 ); from = range.text.length; X_TextRange_collectXTexts( xnode, xtexts = [] ); @@ -298,8 +345,8 @@ function X_TextRange_getOffset(){ ret = { 'offset' : n, // elm の何個目の node か? - 'from' : this.v1 = from - n, - 'to' : this.v2 = from - n + result.text.length, + 'from' : this[ 'v1' ] = from - n, + 'to' : this[ 'v2' ] = from - n + result.text.length, 'text' : xtext }; }; @@ -319,9 +366,9 @@ function X_TextRange_text( v ){ elm = xnode[ '_rawObject' ]; val = X_UA[ 'IE' ] < 9 ? X_Node_Attr_getValueForIE( elm ) : elm.value; - if( this.createFrom === 'char' ){ + if( this[ 'by' ] === 'char' ){ xnode.attr( { - 'value' : val.substr( 0, this.v1 ) + v + val.substr( this.v2 ) + 'value' : val.substr( 0, this[ 'v1' ] ) + v + val.substr( this[ 'v2' ] ) } ); } else { offset = this[ 'getOffset' ](); @@ -330,7 +377,7 @@ function X_TextRange_text( v ){ to = offset[ 'to' ]; if( X_UA[ 'IE' ] < 9 ){ - range = document.selection.createRange(); + range = X_TextRange_selection(); // TODO check textarea range.text = v; // ここには range.text がいない https://msdn.microsoft.com/ja-jp/library/cc427934.aspx @@ -358,18 +405,18 @@ function X_TextRange_move( from, to ){ len, range; if( 0 <= from ){ - this.v1 = from; + this[ 'v1' ] = from; } else { - this.v1 = this.v1 + from; - this.v1 < 0 && ( this.v1 = 0 ); + this[ 'v1' ] = this[ 'v1' ] + from; + this[ 'v1' ] < 0 && ( this[ 'v1' ] = 0 ); }; if( X_Type_isNumber( to ) ){ if( 0 <= to ){ - this.v2 = to; + this[ 'v2' ] = to; } else { - this.v2 = this.v2 + to; - this.v2 < this.v1 && ( this.v2 = this.v1 ); + this[ 'v2' ] = this[ 'v2' ] + to; + this[ 'v2' ] < this[ 'v1' ] && ( this[ 'v2' ] = this[ 'v1' ] ); }; }; @@ -380,24 +427,22 @@ function X_TextRange_move( from, to ){ len = ( X_UA[ 'IE' ] < 9 ? X_Node_Attr_getValueForIE( elm ) : elm.value ).length; if( X_UA[ 'Opera' ] ){ - X_EventDispatcher_ignoreActualEvent = 'focus'; - elm.focus(); // Operaの為(IEでは無くても大丈夫) - X_EventDispatcher_ignoreActualEvent = ''; + FocusUtility_setTemporarilyFocus( elm ); }; range = elm.createTextRange(); - if( this.v1 === this.v2 && this.v1 === 0 ){ + if( this[ 'v1' ] === this[ 'v2' ] && this[ 'v1' ] === 0 ){ range.collapse( true ); // 先頭に移動 } else { - if( this.v1 !== this.v2 || this.v1 < len ){ + if( this[ 'v1' ] !== this[ 'v2' ] || this[ 'v1' ] < len ){ range.collapse(); // おまじない? - if( this.v1 === this.v2 ){ - range.move( 'character', this.v1 ); + if( this[ 'v1' ] === this[ 'v2' ] ){ + range.move( 'character', this[ 'v1' ] ); } else { - range.moveEnd( 'character', this.v2 ); - range.moveStart( 'character', this.v1 ); + range.moveEnd( 'character', this[ 'v2' ] ); + range.moveStart( 'character', this[ 'v1' ] ); }; } else { range.collapse( false ); // 末美に移動 @@ -406,7 +451,7 @@ function X_TextRange_move( from, to ){ range.select(); } else if( elm.setSelectionRange ){ - elm.setSelectionRange( this.v1, this.v2 ); + elm.setSelectionRange( this[ 'v1' ], this[ 'v2' ] ); }; }; }; @@ -422,15 +467,13 @@ function X_TextRange_select( v ){ // https://web.archive.org/web/20090904183807/http://www.dedestruct.com/cursorPosition.html function cursorPosition( textarea ){ - var selection_range = document.selection.createRange().duplicate(); + var selection_range = X_TextRange_selection().duplicate(); if (selection_range.parentElement() !== textarea) { // TODO 正しくはカーソル位置・選択範囲の復帰 - X_EventDispatcher_ignoreActualEvent = 'focus'; - textarea.focus(); - X_EventDispatcher_ignoreActualEvent = ''; + FocusUtility_setTemporarilyFocus( textarea ); // BODY要素のテキスト範囲を作成する selection_range = X_elmBody.createTextRange(); @@ -523,8 +566,8 @@ function X_TextRange_select( v ){ var startPoint = untrimmed_before_text.split( '\r' ).join( '' ).length; // alert(startPoint); return { - 'from' : this.v1 = startPoint, - 'to' : this.v2 = startPoint + untrimmed_selection_text.split( '\r' ).join( '' ).length + 'from' : this[ 'v1' ] = startPoint, + 'to' : this[ 'v2' ] = startPoint + untrimmed_selection_text.split( '\r' ).join( '' ).length }; //} }