* $textbox.textSelection( 'encapsulateSelection', { pre: '<b>', post: '</b>' } );
* // Result: Textbox contains 'This is <b>bold</b>!', with cursor before the '!'
*/
-( function ( $ ) {
+( function () {
/**
* Do things to the selection in a `<textarea>`, or a textarea-like editable element.
*
* - {@link jQuery.plugin.textSelection#getContents getContents}
* - {@link jQuery.plugin.textSelection#setContents setContents}
* - {@link jQuery.plugin.textSelection#getSelection getSelection}
+ * - {@link jQuery.plugin.textSelection#replaceSelection replaceSelection}
* - {@link jQuery.plugin.textSelection#encapsulateSelection encapsulateSelection}
* - {@link jQuery.plugin.textSelection#getCaretPosition getCaretPosition}
* - {@link jQuery.plugin.textSelection#setSelection setSelection}
*
* @private
* @param {string} content
+ * @return {jQuery}
+ * @chainable
*/
setContents: function ( content ) {
- this.val( content );
+ return this.each( function () {
+ var scrollTop = this.scrollTop;
+ this.select();
+ if ( !document.execCommand( 'insertText', false, content ) ) {
+ $( this ).val( content );
+ }
+ // Setting this.value may scroll the textarea, restore the scroll position
+ this.scrollTop = scrollTop;
+ } );
},
/**
return retval;
},
+ /**
+ * Replace the selected text in the textarea with the given text, or insert it at the cursor.
+ *
+ * @private
+ * @param {string} value
+ * @return {jQuery}
+ * @chainable
+ */
+ replaceSelection: function ( value ) {
+ return this.each( function () {
+ var allText, currSelection, startPos, endPos;
+
+ if ( !document.execCommand( 'insertText', false, value ) ) {
+ allText = $( this ).textSelection( 'getContents' );
+ currSelection = $( this ).textSelection( 'getCaretPosition', { startAndEnd: true } );
+ startPos = currSelection[ 0 ];
+ endPos = currSelection[ 1 ];
+
+ $( this ).textSelection( 'setContents', allText.slice( 0, startPos ) + value +
+ allText.slice( endPos ) );
+ $( this ).textSelection( 'setSelection', {
+ start: startPos,
+ end: startPos + value.length
+ } );
+ }
+ } );
+ },
+
/**
* Insert text at the beginning and end of a text selection, optionally
* inserting text at the caret when selection is empty.
*/
encapsulateSelection: function ( options ) {
return this.each( function () {
- var selText, scrollTop, insertText,
+ var selText, allText, currSelection, insertText,
+ combiningCharSelectionBug = false,
isSample, startPos, endPos,
pre = options.pre,
post = options.post;
}
selText = $( this ).textSelection( 'getSelection' );
- startPos = this.selectionStart;
- endPos = this.selectionEnd;
- scrollTop = this.scrollTop;
+ allText = $( this ).textSelection( 'getContents' );
+ currSelection = $( this ).textSelection( 'getCaretPosition', { startAndEnd: true } );
+ startPos = currSelection[ 0 ];
+ endPos = currSelection[ 1 ];
checkSelectedText();
if (
options.selectionStart !== undefined &&
endPos - startPos !== options.selectionEnd - options.selectionStart
) {
// This means there is a difference in the selection range returned by browser and what we passed.
- // This happens for Chrome in the case of composite characters. Ref bug #30130
+ // This happens for Safari 5.1, Chrome 12 in the case of composite characters. Ref T32130
// Set the startPos to the correct position.
startPos = options.selectionStart;
+ combiningCharSelectionBug = true;
+ // TODO: The comment above is from 2011. Is this still a problem for browsers we support today?
+ // Minimal test case: https://jsfiddle.net/z4q7a2ko/
}
insertText = pre + selText + post;
insertText = doSplitLines( selText, pre, post );
}
if ( options.ownline ) {
- if ( startPos !== 0 && this.value.charAt( startPos - 1 ) !== '\n' && this.value.charAt( startPos - 1 ) !== '\r' ) {
+ if ( startPos !== 0 && allText.charAt( startPos - 1 ) !== '\n' && allText.charAt( startPos - 1 ) !== '\r' ) {
insertText = '\n' + insertText;
pre += '\n';
}
- if ( this.value.charAt( endPos ) !== '\n' && this.value.charAt( endPos ) !== '\r' ) {
+ if ( allText.charAt( endPos ) !== '\n' && allText.charAt( endPos ) !== '\r' ) {
insertText += '\n';
post += '\n';
}
}
- this.value = this.value.slice( 0, startPos ) + insertText +
- this.value.slice( endPos );
- // Setting this.value scrolls the textarea to the top, restore the scroll position
- this.scrollTop = scrollTop;
+ if ( combiningCharSelectionBug ) {
+ $( this ).textSelection( 'setContents', allText.slice( 0, startPos ) + insertText +
+ allText.slice( endPos ) );
+ } else {
+ $( this ).textSelection( 'replaceSelection', insertText );
+ }
if ( isSample && options.selectPeri && ( !options.splitlines || ( options.splitlines && selText.indexOf( '\n' ) === -1 ) ) ) {
- this.selectionStart = startPos + pre.length;
- this.selectionEnd = startPos + pre.length + selText.length;
+ $( this ).textSelection( 'setSelection', {
+ start: startPos + pre.length,
+ end: startPos + pre.length + selText.length
+ } );
} else {
- this.selectionStart = startPos + insertText.length;
- this.selectionEnd = this.selectionStart;
+ $( this ).textSelection( 'setSelection', {
+ start: startPos + insertText.length
+ } );
}
$( this ).trigger( 'encapsulateSelection', [ options.pre, options.peri, options.post, options.ownline,
options.replace, options.splitlines ] );
// case 'getContents': // no params
// case 'setContents': // no params with defaults
// case 'getSelection': // no params
+ // case 'replaceSelection': // no params with defaults
case 'encapsulateSelection':
options = $.extend( {
pre: '',
* @inheritdoc jQuery.plugin.textSelection#textSelection
*/
-}( jQuery ) );
+}() );