jenkins-bot has submitted this change and it was merged.

Change subject: Update jquery.ime from upstream
......................................................................


Update jquery.ime from upstream

* Digit fix in Southern Kurdish.
* Rodali, Or-Lekhani and OdiScript layouts.
* Major updates for VisualEditor integration by David Chan.

Change-Id: Ia7301bddb79c1fbce2af7190494bdd7bdd909862
---
M lib/jquery.ime/jquery.ime.js
A lib/jquery.ime/rules/as/as-rodali.js
A lib/jquery.ime/rules/or/or-OdiScript.js
M lib/jquery.ime/rules/or/or-lekhani.js
M lib/jquery.ime/rules/sdh/sdh-kbd.js
M lib/jquery.ime/rules/si/si-singlish.js
M lib/jquery.ime/rules/yo/yo-alt.js
7 files changed, 653 insertions(+), 370 deletions(-)

Approvals:
  Divec: Looks good to me, approved
  jenkins-bot: Verified



diff --git a/lib/jquery.ime/jquery.ime.js b/lib/jquery.ime/jquery.ime.js
index 8bb2d4f..283c51c 100644
--- a/lib/jquery.ime/jquery.ime.js
+++ b/lib/jquery.ime/jquery.ime.js
@@ -3,18 +3,51 @@
 * Copyright (c) 2015 Santhosh Thottingal; Licensed GPL, MIT */
 ( function ( $ ) {
        'use strict';
+       var TextEntryFactory, TextEntry, FormWidgetEntry, ContentEditableEntry,
+               defaultInputMethod;
 
        // rangy is defined in the rangy library
        /*global rangy */
 
        /**
+        * Just initializes an empty static object.
+        * Similar to initClass in https://www.mediawiki.org/wiki/OOjs
+        *
+        * @param {Function} fn
+        */
+       function initClass( fn ) {
+               fn.static = fn.static || {};
+       }
+
+       /**
+        * Inheritance. Uses pattern similar to OOjs 
(https://www.mediawiki.org/wiki/OOjs).
+        * Extend prototype and static methods and properties of child 
constructor from
+        * a parent constructor.
+        *
+        * @param {Function} targetFn
+        * @param {Function} originFn
+        */
+       function inheritClass( targetFn, originFn ) {
+               targetFn.parent = originFn;
+               targetFn.prototype = $.extend( {}, originFn.prototype );
+               targetFn.prototype.constructor = originFn.constructor;
+               targetFn.static = $.extend( {}, originFn.static );
+       }
+
+       /**
         * IME Class
+        * @class
+        *
+        * @constructor
+        * @param {HTMLElement} element Element on which to listen for events
+        * @param {TextEntry} textEntry Text entry object to use to get/set text
         * @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 ) {
+       function IME( element, textEntry, options ) {
                this.$element = $( element );
+               this.textEntry = textEntry;
                // 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 );
@@ -127,7 +160,7 @@
                 */
                keypress: function ( e ) {
                        var altGr = false,
-                               c, startPos, pos, endPos, divergingPos, input, 
replacement;
+                               c, input, replacement;
 
                        if ( !this.active ) {
                                return true;
@@ -160,24 +193,10 @@
 
                        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,
+                       // Append the character being typed to the preceding 
few characters,
                        // 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 );
+                       input = this.textEntry.getTextBeforeSelection( 
this.inputmethod.maxKeyLength );
+                       replacement = this.transliterate( input + c, 
this.context, altGr );
 
                        // Update the context
                        this.context += c;
@@ -198,11 +217,7 @@
                                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 );
+                       this.textEntry.replaceTextAtSelection( input.length, 
replacement.output );
 
                        e.stopPropagation();
 
@@ -332,121 +347,200 @@
 
                        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 );
-               },
+       /**
+        * TextEntry factory
+        * @class
+        *
+        * @constructor
+        */
+       TextEntryFactory = function IMETextEntryFactory() {
+               this.TextEntryClasses = [];
+       };
 
-               /**
-                * 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 );
-               },
+       /* Inheritance */
 
-               /**
-                * 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 );
-               },
+       initClass( TextEntryFactory );
 
-               /**
-                * 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 );
+       /* Methods */
+
+       /**
+        * Register a TextEntry class, with priority over previous registrations
+        *
+        * @param {TextEntry} Class to register
+        */
+       TextEntryFactory.prototype.register = function ( TextEntryClass ) {
+               this.TextEntryClasses.unshift( TextEntryClass );
+       };
+
+       /**
+        * Wrap an editable element with the appropriate TextEntry class
+        *
+        * @param {jQuery} $element The element to wrap
+        * @return {TextEntry|undefined} A TextEntry, or undefined if no match
+        */
+       TextEntryFactory.prototype.wrap = function ( $element ) {
+               var i, len, TextEntryClass;
+               for ( i = 0, len = this.TextEntryClasses.length; i < len; i++ ) 
{
+                       TextEntryClass = this.TextEntryClasses[ i ];
+                       if ( TextEntryClass.static.canWrap( $element ) ) {
+                               return new TextEntryClass( $element );
+                       }
+               }
+               return undefined;
+       };
+
+       /* Initialization */
+
+       TextEntryFactory.static.singleton = new TextEntryFactory();
+
+       /**
+        * Generic text entry
+        * @class
+        *
+        * @abstract
+        */
+       TextEntry = function IMETextEntry() {
+       };
+
+       /* Inheritance */
+
+       initClass( TextEntry );
+
+       /* Static methods */
+
+       /**
+        * Test whether can wrap this type of element
+        *
+        * @param {jQuery} $element The element to wrap
+        * @return {boolean} Whether the element can be wrapped
+        */
+       TextEntry.static.canWrap = function () {
+               return false;
+       };
+
+       /* Abstract methods */
+
+       /**
+        * Get text immediately before the current selection start.
+        *
+        * This SHOULD return the empty string for non-collapsed selections.
+        *
+        * @param {number} maxLength Maximum number of chars (code units) to 
return
+        * @return {String} Up to maxLength of text
+        */
+       TextEntry.prototype.getTextBeforeSelection = null;
+
+       /**
+        * Replace the currently selected text and/or text before the selection
+        *
+        * @param {number} precedingCharCount Number of chars before selection 
to replace
+        * @param {String} newText Replacement text
+        */
+       TextEntry.prototype.replaceTextAtSelection = null;
+
+       /**
+        * TextEntry class for input/textarea widgets
+        * @class
+        *
+        * @constructor
+        * @param {jQuery} $element The element to wrap
+        */
+       FormWidgetEntry = function IMEFormWidgetEntry( $element ) {
+               this.$element = $element;
+       };
+
+       /* Inheritance */
+
+       inheritClass( FormWidgetEntry, TextEntry );
+
+       /* Static methods */
+
+       /**
+        * @inheritdoc TextEntry
+        */
+       FormWidgetEntry.static.canWrap = function ( $element ) {
+               return $element.is( 'input:not([type]), input[type=text], 
input[type=search], textarea' ) &&
+                       !$element.prop( 'readonly' ) &&
+                       !$element.prop( 'disabled' ) &&
+                       !$element.hasClass( 'noime' );
+       };
+
+       /* Instance methods */
+
+       /**
+        * @inheritdoc TextEntry
+        */
+       FormWidgetEntry.prototype.getTextBeforeSelection = function ( maxLength 
) {
+               var pos = this.getCaretPosition();
+               return this.$element.val().substring(
+                       Math.max( 0, pos.start - maxLength ),
+                       pos.start
+               );
+       };
+
+       /**
+        * @inheritdoc TextEntry
+        */
+       FormWidgetEntry.prototype.replaceTextAtSelection = function ( 
precedingCharCount, newText ) {
+               var selection,
+                       length,
+                       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;
+
+                       // Replace the whole text of the text area:
+                       // text before + newText + 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 - 
precedingCharCount ) +
+                               newText +
+                               element.value.substring( element.selectionEnd, 
element.value.length );
+
+                       // restore scroll
+                       element.scrollTop = scrollTop;
+                       // set selection
+                       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();
                }
        };
 
        /**
-        * jQuery plugin ime
-        * @param {Object} option
+        * Get the current selection offsets inside the widget
+        *
+        * @return {Object} Offsets in chars (0 means first offset *or* no 
selection in widget)
+        * @return.start {number} Selection start
+        * @return.end {number} Selection end
         */
