Henning Snater has uploaded a new change for review.
https://gerrit.wikimedia.org/r/74116
Change subject: Split off adaptLetterCase and autocompleteString from suggester
......................................................................
Split off adaptLetterCase and autocompleteString from suggester
Change-Id: I83d751ca22f2b0579d21849233da011417b92e5d
---
M ValueView/ValueView.resources.mw.php
M ValueView/ValueView.tests.qunit.php
M ValueView/resources/jquery.ui/jquery.ui.suggester.js
A ValueView/resources/jquery.util/jquery.util.adaptlettercase.js
A ValueView/resources/jquery/jquery.autocompletestring.js
M ValueView/tests/qunit/jquery.ui/jquery.ui.suggester.tests.js
A ValueView/tests/qunit/jquery.util/jquery.util.adaptlettercase.tests.js
A ValueView/tests/qunit/jquery/jquery.autocompletestring.tests.js
8 files changed, 306 insertions(+), 158 deletions(-)
git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/DataValues
refs/changes/16/74116/1
diff --git a/ValueView/ValueView.resources.mw.php
b/ValueView/ValueView.resources.mw.php
index d135403..d29a412 100644
--- a/ValueView/ValueView.resources.mw.php
+++ b/ValueView/ValueView.resources.mw.php
@@ -86,7 +86,10 @@
'jquery.ui/jquery.ui.suggester.css'
),
'dependencies' => array(
- 'jquery.ui.autocomplete'
+ 'jquery.autocompletestring',
+ 'jquery.ui.autocomplete',
+ 'jquery.ui.widget',
+ 'jquery.util.adaptlettercase',
)
),
@@ -127,7 +130,6 @@
),
),
-
'jquery.ui.listrotator' => $moduleTemplate + array(
'scripts' => array(
'jquery.ui/jquery.ui.listrotator.js',
@@ -144,6 +146,22 @@
'valueview-listrotator-auto',
),
),
+
+ 'jquery.autocompletestring' => $moduleTemplate + array(
+ 'scripts' => array(
+ 'jquery/jquery.autocompletestring.js',
+ ),
+ 'dependencies' => array(
+ 'jquery.util.adaptlettercase',
+ ),
+ ),
+
+ 'jquery.util.adaptlettercase' => $moduleTemplate + array(
+ 'scripts' => array(
+ 'jquery.util/jquery.util.adaptlettercase.js',
+ ),
+ ),
+
);
// return jQuery.valueview's native resources plus those required by
the MW extension:
diff --git a/ValueView/ValueView.tests.qunit.php
b/ValueView/ValueView.tests.qunit.php
index 3b4dac0..010fa47 100644
--- a/ValueView/ValueView.tests.qunit.php
+++ b/ValueView/ValueView.tests.qunit.php
@@ -174,6 +174,25 @@
'jquery.valueview.experts.timeinput',
),
),
+
+ 'jquery.autocompletestring.tests' => array(
+ 'scripts' => array(
+ "$bp/jquery/jquery.autocompletestring.tests.js",
+ ),
+ 'dependencies' => array(
+ 'jquery.autocompletestring',
+ ),
+ ),
+
+ 'jquery.util.adaptlettercase.tests' => array(
+ 'scripts' => array(
+
"$bp/jquery.util/jquery.util.adaptlettercase.tests.js",
+ ),
+ 'dependencies' => array(
+ 'jquery.util.adaptlettercase',
+ ),
+ ),
+
);
} );
diff --git a/ValueView/resources/jquery.ui/jquery.ui.suggester.js
b/ValueView/resources/jquery.ui/jquery.ui.suggester.js
index db1ca3e..d52e9a7 100644
--- a/ValueView/resources/jquery.ui/jquery.ui.suggester.js
+++ b/ValueView/resources/jquery.ui/jquery.ui.suggester.js
@@ -78,7 +78,10 @@
* (2) {String} Error text status.
* (3) {Object} Detailed error information.
*
+ * @dependency jquery.autocompletestring
+ * @dependency jquery.eachchange
* @dependency jquery.ui.autocomplete
+ * @dependency jquery.util.adaptlettercase
*/
( function( $ ) {
'use strict';
@@ -230,7 +233,10 @@
var resultSet = $.ui.autocomplete.filter(
this.options.source, request.term );
if ( resultSet.length && this.options.adaptLetterCase )
{
- this.term = this._adaptLetterCase( this.term,
resultSet[0] );
+ this.term = $.util.adaptLetterCase( this.term,
+ resultSet[0],
+ this.options.adaptLetterCase
+ );
this.element.val( this.term );
}
@@ -289,10 +295,18 @@
// auto-complete input box text (because of the
API call lag, this is
// avoided when hitting backspace, since the
value would be reset too slow)
if ( this._lastKeyDown !== 8 &&
response[1].length > 0 ) {
- this.autocompleteString(
- response[0],
- response[1][0]
- );
+ var incomplete = response[0],
+ complete = response[1][0];
+
+ if ( this.options.adaptLetterCase ) {
+ this.term = incomplete =
$.util.adaptLetterCase(
+ incomplete,
+ complete,
+
this.options.adaptLetterCase
+ );
+ }
+
+ this.element.autocompletestring(
incomplete, complete );
}
suggest( response[1] ); // pass array of
returned values to callback
@@ -519,24 +533,6 @@
},
/**
- * Adjusts the letter case of a source string to the letter
case in a destination string
- * according to the adaptLetterCase option.
- *
- * @param {String} source
- * @param {String} destination
- * @return {String} Altered source string
- */
- _adaptLetterCase: function( source, destination ) {
- if ( this.options.adaptLetterCase === 'all' ) {
- return destination.substr( 0, source.length );
- } else if ( this.options.adaptLetterCase === 'first' ) {
- return destination.substr( 0, 1 ) +
source.substr( 1 );
- } else {
- return source;
- }
- },
-
- /**
* Sets/gets the plain input box value.
*
* @param {String} [value] Value to be set
@@ -556,65 +552,6 @@
this.menu.element.position( $.extend( {
of: this.element
}, this.options.position ) );
- },
-
- /**
- * Completes the input box with the remaining characters of a
given string. The characters
- * of the remaining part are text-highlighted, so the will be
overwritten if typing
- * characters is continue. Tabbing or clicking outside of the
input box will leave the
- * completed string in the input box.
- *
- * @param incomplete {String}
- * @param complete {String}
- * @return {Number} number of characters added (and
highlighted) at the end of the
- * incomplete string
- */
- autocompleteString: function( incomplete, complete ) {
- if(
- // if nothing to complete, just return and
don't move the cursor
- // (can be annoying in this situation)
- incomplete === complete
- // The following statement is a work-around for
a technically unexpected search
- // behaviour: e.g. in English Wikipedia
opensearch for "Allegro [...]" returns
- // "Allegro" as first result instead of
"Allegro (music)", so auto-completion should
- // probably be prevented here since it would
always reset the input box's value to
- // "Allegro"
- || complete.toLowerCase().indexOf(
this.element.val().toLowerCase() ) === -1
- ) {
- return 0;
- }
-
- // set value to complete value...
- if ( this.options.adaptLetterCase ) {
- this.term = this._adaptLetterCase( incomplete,
complete );
- if ( complete.indexOf( this.term ) === 0 ) {
- this.element.val( complete );
- }
- } else if ( incomplete === complete.substr( 0,
incomplete.length ) ) {
- this.element.val( incomplete + complete.substr(
incomplete.length ) );
- }
-
- // ... and select the suggested, not manually typed
part of the value
- var start = incomplete.length,
- end = complete.length,
- node = this.element[0];
-
- // highlighting takes some browser specific
implementation
- if( node.createTextRange ) { // opera < 10.5 and IE
- var selRange = node.createTextRange();
- selRange.collapse( true );
- selRange.moveStart( 'character', start);
- selRange.moveEnd( 'character', end);
- selRange.select();
- } else if( node.setSelectionRange ) { // major modern
browsers
- // make a 'backward' selection so pressing
arrow left won't put the cursor near the
- // selections end but rather at the typing
position
- node.setSelectionRange( start, end, 'backward'
);
- } else if( node.selectionStart ) {
- node.selectionStart = start;
- node.selectionEnd = end;
- }
- return ( end - start );
}
} );
diff --git a/ValueView/resources/jquery.util/jquery.util.adaptlettercase.js
b/ValueView/resources/jquery.util/jquery.util.adaptlettercase.js
new file mode 100644
index 0000000..0215dee
--- /dev/null
+++ b/ValueView/resources/jquery.util/jquery.util.adaptlettercase.js
@@ -0,0 +1,46 @@
+/**
+ * adaptlettercase helper function
+ *
+ * @licence GNU GPL v2+
+ * @author H. Snater < [email protected] >
+ *
+ * @dependency jQuery
+ */
+jQuery.util = jQuery.util || {};
+
+jQuery.util.adaptlettercase = ( function( $ ) {
+ 'use strict';
+
+ /**
+ * Adapts the letter case of a source string to a destination string.
The destination string is
+ * supposed to consist our of the source string's first letter(s).
+ *
+ * @param {string} source
+ * @param {string} destination
+ * @param {string|undefined} method "all" will adapt source's letter
case for all destination
+ * characters, "first" will adapt the first letter only. By
default, no adaption is
+ * taking place.
+ * @return {string}
+ *
+ * @throws {Error} if source and/or destination string is not specified.
+ * @throws {Error} if source string does not start with destination
string.
+ */
+ return function( source, destination, method ) {
+ if( !source || !destination ) {
+ throw new Error( 'Source and destination need to be
specified.' );
+ }
+
+ if( source.toLowerCase().indexOf( destination.toLowerCase() )
=== -1 ) {
+ throw new Error( source + ' does not start with ' +
destination + '.' );
+ }
+
+ if ( method === 'all' ) {
+ return destination.substr( 0, source.length );
+ } else if ( method === 'first' ) {
+ return destination.substr( 0, 1 ) + source.substr( 1 );
+ } else {
+ return source;
+ }
+ };
+
+} )( jQuery );
diff --git a/ValueView/resources/jquery/jquery.autocompletestring.js
b/ValueView/resources/jquery/jquery.autocompletestring.js
new file mode 100644
index 0000000..6408add
--- /dev/null
+++ b/ValueView/resources/jquery/jquery.autocompletestring.js
@@ -0,0 +1,79 @@
+/**
+ * autocompletestring jQuery plugin
+ *
+ * @licence GNU GPL v2+
+ * @author H. Snater < [email protected] >
+ *
+ * @dependency jQuery
+ */
+jQuery.fn.autocompletestring = ( function( $ ) {
+ 'use strict';
+
+ /**
+ * Applied to an input or textarea element,
jQuery.fn.autocompletestring is fed with a
+ * "complete" string and an "incomplete" - latter is supposed to
consist out of the first
+ * letter(s) of the "complete" string. The form element is filled with
the "complete" string
+ * while a text selection is applied to the characters missing in the
"incomplete" string.
+ *
+ * @param {string} incomplete
+ * @param {string} complete
+ * @return {jQuery}
+ */
+ var autocompletestring = function( incomplete, complete ) {
+ if(
+ !incomplete || !complete
+ || complete.toLowerCase().indexOf(
incomplete.toLowerCase() ) !== 0
+ ) {
+ return this;
+ }
+
+ return this.each( function() {
+ var $this = $( this );
+
+ // Only auto-complete when incomplete string actually
is a part of the complete string:
+ if( incomplete === complete.substr( 0,
incomplete.length ) ) {
+ $this.val( incomplete + complete.substr(
incomplete.length ) );
+ }
+
+ $.fn.autocompletestring.selectText( this,
incomplete.length, complete.length );
+ } );
+ };
+
+ /**
+ * Creates a text selection.
+ *
+ * @param {object} node
+ * @param {number} start
+ * @param {number} end
+ * @return {number} Text selection length.
+ */
+ autocompletestring.selectText = function( node, start, end ) {
+ if( end > node.value.length ) {
+ end = node.value.length;
+ }
+
+ if( start > end ) {
+ return 0;
+ }
+
+ if( node.createTextRange ) { // Opera < 10.5 and IE
+ var selRange = node.createTextRange();
+ selRange.collapse( true );
+ selRange.moveStart( 'character', start );
+ selRange.moveEnd( 'character', end );
+ selRange.select();
+ } else if( node.setSelectionRange ) { // major modern browsers
+ // Make a 'backward' selection so pressing arrow left
won't put the cursor near the
+ // selections end but rather at the typing position:
+ node.setSelectionRange( start, end, 'backward' );
+ } else if( node.selectionStart ) {
+ node.selectionStart = start;
+ node.selectionEnd = end;
+ }
+
+ return ( end - start );
+ };
+
+ return autocompletestring;
+
+} )( jQuery );
diff --git a/ValueView/tests/qunit/jquery.ui/jquery.ui.suggester.tests.js
b/ValueView/tests/qunit/jquery.ui/jquery.ui.suggester.tests.js
index 2885696..b5e88a8 100644
--- a/ValueView/tests/qunit/jquery.ui/jquery.ui.suggester.tests.js
+++ b/ValueView/tests/qunit/jquery.ui/jquery.ui.suggester.tests.js
@@ -103,83 +103,9 @@
'Detected scrollbar width.'
);
- // Firefox will throw an error when the input element is not
part of the DOM while trying to
- // set the selection range which is part of the following
assertion
- $( 'body' ).append( $input );
- assert.equal(
- suggester.autocompleteString( $input.val(), 'ab' ),
- 1,
- 'Auto-completed text.'
- );
-
suggester.destroy();
$input.remove();
- } );
-
- QUnit.test( 'Adapt letter case', function( assert ) {
- var $input = newTestSuggester();
- var suggester = $input.data( 'suggester' );
-
- assert.equal(
- suggester._adaptLetterCase( 'abc', 'AbC' ),
- 'abc',
- "adaptLetterCase: Did not adapt any letter case."
- );
-
- $input.val( 'ef' );
- suggester.search( 'EF' ); // simulate case-insensitive search
-
- assert.equal(
- $input.val(),
- 'ef',
- "Did not adjusted input value's letter case according
to suggestion list's first result set."
- );
-
- suggester.destroy();
- $input.remove();
-
- $input = newTestSuggester( { adaptLetterCase: 'all' } );
- suggester = $input.data( 'suggester' );
-
- assert.equal(
- suggester._adaptLetterCase( 'abc', 'AbC' ),
- 'AbC',
- "adjustLetterCase: Adapted the case of all letters."
- );
-
- $input.val( 'ef' );
- suggester.search( 'EF' );
-
- assert.equal(
- $input.val(),
- 'EF',
- "Adjusted input value's letter case according to
suggestion list's first result set."
- );
-
- suggester.destroy();
- $input.remove();
-
- $input = newTestSuggester( { adaptLetterCase: 'first' } );
- suggester = $input.data( 'suggester' );
-
- assert.equal(
- suggester._adaptLetterCase( 'abc', 'AbC' ),
- 'Abc',
- "adaptLetterCase: Adapted the case of the first letter."
- );
-
- $input.val( 'ef' );
- suggester.search( 'EF' );
-
- assert.equal(
- $input.val(),
- 'Ef',
- "Capitalized input value's letters according to
suggestion list's first result set."
- );
-
- suggester.destroy();
- $input.remove();
} );
QUnit.test( 'automatic height adjustment', function( assert ) {
diff --git
a/ValueView/tests/qunit/jquery.util/jquery.util.adaptlettercase.tests.js
b/ValueView/tests/qunit/jquery.util/jquery.util.adaptlettercase.tests.js
new file mode 100644
index 0000000..932238a
--- /dev/null
+++ b/ValueView/tests/qunit/jquery.util/jquery.util.adaptlettercase.tests.js
@@ -0,0 +1,53 @@
+/**
+ * @licence GNU GPL v2+
+ * @author H. Snater < [email protected] >
+ */
+
+( function( $, QUnit ) {
+ 'use strict';
+
+ QUnit.module( 'jquery.util.adaptlettercase' );
+
+ QUnit.test( 'Basic tests', function( assert ) {
+
+ assert.equal(
+ $.util.adaptlettercase( 'abc', 'AbC' ),
+ 'abc',
+ 'Not adapting any letter-case when omitting \'method\'
parameter.'
+ );
+
+ assert.equal(
+ $.util.adaptlettercase( 'abc', 'AbC', 'all' ),
+ 'AbC',
+ 'Adapting the case of all letters when specifying
\'all\' as method.'
+ );
+
+ assert.equal(
+ $.util.adaptlettercase( 'ABC', 'abc', 'first' ),
+ 'aBC',
+ 'Adapting the first letter\'s case when specifying
\'first\' as method.'
+ );
+
+ assert.equal(
+ $.util.adaptlettercase( 'AB', 'ab', 'first' ),
+ 'aB',
+ 'Adapting the first letter\'s case when specifying
\'first\' as method with ' +
+ 'destination being a a part of source.'
+ );
+
+ assert.equal(
+ $.util.adaptlettercase( '123', '123', 'all' ),
+ '123',
+ 'No replacement taking place when not passing letters.'
+ );
+
+ assert.throws(
+ function() {
+ $.util.adaptlettercase( 'abc', '123', 'all' );
+ },
+ 'Error thrown when destination does not match source.'
+ );
+
+ } );
+
+}( jQuery, QUnit ) );
\ No newline at end of file
diff --git a/ValueView/tests/qunit/jquery/jquery.autocompletestring.tests.js
b/ValueView/tests/qunit/jquery/jquery.autocompletestring.tests.js
new file mode 100644
index 0000000..6be97af
--- /dev/null
+++ b/ValueView/tests/qunit/jquery/jquery.autocompletestring.tests.js
@@ -0,0 +1,70 @@
+/**
+ * @licence GNU GPL v2+
+ * @author H. Snater < [email protected] >
+ */
+
+( function( $, QUnit ) {
+ 'use strict';
+
+ /**
+ * Creates an input element suitable for testing.
+ * @return {jQuery}
+ */
+ function createTestInput() {
+ return $( '<input/>' ).addClass( 'test-autocompletestring'
).appendTo( 'body' );
+ }
+
+ QUnit.module( 'jquery.autocompletestring', {
+ teardown: function() {
+ $( '.test-autocompletestring' ).remove();
+ }
+ } );
+
+ QUnit.test( 'Adapt letter case', function( assert ) {
+ var $input = createTestInput();
+
+ assert.equal(
+ $input.autocompletestring( 'a', 'abc' ).val(),
+ 'abc',
+ 'Auto-completed \'a\' to \'abc\'.'
+ );
+
+ assert.equal(
+ $input.autocompletestring( '12', '123' ).val(),
+ '123',
+ 'Auto-completed \'12\' to \'123\'.'
+ );
+
+ assert.equal(
+ $input.autocompletestring( 'abc', 'abc' ).val(),
+ 'abc',
+ 'Value remains the same when \'incomplete\' and
\'complete\' string match.'
+ );
+
+ assert.equal(
+ $input.autocompletestring( 'a', 'ABC' ).val(),
+ 'abc',
+ 'No auto-completion is performed when \'incomplete\' is
not part of \'complete\' '
+ + 'string. Input value remains unchanged.'
+ );
+ } );
+
+ QUnit.test( 'selectText()', function( assert ) {
+ var $input = createTestInput().val( '0123456789' );
+
+ assert.equal(
+ $.fn.autocompletestring.selectText( $input[0], 0, 1 ),
+ 1,
+ 'Applied text selection with length of 1.'
+ );
+
+ assert.equal(
+ $.fn.autocompletestring.selectText( $input[0], 0, 20 ),
+ 10,
+ 'Applied a text selection with the input value\'s
character length since it is shorter '
+ + 'than the selection length trying to apply.'
+ );
+
+ } );
+
+}( jQuery, QUnit ) );
--
To view, visit https://gerrit.wikimedia.org/r/74116
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I83d751ca22f2b0579d21849233da011417b92e5d
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/DataValues
Gerrit-Branch: master
Gerrit-Owner: Henning Snater <[email protected]>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits