*/\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
\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
};\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
\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
\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
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
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
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
\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
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
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
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
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
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
// 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
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