-       $.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 ),
+       FormWidgetEntry.prototype.getCaretPosition = function () {
+               var el = this.$element.get( 0 ),
                        start = 0,
                        end = 0,
                        normalizedValue,
@@ -455,10 +549,6 @@
                        len,
                        newLines,
                        endRange;
-
-               if ( $element.is( '[contenteditable]' ) ) {
-                       return getDivCaretPosition( el );
-               }
 
                if ( typeof el.selectionStart === 'number' && typeof 
el.selectionEnd === 'number' ) {
                        start = el.selectionStart;
@@ -499,264 +589,189 @@
                                }
                        }
                }
+               return { start: start, end: end };
+       };
 
-               return [start, end];
-       }
+       TextEntryFactory.static.singleton.register( FormWidgetEntry );
 
        /**
-        * Helper function to get an IE TextRange object for an element
+        * TextEntry class for ContentEditable
+        * @class
+        *
+        * @constructor
+        * @param {jQuery} $element The element to wrap
         */
-       function rangeForElementIE( element ) {
-               var selection;
+       ContentEditableEntry = function IMEContentEditableEntry( $element ) {
+               this.$element = $element;
+       };
 
-               if ( element.nodeName.toLowerCase() === 'input' ) {
-                       selection = element.createTextRange();
-               } else {
-                       selection = document.body.createTextRange();
-                       selection.moveToElementText( element );
+       /* Inheritance */
+
+       inheritClass( ContentEditableEntry, TextEntry );
+
+       /* Static methods */
+
+       /**
+        * @inheritdoc TextEntry
+        */
+       ContentEditableEntry.static.canWrap = function ( $element ) {
+               return $element.is( '[contenteditable]' ) && 
!$element.hasClass( 'noime' );
+       };
+
+       /* Instance methods */
+
+       /**
+        * @inheritdoc TextEntry
+        */
+       ContentEditableEntry.prototype.getTextBeforeSelection = function ( 
maxLength ) {
+               var range = this.getSelectedRange();
+               if ( !range || !range.collapsed || 
range.startContainer.nodeType !== Node.TEXT_NODE ) {
+                       return '';
                }
+               return range.startContainer.nodeValue.substring(
+                       Math.max( 0, range.startOffset - maxLength ),
+                       range.startOffset
+               );
+       };
 
-               return selection;
-       }
+       /**
+        * @inheritdoc SelectionWrapper
+        */
+       ContentEditableEntry.prototype.replaceTextAtSelection = function ( 
precedingCharCount, newText ) {
+               var range, textNode, textOffset, newOffset, newRange;
 
-       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
-                       } );
-
-                       rangy.init();
-                       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
-                       } );
-
+               if ( !this.getSelectedRange() ) {
                        return;
                }
 
