Update jquery.ime
Change-Id: Ibcf7a4f076ceb86b91e81310bcb2fa64abc551dd
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
/*! jquery.ime - v0.2.0+20200614
|
/*! jquery.ime - v0.2.0+20200724
|
||||||
* https://github.com/wikimedia/jquery.ime
|
* https://github.com/wikimedia/jquery.ime
|
||||||
* Copyright (c) 2020 Santhosh Thottingal; License: (GPL-2.0-or-later OR MIT) */
|
* Copyright (c) 2020 Santhosh Thottingal; License: (GPL-2.0-or-later OR MIT) */
|
||||||
( function ( $ ) {
|
( function ( $ ) {
|
||||||
@@ -7,11 +7,9 @@
|
|||||||
var TextEntryFactory, TextEntry, FormWidgetEntry, ContentEditableEntry,
|
var TextEntryFactory, TextEntry, FormWidgetEntry, ContentEditableEntry,
|
||||||
defaultInputMethod;
|
defaultInputMethod;
|
||||||
|
|
||||||
// rangy is defined in the rangy library
|
|
||||||
/* global rangy */
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* private function for debugging
|
* private function for debugging
|
||||||
|
*
|
||||||
* @param {jQuery} [$obj]
|
* @param {jQuery} [$obj]
|
||||||
*/
|
*/
|
||||||
function debug( $obj ) {
|
function debug( $obj ) {
|
||||||
@@ -48,7 +46,7 @@
|
|||||||
/**
|
/**
|
||||||
* IME Class
|
* IME Class
|
||||||
*
|
*
|
||||||
* @class
|
* @class IME
|
||||||
* @constructor
|
* @constructor
|
||||||
* @param {HTMLElement} element Element on which to listen for events
|
* @param {HTMLElement} element Element on which to listen for events
|
||||||
* @param {TextEntry} textEntry Text entry object to use to get/set text
|
* @param {TextEntry} textEntry Text entry object to use to get/set text
|
||||||
@@ -75,6 +73,10 @@
|
|||||||
this.language = null;
|
this.language = null;
|
||||||
this.context = '';
|
this.context = '';
|
||||||
if ( this.options.showSelector ) {
|
if ( this.options.showSelector ) {
|
||||||
|
this.options.selectorInside = options.selectorInside !== undefined ?
|
||||||
|
options.selectorInside :
|
||||||
|
// eslint-disable-next-line no-jquery/no-class-state
|
||||||
|
this.$element.hasClass( 'ime-position-inside' );
|
||||||
this.selector = this.$element.imeselector( this.options );
|
this.selector = this.$element.imeselector( this.options );
|
||||||
}
|
}
|
||||||
this.listen();
|
this.listen();
|
||||||
@@ -405,7 +407,7 @@
|
|||||||
/**
|
/**
|
||||||
* TextEntry factory
|
* TextEntry factory
|
||||||
*
|
*
|
||||||
* @class
|
* @class TextEntryFactory
|
||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
TextEntryFactory = function IMETextEntryFactory() {
|
TextEntryFactory = function IMETextEntryFactory() {
|
||||||
@@ -431,17 +433,21 @@
|
|||||||
* Wrap an editable element with the appropriate TextEntry class
|
* Wrap an editable element with the appropriate TextEntry class
|
||||||
*
|
*
|
||||||
* @param {jQuery} $element The element to wrap
|
* @param {jQuery} $element The element to wrap
|
||||||
* @return {TextEntry|undefined} A TextEntry, or undefined if no match
|
* @return {TextEntry|null} A TextEntry, or null if no match
|
||||||
*/
|
*/
|
||||||
TextEntryFactory.prototype.wrap = function ( $element ) {
|
TextEntryFactory.prototype.wrap = function ( $element ) {
|
||||||
var i, len, TextEntryClass;
|
var i, len, TextEntryClass;
|
||||||
|
// eslint-disable-next-line no-jquery/no-class-state
|
||||||
|
if ( $element.hasClass( 'noime' ) ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
for ( i = 0, len = this.TextEntryClasses.length; i < len; i++ ) {
|
for ( i = 0, len = this.TextEntryClasses.length; i < len; i++ ) {
|
||||||
TextEntryClass = this.TextEntryClasses[ i ];
|
TextEntryClass = this.TextEntryClasses[ i ];
|
||||||
if ( TextEntryClass.static.canWrap( $element ) ) {
|
if ( TextEntryClass.static.canWrap( $element ) ) {
|
||||||
return new TextEntryClass( $element );
|
return new TextEntryClass( $element );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return undefined;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Initialization */
|
/* Initialization */
|
||||||
@@ -451,7 +457,7 @@
|
|||||||
/**
|
/**
|
||||||
* Generic text entry
|
* Generic text entry
|
||||||
*
|
*
|
||||||
* @class
|
* @class TextEntry
|
||||||
* @abstract
|
* @abstract
|
||||||
*/
|
*/
|
||||||
TextEntry = function IMETextEntry() {
|
TextEntry = function IMETextEntry() {
|
||||||
@@ -496,7 +502,7 @@
|
|||||||
/**
|
/**
|
||||||
* TextEntry class for input/textarea widgets
|
* TextEntry class for input/textarea widgets
|
||||||
*
|
*
|
||||||
* @class
|
* @class FormWidgetEntry
|
||||||
* @constructor
|
* @constructor
|
||||||
* @param {jQuery} $element The element to wrap
|
* @param {jQuery} $element The element to wrap
|
||||||
*/
|
*/
|
||||||
@@ -516,9 +522,7 @@
|
|||||||
FormWidgetEntry.static.canWrap = function ( $element ) {
|
FormWidgetEntry.static.canWrap = function ( $element ) {
|
||||||
return $element.is( 'input:not([type]), input[type=text], input[type=search], textarea' ) &&
|
return $element.is( 'input:not([type]), input[type=text], input[type=search], textarea' ) &&
|
||||||
!$element.prop( 'readonly' ) &&
|
!$element.prop( 'readonly' ) &&
|
||||||
!$element.prop( 'disabled' ) &&
|
!$element.prop( 'disabled' );
|
||||||
// eslint-disable-next-line no-jquery/no-class-state
|
|
||||||
!$element.hasClass( 'noime' );
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Instance methods */
|
/* Instance methods */
|
||||||
@@ -527,10 +531,10 @@
|
|||||||
* @inheritdoc TextEntry
|
* @inheritdoc TextEntry
|
||||||
*/
|
*/
|
||||||
FormWidgetEntry.prototype.getTextBeforeSelection = function ( maxLength ) {
|
FormWidgetEntry.prototype.getTextBeforeSelection = function ( maxLength ) {
|
||||||
var pos = this.getCaretPosition();
|
var element = this.$element.get( 0 );
|
||||||
return this.$element.val().substring(
|
return this.$element.val().substring(
|
||||||
Math.max( 0, pos.start - maxLength ),
|
Math.max( 0, element.selectionStart - maxLength ),
|
||||||
pos.start
|
element.selectionStart
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -538,17 +542,8 @@
|
|||||||
* @inheritdoc TextEntry
|
* @inheritdoc TextEntry
|
||||||
*/
|
*/
|
||||||
FormWidgetEntry.prototype.replaceTextAtSelection = function ( precedingCharCount, newText ) {
|
FormWidgetEntry.prototype.replaceTextAtSelection = function ( precedingCharCount, newText ) {
|
||||||
var selection,
|
var element = this.$element.get( 0 ),
|
||||||
length,
|
start = element.selectionStart,
|
||||||
newLines,
|
|
||||||
start,
|
|
||||||
scrollTop,
|
|
||||||
pos,
|
|
||||||
element = this.$element.get( 0 );
|
|
||||||
|
|
||||||
if ( typeof element.selectionStart === 'number' && typeof element.selectionEnd === 'number' ) {
|
|
||||||
// IE9+ and all other browsers
|
|
||||||
start = element.selectionStart;
|
|
||||||
scrollTop = element.scrollTop;
|
scrollTop = element.scrollTop;
|
||||||
|
|
||||||
// Replace the whole text of the text area:
|
// Replace the whole text of the text area:
|
||||||
@@ -565,85 +560,6 @@
|
|||||||
element.scrollTop = scrollTop;
|
element.scrollTop = scrollTop;
|
||||||
// set selection
|
// set selection
|
||||||
element.selectionStart = element.selectionEnd = start - precedingCharCount + newText.length;
|
element.selectionStart = element.selectionEnd = start - precedingCharCount + newText.length;
|
||||||
} else {
|
|
||||||
// IE8 and lower
|
|
||||||
pos = this.getCaretPosition();
|
|
||||||
selection = element.createTextRange();
|
|
||||||
length = element.value.length;
|
|
||||||
// IE doesn't count \n when computing the offset, so we won't either
|
|
||||||
newLines = element.value.match( /\n/g );
|
|
||||||
|
|
||||||
if ( newLines ) {
|
|
||||||
length = length - newLines.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
selection.moveStart( 'character', pos.start - precedingCharCount );
|
|
||||||
selection.moveEnd( 'character', pos.end - length );
|
|
||||||
|
|
||||||
selection.text = newText;
|
|
||||||
selection.collapse( false );
|
|
||||||
selection.select();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the current selection offsets inside the widget
|
|
||||||
*
|
|
||||||
* @return {Object} return Offsets in chars (0 means first offset *or* no selection in widget)
|
|
||||||
* @return {number} return.start Selection start
|
|
||||||
* @return {number} return.end Selection end
|
|
||||||
*/
|
|
||||||
FormWidgetEntry.prototype.getCaretPosition = function () {
|
|
||||||
var el = this.$element.get( 0 ),
|
|
||||||
start = 0,
|
|
||||||
end = 0,
|
|
||||||
normalizedValue,
|
|
||||||
range,
|
|
||||||
textInputRange,
|
|
||||||
len,
|
|
||||||
newLines,
|
|
||||||
endRange;
|
|
||||||
|
|
||||||
if ( typeof el.selectionStart === 'number' && typeof el.selectionEnd === 'number' ) {
|
|
||||||
start = el.selectionStart;
|
|
||||||
end = el.selectionEnd;
|
|
||||||
} else {
|
|
||||||
// IE
|
|
||||||
range = document.selection.createRange();
|
|
||||||
|
|
||||||
if ( range && range.parentElement() === el ) {
|
|
||||||
len = el.value.length;
|
|
||||||
normalizedValue = el.value.replace( /\r\n/g, '\n' );
|
|
||||||
newLines = normalizedValue.match( /\n/g );
|
|
||||||
|
|
||||||
// Create a working TextRange that lives only in the input
|
|
||||||
textInputRange = el.createTextRange();
|
|
||||||
textInputRange.moveToBookmark( range.getBookmark() );
|
|
||||||
|
|
||||||
// Check if the start and end of the selection are at the very end
|
|
||||||
// of the input, since moveStart/moveEnd doesn't return what we want
|
|
||||||
// in those cases
|
|
||||||
endRange = el.createTextRange();
|
|
||||||
endRange.collapse( false );
|
|
||||||
|
|
||||||
if ( textInputRange.compareEndPoints( 'StartToEnd', endRange ) > -1 ) {
|
|
||||||
if ( newLines ) {
|
|
||||||
start = end = len - newLines.length;
|
|
||||||
} else {
|
|
||||||
start = end = len;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
start = -textInputRange.moveStart( 'character', -len );
|
|
||||||
|
|
||||||
if ( textInputRange.compareEndPoints( 'EndToEnd', endRange ) > -1 ) {
|
|
||||||
end = len;
|
|
||||||
} else {
|
|
||||||
end = -textInputRange.moveEnd( 'character', -len );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return { start: start, end: end };
|
|
||||||
};
|
};
|
||||||
|
|
||||||
TextEntryFactory.static.singleton.register( FormWidgetEntry );
|
TextEntryFactory.static.singleton.register( FormWidgetEntry );
|
||||||
@@ -651,7 +567,7 @@
|
|||||||
/**
|
/**
|
||||||
* TextEntry class for ContentEditable
|
* TextEntry class for ContentEditable
|
||||||
*
|
*
|
||||||
* @class
|
* @class ContentEditableEntry
|
||||||
* @constructor
|
* @constructor
|
||||||
* @param {jQuery} $element The element to wrap
|
* @param {jQuery} $element The element to wrap
|
||||||
*/
|
*/
|
||||||
@@ -669,8 +585,7 @@
|
|||||||
* @inheritdoc TextEntry
|
* @inheritdoc TextEntry
|
||||||
*/
|
*/
|
||||||
ContentEditableEntry.static.canWrap = function ( $element ) {
|
ContentEditableEntry.static.canWrap = function ( $element ) {
|
||||||
// eslint-disable-next-line no-jquery/no-class-state
|
return $element.is( '[contenteditable]' );
|
||||||
return $element.is( '[contenteditable]' ) && !$element.hasClass( 'noime' );
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Instance methods */
|
/* Instance methods */
|
||||||
@@ -693,9 +608,11 @@
|
|||||||
* @inheritdoc SelectionWrapper
|
* @inheritdoc SelectionWrapper
|
||||||
*/
|
*/
|
||||||
ContentEditableEntry.prototype.replaceTextAtSelection = function ( precedingCharCount, newText ) {
|
ContentEditableEntry.prototype.replaceTextAtSelection = function ( precedingCharCount, newText ) {
|
||||||
var range, textNode, textOffset, newOffset, newRange;
|
var textNode, textOffset, newOffset, newRange,
|
||||||
|
sel = window.getSelection(),
|
||||||
|
range = this.getSelectedRange();
|
||||||
|
|
||||||
if ( !this.getSelectedRange() ) {
|
if ( !range ) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -705,12 +622,11 @@
|
|||||||
// browsers that do not support it.
|
// browsers that do not support it.
|
||||||
this.$element.trigger( 'compositionstart' );
|
this.$element.trigger( 'compositionstart' );
|
||||||
|
|
||||||
range = this.getSelectedRange();
|
|
||||||
|
|
||||||
if ( !range.collapsed ) {
|
if ( !range.collapsed ) {
|
||||||
range.deleteContents();
|
range.deleteContents();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
newRange = document.createRange();
|
||||||
if ( range.startContainer.nodeType === Node.TEXT_NODE ) {
|
if ( range.startContainer.nodeType === Node.TEXT_NODE ) {
|
||||||
// Alter this text node's content and move the cursor
|
// Alter this text node's content and move the cursor
|
||||||
textNode = range.startContainer;
|
textNode = range.startContainer;
|
||||||
@@ -720,10 +636,8 @@
|
|||||||
newText +
|
newText +
|
||||||
textNode.nodeValue.substr( textOffset );
|
textNode.nodeValue.substr( textOffset );
|
||||||
newOffset = textOffset - precedingCharCount + newText.length;
|
newOffset = textOffset - precedingCharCount + newText.length;
|
||||||
newRange = rangy.createRange();
|
|
||||||
newRange.setStart( range.startContainer, newOffset );
|
newRange.setStart( range.startContainer, newOffset );
|
||||||
newRange.setEnd( range.startContainer, newOffset );
|
newRange.setEnd( range.startContainer, newOffset );
|
||||||
rangy.getSelection().setSingleRange( newRange );
|
|
||||||
} else {
|
} else {
|
||||||
// XXX assert precedingCharCount === 0
|
// XXX assert precedingCharCount === 0
|
||||||
// Insert a new text node with the new text
|
// Insert a new text node with the new text
|
||||||
@@ -732,11 +646,11 @@
|
|||||||
textNode,
|
textNode,
|
||||||
range.startContainer.childNodes[ range.startOffset ]
|
range.startContainer.childNodes[ range.startOffset ]
|
||||||
);
|
);
|
||||||
newRange = rangy.createRange();
|
|
||||||
newRange.setStart( textNode, textNode.length );
|
newRange.setStart( textNode, textNode.length );
|
||||||
newRange.setEnd( textNode, textNode.length );
|
newRange.setEnd( textNode, textNode.length );
|
||||||
rangy.getSelection().setSingleRange( newRange );
|
|
||||||
}
|
}
|
||||||
|
sel.removeAllRanges();
|
||||||
|
sel.addRange( newRange );
|
||||||
|
|
||||||
// Trigger any externally registered jQuery compositionend / input event listeners.
|
// Trigger any externally registered jQuery compositionend / input event listeners.
|
||||||
// TODO: Try node.dispatchEvent( new CompositionEvent(...) ) so listeners not
|
// TODO: Try node.dispatchEvent( new CompositionEvent(...) ) so listeners not
|
||||||
@@ -752,9 +666,9 @@
|
|||||||
* @return {Range|null} The selection range
|
* @return {Range|null} The selection range
|
||||||
*/
|
*/
|
||||||
ContentEditableEntry.prototype.getSelectedRange = function () {
|
ContentEditableEntry.prototype.getSelectedRange = function () {
|
||||||
var sel, range;
|
var range,
|
||||||
rangy.init();
|
sel = window.getSelection();
|
||||||
sel = rangy.getSelection();
|
|
||||||
if ( sel.rangeCount === 0 ) {
|
if ( sel.rangeCount === 0 ) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -784,7 +698,7 @@
|
|||||||
data = $this.data( 'ime' );
|
data = $this.data( 'ime' );
|
||||||
if ( !data ) {
|
if ( !data ) {
|
||||||
textEntry = TextEntryFactory.static.singleton.wrap( $this );
|
textEntry = TextEntryFactory.static.singleton.wrap( $this );
|
||||||
if ( textEntry === undefined ) {
|
if ( !textEntry ) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
data = new IME( this, textEntry, options );
|
data = new IME( this, textEntry, options );
|
||||||
@@ -877,7 +791,8 @@
|
|||||||
$.ime.defaults = {
|
$.ime.defaults = {
|
||||||
languages: [], // Languages to be used- by default all languages
|
languages: [], // Languages to be used- by default all languages
|
||||||
helpHandler: null, // Called for each ime option in the menu
|
helpHandler: null, // Called for each ime option in the menu
|
||||||
showSelector: true
|
showSelector: true,
|
||||||
|
selectorInside: undefined // If not set will check if '.ime-position-inside' class is preset
|
||||||
};
|
};
|
||||||
}( jQuery ) );
|
}( jQuery ) );
|
||||||
|
|
||||||
@@ -1144,7 +1059,7 @@
|
|||||||
/**
|
/**
|
||||||
* Keydown event handler. Handles shortcut key presses
|
* Keydown event handler. Handles shortcut key presses
|
||||||
*
|
*
|
||||||
* @context {HTMLElement}
|
* @this HTMLElement
|
||||||
* @param {jQuery.Event} e
|
* @param {jQuery.Event} e
|
||||||
* @return {boolean}
|
* @return {boolean}
|
||||||
*/
|
*/
|
||||||
@@ -1215,6 +1130,10 @@
|
|||||||
this.$imeSetting.outerWidth();
|
this.$imeSetting.outerWidth();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( this.options.selectorInside ) {
|
||||||
|
top -= this.$imeSetting.outerHeight();
|
||||||
|
}
|
||||||
|
|
||||||
// While determining whether to place the selector above or below the input box,
|
// While determining whether to place the selector above or below the input box,
|
||||||
// take into account the value of scrollTop, to avoid the selector from always
|
// take into account the value of scrollTop, to avoid the selector from always
|
||||||
// getting placed above the input box since window.height would be less than top
|
// getting placed above the input box since window.height would be less than top
|
||||||
@@ -1223,6 +1142,9 @@
|
|||||||
|
|
||||||
if ( verticalRoom < this.$imeSetting.outerHeight() ) {
|
if ( verticalRoom < this.$imeSetting.outerHeight() ) {
|
||||||
top = elementPosition.top - this.$imeSetting.outerHeight();
|
top = elementPosition.top - this.$imeSetting.outerHeight();
|
||||||
|
if ( this.options.selectorInside ) {
|
||||||
|
top += this.$imeSetting.outerHeight();
|
||||||
|
}
|
||||||
menuTop = this.$menu.outerHeight() +
|
menuTop = this.$menu.outerHeight() +
|
||||||
this.$imeSetting.outerHeight();
|
this.$imeSetting.outerHeight();
|
||||||
|
|
||||||
@@ -1278,7 +1200,7 @@
|
|||||||
* Select a language
|
* Select a language
|
||||||
*
|
*
|
||||||
* @param {string} languageCode
|
* @param {string} languageCode
|
||||||
* @return {string|bool} Selected input method id or false
|
* @return {string|boolean} Selected input method id or false
|
||||||
*/
|
*/
|
||||||
selectLanguage: function ( languageCode ) {
|
selectLanguage: function ( languageCode ) {
|
||||||
var ime, imePref, language;
|
var ime, imePref, language;
|
||||||
@@ -1341,6 +1263,7 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Decide on initial language to select
|
* Decide on initial language to select
|
||||||
|
*
|
||||||
* @return {string}
|
* @return {string}
|
||||||
*/
|
*/
|
||||||
decideLanguage: function () {
|
decideLanguage: function () {
|
||||||
|
|||||||
@@ -70,7 +70,7 @@
|
|||||||
[ 'q', '\u0B4C' ],
|
[ 'q', '\u0B4C' ],
|
||||||
[ 'd', '\u0B4D' ],
|
[ 'd', '\u0B4D' ],
|
||||||
[ '/', '\u0B5F' ],
|
[ '/', '\u0B5F' ],
|
||||||
[ '\\>', '\u0B64' ],
|
[ '\\>', '\u0964' ],
|
||||||
[ '0', '\u0B66' ],
|
[ '0', '\u0B66' ],
|
||||||
[ '1', '\u0B67' ],
|
[ '1', '\u0B67' ],
|
||||||
[ '2', '\u0B68' ],
|
[ '2', '\u0B68' ],
|
||||||
|
|||||||
Reference in New Issue
Block a user