Upstream: https://github.com/wikimedia/jquery.ime Changes: * hi-inscript: Fixed incorrectly mapped Devanagari letter TTHA * ml-inscript: Correct the mapping of ഠ * or-inscript: fix mapping of Oriya letters TTA and TTHA * Remove unwanted escape for double quotes from rules files * Remove prefixed border-radius rules * Avoid JavaSript error while trying to get autonym * Rules loading: Use $.ajax to cache the rule js file Change-Id: I49b88551263434a93b600278d665e80737edb555
2473 lines
62 KiB
JavaScript
2473 lines
62 KiB
JavaScript
/*! jquery.ime - v0.1.0+20140408
|
||
* https://github.com/wikimedia/jquery.ime
|
||
* Copyright (c) 2014 Santhosh Thottingal; Licensed GPL, MIT */
|
||
( function ( $ ) {
|
||
'use strict';
|
||
|
||
// rangy is defined in the rangy library
|
||
/*global rangy */
|
||
|
||
/**
|
||
* IME Class
|
||
* @param {Function} [options.helpHandler] Called for each input method row in the selector
|
||
* @param {Object} options.helpHandler.imeSelector
|
||
* @param {String} options.helpHandler.ime Id of the input method
|
||
*/
|
||
function IME( element, options ) {
|
||
this.$element = $( element );
|
||
// This needs to be delayed here since extending language list happens at DOM ready
|
||
$.ime.defaults.languages = arrayKeys( $.ime.languages );
|
||
this.options = $.extend( {}, $.ime.defaults, options );
|
||
this.active = false;
|
||
this.shifted = false;
|
||
this.inputmethod = null;
|
||
this.language = null;
|
||
this.context = '';
|
||
this.selector = this.$element.imeselector( this.options );
|
||
this.listen();
|
||
}
|
||
|
||
IME.prototype = {
|
||
constructor: IME,
|
||
|
||
/**
|
||
* Listen for events and bind to handlers
|
||
*/
|
||
listen: function () {
|
||
this.$element.on( 'keypress.ime', $.proxy( this.keypress, this ) );
|
||
this.$element.on( 'keyup.ime', $.proxy( this.keyup, this ) );
|
||
this.$element.on( 'keydown.ime', $.proxy( this.keydown, this ) );
|
||
this.$element.on( 'destroy.ime', $.proxy( this.destroy, this ) );
|
||
this.$element.on( 'enable.ime', $.proxy( this.enable, this ) );
|
||
this.$element.on( 'disable.ime', $.proxy( this.disable, this ) );
|
||
},
|
||
|
||
/**
|
||
* Transliterate a given string input based on context and input method definition.
|
||
* If there are no matching rules defined, returns the original string.
|
||
*
|
||
* @param {string} input
|
||
* @param {string} context
|
||
* @param {boolean} altGr whether altGr key is pressed or not
|
||
* @returns {object} transliteration object
|
||
* @returns {bool} return.noop Whether to consider input processed or passed through.
|
||
* @returns {string} return.output the transliterated input or input unmodified.
|
||
*/
|
||
transliterate: function ( input, context, altGr ) {
|
||
var patterns, regex, rule, replacement, i, retval;
|
||
|
||
if ( altGr ) {
|
||
patterns = this.inputmethod.patterns_x || [];
|
||
} else {
|
||
patterns = this.inputmethod.patterns || [];
|
||
}
|
||
|
||
if ( this.shifted ) {
|
||
// if shift is pressed give priority for the patterns_shift
|
||
// if exists.
|
||
// Example: Shift+space where shift does not alter the keycode
|
||
patterns = ( this.inputmethod.patterns_shift || [] )
|
||
.concat( patterns );
|
||
}
|
||
|
||
if ( $.isFunction( patterns ) ) {
|
||
// For backwards compatibility, allow the rule functions to return plain
|
||
// string. Determine noop by checking whether input is different from
|
||
// output. If the rule function returns object, just return it as-is.
|
||
retval = patterns.call( this, input, context );
|
||
if ( typeof retval === 'string' ) {
|
||
return { noop: input === retval, output: retval };
|
||
}
|
||
|
||
return retval;
|
||
}
|
||
|
||
for ( i = 0; i < patterns.length; i++ ) {
|
||
rule = patterns[i];
|
||
regex = new RegExp( rule[0] + '$' );
|
||
|
||
// Last item in the rules.
|
||
// It can also be a function, because the replace
|
||
// method can have a function as the second argument.
|
||
replacement = rule.slice( -1 )[0];
|
||
|
||
// Input string match test
|
||
if ( regex.test( input ) ) {
|
||
// Context test required?
|
||
if ( rule.length === 3 ) {
|
||
if ( new RegExp( rule[1] + '$' ).test( context ) ) {
|
||
return { noop: false, output: input.replace( regex, replacement ) };
|
||
}
|
||
} else {
|
||
return { noop: false, output: input.replace( regex, replacement ) };
|
||
}
|
||
}
|
||
}
|
||
|
||
// No matches, return the input
|
||
return { noop: true, output: input };
|
||
},
|
||
|
||
keyup: function ( e ) {
|
||
if ( e.which === 16 ) { // shift key
|
||
this.shifted = false;
|
||
}
|
||
},
|
||
|
||
keydown: function ( e ) {
|
||
if ( e.which === 16 ) { // shift key
|
||
this.shifted = true;
|
||
}
|
||
},
|
||
|
||
/**
|
||
* Keypress handler
|
||
* @param {jQuery.Event} e Event
|
||
* @returns {Boolean}
|
||
*/
|
||
keypress: function ( e ) {
|
||
var altGr = false,
|
||
c, startPos, pos, endPos, divergingPos, input, replacement;
|
||
|
||
if ( !this.active ) {
|
||
return true;
|
||
}
|
||
|
||
if ( !this.inputmethod ) {
|
||
return true;
|
||
}
|
||
|
||
// handle backspace
|
||
if ( e.which === 8 ) {
|
||
// Blank the context
|
||
this.context = '';
|
||
return true;
|
||
}
|
||
|
||
if ( e.altKey || e.altGraphKey ) {
|
||
altGr = true;
|
||
}
|
||
|
||
// Don't process ASCII control characters except linefeed,
|
||
// as well as anything involving Ctrl, Meta and Alt,
|
||
// but do process extended keymaps
|
||
if ( ( e.which < 32 && e.which !== 13 && !altGr ) || e.ctrlKey || e.metaKey ) {
|
||
// Blank the context
|
||
this.context = '';
|
||
|
||
return true;
|
||
}
|
||
|
||
c = String.fromCharCode( e.which );
|
||
|
||
// Get the current caret position. The user may have selected text to overwrite,
|
||
// so get both the start and end position of the selection. If there is no selection,
|
||
// startPos and endPos will be equal.
|
||
pos = this.getCaretPosition( this.$element );
|
||
startPos = pos[0];
|
||
endPos = pos[1];
|
||
|
||
// Get the last few characters before the one the user just typed,
|
||
// to provide context for the transliteration regexes.
|
||
// We need to append c because it hasn't been added to $this.val() yet
|
||
input = this.lastNChars(
|
||
this.$element.val() || this.$element.text(),
|
||
startPos,
|
||
this.inputmethod.maxKeyLength
|
||
);
|
||
input += c;
|
||
|
||
replacement = this.transliterate( input, this.context, altGr );
|
||
|
||
// Update the context
|
||
this.context += c;
|
||
|
||
if ( this.context.length > this.inputmethod.contextLength ) {
|
||
// The buffer is longer than needed, truncate it at the front
|
||
this.context = this.context.substring(
|
||
this.context.length - this.inputmethod.contextLength
|
||
);
|
||
}
|
||
|
||
// Allow rules to explicitly define whether we match something.
|
||
// Otherwise we cannot distinguish between no matching rule and
|
||
// rule that provides identical output but consumes the event
|
||
// to prevent normal behavior. See Udmurt layout which uses
|
||
// altgr rules to allow typing the original character.
|
||
if ( replacement.noop ) {
|
||
return true;
|
||
}
|
||
|
||
// Drop a common prefix, if any
|
||
divergingPos = this.firstDivergence( input, replacement.output );
|
||
input = input.substring( divergingPos );
|
||
replacement.output = replacement.output.substring( divergingPos );
|
||
replaceText( this.$element, replacement.output, startPos - input.length + 1, endPos );
|
||
|
||
e.stopPropagation();
|
||
|
||
return false;
|
||
},
|
||
|
||
/**
|
||
* Check whether the input method is active or not
|
||
* @returns {Boolean}
|
||
*/
|
||
isActive: function () {
|
||
return this.active;
|
||
},
|
||
|
||
/**
|
||
* Disable the input method
|
||
*/
|
||
disable: function () {
|
||
this.active = false;
|
||
$.ime.preferences.setIM( 'system' );
|
||
},
|
||
|
||
/**
|
||
* Enable the input method
|
||
*/
|
||
enable: function () {
|
||
this.active = true;
|
||
},
|
||
|
||
/**
|
||
* Toggle the active state of input method
|
||
*/
|
||
toggle: function () {
|
||
this.active = !this.active;
|
||
},
|
||
|
||
/**
|
||
* Destroy the binding of ime to the editable element
|
||
*/
|
||
destroy: function () {
|
||
$( 'body' ).off( '.ime' );
|
||
this.$element.off( '.ime' ).removeData( 'ime' ).removeData( 'imeselector' );
|
||
},
|
||
|
||
/**
|
||
* Get the current input method
|
||
* @returns {string} Current input method id
|
||
*/
|
||
getIM: function () {
|
||
return this.inputmethod;
|
||
},
|
||
|
||
/**
|
||
* Set the current input method
|
||
* @param {string} inputmethodId
|
||
*/
|
||
setIM: function ( inputmethodId ) {
|
||
this.inputmethod = $.ime.inputmethods[inputmethodId];
|
||
$.ime.preferences.setIM( inputmethodId );
|
||
},
|
||
|
||
/**
|
||
* Set the current Language
|
||
* @param {string} languageCode
|
||
* @returns {Boolean}
|
||
*/
|
||
setLanguage: function ( languageCode ) {
|
||
if ( !$.ime.languages[languageCode] ) {
|
||
debug( 'Language ' + languageCode + ' is not known to jquery.ime.' );
|
||
|
||
return false;
|
||
}
|
||
|
||
this.language = languageCode;
|
||
$.ime.preferences.setLanguage( languageCode );
|
||
return true;
|
||
},
|
||
|
||
/**
|
||
* Get current language
|
||
* @returns {string}
|
||
*/
|
||
getLanguage: function () {
|
||
return this.language;
|
||
},
|
||
|
||
/**
|
||
* load an input method by given id
|
||
* @param {string} inputmethodId
|
||
* @return {jQuery.Promise}
|
||
*/
|
||
load: function ( inputmethodId ) {
|
||
var ime = this,
|
||
deferred = $.Deferred(),
|
||
dependency;
|
||
|
||
if ( $.ime.inputmethods[inputmethodId] ) {
|
||
return deferred.resolve();
|
||
}
|
||
|
||
// Validate the input method id.
|
||
if ( !$.ime.sources[inputmethodId] ) {
|
||
return deferred.reject();
|
||
}
|
||
|
||
dependency = $.ime.sources[inputmethodId].depends;
|
||
if ( dependency && !$.ime.inputmethods[dependency] ) {
|
||
ime.load( dependency ).done( function () {
|
||
ime.load( inputmethodId ).done( function () {
|
||
deferred.resolve();
|
||
} );
|
||
} );
|
||
|
||
return deferred;
|
||
}
|
||
|
||
debug( 'Loading ' + inputmethodId );
|
||
deferred = $.ajax( {
|
||
url: ime.options.imePath + $.ime.sources[inputmethodId].source,
|
||
dataType: 'script',
|
||
cache: true
|
||
} ).done( function () {
|
||
debug( inputmethodId + ' loaded' );
|
||
} ).fail( function ( jqxhr, settings, exception ) {
|
||
debug( 'Error in loading inputmethod ' + inputmethodId + ' Exception: ' + exception );
|
||
} );
|
||
|
||
return deferred.promise();
|
||
},
|
||
|
||
/**
|
||
* Returns an array [start, end] of the beginning
|
||
* and the end of the current selection in $element
|
||
* @returns {Array}
|
||
*/
|
||
getCaretPosition: function ( $element ) {
|
||
return getCaretPosition( $element );
|
||
},
|
||
|
||
/**
|
||
* Set the caret position in the div.
|
||
* @param {jQuery} $element The content editable div element
|
||
* @param {Object} position An object with start and end properties.
|
||
* @return {Array} If the cursor could not be placed at given position, how
|
||
* many characters had to go back to place the cursor
|
||
*/
|
||
setCaretPosition: function ( $element, position ) {
|
||
return setCaretPosition( $element, position );
|
||
},
|
||
|
||
/**
|
||
* Find the point at which a and b diverge, i.e. the first position
|
||
* at which they don't have matching characters.
|
||
*
|
||
* @param a String
|
||
* @param b String
|
||
* @return Position at which a and b diverge, or -1 if a === b
|
||
*/
|
||
firstDivergence: function ( a, b ) {
|
||
return firstDivergence( a, b );
|
||
},
|
||
|
||
/**
|
||
* Get the n characters in str that immediately precede pos
|
||
* Example: lastNChars( 'foobarbaz', 5, 2 ) === 'ba'
|
||
*
|
||
* @param str String to search in
|
||
* @param pos Position in str
|
||
* @param n Number of characters to go back from pos
|
||
* @return Substring of str, at most n characters long, immediately preceding pos
|
||
*/
|
||
lastNChars: function ( str, pos, n ) {
|
||
return lastNChars( str, pos, n );
|
||
}
|
||
};
|
||
|
||
/**
|
||
* jQuery plugin ime
|
||
* @param {Object} option
|
||
*/
|
||
$.fn.ime = function ( option ) {
|
||
return this.each( function () {
|
||
var data,
|
||
$this = $( this ),
|
||
options = typeof option === 'object' && option;
|
||
|
||
// Some exclusions: IME shouldn't be applied to textareas with
|
||
// these properties.
|
||
if ( $this.prop( 'readonly' ) ||
|
||
$this.prop( 'disabled' ) ||
|
||
$this.hasClass( 'noime' ) ) {
|
||
return;
|
||
}
|
||
|
||
data = $this.data( 'ime' );
|
||
|
||
if ( !data ) {
|
||
data = new IME( this, options );
|
||
$this.data( 'ime', data );
|
||
}
|
||
|
||
if ( typeof option === 'string' ) {
|
||
data[option]();
|
||
}
|
||
} );
|
||
};
|
||
|
||
$.ime = {};
|
||
$.ime.inputmethods = {};
|
||
$.ime.sources = {};
|
||
$.ime.preferences = {};
|
||
$.ime.languages = {};
|
||
|
||
var defaultInputMethod = {
|
||
contextLength: 0,
|
||
maxKeyLength: 1
|
||
};
|
||
|
||
$.ime.register = function ( inputMethod ) {
|
||
$.ime.inputmethods[inputMethod.id] = $.extend( {}, defaultInputMethod, inputMethod );
|
||
};
|
||
|
||
// default options
|
||
$.ime.defaults = {
|
||
imePath: '../', // Relative/Absolute path for the rules folder of jquery.ime
|
||
languages: [], // Languages to be used- by default all languages
|
||
helpHandler: null // Called for each ime option in the menu
|
||
};
|
||
|
||
/**
|
||
* private function for debugging
|
||
*/
|
||
function debug( $obj ) {
|
||
if ( window.console && window.console.log ) {
|
||
window.console.log( $obj );
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Returns an array [start, end] of the beginning
|
||
* and the end of the current selection in $element
|
||
*/
|
||
function getCaretPosition( $element ) {
|
||
var el = $element.get( 0 ),
|
||
start = 0,
|
||
end = 0,
|
||
normalizedValue,
|
||
range,
|
||
textInputRange,
|
||
len,
|
||
newLines,
|
||
endRange;
|
||
|
||
if ( $element.is( '[contenteditable]' ) ) {
|
||
return getDivCaretPosition( el );
|
||
}
|
||
|
||
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, end];
|
||
}
|
||
|
||
/**
|
||
* Helper function to get an IE TextRange object for an element
|
||
*/
|
||
function rangeForElementIE( element ) {
|
||
var selection;
|
||
|
||
if ( element.nodeName.toLowerCase() === 'input' ) {
|
||
selection = element.createTextRange();
|
||
} else {
|
||
selection = document.body.createTextRange();
|
||
selection.moveToElementText( element );
|
||
}
|
||
|
||
return selection;
|
||
}
|
||
|
||
function replaceText( $element, replacement, start, end ) {
|
||
var selection,
|
||
length,
|
||
newLines,
|
||
scrollTop,
|
||
range,
|
||
correction,
|
||
textNode,
|
||
element = $element.get( 0 );
|
||
|
||
if ( $element.is( '[contenteditable]' ) ) {
|
||
correction = setCaretPosition( $element, {
|
||
start: start,
|
||
end: end
|
||
} );
|
||
|
||
selection = rangy.getSelection();
|
||
range = selection.getRangeAt( 0 );
|
||
|
||
if ( correction[0] > 0 ) {
|
||
replacement = selection.toString().substring( 0, correction[0] ) + replacement;
|
||
}
|
||
|
||
textNode = document.createTextNode( replacement );
|
||
range.deleteContents();
|
||
range.insertNode( textNode );
|
||
range.commonAncestorContainer.normalize();
|
||
start = end = start + replacement.length - correction[0];
|
||
setCaretPosition( $element, {
|
||
start: start,
|
||
end: end
|
||
} );
|
||
|
||
return;
|
||
}
|
||
|
||
if ( typeof element.selectionStart === 'number' && typeof element.selectionEnd === 'number' ) {
|
||
// IE9+ and all other browsers
|
||
scrollTop = element.scrollTop;
|
||
|
||
// Replace the whole text of the text area:
|
||
// text before + replacement + text after.
|
||
// This could be made better if range selection worked on browsers.
|
||
// But for complex scripts, browsers place cursor in unexpected places
|
||
// and it's not possible to fix cursor programmatically.
|
||
// Ref Bug https://bugs.webkit.org/show_bug.cgi?id=66630
|
||
element.value = element.value.substring( 0, start ) +
|
||
replacement +
|
||
element.value.substring( end, element.value.length );
|
||
|
||
// restore scroll
|
||
element.scrollTop = scrollTop;
|
||
// set selection
|
||
element.selectionStart = element.selectionEnd = start + replacement.length;
|
||
} else {
|
||
// IE8 and lower
|
||
selection = rangeForElementIE(element);
|
||
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', start );
|
||
selection.moveEnd( 'character', end - length );
|
||
|
||
selection.text = replacement;
|
||
selection.collapse( false );
|
||
selection.select();
|
||
}
|
||
}
|
||
|
||
function getDivCaretPosition( element ) {
|
||
var charIndex = 0,
|
||
start = 0,
|
||
end = 0,
|
||
foundStart = false,
|
||
foundEnd = false,
|
||
sel = rangy.getSelection();
|
||
|
||
function traverseTextNodes( node, range ) {
|
||
var i, childNodesCount;
|
||
|
||
if ( node.nodeType === Node.TEXT_NODE ) {
|
||
if ( !foundStart && node === range.startContainer ) {
|
||
start = charIndex + range.startOffset;
|
||
foundStart = true;
|
||
}
|
||
|
||
if ( foundStart && node === range.endContainer ) {
|
||
end = charIndex + range.endOffset;
|
||
foundEnd = true;
|
||
}
|
||
|
||
charIndex += node.length;
|
||
} else {
|
||
childNodesCount = node.childNodes.length;
|
||
|
||
for ( i = 0; i < childNodesCount; ++i ) {
|
||
traverseTextNodes( node.childNodes[i], range );
|
||
if ( foundEnd ) {
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
if ( sel.rangeCount ) {
|
||
traverseTextNodes( element, sel.getRangeAt( 0 ) );
|
||
}
|
||
|
||
return [ start, end ];
|
||
}
|
||
|
||
function setCaretPosition( $element, position ) {
|
||
var currentPosition,
|
||
startCorrection = 0,
|
||
endCorrection = 0,
|
||
element = $element[0];
|
||
|
||
setDivCaretPosition( element, position );
|
||
currentPosition = getDivCaretPosition( element );
|
||
// see Bug https://bugs.webkit.org/show_bug.cgi?id=66630
|
||
while ( position.start !== currentPosition[0] ) {
|
||
position.start -= 1; // go back one more position.
|
||
if ( position.start < 0 ) {
|
||
// never go beyond 0
|
||
break;
|
||
}
|
||
setDivCaretPosition( element, position );
|
||
currentPosition = getDivCaretPosition( element );
|
||
startCorrection += 1;
|
||
}
|
||
|
||
while ( position.end !== currentPosition[1] ) {
|
||
position.end += 1; // go forward one more position.
|
||
setDivCaretPosition( element, position );
|
||
currentPosition = getDivCaretPosition( element );
|
||
endCorrection += 1;
|
||
if ( endCorrection > 10 ) {
|
||
// XXX avoid rare case of infinite loop here.
|
||
break;
|
||
}
|
||
}
|
||
|
||
return [startCorrection, endCorrection];
|
||
}
|
||
|
||
/**
|
||
* Set the caret position in the div.
|
||
* @param {Element} element The content editable div element
|
||
* @param position
|
||
*/
|
||
function setDivCaretPosition( element, position ) {
|
||
var nextCharIndex,
|
||
charIndex = 0,
|
||
range = rangy.createRange(),
|
||
foundStart = false,
|
||
foundEnd = false;
|
||
|
||
range.collapseToPoint( element, 0 );
|
||
|
||
function traverseTextNodes( node ) {
|
||
var i, len;
|
||
|
||
if ( node.nodeType === 3 ) {
|
||
nextCharIndex = charIndex + node.length;
|
||
|
||
if ( !foundStart && position.start >= charIndex && position.start <= nextCharIndex ) {
|
||
range.setStart( node, position.start - charIndex );
|
||
foundStart = true;
|
||
}
|
||
|
||
if ( foundStart && position.end >= charIndex && position.end <= nextCharIndex ) {
|
||
range.setEnd( node, position.end - charIndex );
|
||
foundEnd = true;
|
||
}
|
||
|
||
charIndex = nextCharIndex;
|
||
} else {
|
||
for ( i = 0, len = node.childNodes.length; i < len; ++i ) {
|
||
traverseTextNodes( node.childNodes[i] );
|
||
if ( foundEnd ) {
|
||
rangy.getSelection().setSingleRange( range );
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
traverseTextNodes( element );
|
||
|
||
}
|
||
|
||
/**
|
||
* Find the point at which a and b diverge, i.e. the first position
|
||
* at which they don't have matching characters.
|
||
*
|
||
* @param a String
|
||
* @param b String
|
||
* @return Position at which a and b diverge, or -1 if a === b
|
||
*/
|
||
function firstDivergence( a, b ) {
|
||
var minLength, i;
|
||
|
||
minLength = a.length < b.length ? a.length : b.length;
|
||
|
||
for ( i = 0; i < minLength; i++ ) {
|
||
if ( a.charCodeAt( i ) !== b.charCodeAt( i ) ) {
|
||
return i;
|
||
}
|
||
}
|
||
|
||
return -1;
|
||
}
|
||
|
||
/**
|
||
* Get the n characters in str that immediately precede pos
|
||
* Example: lastNChars( 'foobarbaz', 5, 2 ) === 'ba'
|
||
*
|
||
* @param str String to search in
|
||
* @param pos Position in str
|
||
* @param n Number of characters to go back from pos
|
||
* @return Substring of str, at most n characters long, immediately preceding pos
|
||
*/
|
||
function lastNChars( str, pos, n ) {
|
||
if ( n === 0 ) {
|
||
return '';
|
||
} else if ( pos <= n ) {
|
||
return str.substr( 0, pos );
|
||
} else {
|
||
return str.substr( pos - n, n );
|
||
}
|
||
}
|
||
|
||
function arrayKeys ( obj ) {
|
||
return $.map( obj, function( element, index ) {
|
||
return index;
|
||
} );
|
||
}
|
||
}( jQuery ) );
|
||
|
||
( function ( $ ) {
|
||
'use strict';
|
||
|
||
var selectorTemplate, MutationObserver;
|
||
|
||
function IMESelector( element, options ) {
|
||
this.$element = $( element );
|
||
this.options = $.extend( {}, IMESelector.defaults, options );
|
||
this.active = false;
|
||
this.$imeSetting = null;
|
||
this.$menu = null;
|
||
this.inputmethod = null;
|
||
this.timer = null;
|
||
this.init();
|
||
this.listen();
|
||
}
|
||
|
||
IMESelector.prototype = {
|
||
constructor: IMESelector,
|
||
|
||
init: function () {
|
||
this.prepareSelectorMenu();
|
||
this.position();
|
||
this.$imeSetting.hide();
|
||
},
|
||
|
||
prepareSelectorMenu: function () {
|
||
// TODO: In this approach there is a menu for each editable area.
|
||
// With correct event mapping we can probably reduce it to one menu.
|
||
this.$imeSetting = $( selectorTemplate );
|
||
this.$menu = $( '<div class="imeselector-menu" role="menu">' );
|
||
this.$menu.append(
|
||
imeListTitle(),
|
||
imeList(),
|
||
toggleMenuItem(),
|
||
languageListTitle()
|
||
);
|
||
|
||
this.prepareLanguageList();
|
||
this.$menu.append( this.helpLink() );
|
||
|
||
if ( $.i18n ) {
|
||
this.$menu.i18n();
|
||
}
|
||
|
||
this.$imeSetting.append( this.$menu );
|
||
$( 'body' ).append( this.$imeSetting );
|
||
},
|
||
|
||
stopTimer: function () {
|
||
if ( this.timer ) {
|
||
clearTimeout( this.timer );
|
||
this.timer = null;
|
||
}
|
||
|
||
this.$imeSetting.stop( true, true );
|
||
},
|
||
|
||
resetTimer: function () {
|
||
var imeselector = this;
|
||
|
||
this.stopTimer();
|
||
|
||
this.timer = setTimeout(
|
||
function () {
|
||
imeselector.$imeSetting.animate( {
|
||
'opacity': 0,
|
||
'marginTop': '-20px'
|
||
}, 500, function () {
|
||
imeselector.$imeSetting.hide();
|
||
// Restore properties for the next time it becomes visible:
|
||
imeselector.$imeSetting.css( 'opacity', 1 );
|
||
imeselector.$imeSetting.css( 'margin-top', 0 );
|
||
} );
|
||
}, this.options.timeout
|
||
);
|
||
},
|
||
|
||
focus: function () {
|
||
// Hide all other IME settings and collapse open menus
|
||
$( 'div.imeselector' ).hide();
|
||
$( 'div.imeselector-menu' ).removeClass( 'ime-open' );
|
||
this.$imeSetting.show();
|
||
this.resetTimer();
|
||
},
|
||
|
||
show: function () {
|
||
this.$menu.addClass( 'ime-open' );
|
||
this.stopTimer();
|
||
this.$imeSetting.show();
|
||
|
||
return false;
|
||
},
|
||
|
||
hide: function () {
|
||
this.$menu.removeClass( 'ime-open' );
|
||
this.resetTimer();
|
||
|
||
return false;
|
||
},
|
||
|
||
toggle: function () {
|
||
if ( this.$menu.hasClass( 'ime-open' ) ) {
|
||
this.hide();
|
||
} else {
|
||
this.show();
|
||
}
|
||
},
|
||
|
||
/**
|
||
* Bind the events and listen
|
||
*/
|
||
listen: function () {
|
||
var imeselector = this;
|
||
|
||
imeselector.$imeSetting.on( 'click.ime', function ( e ) {
|
||
var t = $( e.target );
|
||
|
||
if ( t.hasClass( 'imeselector-toggle' ) ) {
|
||
imeselector.toggle();
|
||
}
|
||
|
||
return false;
|
||
} );
|
||
|
||
imeselector.$element.on( 'blur.ime', function () {
|
||
if ( !imeselector.$imeSetting.hasClass( 'ime-onfocus' ) ) {
|
||
imeselector.$imeSetting.hide();
|
||
imeselector.hide();
|
||
}
|
||
} );
|
||
|
||
// Hide the menu when clicked outside
|
||
$( 'html' ).click( function () {
|
||
imeselector.hide();
|
||
} );
|
||
|
||
// ... but when clicked on window do not propagate it.
|
||
this.$menu.on( 'click', function ( event ) {
|
||
event.stopPropagation();
|
||
} );
|
||
|
||
imeselector.$imeSetting.mouseenter( function () {
|
||
// We don't want the selector to disappear
|
||
// while the user is trying to click it
|
||
imeselector.stopTimer();
|
||
imeselector.$imeSetting.addClass( 'ime-onfocus' );
|
||
} ).mouseleave( function () {
|
||
imeselector.resetTimer();
|
||
imeselector.$imeSetting.removeClass( 'ime-onfocus' );
|
||
} );
|
||
|
||
imeselector.$menu.on( 'click.ime', 'li', function() {
|
||
imeselector.$element.focus();
|
||
|
||
return false;
|
||
} );
|
||
|
||
imeselector.$menu.on( 'click.ime', 'li.ime-im', function () {
|
||
imeselector.selectIM( $( this ).data( 'ime-inputmethod' ) );
|
||
imeselector.$element.trigger( 'setim.ime', $( this ).data( 'ime-inputmethod' ) );
|
||
|
||
return false;
|
||
} );
|
||
|
||
imeselector.$menu.on( 'click.ime', 'li.ime-lang', function () {
|
||
var im = imeselector.selectLanguage( $( this ).attr( 'lang' ) );
|
||
|
||
imeselector.$element.trigger( 'setim.ime', im );
|
||
|
||
return false;
|
||
} );
|
||
|
||
imeselector.$menu.on( 'click.ime', 'div.ime-disable', function () {
|
||
imeselector.disableIM();
|
||
|
||
return false;
|
||
} );
|
||
|
||
// Just make it work as a regular link
|
||
imeselector.$menu.on( 'click.ime', '.ime-help-link', function ( e ) {
|
||
e.stopPropagation();
|
||
} );
|
||
|
||
imeselector.$element.on( 'focus.ime', function ( e ) {
|
||
imeselector.selectLanguage( imeselector.decideLanguage() );
|
||
imeselector.focus();
|
||
e.stopPropagation();
|
||
} );
|
||
|
||
imeselector.$element.attrchange( function ( ) {
|
||
if ( imeselector.$element.is( ':hidden' ) ) {
|
||
imeselector.$imeSetting.hide();
|
||
}
|
||
} );
|
||
|
||
// Possible resize of textarea
|
||
imeselector.$element.on( 'mouseup.ime', $.proxy( this.position, this ) );
|
||
imeselector.$element.on( 'keydown.ime', $.proxy( this.keydown, this ) );
|
||
|
||
// Update IM selector position when the window is resized
|
||
// or the browser window is zoomed in or zoomed out
|
||
$( window ).resize( function () {
|
||
imeselector.position();
|
||
} );
|
||
},
|
||
|
||
/**
|
||
* Keydown event handler. Handles shortcut key presses
|
||
*
|
||
* @context {HTMLElement}
|
||
* @param {jQuery.Event} e
|
||
*/
|
||
keydown: function ( e ) {
|
||
var ime = $( e.target ).data( 'ime' ),
|
||
firstInputmethod,
|
||
previousInputMethods,
|
||
languageCode;
|
||
|
||
this.focus(); // shows the trigger in case it is hidden
|
||
|
||
if ( isShortcutKey( e ) ) {
|
||
if ( ime.isActive() ) {
|
||
this.disableIM();
|
||
this.$element.trigger( 'setim.ime', 'system' );
|
||
} else {
|
||
if ( this.inputmethod !== null ) {
|
||
this.selectIM( this.inputmethod.id );
|
||
this.$element.trigger( 'setim.ime', this.inputmethod.id );
|
||
} else {
|
||
languageCode = this.decideLanguage();
|
||
this.selectLanguage( languageCode );
|
||
|
||
if ( !ime.isActive() && $.ime.languages[languageCode] ) {
|
||
// Even after pressing toggle shortcut again, it is still disabled
|
||
// Check if there is a previously used input method.
|
||
previousInputMethods = $.ime.preferences.getPreviousInputMethods();
|
||
|
||
if ( previousInputMethods[0] ) {
|
||
this.selectIM( previousInputMethods[0] );
|
||
} else {
|
||
// Provide the default input method in this case.
|
||
firstInputmethod = $.ime.languages[languageCode].inputmethods[0];
|
||
this.selectIM( firstInputmethod );
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
e.preventDefault();
|
||
e.stopPropagation();
|
||
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
},
|
||
|
||
/**
|
||
* Position the im selector relative to the edit area
|
||
*/
|
||
position: function () {
|
||
var menuWidth, menuTop, menuLeft, elementPosition,
|
||
top, left, verticalRoom, overflowsOnRight,
|
||
imeSelector = this,
|
||
rtlElement = this.$element.css( 'direction' ) === 'rtl',
|
||
$window = $( window );
|
||
|
||
this.focus(); // shows the trigger in case it is hidden
|
||
|
||
elementPosition = this.$element.offset();
|
||
top = elementPosition.top + this.$element.outerHeight();
|
||
left = elementPosition.left;
|
||
|
||
// RTL element position fix
|
||
if ( !rtlElement ) {
|
||
left = elementPosition.left + this.$element.outerWidth() -
|
||
this.$imeSetting.outerWidth();
|
||
}
|
||
|
||
// 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
|
||
// getting placed above the input box since window.height would be less than top
|
||
// if the page has been scrolled.
|
||
verticalRoom = $window.height() + $( document ).scrollTop() - top;
|
||
|
||
if ( verticalRoom < this.$imeSetting.outerHeight() ) {
|
||
top = elementPosition.top - this.$imeSetting.outerHeight();
|
||
menuTop = this.$menu.outerHeight() +
|
||
this.$imeSetting.outerHeight();
|
||
|
||
// Flip the menu to the top only if it can fit in the space there
|
||
if ( menuTop < top ) {
|
||
this.$menu
|
||
.addClass( 'ime-position-top' )
|
||
.css( 'top', -menuTop );
|
||
}
|
||
}
|
||
|
||
this.$element.parents().each( function() {
|
||
if ( $( this ).css( 'position' ) === 'fixed' ) {
|
||
imeSelector.$imeSetting.css( 'position', 'fixed' );
|
||
|
||
return false;
|
||
}
|
||
} );
|
||
|
||
this.$imeSetting.css( {
|
||
top: top,
|
||
left: left
|
||
} );
|
||
|
||
menuWidth = this.$menu.width();
|
||
overflowsOnRight = ( left + menuWidth ) > $window.width();
|
||
|
||
// Adjust horizontal position if there's
|
||
// not enough space on any side
|
||
if ( menuWidth > left ||
|
||
rtlElement && overflowsOnRight
|
||
) {
|
||
if ( rtlElement ) {
|
||
if ( overflowsOnRight ) {
|
||
this.$menu.addClass( 'ime-right' );
|
||
menuLeft = this.$imeSetting.outerWidth() - menuWidth;
|
||
} else {
|
||
menuLeft = 0;
|
||
}
|
||
} else {
|
||
this.$menu.addClass( 'ime-right' );
|
||
menuLeft = elementPosition.left;
|
||
}
|
||
|
||
this.$menu.css( 'left', menuLeft );
|
||
}
|
||
},
|
||
|
||
/**
|
||
* Select a language
|
||
*
|
||
* @param {string} languageCode
|
||
* @return {string|bool} Selected input method id or false
|
||
*/
|
||
selectLanguage: function ( languageCode ) {
|
||
var ime, imePref, language;
|
||
|
||
// consider language codes case insensitive
|
||
languageCode = languageCode && languageCode.toLowerCase();
|
||
|
||
ime = this.$element.data( 'ime' );
|
||
imePref = $.ime.preferences.getIM( languageCode );
|
||
language = $.ime.languages[languageCode];
|
||
|
||
this.setMenuTitle( this.getAutonym( languageCode ) );
|
||
|
||
if ( !language ) {
|
||
return false;
|
||
}
|
||
|
||
if ( ime.getLanguage() === languageCode ) {
|
||
// Nothing to do. It is same as the current language,
|
||
// but check whether the input method changed.
|
||
if ( ime.inputmethod && ime.inputmethod.id !== imePref ) {
|
||
this.selectIM( $.ime.preferences.getIM( languageCode ) );
|
||
}
|
||
|
||
return $.ime.preferences.getIM( languageCode );
|
||
}
|
||
|
||
this.$menu.find( 'li.ime-lang' ).show();
|
||
this.$menu.find( 'li[lang=' + languageCode + ']' ).hide();
|
||
|
||
this.prepareInputMethods( languageCode );
|
||
this.hide();
|
||
// And select the default inputmethod
|
||
ime.setLanguage( languageCode );
|
||
this.inputmethod = null;
|
||
this.selectIM( $.ime.preferences.getIM( languageCode ) );
|
||
|
||
return $.ime.preferences.getIM( languageCode );
|
||
},
|
||
|
||
/**
|
||
* Get the autonym by language code.
|
||
*
|
||
* @param {string} languageCode
|
||
* @return {string} The autonym
|
||
*/
|
||
getAutonym: function ( languageCode ) {
|
||
return $.ime.languages[languageCode]
|
||
&& $.ime.languages[languageCode].autonym;
|
||
},
|
||
|
||
/**
|
||
* Set the title of the selector menu.
|
||
*
|
||
* @param {string} title
|
||
*/
|
||
setMenuTitle: function ( title ) {
|
||
this.$menu.find( '.ime-list-title' ).text( title );
|
||
},
|
||
|
||
/**
|
||
* Decide on initial language to select
|
||
*/
|
||
decideLanguage: function () {
|
||
if ( $.ime.preferences.getLanguage() ) {
|
||
// There has been an override by the user,
|
||
// so return the language selected by user
|
||
return $.ime.preferences.getLanguage();
|
||
}
|
||
|
||
if ( this.$element.attr( 'lang' ) &&
|
||
$.ime.languages[ this.$element.attr( 'lang' ) ]
|
||
) {
|
||
return this.$element.attr( 'lang' );
|
||
}
|
||
|
||
// There is either no IMs for the given language attr
|
||
// or there is no lang attr at all.
|
||
return $.ime.preferences.getDefaultLanguage();
|
||
},
|
||
|
||
/**
|
||
* Select an input method
|
||
*
|
||
* @param {string} inputmethodId
|
||
*/
|
||
selectIM: function ( inputmethodId ) {
|
||
var imeselector = this,
|
||
ime;
|
||
|
||
if ( !inputmethodId ) {
|
||
return;
|
||
}
|
||
|
||
this.$menu.find( '.ime-checked' ).removeClass( 'ime-checked' );
|
||
this.$menu.find( 'li[data-ime-inputmethod=' + inputmethodId + ']' )
|
||
.addClass( 'ime-checked' );
|
||
ime = this.$element.data( 'ime' );
|
||
|
||
if ( inputmethodId === 'system' ) {
|
||
this.disableIM();
|
||
|
||
return;
|
||
}
|
||
|
||
ime.load( inputmethodId ).done( function () {
|
||
imeselector.inputmethod = $.ime.inputmethods[inputmethodId];
|
||
imeselector.hide();
|
||
ime.enable();
|
||
ime.setIM( inputmethodId );
|
||
imeselector.$imeSetting.find( 'a.ime-name' ).text(
|
||
$.ime.sources[inputmethodId].name
|
||
);
|
||
|
||
imeselector.position();
|
||
|
||
// Save this preference
|
||
$.ime.preferences.save();
|
||
} );
|
||
},
|
||
|
||
/**
|
||
* Disable the inputmethods (Use the system input method)
|
||
*/
|
||
disableIM: function () {
|
||
this.$menu.find( '.ime-checked' ).removeClass( 'ime-checked' );
|
||
this.$menu.find( 'div.ime-disable' ).addClass( 'ime-checked' );
|
||
this.$element.data( 'ime' ).disable();
|
||
this.$imeSetting.find( 'a.ime-name' ).text( '' );
|
||
this.hide();
|
||
this.position();
|
||
|
||
// Save this preference
|
||
$.ime.preferences.save();
|
||
},
|
||
|
||
/**
|
||
* Prepare language list
|
||
*/
|
||
prepareLanguageList: function () {
|
||
var languageCodeIndex,
|
||
$languageListWrapper,
|
||
$languageList,
|
||
languageList,
|
||
$languageItem,
|
||
$language,
|
||
languageCode,
|
||
language;
|
||
|
||
// Language list can be very long, so we use a container with
|
||
// overflow auto
|
||
$languageListWrapper = $( '<div class="ime-language-list-wrapper">' );
|
||
$languageList = $( '<ul class="ime-language-list">' );
|
||
|
||
if ( $.isFunction( this.options.languages ) ) {
|
||
languageList = this.options.languages();
|
||
} else {
|
||
languageList = this.options.languages;
|
||
}
|
||
|
||
for ( languageCodeIndex in languageList ) {
|
||
languageCode = languageList[languageCodeIndex];
|
||
language = $.ime.languages[languageCode];
|
||
|
||
if ( !language ) {
|
||
continue;
|
||
}
|
||
|
||
$languageItem = $( '<a>' )
|
||
.attr( 'href', '#' )
|
||
.text( this.getAutonym( languageCode ) )
|
||
.addClass( 'selectable-row-item autonym' );
|
||
$language = $( '<li class="ime-lang selectable-row">' ).attr( 'lang', languageCode );
|
||
$language.append( $languageItem );
|
||
$languageList.append( $language );
|
||
}
|
||
|
||
$languageListWrapper.append( $languageList );
|
||
this.$menu.append( $languageListWrapper );
|
||
|
||
if ( this.options.languageSelector ) {
|
||
this.$menu.append( this.options.languageSelector() );
|
||
}
|
||
},
|
||
|
||
/**
|
||
* Prepare input methods in menu for the given language code
|
||
*
|
||
* @param {string} languageCode
|
||
*/
|
||
prepareInputMethods: function ( languageCode ) {
|
||
var language = $.ime.languages[languageCode],
|
||
$imeList = this.$menu.find( '.ime-list' ),
|
||
imeSelector = this;
|
||
|
||
$imeList.empty();
|
||
|
||
$.each( language.inputmethods, function ( index, inputmethod ) {
|
||
var $imeItem, $inputMethod, source, name;
|
||
|
||
source = $.ime.sources[inputmethod];
|
||
if ( !source ) {
|
||
return;
|
||
}
|
||
name = source.name;
|
||
|
||
$imeItem = $( '<a>' )
|
||
.attr( 'href', '#' )
|
||
.text( name )
|
||
.addClass( 'selectable-row-item' );
|
||
|
||
$inputMethod = $( '<li>' )
|
||
.attr( 'data-ime-inputmethod', inputmethod )
|
||
.addClass( 'ime-im selectable-row' )
|
||
.append( '<span class="ime-im-check"></span>', $imeItem );
|
||
|
||
if ( imeSelector.options.helpHandler ) {
|
||
$inputMethod.append( imeSelector.options.helpHandler.call( imeSelector, inputmethod ) );
|
||
}
|
||
|
||
$imeList.append( $inputMethod );
|
||
} );
|
||
},
|
||
|
||
/**
|
||
* Create a help link element.
|
||
* @return {jQuery}
|
||
*/
|
||
helpLink: function () {
|
||
return $( '<div class="ime-help-link selectable-row">' )
|
||
.append( $( '<a>' ).text( 'Help' )
|
||
.addClass( 'selectable-row-item' )
|
||
.attr( {
|
||
'href': 'http://github.com/wikimedia/jquery.ime',
|
||
'target': '_blank',
|
||
'data-i18n': 'jquery-ime-help'
|
||
} )
|
||
);
|
||
}
|
||
};
|
||
|
||
IMESelector.defaults = {
|
||
defaultLanguage: 'en',
|
||
timeout: 2500 // Milliseconds after which IME widget hides itself.
|
||
};
|
||
|
||
/*
|
||
* imeselector PLUGIN DEFINITION
|
||
*/
|
||
|
||
$.fn.imeselector = function ( options ) {
|
||
return this.each( function () {
|
||
var $this = $( this ),
|
||
data = $this.data( 'imeselector' );
|
||
|
||
if ( !data ) {
|
||
$this.data( 'imeselector', ( data = new IMESelector( this, options ) ) );
|
||
}
|
||
|
||
if ( typeof options === 'string' ) {
|
||
data[options].call( $this );
|
||
}
|
||
} );
|
||
};
|
||
|
||
$.fn.imeselector.Constructor = IMESelector;
|
||
|
||
function languageListTitle() {
|
||
return $( '<h3>' )
|
||
.addClass( 'ime-lang-title' )
|
||
.attr( 'data-i18n', 'jquery-ime-other-languages' )
|
||
.text( 'Other languages' );
|
||
}
|
||
|
||
function imeList() {
|
||
return $( '<ul>' ).addClass( 'ime-list' );
|
||
}
|
||
|
||
function imeListTitle() {
|
||
return $( '<h3>' ).addClass( 'ime-list-title autonym' );
|
||
}
|
||
|
||
function toggleMenuItem() {
|
||
return $( '<div class="ime-disable selectable-row">' ).append(
|
||
$( '<span>' )
|
||
.attr( {
|
||
'class': 'ime-disable-link',
|
||
'data-i18n': 'jquery-ime-disable-text'
|
||
} )
|
||
.addClass( 'ime-checked' )
|
||
.text( 'System input method' ),
|
||
$( '<span>' )
|
||
.addClass( 'ime-disable-shortcut' )
|
||
.text( 'CTRL+M' )
|
||
);
|
||
}
|
||
|
||
selectorTemplate = '<div class="imeselector imeselector-toggle">' +
|
||
'<a class="ime-name imeselector-toggle" href="#"></a>' +
|
||
'<b class="ime-setting-caret imeselector-toggle"></b></div>';
|
||
|
||
MutationObserver = window.MutationObserver ||
|
||
window.WebKitMutationObserver ||
|
||
window.MozMutationObserver;
|
||
|
||
/**
|
||
* Check whether a keypress event corresponds to the shortcut key
|
||
*
|
||
* @param {event} event
|
||
* @return {bool} true if the key is a shortcut key
|
||
*/
|
||
function isShortcutKey( event ) {
|
||
// 77 - The letter M, for Ctrl-M
|
||
// 13 - The Enter key
|
||
return event.ctrlKey && !event.altKey && ( event.which === 77 || event.which === 13 );
|
||
}
|
||
|
||
function isDOMAttrModifiedSupported() {
|
||
var p = document.createElement( 'p' ),
|
||
flag = false;
|
||
|
||
if ( p.addEventListener ) {
|
||
p.addEventListener( 'DOMAttrModified', function () {
|
||
flag = true;
|
||
}, false );
|
||
} else if ( p.attachEvent ) {
|
||
p.attachEvent( 'onDOMAttrModified', function () {
|
||
flag = true;
|
||
} );
|
||
} else {
|
||
return false;
|
||
}
|
||
|
||
p.setAttribute( 'id', 'target' );
|
||
|
||
return flag;
|
||
}
|
||
|
||
$.fn.attrchange = function ( callback ) {
|
||
if ( MutationObserver ) {
|
||
var observer;
|
||
|
||
observer = new MutationObserver( function ( mutations ) {
|
||
mutations.forEach( function ( e ) {
|
||
callback.call( e.target, e.attributeName );
|
||
} );
|
||
} );
|
||
|
||
return this.each( function () {
|
||
observer.observe( this, {
|
||
subtree: false,
|
||
attributes: true
|
||
} );
|
||
} );
|
||
} else if ( isDOMAttrModifiedSupported() ) {
|
||
return this.on( 'DOMAttrModified', function ( e ) {
|
||
callback.call( this, e.attrName );
|
||
} );
|
||
} else if ( 'onpropertychange' in document.body ) {
|
||
return this.on( 'propertychange', function () {
|
||
callback.call( this, window.event.propertyName );
|
||
} );
|
||
}
|
||
};
|
||
}( jQuery ) );
|
||
|
||
( function ( $ ) {
|
||
'use strict';
|
||
|
||
$.extend( $.ime.preferences, {
|
||
registry: {
|
||
isDirty: false,
|
||
language: null,
|
||
previousLanguages: [], // array of previous languages
|
||
previousInputMethods: [], // array of previous inputmethods
|
||
imes: {
|
||
'en': 'system'
|
||
}
|
||
},
|
||
|
||
setLanguage: function ( language ) {
|
||
// Do nothing if there's no actual change
|
||
if ( language === this.registry.language ) {
|
||
return;
|
||
}
|
||
|
||
this.registry.language = language;
|
||
this.registry.isDirty = true;
|
||
if ( !this.registry.previousLanguages ) {
|
||
this.registry.previousLanguages = [];
|
||
}
|
||
|
||
// Add to the previous languages, but avoid duplicates.
|
||
if ( $.inArray( language, this.registry.previousLanguages ) === -1 ) {
|
||
this.registry.previousLanguages.unshift( language );
|
||
this.registry.previousLanguages = this.registry.previousLanguages.slice( 0, 5 );
|
||
}
|
||
},
|
||
|
||
getLanguage: function () {
|
||
return this.registry.language;
|
||
},
|
||
|
||
getDefaultLanguage: function () {
|
||
return 'en';
|
||
},
|
||
|
||
getPreviousLanguages: function () {
|
||
return this.registry.previousLanguages;
|
||
},
|
||
|
||
getPreviousInputMethods: function () {
|
||
return this.registry.previousInputMethods;
|
||
},
|
||
|
||
// Set the given IM as the last used for the language
|
||
setIM: function ( inputMethod ) {
|
||
if ( !this.registry.imes ) {
|
||
this.registry.imes = {};
|
||
}
|
||
|
||
// Do nothing if there's no actual change
|
||
if ( inputMethod === this.registry.imes[this.registry.language] ) {
|
||
return;
|
||
}
|
||
|
||
this.registry.imes[this.getLanguage()] = inputMethod;
|
||
this.registry.isDirty = true;
|
||
if ( !this.registry.previousInputMethods ) {
|
||
this.registry.previousInputMethods = [];
|
||
}
|
||
|
||
// Add to the previous languages,
|
||
if ( inputMethod !== 'system' ) {
|
||
this.registry.previousInputMethods.unshift( inputMethod );
|
||
this.registry.previousInputMethods = this.registry.previousInputMethods.slice( 0, 5 );
|
||
}
|
||
},
|
||
|
||
// Return the last used or the default IM for language
|
||
getIM: function ( language ) {
|
||
if ( !this.registry.imes ) {
|
||
this.registry.imes = {};
|
||
}
|
||
|
||
return this.registry.imes[language] || 'system';
|
||
},
|
||
|
||
save: function () {
|
||
// save registry in cookies or localstorage
|
||
},
|
||
|
||
load: function () {
|
||
// load registry from cookies or localstorage
|
||
}
|
||
} );
|
||
}( jQuery ) );
|
||
|
||
( function ( $ ) {
|
||
'use strict';
|
||
|
||
$.extend( $.ime.sources, {
|
||
'am-transliteration': {
|
||
name: 'ትራንስልተራትዖን',
|
||
source: 'rules/am/am-transliteration.js'
|
||
},
|
||
'ar-kbd': {
|
||
name: 'أرابيك',
|
||
source: 'rules/ar/ar-kbd.js'
|
||
},
|
||
'as-avro': {
|
||
name: 'অভ্ৰ',
|
||
source: 'rules/as/as-avro.js'
|
||
},
|
||
'as-bornona': {
|
||
name: 'বৰ্ণনা',
|
||
source: 'rules/as/as-bornona.js'
|
||
},
|
||
'as-inscript': {
|
||
name: 'ইনস্ক্ৰিপ্ট',
|
||
source: 'rules/as/as-inscript.js'
|
||
},
|
||
'as-inscript2': {
|
||
name: 'ইনস্ক্ৰিপ্ট ২',
|
||
source: 'rules/as/as-inscript2.js'
|
||
},
|
||
'as-phonetic': {
|
||
name: 'ফনেটিক',
|
||
source: 'rules/as/as-phonetic.js'
|
||
},
|
||
'as-transliteration': {
|
||
name: 'প্ৰতিৰূপান্তৰণ',
|
||
source: 'rules/as/as-transliteration.js'
|
||
},
|
||
'be-kbd': {
|
||
name: 'Стандартная',
|
||
source: 'rules/be/be-kbd.js'
|
||
},
|
||
'be-latin': {
|
||
name: 'Łacinka',
|
||
source: 'rules/be/be-latin.js'
|
||
},
|
||
'be-transliteration': {
|
||
name: 'Транслітэрацыя',
|
||
source: 'rules/be/be-transliteration.js'
|
||
},
|
||
'ber-tfng': {
|
||
name: 'Tifinagh',
|
||
source: 'rules/ber/ber-tfng.js'
|
||
},
|
||
'bn-avro': {
|
||
name: 'অভ্র',
|
||
source: 'rules/bn/bn-avro.js'
|
||
},
|
||
'bn-inscript': {
|
||
name: 'ইনস্ক্ৰিপ্ট',
|
||
source: 'rules/bn/bn-inscript.js'
|
||
},
|
||
'bn-inscript2': {
|
||
name: 'ইনস্ক্ৰিপ্ট ২',
|
||
source: 'rules/bn/bn-inscript2.js'
|
||
},
|
||
'bn-nkb': {
|
||
name: 'ন্যাশনাল কিবোর্ড',
|
||
source: 'rules/bn/bn-nkb.js'
|
||
},
|
||
'bn-probhat': {
|
||
name: 'প্রভাত',
|
||
source: 'rules/bn/bn-probhat.js'
|
||
},
|
||
'brx-inscript': {
|
||
name: 'इनस्क्रिप्ट',
|
||
source: 'rules/brx/brx-inscript.js'
|
||
},
|
||
'brx-inscript2': {
|
||
name: 'इनस्क्रिप्ट २',
|
||
source: 'rules/brx/brx-inscript2.js'
|
||
},
|
||
'ckb-transliteration-arkbd': {
|
||
name: 'باشووری',
|
||
source: 'rules/ckb/ckb-transliteration-arkbd.js'
|
||
},
|
||
'ckb-transliteration-fakbd': {
|
||
name: 'ڕۆژھەڵاتی',
|
||
source: 'rules/ckb/ckb-transliteration-fakbd.js'
|
||
},
|
||
'ckb-transliteration-lakbd': {
|
||
name: 'لاتینی',
|
||
source: 'rules/ckb/ckb-transliteration-lakbd.js'
|
||
},
|
||
'cv-cyr-altgr': {
|
||
name: 'Чăвашла (AltGr)',
|
||
source: 'rules/cv/cv-cyr-altgr.js'
|
||
},
|
||
'cv-lat-altgr': {
|
||
name: 'Căvašla (AltGr)',
|
||
source: 'rules/cv/cv-lat-altgr.js'
|
||
},
|
||
'cv-cyr-numbers': {
|
||
name: 'Чăвашла (цифрилисем)',
|
||
source: 'rules/cv/cv-cyr-numbers.js'
|
||
},
|
||
'cyrl-palochka': {
|
||
name: 'Palochka',
|
||
source: 'rules/cyrl/cyrl-palochka.js'
|
||
},
|
||
'da-normforms': {
|
||
name: 'normalformer',
|
||
source: 'rules/da/da-normforms.js'
|
||
},
|
||
'doi-inscript2': {
|
||
name: 'इनस्क्रिप्ट २',
|
||
source: 'rules/doi/doi-inscript2.js'
|
||
},
|
||
'eo-transliteration': {
|
||
name: 'transliterigo',
|
||
source: 'rules/eo/eo-transliteration.js'
|
||
},
|
||
'eo-h': {
|
||
name: 'Esperanto h',
|
||
source: 'rules/eo/eo-h.js'
|
||
},
|
||
'eo-h-f': {
|
||
name: 'Esperanto h fundamente',
|
||
source: 'rules/eo/eo-h-f.js'
|
||
},
|
||
'eo-plena': {
|
||
name: 'Esperanto plena',
|
||
source: 'rules/eo/eo-plena.js'
|
||
},
|
||
'eo-q': {
|
||
name: 'Esperanto q sistemo',
|
||
source: 'rules/eo/eo-q.js'
|
||
},
|
||
'eo-vi': {
|
||
name: 'Esperanto vi sistemo',
|
||
source: 'rules/eo/eo-vi.js'
|
||
},
|
||
'eo-x': {
|
||
name: 'Esperanto x sistemo',
|
||
source: 'rules/eo/eo-x.js'
|
||
},
|
||
'fa-kbd': {
|
||
name: 'فارسی',
|
||
source: 'rules/fa/fa-kbd.js'
|
||
},
|
||
'fo-normforms': {
|
||
name: 'Føroyskt',
|
||
source: 'rules/fo/fo-normforms.js'
|
||
},
|
||
'fi-transliteration': {
|
||
name: 'translitterointi',
|
||
source: 'rules/fi/fi-transliteration.js'
|
||
},
|
||
'hi-transliteration': {
|
||
name: 'लिप्यंतरण',
|
||
source: 'rules/hi/hi-transliteration.js'
|
||
},
|
||
'hi-inscript': {
|
||
name: 'इनस्क्रिप्ट',
|
||
source: 'rules/hi/hi-inscript.js'
|
||
},
|
||
'hi-inscript2': {
|
||
name: 'इनस्क्रिप्ट २',
|
||
source: 'rules/hi/hi-inscript2.js'
|
||
},
|
||
'hi-phonetic': {
|
||
name: 'फोनेटिक',
|
||
source: 'rules/hi/hi-phonetic.js'
|
||
},
|
||
'is-normforms': {
|
||
name: 'Venjuleg eyðublöð',
|
||
source: 'rules/is/is-normforms.js'
|
||
},
|
||
'jv-transliteration': {
|
||
name: 'Transliteration',
|
||
source: 'rules/jv/jv-transliteration.js'
|
||
},
|
||
'mai-inscript': {
|
||
name: 'इनस्क्रिप्ट',
|
||
source: 'rules/mai/mai-inscript.js',
|
||
depends: 'hi-inscript'
|
||
},
|
||
'mai-inscript2': {
|
||
name: 'इनस्क्रिप्ट २',
|
||
source: 'rules/mai/mai-inscript2.js',
|
||
depends: 'hi-inscript2'
|
||
},
|
||
'hi-bolnagri': {
|
||
name: 'बोलनागरी',
|
||
source: 'rules/hi/hi-bolnagri.js'
|
||
},
|
||
'ml-transliteration': {
|
||
name: 'ലിപ്യന്തരണം',
|
||
source: 'rules/ml/ml-transliteration.js'
|
||
},
|
||
'ml-inscript': {
|
||
name: 'ഇൻസ്ക്രിപ്റ്റ്',
|
||
source: 'rules/ml/ml-inscript.js'
|
||
},
|
||
'ml-inscript2': {
|
||
name: 'ഇൻസ്ക്രിപ്റ്റ് 2',
|
||
source: 'rules/ml/ml-inscript2.js'
|
||
},
|
||
'sv-normforms': {
|
||
name: 'Normal forms',
|
||
source: 'rules/sv/sv-normforms.js'
|
||
},
|
||
'ta-inscript': {
|
||
name: 'இன்ஸ்கிரிப்ட்',
|
||
source: 'rules/ta/ta-inscript.js'
|
||
},
|
||
'ta-inscript2': {
|
||
name: 'இன்ஸ்கிரிப்ட் 2',
|
||
source: 'rules/ta/ta-inscript2.js'
|
||
},
|
||
'ta-transliteration': {
|
||
name: 'எழுத்துப்பெயர்ப்பு',
|
||
source: 'rules/ta/ta-transliteration.js'
|
||
},
|
||
'ta-99': {
|
||
name: 'தமிழ்99',
|
||
source: 'rules/ta/ta-99.js'
|
||
},
|
||
'ta-bamini': {
|
||
name: 'பாமினி',
|
||
source: 'rules/ta/ta-bamini.js'
|
||
},
|
||
'th-kedmanee': {
|
||
name: 'เกษมณี',
|
||
source: 'rules/th/th-kedmanee.js'
|
||
},
|
||
'th-pattachote': {
|
||
name: 'ปัตตะโชติ',
|
||
source: 'rules/th/th-pattachote.js'
|
||
},
|
||
'de-transliteration': {
|
||
name: 'Deutsch Tilde',
|
||
source: 'rules/de/de-transliteration.js'
|
||
},
|
||
'el-kbd': {
|
||
name: 'Τυπική πληκτρολόγιο',
|
||
source: 'rules/el/el-kbd.js'
|
||
},
|
||
'he-standard-2012': {
|
||
name: 'עברית עם ניקוד על בסיס אנגלית',
|
||
source: 'rules/he/he-standard-2012.js'
|
||
},
|
||
'he-standard-2012-extonly': {
|
||
name: 'עברית עם ניקוד',
|
||
source: 'rules/he/he-standard-2012-extonly.js'
|
||
},
|
||
'hr-kbd': {
|
||
name: 'Croatian kbd',
|
||
source: 'rules/hr/hr-kbd.js'
|
||
},
|
||
'hy-ephonetic': {
|
||
name: 'Հնչյունային դասավորություն',
|
||
source: 'rules/hy/hy-ephonetic.js'
|
||
},
|
||
'hy-typewriter': {
|
||
name: 'Գրամեքենայի դասավորություն',
|
||
source: 'rules/hy/hy-typewriter.js'
|
||
},
|
||
'hy-ephoneticalt': {
|
||
name: 'Հնչյունային նոր (R→Ր, F→Թ)',
|
||
source: 'rules/hy/hy-ephoneticalt.js'
|
||
},
|
||
'hy-emslegacy': {
|
||
name: 'Մայքրոսոֆթի հին արևելահայերեն',
|
||
source: 'rules/hy/hy-emslegacy.js'
|
||
},
|
||
'hy-wmslegacy': {
|
||
name: 'Մայքրոսոֆթի հին արևմտահայերեն',
|
||
source: 'rules/hy/hy-wmslegacy.js'
|
||
},
|
||
'gu-inscript': {
|
||
name: 'ઇનસ્ક્રિપ્ટ',
|
||
source: 'rules/gu/gu-inscript.js'
|
||
},
|
||
'gu-inscript2': {
|
||
name: 'ઇનસ્ક્રિપ્ટ ૨',
|
||
source: 'rules/gu/gu-inscript2.js'
|
||
},
|
||
'gu-phonetic': {
|
||
name: 'ફોનૅટિક',
|
||
source: 'rules/gu/gu-phonetic.js'
|
||
},
|
||
'gu-transliteration': {
|
||
name: 'લિપ્યાંતરણ',
|
||
source: 'rules/gu/gu-transliteration.js'
|
||
},
|
||
'ka-transliteration': {
|
||
name: 'ტრანსლიტერაცია',
|
||
source: 'rules/ka/ka-transliteration.js'
|
||
},
|
||
'ka-kbd': {
|
||
name: 'სტანდარტული კლავიატურის',
|
||
source: 'rules/ka/ka-kbd.js'
|
||
},
|
||
'kk-arabic': {
|
||
name: 'Kazakh Arabic transliteration',
|
||
source: 'rules/kk/kk-arabic.js'
|
||
},
|
||
'kk-kbd': {
|
||
name: 'Кирил',
|
||
source: 'rules/kk/kk-kbd.js'
|
||
},
|
||
'kn-inscript': {
|
||
name: 'ಇನ್ಸ್ಕ್ರಿಪ್ಟ್',
|
||
source: 'rules/kn/kn-inscript.js'
|
||
},
|
||
'kn-inscript2': {
|
||
name: 'ಇನ್\u200cಸ್ಕ್ರಿಪ್ಟ್ ೨',
|
||
source: 'rules/kn/kn-inscript2.js'
|
||
},
|
||
'kn-transliteration': {
|
||
name: 'ಲಿಪ್ಯಂತರಣ',
|
||
source: 'rules/kn/kn-transliteration.js'
|
||
},
|
||
'kn-kgp': {
|
||
name: 'KGP/Nudi/KP Rao',
|
||
source: 'rules/kn/kn-kgp.js'
|
||
},
|
||
'ky-cyrl-alt': {
|
||
name: 'Кыргыз Alt',
|
||
source: 'rules/ky/ky-cyrl-alt.js'
|
||
},
|
||
'gom-inscript2': {
|
||
name: 'इनस्क्रिप्ट २',
|
||
source: 'rules/gom/gom-inscript2.js'
|
||
},
|
||
'ks-inscript': {
|
||
name: 'इनस्क्रिप्ट',
|
||
source: 'rules/ks/ks-inscript.js'
|
||
},
|
||
'ks-kbd': {
|
||
name: 'Kashmiri Arabic',
|
||
source: 'rules/ks/ks-kbd.js'
|
||
},
|
||
'ku-h': {
|
||
name: 'Kurdî-h',
|
||
source: 'rules/ku/ku-h.js'
|
||
},
|
||
'ku-tr': {
|
||
name: 'Kurdî-tr',
|
||
source: 'rules/ku/ku-tr.js'
|
||
},
|
||
'lo-kbd': {
|
||
name: 'າຶກ',
|
||
source: 'rules/lo/lo-kbd.js'
|
||
},
|
||
'mh': {
|
||
name: 'Kajin M̧ajeļ',
|
||
source: 'rules/mh/mh.js'
|
||
},
|
||
'mn-cyrl': {
|
||
name: 'Кирилл',
|
||
source: 'rules/mn/mn-cyrl.js'
|
||
},
|
||
'mni-inscript2': {
|
||
name: 'ইনস্ক্ৰিপ্ট ২',
|
||
source: 'rules/mni/mni-inscript2.js'
|
||
},
|
||
'mr-inscript': {
|
||
name: 'मराठी लिपी',
|
||
source: 'rules/mr/mr-inscript.js'
|
||
},
|
||
'mr-inscript2': {
|
||
name: 'मराठी इनस्क्रिप्ट २',
|
||
source: 'rules/mr/mr-inscript2.js'
|
||
},
|
||
'mr-transliteration': {
|
||
name: 'अक्षरांतरण',
|
||
source: 'rules/mr/mr-transliteration.js'
|
||
},
|
||
'mr-phonetic': {
|
||
name: 'फोनेटिक',
|
||
source: 'rules/mr/mr-phonetic.js'
|
||
},
|
||
'my-xkb': {
|
||
name: 'မြန်မာဘာသာ xkb',
|
||
source: 'rules/my/my-xkb.js'
|
||
},
|
||
'ne-inscript': {
|
||
name: 'इनस्क्रिप्ट',
|
||
source: 'rules/ne/ne-inscript.js'
|
||
},
|
||
'ne-inscript2': {
|
||
name: 'इनस्क्रिप्ट २',
|
||
source: 'rules/ne/ne-inscript2.js'
|
||
},
|
||
'ne-transliteration': {
|
||
name: 'ट्रांस्लितेरेशन',
|
||
source: 'rules/ne/ne-transliteration.js'
|
||
},
|
||
'ne-rom': {
|
||
name: 'Romanized',
|
||
source: 'rules/ne/ne-rom.js'
|
||
},
|
||
'ne-trad': {
|
||
name: 'Traditional',
|
||
source: 'rules/ne/ne-trad.js'
|
||
},
|
||
'nb-normforms': {
|
||
name: 'Normal transliterasjon',
|
||
source: 'rules/nb/nb-normforms.js'
|
||
},
|
||
'nb-tildeforms': {
|
||
name: 'Tildemerket transliterasjon',
|
||
source: 'rules/nb/nb-tildeforms.js'
|
||
},
|
||
'nn-tildeforms': {
|
||
name: 'Tildemerkt transliterasjon',
|
||
source: 'rules/nb/nb-tildeforms.js'
|
||
},
|
||
'or-transliteration': {
|
||
name: 'ଟ୍ରାନ୍ସଲିଟରେସନ',
|
||
source: 'rules/or/or-transliteration.js'
|
||
},
|
||
'or-inscript': {
|
||
name: 'ଇନସ୍କ୍ରିପ୍ଟ',
|
||
source: 'rules/or/or-inscript.js'
|
||
},
|
||
'or-inscript2': {
|
||
name: 'ଇନସ୍କ୍ରିପ୍ଟ2',
|
||
source: 'rules/or/or-inscript2.js'
|
||
},
|
||
'or-lekhani': {
|
||
name: 'ଲେଖନୀ',
|
||
source: 'rules/or/or-lekhani.js'
|
||
},
|
||
'or-phonetic': {
|
||
name: 'ଫୋନେଟିକ',
|
||
source: 'rules/or/or-phonetic.js'
|
||
},
|
||
'sd-inscript2': {
|
||
name: 'इनस्क्रिप्ट २',
|
||
source: 'rules/sd/sd-inscript2.js'
|
||
},
|
||
'se-normforms': {
|
||
name: 'Normal forms',
|
||
source: 'rules/se/se-normforms.js'
|
||
},
|
||
'sk-kbd': {
|
||
name: 'Štandardná',
|
||
source: 'rules/sk/sk-kbd.js'
|
||
},
|
||
'sr-kbd': {
|
||
name: 'Стандардна',
|
||
source: 'rules/sr/sr-kbd.js'
|
||
},
|
||
'te-inscript': {
|
||
name: 'ఇన్\u200dస్క్రిప్ట్',
|
||
source: 'rules/te/te-inscript.js'
|
||
},
|
||
'te-inscript2': {
|
||
name: 'ఇన్\u200dస్క్రిప్ట్ 2',
|
||
source: 'rules/te/te-inscript2.js'
|
||
},
|
||
'te-transliteration': {
|
||
name: 'లిప్యంతరీకరణ',
|
||
source: 'rules/te/te-transliteration.js'
|
||
},
|
||
'pa-inscript': {
|
||
name: 'ਇਨਸ੍ਕ੍ਰਿਪ੍ਟ',
|
||
source: 'rules/pa/pa-inscript.js'
|
||
},
|
||
'pa-inscript2': {
|
||
name: 'ਇਨਸ੍ਕ੍ਰਿਪ੍ਟ2',
|
||
source: 'rules/pa/pa-inscript2.js'
|
||
},
|
||
'pa-jhelum': {
|
||
name: 'ਜੇਹਲਮ',
|
||
source: 'rules/pa/pa-jhelum.js'
|
||
},
|
||
'pa-transliteration': {
|
||
name: 'ਤ੍ਰਾਨ੍ਸ੍ਲਿਤੇਰਾਤਿਓਂ',
|
||
source: 'rules/pa/pa-transliteration.js'
|
||
},
|
||
'pa-phonetic': {
|
||
name: 'ਫੋਨੇਟਿਕ',
|
||
source: 'rules/pa/pa-phonetic.js'
|
||
},
|
||
'ru-jcuken': {
|
||
name: 'ЙЦУКЕН',
|
||
source: 'rules/ru/ru-jcuken.js'
|
||
},
|
||
'ru-kbd': {
|
||
name: 'кбд',
|
||
source: 'rules/ru/ru-kbd.js'
|
||
},
|
||
'ru-phonetic': {
|
||
name: 'фонетический',
|
||
source: 'rules/ru/ru-phonetic.js'
|
||
},
|
||
'ru-yawerty': {
|
||
name: 'yawerty',
|
||
source: 'rules/ru/ru-yawerty.js'
|
||
},
|
||
'sa-iast': {
|
||
name: 'Romanized',
|
||
source: 'rules/sa/sa-iast.js'
|
||
},
|
||
'sa-inscript': {
|
||
name: 'इनस्क्रिप्ट',
|
||
source: 'rules/sa/sa-inscript.js'
|
||
},
|
||
'sa-inscript2': {
|
||
name: 'इनस्क्रिप्ट २',
|
||
source: 'rules/sa/sa-inscript2.js'
|
||
},
|
||
'sa-transliteration': {
|
||
name: 'ट्रन्स्लितेरतिओन्',
|
||
source: 'rules/sa/sa-transliteration.js'
|
||
},
|
||
'sah-transliteration': {
|
||
name: 'Transliteration',
|
||
source: 'rules/sah/sah-transliteration.js'
|
||
},
|
||
'sat-inscript2': {
|
||
name: 'इनस्क्रिप्ट २',
|
||
source: 'rules/sat/sat-inscript2.js'
|
||
},
|
||
'si-singlish': {
|
||
name: 'සිංග්ලිෂ්',
|
||
source: 'rules/si/si-singlish.js'
|
||
},
|
||
'si-wijesekara': {
|
||
name: 'විජේසේකර',
|
||
source: 'rules/si/si-wijesekara.js'
|
||
},
|
||
'ur-phonetic': {
|
||
name: 'صوتی',
|
||
source: 'rules/ur/ur-phonetic.js'
|
||
},
|
||
'ur-transliteration': {
|
||
name: 'ٹرانسلٹریشن',
|
||
source: 'rules/ur/ur-transliteration.js'
|
||
},
|
||
'ipa-sil': {
|
||
name: 'International Phonetic Alphabet - SIL',
|
||
source: 'rules/fonipa/ipa-sil.js'
|
||
},
|
||
'ipa-x-sampa': {
|
||
name: 'International Phonetic Alphabet - X-SAMPA',
|
||
source: 'rules/fonipa/ipa-x-sampa.js'
|
||
},
|
||
'udm-alt': {
|
||
name: 'Удмурт ALT',
|
||
source: 'rules/udm/udm-alt.js'
|
||
},
|
||
'uk-kbd': {
|
||
name: 'кбд',
|
||
source: 'rules/uk/uk-kbd.js'
|
||
},
|
||
'ug-kbd': {
|
||
name: 'Uyghur kbd',
|
||
source: 'rules/ug/ug-kbd.js'
|
||
},
|
||
'uz-kbd': {
|
||
name: 'Uzbek kbd',
|
||
source: 'rules/uz/uz-kbd.js'
|
||
},
|
||
'vec-GVU': {
|
||
name: 'Venetian',
|
||
source: 'rules/vec/vec-GVU.js'
|
||
}
|
||
} );
|
||
|
||
$.extend( $.ime.languages, {
|
||
'ady': {
|
||
autonym: 'адыгэбзэ',
|
||
inputmethods: [ 'cyrl-palochka' ]
|
||
},
|
||
'ahr': {
|
||
autonym: 'अहिराणी',
|
||
inputmethods: [ 'mr-transliteration', 'mr-inscript' ]
|
||
},
|
||
'am': {
|
||
autonym: 'አማርኛ',
|
||
inputmethods: [ 'am-transliteration' ]
|
||
},
|
||
'ar': {
|
||
autonym: 'العربية',
|
||
inputmethods: [ 'ar-kbd' ]
|
||
},
|
||
'as': {
|
||
autonym: 'অসমীয়া',
|
||
inputmethods: [ 'as-transliteration', 'as-avro', 'as-bornona', 'as-inscript', 'as-phonetic', 'as-inscript2' ]
|
||
},
|
||
'av': {
|
||
autonym: 'авар',
|
||
inputmethods: [ 'cyrl-palochka' ]
|
||
},
|
||
'be': {
|
||
autonym: 'беларуская',
|
||
inputmethods: [ 'be-transliteration', 'be-latin', 'be-kbd' ]
|
||
},
|
||
'be-tarask': {
|
||
autonym: 'беларуская (тарашкевіца)',
|
||
inputmethods: [ 'be-transliteration', 'be-latin' ]
|
||
},
|
||
'bh': {
|
||
autonym: 'भोजपुरी',
|
||
inputmethods: [ 'hi-transliteration' ]
|
||
},
|
||
'bho': {
|
||
autonym: 'भोजपुरी',
|
||
inputmethods: [ 'hi-transliteration' ]
|
||
},
|
||
'bn': {
|
||
autonym: 'বাংলা',
|
||
inputmethods: [ 'bn-avro', 'bn-inscript', 'bn-nkb', 'bn-probhat', 'bn-inscript2' ]
|
||
},
|
||
'brx': {
|
||
autonym: 'बोड़ो',
|
||
inputmethods: [ 'brx-inscript', 'brx-inscript2' ]
|
||
},
|
||
'ckb': {
|
||
autonym: 'کوردی',
|
||
inputmethods: [ 'ckb-transliteration-arkbd', 'ckb-transliteration-fakbd', 'ckb-transliteration-lakbd' ]
|
||
},
|
||
'ce': {
|
||
autonym: 'нохчийн',
|
||
inputmethods: [ 'cyrl-palochka' ]
|
||
},
|
||
'cv': {
|
||
autonym: 'Чăвашла',
|
||
inputmethods: [ 'cv-cyr-altgr', 'cv-lat-altgr', 'cv-cyr-numbers' ]
|
||
},
|
||
'da': {
|
||
autonym: 'Dansk',
|
||
inputmethods: [ 'da-normforms' ]
|
||
},
|
||
'de': {
|
||
autonym: 'Deutsch',
|
||
inputmethods: [ 'de-transliteration' ]
|
||
},
|
||
'diq': {
|
||
autonym: 'Kirdkî',
|
||
inputmethods: [ 'ku-h', 'ku-tr' ]
|
||
},
|
||
'doi': {
|
||
autonym: 'डोगरी',
|
||
inputmethods: [ 'doi-inscript2' ]
|
||
},
|
||
'en': {
|
||
autonym: 'English',
|
||
inputmethods: [ 'ipa-sil', 'ipa-x-sampa' ]
|
||
},
|
||
'el': {
|
||
autonym: 'Ελληνικά',
|
||
inputmethods: [ 'el-kbd' ]
|
||
},
|
||
'eo': {
|
||
autonym: 'Esperanto',
|
||
inputmethods: [ 'eo-transliteration', 'eo-h', 'eo-h-f', 'eo-plena', 'eo-q', 'eo-vi', 'eo-x' ]
|
||
},
|
||
'fa': {
|
||
autonym: 'فارسی',
|
||
inputmethods: [ 'fa-kbd' ]
|
||
},
|
||
'fo': {
|
||
autonym: 'Føroyskt',
|
||
inputmethods: [ 'fo-normforms' ]
|
||
},
|
||
'fi': {
|
||
autonym: 'Suomi',
|
||
inputmethods: [ 'fi-transliteration' ]
|
||
},
|
||
'gom': {
|
||
autonym: 'कोंकणी',
|
||
inputmethods: [ 'hi-transliteration', 'hi-inscript', 'gom-inscript2' ]
|
||
},
|
||
'gu': {
|
||
autonym: 'ગુજરાતી',
|
||
inputmethods: [ 'gu-transliteration', 'gu-inscript', 'gu-inscript2', 'gu-phonetic' ]
|
||
},
|
||
'he': {
|
||
autonym: 'עברית',
|
||
inputmethods: [ 'he-standard-2012-extonly', 'he-standard-2012' ]
|
||
},
|
||
'hi': {
|
||
autonym: 'हिन्दी',
|
||
inputmethods: [ 'hi-transliteration', 'hi-inscript', 'hi-bolnagri', 'hi-phonetic', 'hi-inscript2' ]
|
||
},
|
||
'hr': {
|
||
autonym: 'Hrvatski',
|
||
inputmethods: [ 'hr-kbd' ]
|
||
},
|
||
'hy': {
|
||
autonym: 'հայերեն',
|
||
inputmethods: [ 'hy-ephonetic', 'hy-typewriter', 'hy-ephoneticalt', 'hy-emslegacy', 'hy-wmslegacy' ]
|
||
},
|
||
'hne': {
|
||
autonym: 'छत्तीसगढ़ी',
|
||
inputmethods: [ 'hi-transliteration' ]
|
||
},
|
||
'is': {
|
||
autonym: 'Íslenska',
|
||
inputmethods: [ 'is-normforms' ]
|
||
},
|
||
'fonipa': {
|
||
autonym: 'International Phonetic Alphabet',
|
||
inputmethods: [ 'ipa-sil', 'ipa-x-sampa' ]
|
||
},
|
||
'jv': {
|
||
autonym: 'ꦧꦱꦗꦮ',
|
||
inputmethods: [ 'jv-transliteration' ]
|
||
},
|
||
'ka': {
|
||
autonym: 'ქართული ენა',
|
||
inputmethods: [ 'ka-transliteration', 'ka-kbd' ]
|
||
},
|
||
'kbd': {
|
||
autonym: 'адыгэбзэ (къэбэрдеибзэ)',
|
||
inputmethods: [ 'cyrl-palochka' ]
|
||
},
|
||
'kk': {
|
||
autonym: 'Қазақша',
|
||
inputmethods: [ 'kk-kbd', 'kk-arabic' ]
|
||
},
|
||
'kn': {
|
||
autonym: 'ಕನ್ನಡ',
|
||
inputmethods: [ 'kn-transliteration', 'kn-inscript', 'kn-kgp', 'kn-inscript2' ]
|
||
},
|
||
'ks': {
|
||
autonym: 'कॉशुर / کٲشُر',
|
||
inputmethods: [ 'ks-inscript', 'ks-kbd' ]
|
||
},
|
||
'ky': {
|
||
autonym: 'Кыргыз',
|
||
inputmethods: [ 'ky-cyrl-alt' ]
|
||
},
|
||
'kab': {
|
||
autonym: 'ⵜⴰⵇⴱⴰⵢⵍⵉⵜ',
|
||
inputmethods: [ 'ber-tfng' ]
|
||
},
|
||
'ku': {
|
||
autonym: 'Kurdî',
|
||
inputmethods: [ 'ku-h', 'ku-tr' ]
|
||
},
|
||
'lbe': {
|
||
autonym: 'лакку',
|
||
inputmethods: [ 'cyrl-palochka' ]
|
||
},
|
||
'lez': {
|
||
autonym: 'лезги',
|
||
inputmethods: [ 'cyrl-palochka' ]
|
||
},
|
||
'lo': {
|
||
autonym: 'ລາວ',
|
||
inputmethods: [ 'lo-kbd' ]
|
||
},
|
||
'mai': {
|
||
autonym: 'मैथिली',
|
||
inputmethods: [ 'mai-inscript', 'mai-inscript2' ]
|
||
},
|
||
'mh': {
|
||
autonym: 'Kajin M̧ajeļ',
|
||
inputmethods: [ 'mh' ]
|
||
},
|
||
'ml': {
|
||
autonym: 'മലയാളം',
|
||
inputmethods: [ 'ml-transliteration', 'ml-inscript', 'ml-inscript2' ]
|
||
},
|
||
'mn': {
|
||
autonym: 'Монгол',
|
||
inputmethods: [ 'mn-cyrl' ]
|
||
},
|
||
'mni': {
|
||
autonym: 'Manipuri',
|
||
inputmethods: [ 'mni-inscript2' ]
|
||
},
|
||
'mr': {
|
||
autonym: 'मराठी',
|
||
inputmethods: [ 'mr-transliteration', 'mr-inscript2', 'mr-inscript', 'mr-phonetic' ]
|
||
},
|
||
'my': {
|
||
autonym: 'မြန်မာ',
|
||
inputmethods: [ 'my-xkb' ]
|
||
},
|
||
'ne': {
|
||
autonym: 'नेपाली',
|
||
inputmethods: [ 'ne-transliteration', 'ne-inscript2', 'ne-inscript', 'ne-rom', 'ne-trad' ]
|
||
},
|
||
'new': {
|
||
autonym: 'नेपाल भाषा',
|
||
inputmethods: [ 'hi-transliteration', 'hi-inscript' ]
|
||
},
|
||
'nb': {
|
||
autonym: 'Norsk (bokmål)',
|
||
inputmethods: [ 'nb-normforms', 'nb-tildeforms' ]
|
||
},
|
||
'nn': {
|
||
autonym: 'Norsk (nynorsk)',
|
||
inputmethods: [ 'nb-normforms', 'nn-tildeforms' ]
|
||
},
|
||
'or': {
|
||
autonym: 'ଓଡ଼ିଆ',
|
||
inputmethods: [ 'or-phonetic', 'or-transliteration', 'or-inscript', 'or-inscript2', 'or-lekhani' ]
|
||
},
|
||
'pa': {
|
||
autonym: 'ਪੰਜਾਬੀ',
|
||
inputmethods: [ 'pa-transliteration', 'pa-inscript', 'pa-phonetic', 'pa-inscript2', 'pa-jhelum' ]
|
||
},
|
||
'rif': {
|
||
autonym: 'ⵜⴰⵔⵉⴼⵉⵜ',
|
||
inputmethods: [ 'ber-tfng' ]
|
||
},
|
||
'ru': {
|
||
autonym: 'русский',
|
||
inputmethods: [ 'ru-jcuken', 'ru-kbd', 'ru-phonetic', 'ru-yawerty' ]
|
||
},
|
||
'sah': {
|
||
autonym: 'саха тыла',
|
||
inputmethods: [ 'sah-transliteration' ]
|
||
},
|
||
'sa': {
|
||
autonym: 'संस्कृत',
|
||
inputmethods: [ 'sa-transliteration', 'sa-inscript2', 'sa-inscript', 'sa-iast' ]
|
||
},
|
||
'sat': {
|
||
autonym: 'संताली',
|
||
inputmethods: [ 'sat-inscript2']
|
||
},
|
||
'sd': {
|
||
autonym: 'सिंधी',
|
||
inputmethods: [ 'sd-inscript2' ]
|
||
},
|
||
'se': {
|
||
autonym: 'Davvisámegiella',
|
||
inputmethods: [ 'se-normforms' ]
|
||
},
|
||
'shi': {
|
||
autonym: 'ⵜⴰⵛⵍⵃⵉⵜ',
|
||
inputmethods: [ 'ber-tfng' ]
|
||
},
|
||
'si': {
|
||
autonym: 'සිංහල',
|
||
inputmethods: [ 'si-singlish', 'si-wijesekara' ]
|
||
},
|
||
'sk': {
|
||
autonym: 'Slovenčina',
|
||
inputmethods: [ 'sk-kbd' ]
|
||
},
|
||
'sr': {
|
||
autonym: 'Српски / srpski',
|
||
inputmethods: [ 'sr-kbd' ]
|
||
},
|
||
'sv': {
|
||
autonym: 'Svenska',
|
||
inputmethods: [ 'sv-normforms' ]
|
||
},
|
||
'ta': {
|
||
autonym: 'தமிழ்',
|
||
inputmethods: [ 'ta-transliteration', 'ta-99', 'ta-inscript', 'ta-bamini', 'ta-inscript2' ]
|
||
},
|
||
'tcy': {
|
||
autonym: 'ತುಳು',
|
||
inputmethods: [ 'kn-transliteration' ]
|
||
},
|
||
'te': {
|
||
autonym: 'తెలుగు',
|
||
inputmethods: [ 'te-transliteration', 'te-inscript', 'te-inscript2' ]
|
||
},
|
||
'th': {
|
||
autonym: 'ไทย',
|
||
inputmethods: [ 'th-kedmanee', 'th-pattachote' ]
|
||
},
|
||
'tkr': {
|
||
autonym: 'цӀаӀхна миз',
|
||
inputmethods: [ 'cyrl-palochka' ]
|
||
},
|
||
'tzm': {
|
||
autonym: 'ⵜⴰⵎⴰⵣⵉⵖⵜ',
|
||
inputmethods: [ 'ber-tfng' ]
|
||
},
|
||
'udm': {
|
||
autonym: 'удмурт',
|
||
inputmethods: [ 'udm-alt' ]
|
||
},
|
||
'uk': {
|
||
autonym: 'Українська',
|
||
inputmethods: [ 'uk-kbd' ]
|
||
},
|
||
'ug': {
|
||
autonym: 'ئۇيغۇرچە / Uyghurche',
|
||
inputmethods: [ 'ug-kbd' ]
|
||
},
|
||
'ur': {
|
||
autonym: 'اردو',
|
||
inputmethods: [ 'ur-transliteration', 'ur-phonetic' ]
|
||
},
|
||
'uz': {
|
||
autonym: 'Oʻzbekcha',
|
||
inputmethods: [ 'uz-kbd' ]
|
||
},
|
||
'vec': {
|
||
autonym: 'Venetian',
|
||
inputmethods: [ 'vec-GVU' ]
|
||
}
|
||
} );
|
||
|
||
}( jQuery ) );
|