-               if ( typeof element.selectionStart === 'number' && typeof 
element.selectionEnd === 'number' ) {
-                       // IE9+ and all other browsers
-                       scrollTop = element.scrollTop;
+               // Trigger any externally registered jQuery compositionstart 
event listeners.
+               // TODO: Try node.dispatchEvent( new CompositionEvent(...) ) so 
listeners not
+               // registered using jQuery will also get triggered, then 
fallback gracefully for
+               // browsers that do not support it.
+               this.$element.trigger( 'compositionstart' );
 
-                       // 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 );
+               range = this.getSelectedRange();
 
-                       // 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();
+               if ( !range.collapsed ) {
+                       range.deleteContents();
                }
-       }
 
-       function getDivCaretPosition( element ) {
-               var charIndex = 0,
-                       start = 0,
-                       end = 0,
-                       foundStart = false,
-                       foundEnd = false,
-                       sel;
+               if ( range.startContainer.nodeType === Node.TEXT_NODE ) {
+                       // Alter this text node's content and move the cursor
+                       textNode = range.startContainer;
+                       textOffset = range.startOffset;
+                       textNode.nodeValue =
+                               textNode.nodeValue.substr( 0, textOffset - 
precedingCharCount ) +
+                               newText +
+                               textNode.nodeValue.substr( textOffset );
+                       newOffset = textOffset - precedingCharCount + 
newText.length;
+                       newRange = rangy.createRange();
+                       newRange.setStart( range.startContainer, newOffset );
+                       newRange.setEnd( range.startContainer, newOffset );
+                       rangy.getSelection().setSingleRange( newRange );
+               } else {
+                       // XXX assert precedingCharCount === 0
+                       // Insert a new text node with the new text
+                       textNode = document.createTextNode( newText );
+                       range.startContainer.insertBefore(
+                               textNode,
+                               range.startContainer.childNodes[ 
range.startOffset ]
+                       );
+                       newRange = rangy.createRange();
+                       newRange.setStart( textNode, textNode.length );
+                       newRange.setEnd( textNode, textNode.length );
+                       rangy.getSelection().setSingleRange( newRange );
+               }
 
+               // Trigger any externally registered jQuery compositionend / 
input event listeners.
+               // TODO: Try node.dispatchEvent( new CompositionEvent(...) ) so 
listeners not
+               // registered using jQuery will also get triggered, then 
fallback gracefully for
+               // browsers that do not support it.
+               this.$element.trigger( 'compositionend' );
+               this.$element.trigger( 'input' );
+       };
+
+       /**
+        * Get the selection range inside the wrapped element, or null
+        *
+        * @return {Range|null} The selection range
+        */
+       ContentEditableEntry.prototype.getSelectedRange = function () {
+               var sel, range;
                rangy.init();
                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 === 0 ) {
+                       return null;
                }
-
-               if ( sel.rangeCount ) {
-                       traverseTextNodes( element, sel.getRangeAt( 0 ) );
+               range = sel.getRangeAt( 0 );
+               if ( !this.$element[ 0 ].contains( 
range.commonAncestorContainer ) ) {
+                       return null;
                }
+               return range;
+       };
 
-               return [ start, end ];
-       }
+       TextEntryFactory.static.singleton.register( ContentEditableEntry );
 
-       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];
-       }
+       /* Exports */
 
        /**
-        * Set the caret position in the div.
-        * @param {Element} element The content editable div element
-        * @param position
+        * jQuery plugin ime
+        * @param {Object} option
         */
-       function setDivCaretPosition( element, position ) {
-               var nextCharIndex,
-                       charIndex = 0,
-                       range = rangy.createRange(),
-                       foundStart = false,
-                       foundEnd = false;
+       $.fn.ime = function ( option ) {
+               return this.each( function () {
+                       var data, textEntry,
+                               $this = $( this ),
+                               options = typeof option === 'object' && option;
 
-               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;
+                       data = $this.data( 'ime' );
+                       if ( !data ) {
+                               textEntry = 
TextEntryFactory.static.singleton.wrap( $this );
+                               if ( textEntry === undefined ) {
+                                       return;
                                }
-
-                               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;
-                                       }
-                               }
+                               data = new IME( this, textEntry, options );
+                               $this.data( 'ime', data );
                        }
-               }
 
-               traverseTextNodes( element );
+                       if ( typeof option === 'string' ) {
+                               data[option]();
+                       }
+               } );
+       };
 
-       }
+       $.ime = {};
+       $.ime.inputmethods = {};
+       $.ime.sources = {};
+       $.ime.preferences = {};
+       $.ime.languages = {};
+
+       $.ime.textEntryFactory = TextEntryFactory.static.singleton;
+       $.ime.TextEntry = TextEntry;
+       $.ime.inheritClass = inheritClass;
+
+       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
+       };
 
        /**
-        * 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
+        * private function for debugging
         */
-       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 debug( $obj ) {
+               if ( window.console && window.console.log ) {
+                       window.console.log( $obj );
                }
        }
 
@@ -1601,6 +1616,10 @@
                        name: 'ফনেটিক',
                        source: 'rules/as/as-phonetic.js'
                },
+               'as-rodali': {
+                       name: 'ৰ\'দালি',
+                       source: 'rules/as/as-rodali.js'
+               },
                'as-transliteration': {
                        name: 'প্ৰতিৰূপান্তৰণ',
                        source: 'rules/as/as-transliteration.js'
@@ -2014,6 +2033,10 @@
                'or-transliteration': {
                        name: 'ଟ୍ରାନ୍ସଲିଟରେସନ',
                        source: 'rules/or/or-transliteration.js'
+               },
+               'or-OdiScript': {
+                       name: 'ଓଡ଼ିସ୍କ୍ରିପ୍ଟ',
+                       source: 'rules/or/or-OdiScript.js'
                },
                'or-inscript': {
                        name: 'ଇନସ୍କ୍ରିପ୍ଟ',
@@ -2435,7 +2458,7 @@
                },
                'or': {
                        autonym: 'ଓଡ଼ିଆ',
-                       inputmethods: [ 'or-phonetic', 'or-transliteration', 
'or-inscript', 'or-inscript2', 'or-lekhani' ]
+                       inputmethods: [ 'or-phonetic', 'or-transliteration', 
'or-inscript', 'or-inscript2', 'or-lekhani', 'or-OdiScript' ]
                },
                'pa': {
                        autonym: 'ਪੰਜਾਬੀ',
diff --git a/lib/jquery.ime/rules/as/as-rodali.js 
b/lib/jquery.ime/rules/as/as-rodali.js
new file mode 100644
index 0000000..a26e6cd
--- /dev/null
+++ b/lib/jquery.ime/rules/as/as-rodali.js
@@ -0,0 +1,147 @@
+( function ( $ ) {
+       'use strict';
+
+       var asRodali = {
+               id: 'as-rodali',
+               name: 'ৰ\'দালি',
+               description: 'Rodali Keyboard',
+               date: '2015-07-26',
+               URL: 'http://github.com/wikimedia/jquery.ime',
+               author: 'Gunadeep Chetia, Subhashish Panigrahi',
+               license: 'GPLv3',
+               version: '1.0',
+               contextLength: 4,
+               maxKeyLength: 5,
+               patterns: [
+                       ['([ক-হড়ঢ়য়])্?ৰৰi','[^a`]', '$1ৃ'],
+                       ['ৰৰi','[^a`]', 'ঋ'],
+                       ['(([ক-হড়ঢ়ৰৱয়]))a','[^a`]', '$1'],
+                       ['([ক-হড়ঢ়ৰৱয়])a','a', '$1া'],
+                       ['([ক-হড়ঢ়ৰৱয়])i','[^a`]', '$1ি'],
+                       ['([ক-হড়ঢ়ৰৱয়])(িi|েe)','[^a`]', '$1ী'],
+                       ['([ক-হড়ঢ়ৰৱয়])u','[^a`]', '$1ু'],
+                       ['([ক-হড়ঢ়ৰৱয়])ুu','[^a`]', '$1ূ'],
+                       ['([ক-হড়ঢ়ৰৱয়])o','[^a`]', '$1ো'],
+                       ['([ক-হড়ঢ়ৰৱয়])e','[^a`]', '$1ে'],
+                       ['([ক-হড়ঢ়ৰৱয়])োi','[^a`]', '$1ৈ'],
+                       ['([ক-হড়ঢ়ৰৱয়])োu','[^a`]', '$1ৌ'],
+                       ['([কঙলষস])(k|K)','[^a`]','$1্ক'],
+                       ['([গঙদল])(g)','[^a`]','$1্গ'],
+                       ['([চশ])c','[^a`]','$1্চ'],
+                       ['([জঞব])j','[^a`]','$1্জ'],
+                       ['নj','[^a`]','ঞ্জ'],
+                       ['([কটণনপলষস])T','[^a`]','$1্ট'],
+                       ['([ডণনল])D','[^a`]','$1্ড'],
+                       ['([গষহ])N','[^a`]','$1্ণ'],
+                       ['([কতনপশসহ])t','[^a`]','$1্ত'],
+                       ['([গদনব])d','[^a`]','$1্দ'],
+                       ['([গঘণতধনপমশসহ])n','[^a`]','$1্ন'],
+                       ['([পমলষস])p','[^a`]','$1্প'],
+                       ['([মস])f', '[^a`]', '$1্ফ'],
+                       ['([বমল])b','[^a`]','$1্ব'],
+                       ['([কগঙটণতদধনমলশষসহ])m','[^a`]','$1্ম'],
+                       ['([ক-ঘচ-ঝট-যলশ-হড়ঢ়য়])r','[^a`]','$1্ৰ'],
+                       ['([কগপ-বমলশসহ])l','[^a`]','$1্ল'],
+                       ['([কনপ])s','[^a`]','$1্স'],
+                       ['([ক-হড়ঢ়য়])(w|b|v)','[^a`]','$1্ব'],
+                       ['([ক-হড়ঢ়য়])y','[^a`]','$1্য'],
+                       ['নc','[^a`]','ঞ্চ'],
+                       ['ৰk','r','ৰ্ক'],
+                       ['ৰg','r','ৰ্গ'],
+                       ['ৰc','r','ৰ্চ'],
+                       ['ৰj','r','ৰ্জ'],
+                       ['ৰT','r','ৰ্ট'],
+                       ['ৰD','r','ৰ্ড'],
+                       ['ৰN','r','ৰ্ণ'],
+                       ['ৰt','r','ৰ্ত'],
+                       ['ৰd','r','ৰ্দ'],
+                       ['ৰn','r','ৰ্ন'],
+                       ['ৰp','r','ৰ্প'],
+                       ['ৰf','r','ৰ্ফ'],
+                       ['ৰb','r','ৰ্ব'],
+                       ['ৰv','r','ৰ্ভ'],
+                       ['ৰm','r','ৰ্ম'],
+                       ['ৰz','r','ৰ্য'],
+                       ['ৰl','r','ৰ্ল'],
+                       ['ৰx','r','ৰ্শ'],
+                       ['ৰS','r','ৰ্ষ'],
+                       ['ৰs','r','ৰ্স'],
+                       ['ৰh','r','ৰ্হ'],
+                       ['ৰR','r','ৰ্ড়'],
+                       ['ৰy','r','ৰ্য়'],
+                       ['ত~','t','ৎ'],
+                       ['অa','a', 'আ'],
+                       ['কh','k', 'খ'],
+                       ['গh','g', 'ঘ'],
+                       ['ণg','N', 'ঙ'],
+                       ['চh','c', 'ছ'],
+                       ['জh','j', 'ঝ'],
+                       ['নG','n', 'ঞ'],
+                       ['টh','T', 'ঠ'],
+                       ['ডh','D', 'ঢ'],
+                       ['তh','t', 'থ'],
+                       ['দh','d', 'ধ'],
+                       ['পh','p', 'ফ'],
+                       ['বh','b', 'ভ'],
+                       ['সh','s', 'শ'],
+                       ['ড়h','R', 'ঢ়'],
+                       ['নg','n', 'ং'],
+                       ['ওi','o', 'ঐ'],
+                       ['ওu','o', 'ঔ'],
+                       ['ইi','i', 'ঈ'],
+                       ['উu','u', 'ঊ'],
+                       ['([kK])', 'ক'],
+                       ['g', 'গ'],
+                       ['G', 'জ্ঞ'],
+                       ['(c|C)', 'চ'],
+                       ['(j|J)', 'জ'],
+                       ['T', 'ট'],
+                       ['D', 'ড'],
+                       ['N', 'ণ'],
+                       ['t', 'ত'],
+                       ['d', 'দ'],
+                       ['n', 'ন'],
+                       ['(p|P)', 'প'],
+                       ['f', 'ফ'],
+                       ['(b|B)', 'ব'],
+                       ['(v|V)', 'ৱ'],
+                       ['(m|M)', 'ম'],
+                       ['z|Z', 'য'],
+                       ['r', 'ৰ'],
+                       ['(l|L)', 'ল'],
+                       ['S', 'ষ'],
+                       ['s', 'স'],
+                       ['h', 'হ'],
+                       ['H', 'ঃ'],
+                       ['R', 'ড়'],
+                       ['(w|W)', 'ৱ'],
+                       ['x', 'শ'],
+                       ['X', 'ক্ষ'],
+                       ['y', 'য়'],
+                       ['a', 'অ'],
+                       ['i', 'ই'],
+                       ['u', 'উ'],
+                       ['e', 'এ'],
+                       ['o', 'ও'],
+                       ['0', '০'],
+                       ['1', '১'],
+                       ['2', '২'],
+                       ['3', '৩'],
+                       ['4', '৪'],
+                       ['5', '৫'],
+                       ['6', '৬'],
+                       ['7', '৭'],
+                       ['8', '৮'],
+                       ['9', '৯'],
+                       ['<','ৃ'],
+                       ['`', '্‌'],
+                       ['\\^', 'ঁ'],
+                       ['\\.', '।'],
+                       ['~', '্'],
+                       ['&', '°'],
+                       ['\\*', '৺']
+               ]
+       };
+
+       $.ime.register( asRodali );
+}( jQuery ) );
diff --git a/lib/jquery.ime/rules/or/or-OdiScript.js 
b/lib/jquery.ime/rules/or/or-OdiScript.js
new file mode 100644
index 0000000..d4927b4
--- /dev/null
+++ b/lib/jquery.ime/rules/or/or-OdiScript.js
@@ -0,0 +1,116 @@
+( function ( $ ) {
+       'use strict';
+
+       var orOdiScript = {
+               id: 'or-OdiScript',
+               name: 'ଓଡ଼ିସ୍କ୍ରିପ୍ଟ',
+               description: 'Odia OdiScript input method',
+               date: '2015-7-28',
+               URL: 'http://github.com/wikimedia/jquery.ime',
+               author: 'Manoj Sahukar and Subhashish Panigrahi',
+               license: 'GPLv3',
+               version: '1.0',
+               contextLength: 6,
+               maxKeyLength: 4,
+               patterns: [
+                       
['/([A-Za-z\\>_\\-\\!@#\\$\\^\\&\\%\\*\\~\\.\\|\\\\0-9])', '$1'],
+                       ['\'([\'])','\"'], //'' to "
+                       ['\"([\'])','\'\'\''], //'''
+                       ['([କ-ହ]୍[କ-ହ])@@', 'ର୍\$1'], //reph
+                       ['([କ-ହ])@@', 'ର୍\$1'], //reph
+                       ['%%', 'ଙ୍କ' ], // Anka
+                       ['"', 'ଁ' ], //chanda bindu
+                       ['([ର])}', 'ର‍୍ୟ'], // special case: Ja phala (ର + ZWJ 
+ ୍ୟ)
+                       ['\\}', '୍ୟ' ], // Ja phala
+                       ['୍\ ', '୍‌ '], // ending with halant+zwnj
+                       ['{', 'ୃ' ], // Ru kara
+                       ['େk', 'ୋ' ], //  l= ekar, lk = okar
+                       ['ାl', 'ୋ' ], //  l= ekar, kl = okar
+                       ['େp', 'ୈ' ], //  
+                       ['ୈk', 'ୌ' ], //  
+                       ['\\]', '୍ର'], // Ra phala
+                       ['\\[', 'ଅ'], // Type ଅ
+                       ['ଅk','ଆ'], // ଅ + ା = ଆ
+                       ['ଓ##','ୱ'],
+                       ['([ର])##', 'ର‍୍ୱ'], // special case ର + ZWJ + ୍ୱ
+                       ['([କ-ହ])##', '$1୍ୱ'], //  
+                       ['ଦQ', 'ହ'],
+                       ['([କନପସମ])Q', 'ତ୍\$1' ],
+                       ['([ଖଥଧ])Q', '$1ି' ],
+                       ['([ନସ])_', '$1୍ତ୍ର' ], //ନ୍ତ୍ର , ସ୍ତ୍ର
+                       ['\\$', 'କ୍ଷ'],
+                       ['\\&&','ଏବଂ'],
+                       ['\\^', 'ଋ'],
+                       ['\\|', 'ଞ'],
+                       ['\\\\', 'ୟ'],
+                       ['<', 'ଏ'],
+                       ['>', 'ଐ'],
+                       ['0', '୦'],
+                       ['1', '୧'],
+                       ['2', '୨'],
+                       ['3', '୩'],
+                       ['4', '୪'],
+                       ['5', '୫'],
+                       ['6', '୬'],
+                       ['7', '୭'],
+                       ['8', '୮'],
+                       ['9', '୯'],
+                       ['a', 'ମ'],
+                       ['A', 'ଢ଼'],
+                       ['b', 'ୁ'],
+                       ['B', 'ଉ'],
+                       ['c', 'ଜ'],
+                       ['C', 'ଝ'],
+                       ['d', '୍'],
+                       ['D', '‌'], //zwnj
+                       ['e', 'ନ'],
+                       ['E', 'ଡ଼'],
+                       ['f', 'ତ'],
+                       ['F', 'ଥ'],
+                       ['G', 'ଇ'],
+                       ['g', 'ି'],
+                       ['H', 'ଈ'],
+                       ['h', 'ୀ'],
+                       ['i', 'ଂ'],
+                       ['I', 'ଠ'],
+                       ['j', 'ର'],
+                       ['J', 'ଣ'],
+                       ['k', 'ା'],
+                       ['K', 'ଶ'],
+                       ['l', 'େ'],
+                       ['L', 'ଷ'],
+                       ['m', 'ଳ'],
+                       ['M', 'ଲ'],
+                       ['n', 'ୂ'],
+                       ['N', 'ଊ'],
+                       ['o', 'ହ'],
+                       ['O', 'ଡ'],
+                       ['p', 'ୈ'],
+                       ['P', 'ଢ'],
+                       ['q', 'ବ'],
+                       ['r', 'ଦ'],
+                       ['R', 'ଧ'],
+                       ['s', 'କ'],
+                       ['S', 'ଖ'],
+                       ['t', 'ସ'],
+                       ['T', 'ଓ'],
+                       ['u', 'ଯ'],
+                       ['U', 'ଟ'],
+                       ['v', 'ଚ'],
+                       ['V', 'ଛ'],
+                       ['w', 'ପ'],
+                       ['W', 'ଫ'],
+                       ['x', 'ଗ'],
+                       ['X', 'ଘ'],
+                       ['y', 'ୌ'],
+                       ['Y', 'ଔ'],
+                       ['z', 'ଭ'],
+                       ['Z', 'ଙ'],
+                       ['`', '।'],
+                       ['~', 'ଃ'],
+               
+               ]
+       };
+
+       $.ime.register( orOdiScript );
+}( jQuery ) );
diff --git a/lib/jquery.ime/rules/or/or-lekhani.js 
b/lib/jquery.ime/rules/or/or-lekhani.js
index eb36675..364890a 100644
--- a/lib/jquery.ime/rules/or/or-lekhani.js
+++ b/lib/jquery.ime/rules/or/or-lekhani.js
@@ -7,7 +7,7 @@
                description: 'Odia Lekhani phonetic input method',
                date: '2012-10-14',
                URL: 'http://github.com/wikimedia/jquery.ime',
-               author: 'Junaid P V and Subhashish Panigrahi',
+               author: 'Junaid P V, Subhashish Panigrahi and Jnanaranjan Sahu',
                license: 'GPLv3',
                version: '1.0',
                contextLength: 4,
@@ -15,7 +15,7 @@
                patterns: [
                        ['\\\\([A-Za-z\\>_~\\.0-9])', '\\\\', '$1'],
 
-                       
['([(କ-ହୟୱଡ଼ଢ଼ଙ୍କଙ୍ଖଙ୍ଗଙ୍ଘଞ୍ଚଞ୍ଛଞ୍ଝଣ୍ଟଣ୍ଠଣ୍ଡଣ୍ଢନ୍ତନ୍ଥନ୍ଦନ୍ଧମ୍ପମ୍ଫମ୍ବମ୍ଭଞ୍ଜ])a', 
'$1ା'],
+                       
['([କ-ହୟୱଡ଼ଢ଼ଙ୍କଙ୍ଖଙ୍ଗଙ୍ଘଞ୍ଚଞ୍ଛଞ୍ଝଣ୍ଟଣ୍ଠଣ୍ଡଣ୍ଢନ୍ତନ୍ଥନ୍ଦନ୍ଧମ୍ପମ୍ଫମ୍ବମ୍ଭଞ୍ଜ])a', 
'$1ା'],
                        
['([କ-ଳଲନ୍ଧଥଡ଼ଢ଼ହୟୱରକ୍ଷଶସଷଙ୍କଙ୍ଖଙ୍ଗଙ୍ଘଞ୍ଚଞ୍ଛଞ୍ଝଣ୍ଟଣ୍ଠଣ୍ଡଣ୍ଢନ୍ତନ୍ଥନ୍ଦନ୍ଧମ୍ପମ୍ଫମ୍ବମ୍ଭଞ୍ଜ])i',
 '$1\u0b3f'],
                        
['([କ-ହୟୱଡ଼ଢ଼ଙ୍କଙ୍ଖଙ୍ଗଙ୍ଘଞ୍ଚଞ୍ଛଞ୍ଝଣ୍ଟଣ୍ଠଣ୍ଡଣ୍ଢନ୍ତନ୍ଥନ୍ଦନ୍ଧମ୍ପମ୍ଫମ୍ବମ୍ଭଞ୍])I', 
'$1ୀ'],
                        
['([କ-ହୟୱଡ଼ଢ଼ଙ୍କଙ୍ଖଙ୍ଗଙ୍ଘଞ୍ଚଞ୍ଛଞ୍ଝଣ୍ଟଣ୍ଠଣ୍ଡଣ୍ଢନ୍ତନ୍ଥନ୍ଦନ୍ଧମ୍ପମ୍ଫମ୍ବମ୍ଭଞ୍])u', 
'$1\u0b41'],
@@ -31,13 +31,13 @@
                        ['([କ-ହୟୱ])(w|v)', '$1୍ୱ'],
                        ['([କ-ହୟୱ])~', '$1\u200C'],
 
-                       ['([କ-ହୟୱ])y', '$1୍ୟ'], // <consonant>y
+                       ['([କ-ହୱ])y', '$1୍ୟ'], // <consonant>y
 
                        ['z', '୍'], // halanta
                        ['\\.', '।'], //purnacheda
                        ['ଅa', 'ଆ'],
                        ['ଏe', 'ଐ'],
-                       ['(ଅu|ଓo|ଓO)', 'ଔ'],
+                       ['(ଅu|O)', 'ଔ'],
                        ['ଋR', 'ୠ'], // RR
                        ['ଳl', 'ଌ'], // Ll
                        ['ଌl', 'ୡ'], // Lll
@@ -45,8 +45,6 @@
                        ['ଞ୍ଜh', 'ଞ୍ଝ'], // njh
                        ['ଙ୍କh', 'ଙ୍ଖ'], // nkh
                        ['ଙ୍ଗh', 'ଙ୍ଘ'], // ngh
-                       ['ହm', 'ହ୍ମ'], // mh
-                       ['ହn', 'ହ୍ନ'], // nh
                        ['ମ୍ବh', 'ମ୍ଭ'], // mbh or nbh
                        ['ଣ୍ଡai', 'ଣ୍ଡାଇ'], // NDai
                        ['ଜ୍ଜh', 'ଜ୍ଝ'], // jjh
@@ -67,7 +65,6 @@
                        ['ନD|ଣD', 'ଣ୍ଡ'], // nd
                        ['ଣDh', 'ଣ୍ଢ'], //ndh
                        ['ନdh', 'ନ୍ଧ'], // ndht
-                       ['ଷT', '$1୍ଟ'], // ST
                        ['ଟh', 'ଠ'], // Th
                        ['ଡh', 'ଢ'], // Dh
                        ['ତh', 'ଥ'], // th
diff --git a/lib/jquery.ime/rules/sdh/sdh-kbd.js 
b/lib/jquery.ime/rules/sdh/sdh-kbd.js
index e1b7c2f..c76a14c 100644
--- a/lib/jquery.ime/rules/sdh/sdh-kbd.js
+++ b/lib/jquery.ime/rules/sdh/sdh-kbd.js
@@ -17,7 +17,7 @@
                        ['3', '٣'],
                        ['4', '٤'],
                        ['5', '٥'],
-                       ['6', '٦'],
+                       ['6', '۶'],
                        ['7', '٧'],
                        ['8', '٨'],
                        ['9', '٩'],
diff --git a/lib/jquery.ime/rules/si/si-singlish.js 
b/lib/jquery.ime/rules/si/si-singlish.js
index 8ba93b1..d872015 100644
--- a/lib/jquery.ime/rules/si/si-singlish.js
+++ b/lib/jquery.ime/rules/si/si-singlish.js
@@ -11,7 +11,7 @@
                license: 'GPLv3',
                version: '1.0',
                contextLength: 5,
-               maxKeyLength: 2,
+               maxKeyLength: 5,
                patterns: [
                        //['ඬ්හ්a', 'ඳ'],       // nndha
                        ['ඬ්h', 'ඳ්'], // nndh
diff --git a/lib/jquery.ime/rules/yo/yo-alt.js 
b/lib/jquery.ime/rules/yo/yo-alt.js
index 2e8c41b..b272472 100644
--- a/lib/jquery.ime/rules/yo/yo-alt.js
+++ b/lib/jquery.ime/rules/yo/yo-alt.js
@@ -16,7 +16,7 @@
                        // Combining grave tone mark
                        ['\\\\', '\u0340'],
                        // Combining acute tone mark
-                       ["/", '\u0341']
+                       ['/', '\u0341']
                ]
        };
 

-- 
To view, visit https://gerrit.wikimedia.org/r/265766
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: Ia7301bddb79c1fbce2af7190494bdd7bdd909862
Gerrit-PatchSet: 3
Gerrit-Project: mediawiki/extensions/UniversalLanguageSelector
Gerrit-Branch: master
Gerrit-Owner: Amire80 <[email protected]>
Gerrit-Reviewer: Divec <[email protected]>
Gerrit-Reviewer: Santhosh <[email protected]>
Gerrit-Reviewer: jenkins-bot <>

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to