Henning Snater has uploaded a new change for review.

  https://gerrit.wikimedia.org/r/93736


Change subject: Implemented "eachchange" event using the jQuery.event.special 
API
......................................................................

Implemented "eachchange" event using the jQuery.event.special API

(bug 45604)
Converted the "eachchange" jQuery plug-in to a jQuery event implementation 
using the
jQuery.event.special API.

Change-Id: I8e674b825f08c656e9bb78296d074d2c73cc01e6
---
M ValueView/README
M ValueView/ValueView.resources.mw.php
M ValueView/ValueView.resources.php
M ValueView/ValueView.tests.qunit.php
M ValueView/resources/jquery.time/jquery.time.timeinput.js
M ValueView/resources/jquery.ui/jquery.ui.inputextender.js
M ValueView/resources/jquery.ui/jquery.ui.suggester.js
M 
ValueView/resources/jquery.valueview/valueview.experts/experts.GlobeCoordinateInput.js
M ValueView/resources/jquery.valueview/valueview.experts/experts.StringValue.js
M 
ValueView/resources/jquery.valueview/valueview.experts/experts.SuggestedStringValue.js
D ValueView/resources/jquery/jquery.eachchange.js
A ValueView/resources/jquery/jquery.event.special.eachchange.js
M ValueView/resources/jquery/jquery.inputAutoExpand.js
D ValueView/tests/qunit/jquery/jquery.eachchange.tests.js
A ValueView/tests/qunit/jquery/jquery.event.special.eachchange.tests.js
15 files changed, 433 insertions(+), 246 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/DataValues 
refs/changes/36/93736/5

diff --git a/ValueView/README b/ValueView/README
index e26b9bd..bd722bd 100644
--- a/ValueView/README
+++ b/ValueView/README
@@ -11,5 +11,5 @@
 * Support of valueview for some basic data value types. These valueview 
experts can be found in
   jQuery.valueview.experts.<constructor-name>.
 * other jQuery extensions required by this library which are not available in 
MediaWiki itself:
-  - jQuery.eachchange
+  - jQuery.event.special.eachchange
   - jQuery.inputAutoExpand
\ No newline at end of file
diff --git a/ValueView/ValueView.resources.mw.php 
b/ValueView/ValueView.resources.mw.php
index 472f641..5a2156e 100644
--- a/ValueView/ValueView.resources.mw.php
+++ b/ValueView/ValueView.resources.mw.php
@@ -63,9 +63,9 @@
                        )
                ),
 
-               'jquery.eachchange' => $moduleTemplate + array(
+               'jquery.event.special.eachchange' => $moduleTemplate + array(
                        'scripts' => array(
-                               'jquery/jquery.eachchange.js'
+                               'jquery/jquery.event.special.eachchange.js'
                        ),
                        'dependencies' => array(
                                'jquery.client'
@@ -77,7 +77,7 @@
                                'jquery/jquery.inputAutoExpand.js',
                        ),
                        'dependencies' => array(
-                               'jquery.eachchange'
+                               'jquery.event.special.eachchange'
                        )
                ),
 
@@ -112,7 +112,7 @@
                        ),
                        'dependencies' => array(
                                'jquery.ui.widget',
-                               'jquery.eachchange',
+                               'jquery.event.special.eachchange',
                                'time.js'
                        ),
                ),
@@ -125,7 +125,7 @@
                                'jquery.ui/jquery.ui.inputextender.css',
                        ),
                        'dependencies' => array(
-                               'jquery.eachchange',
+                               'jquery.event.special.eachchange',
                                'jquery.ui.position',
                                'jquery.ui.widget',
                                'jquery.animateWithEvent',
diff --git a/ValueView/ValueView.resources.php 
b/ValueView/ValueView.resources.php
index 86a0a47..1a93eb5 100644
--- a/ValueView/ValueView.resources.php
+++ b/ValueView/ValueView.resources.php
@@ -6,7 +6,7 @@
  *
  * External dependencies:
  * - jQuery 1.8
- * - jQuery.eachchange
+ * - jQuery.event.special.eachchange
  * - jQuery.inputAutoExpand
  * - jQuery.ui.suggester
  * - jQuery.time.timeinput
@@ -185,7 +185,7 @@
                        ),
                        'dependencies' => array(
                                'jquery.valueview.experts',
-                               'jquery.eachchange',
+                               'jquery.event.special.eachchange',
                                'jquery.inputAutoExpand',
                                'jquery.fn.focusAt',
                        ),
@@ -196,6 +196,7 @@
                                
'jquery.valueview/valueview.experts/experts.GlobeCoordinateInput.js',
                        ),
                        'dependencies' => array(
+                               'jquery.event.special.eachchange',
                                'jquery.valueview.experts',
                                'jquery.fn.focusAt',
                                'jquery.ui.inputextender',
@@ -230,6 +231,7 @@
                                
'jquery.valueview/valueview.experts/experts.TimeInput.css',
                        ),
                        'dependencies' => array(
+                               'jquery.event.special.eachchange',
                                'jquery.valueview.experts',
                                'jquery.fn.focusAt',
                                'jquery.time.timeinput',
@@ -275,6 +277,7 @@
                                
'jquery.valueview/valueview.experts/experts.CommonsMediaType.js',
                        ),
                        'dependencies' => array(
+                               'jquery.event.special.eachchange',
                                'jquery.valueview.experts.staticdom',
                                'jquery.valueview.BifidExpert',
                                'jquery.valueview.experts.stringvalue',
diff --git a/ValueView/ValueView.tests.qunit.php 
b/ValueView/ValueView.tests.qunit.php
index 1d15e5a..3b1cd6b 100644
--- a/ValueView/ValueView.tests.qunit.php
+++ b/ValueView/ValueView.tests.qunit.php
@@ -51,12 +51,12 @@
                        ),
                ),
 
-               'jquery.eachchange.tests' => array(
+               'jquery.event.special.eachchange.tests' => array(
                        'scripts' => array(
-                               "$bp/jquery/jquery.eachchange.tests.js",
+                               
"$bp/jquery/jquery.event.special.eachchange.tests.js",
                        ),
                        'dependencies' => array(
-                               'jquery.eachchange',
+                               'jquery.event.special.eachchange',
                        ),
                ),
 
diff --git a/ValueView/resources/jquery.time/jquery.time.timeinput.js 
b/ValueView/resources/jquery.time/jquery.time.timeinput.js
index f6177e7..c7f0a30 100644
--- a/ValueView/resources/jquery.time/jquery.time.timeinput.js
+++ b/ValueView/resources/jquery.time/jquery.time.timeinput.js
@@ -10,7 +10,7 @@
  *            been changed to.
  *
  * @dependency jQuery.ui.Widget
- * @dependency jQuery.eachchange
+ * @dependency jQuery.event.special.eachchange
  * @dependency time.Time
  */
 ( function( $, Time ) {
@@ -39,7 +39,7 @@
 
                        this.element.addClass( this.widgetName );
 
-                       this.element.eachchange( function( event, oldValue ) {
+                       this.element.on( 'eachchange', function( event, 
oldValue ) {
                                var value;
 
                                try {
diff --git a/ValueView/resources/jquery.ui/jquery.ui.inputextender.js 
b/ValueView/resources/jquery.ui/jquery.ui.inputextender.js
index 511636c..e8df427 100644
--- a/ValueView/resources/jquery.ui/jquery.ui.inputextender.js
+++ b/ValueView/resources/jquery.ui/jquery.ui.inputextender.js
@@ -41,8 +41,8 @@
  *
  * @dependency jQuery
  * @dependency jQuery.Widget
- * @dependency jQuery.eachchange
  * @dependency jQuery.animateWithEvent
+ * @dependency jQuery.event.special.eachchange
  * @dependency jQuery.ui.position
  */
 ( function( $ ) {
@@ -181,7 +181,7 @@
                        } );
 
                        if( this.options.hideWhenInputEmpty ) {
-                               this.element.eachchange( function( event, 
oldValue ) {
+                               this.element.on( 'eachchange', function( event, 
oldValue ) {
                                        if( self.element.val() === '' ) {
                                                self.hideExtension();
                                        } else if ( oldValue === '' ) {
diff --git a/ValueView/resources/jquery.ui/jquery.ui.suggester.js 
b/ValueView/resources/jquery.ui/jquery.ui.suggester.js
index 163f417..1f542f1 100644
--- a/ValueView/resources/jquery.ui/jquery.ui.suggester.js
+++ b/ValueView/resources/jquery.ui/jquery.ui.suggester.js
@@ -79,7 +79,6 @@
  *        (3) {Object} Detailed error information.
  *
  * @dependency jquery.autocompletestring
- * @dependency jquery.eachchange
  * @dependency jquery.ui.autocomplete
  * @dependency jquery.util.adaptlettercase
  * @dependency jquery.util.getscrollbarwidth
diff --git 
a/ValueView/resources/jquery.valueview/valueview.experts/experts.GlobeCoordinateInput.js
 
b/ValueView/resources/jquery.valueview/valueview.experts/experts.GlobeCoordinateInput.js
index 26ebf49..d1d8234 100644
--- 
a/ValueView/resources/jquery.valueview/valueview.experts/experts.GlobeCoordinateInput.js
+++ 
b/ValueView/resources/jquery.valueview/valueview.experts/experts.GlobeCoordinateInput.js
@@ -93,7 +93,7 @@
                                },
                                contentAnimationEvents: 'toggleranimation'
                        } )
-                       .eachchange( function( event, oldValue ) {
+                       .on( 'eachchange', function( event, oldValue ) {
                                self._viewNotifier.notify( 'change' );
                        } );
                },
diff --git 
a/ValueView/resources/jquery.valueview/valueview.experts/experts.StringValue.js 
b/ValueView/resources/jquery.valueview/valueview.experts/experts.StringValue.js
index aecd0b8..4995f3d 100644
--- 
a/ValueView/resources/jquery.valueview/valueview.experts/experts.StringValue.js
+++ 
b/ValueView/resources/jquery.valueview/valueview.experts/experts.StringValue.js
@@ -43,7 +43,7 @@
                                'type': 'text'
                        } )
                        .appendTo( this.$viewPort )
-                       .eachchange( function() {
+                       .on( 'eachchange', function() {
                                notifier.notify( 'change' );
                        } );
                },
diff --git 
a/ValueView/resources/jquery.valueview/valueview.experts/experts.SuggestedStringValue.js
 
b/ValueView/resources/jquery.valueview/valueview.experts/experts.SuggestedStringValue.js
index d77604a..b3046f0 100644
--- 
a/ValueView/resources/jquery.valueview/valueview.experts/experts.SuggestedStringValue.js
+++ 
b/ValueView/resources/jquery.valueview/valueview.experts/experts.SuggestedStringValue.js
@@ -38,7 +38,7 @@
 
                        // Since we're using the input auto expand, we have to 
update the position of the
                        // dropdown whenever the input box expands vertically:
-                       $input.eachchange( function( event, oldValue ) {
+                       $input.on( 'eachchange', function( event, oldValue ) {
                                // TODO/OPTIMIZE: only reposition when 
necessary, i.e. when expanding vertically
                                $input.data( 'suggester' ).repositionMenu();
                        } );
diff --git a/ValueView/resources/jquery/jquery.eachchange.js 
b/ValueView/resources/jquery/jquery.eachchange.js
deleted file mode 100644
index af16555..0000000
--- a/ValueView/resources/jquery/jquery.eachchange.js
+++ /dev/null
@@ -1,99 +0,0 @@
-/**
- * eachchange jquery plugin
- * Can be used on an input element to trigger an event whenever some text was 
changed. This is
- * different from the native 'change' event which only fires when the input 
loses its focus. Once
- * called the event will be triggered for the element. Optionally a function 
can be given to be
- * called, but also jQuery.on( 'eachchange' ) can be used instead.
- *
- * @licence GNU GPL v2+
- * @author H. Snater < mediawiki at snater.com >
- * @author Daniel Werner
- *
- * @example $( 'input' ).eachchange( function( event, oldValue ) { ... } );
- *
- * @dependency jquery.client
- */
-( function( $, undefined ) {
-       'use strict';
-
-       /**
-        * Returns a string to be used for detecting any instant changes of an 
input box. In general,
-        * this should be just 'input' in recent browsers.
-        *
-        * @return {string} event(s)
-        */
-       function getInputEvent() {
-               // IE (at least <= version 9) does not trigger input event when 
pressing backspace
-               // (version <= 8 does not support input event at all anyway)
-               if ( $.client.profile().name === 'msie' && 
$.client.profile().versionNumber >= 9 ) {
-                       return 'input keyup';
-               }
-
-               var fallbackEvents = 'keyup keydown blur cut paste mousedown 
mouseup mouseout',
-                       $input = $( '<input/>' ),
-                       supported = 'oninput' in $input[0];
-
-               return ( supported ) ? 'input' : fallbackEvents;
-       }
-
-       /**
-        * String containing all the events needed to detect any change of the 
input of an element.
-        * @type {string}
-        */
-       var inputEvents = getInputEvent();
-
-       $.fn.eachchange = function( fn ) {
-               var monitoredInputs = $();
-
-               var isMonitoredInput = function( input ) {
-                       return $.inArray( input, monitoredInputs ) >= 0;
-               };
-
-               var monitorEachChange = function( input ) {
-                       if( isMonitoredInput( input ) ) {
-                               return; // don't monitor stuff twice!
-                       }
-
-                       // remember, we are monitoring this from now on!
-                       monitoredInputs.push( input );
-
-                       var oldVal = input.val(); // old val to compare new one 
with
-                       input
-                       .on( inputEvents, function( e ) {
-                               /*
-                                * NOTE: we use 'keyup' here as well, so when 
holding backspace the thing still gets
-                                *       triggered. Also, for some reason in 
some browsers 'keydown' isn't triggered
-                                *       when typing fast, 'keyup' always is.
-                                * @TODO: Take care of context related changes 
via mouse (paste, drag, delete) and
-                                *        DOM blur is used so at least after 
these changes when leaving the field,
-                                *        something happens, mouseout works 
when dragging stuff in.
-                                *        paste and mouseup only work for IE in 
the context menu
-                                */
-                               // compare old value with new value and trigger 
'eachchange' if it differs
-                               var newVal = input.val();
-                               if( oldVal !== newVal ) {
-                                       input.trigger( 'eachchange', oldVal );
-                                       oldVal = input.val();
-                               }
-                       } )
-                       .on( 'keydown', function( e ) {
-                               // store value before key evaluated to make 
comparison afterwards
-                               oldVal = input.val();
-                       } );
-               };
-
-               // works for text input fields and textarea only:
-               this.filter( 'input, textarea' ).each( function() {
-                       var input = $( this );
-
-                       monitorEachChange( input );
-
-                       if( fn !== undefined ) {
-                               input.on( 'eachchange', fn );
-                       }
-               } );
-
-               return this; // return jQuery object
-       };
-
-}( jQuery ) );
diff --git a/ValueView/resources/jquery/jquery.event.special.eachchange.js 
b/ValueView/resources/jquery/jquery.event.special.eachchange.js
new file mode 100644
index 0000000..782b728
--- /dev/null
+++ b/ValueView/resources/jquery/jquery.event.special.eachchange.js
@@ -0,0 +1,194 @@
+/**
+ * eachchange jQuery event
+ *
+ * The "eachchange" event catches all designated input events. In recent 
browsers, it basically
+ * delegates to the "input" event. Older browsers are supported by fallback 
events to achieve some
+ * kind of simulation of the "input" event.
+ *
+ * @licence GNU GPL v2+
+ * @author H. Snater < mediaw...@snater.com >
+ *
+ * @dependency jquery.client
+ */
+( function( $ ) {
+       'use strict';
+
+       /**
+        * Event id used for data binding and as namespace.
+        * @type {string}
+        */
+       var EVENT_ID = 'jqueryEventSpecialEachchange';
+
+       /**
+        * Name(s) of events that are in fact supported by the client.
+        * @type {string}
+        */
+       var inputEvent = null;
+
+       $.event.special.eachchange = {
+               setup: function( data, namespaces, eventHandle ) {
+                       inputEvent = getInputEvent();
+               },
+
+               add: function( handleObj ) {
+                       var eventData = $.data( this, EVENT_ID + 
handleObj.namespace ),
+                               $elem = $( this ),
+                               eventId = EVENT_ID + handleObj.namespace,
+                               eventNameString = assignNamespace( inputEvent, 
eventId );
+
+                       if( !eventData ) {
+                               eventData = { handlers:[], prevVal: getValue( 
$elem ) };
+                       }
+
+                       // Store the handler to be able to determine whether 
handler has been triggered already
+                       // when issuing a .trigger(Handler)():
+                       eventData.handlers.push( handleObj.handler );
+                       $.data( this, eventId, eventData );
+
+                       // Delegate the "eachchange" event to the supported 
input event(s):
+                       $elem.on( eventNameString, function( event ) {
+                               eventData = $.data( this, eventId );
+
+                               event.origType = event.type;
+                               event.type = 'eachchange';
+
+                               handleObj.handler.call( this, event, 
eventData.prevVal );
+
+                               eventData.prevVal = getValue( $elem );
+                               $.data( this, eventId, eventData );
+                       } );
+               },
+
+               remove: function( handleObj ) {
+                       $( this ).off( '.' + EVENT_ID + handleObj.namespace );
+                       $.removeData( this, EVENT_ID + handleObj.namespace );
+               },
+
+               trigger: function( event, data ) {
+                       // Since the value might have changed multiple times 
programmatically before calling
+                       // .trigger(Handlers)(), the previous value will be set 
to the current value and
+                       // forwarded to the handler(s) when issuing 
.trigger(Handler)().
+                       var self = this,
+                               prevVal = getValue( $( this ) );
+
+                       $.each( $.data( this ), function( eventId, eventData ) {
+                               if( eventId.indexOf( EVENT_ID ) === 0 ) {
+                                       eventData.prevVal = prevVal;
+                                       $.data( self, eventId, eventData );
+                               }
+                       } );
+
+                       // Reset cache of already triggered handlers:
+                       triggeredHandlers = [];
+               },
+
+               handle: function( event, data ) {
+                       if( event.namespace !== '' ) {
+                               var eventData = $.data( this, EVENT_ID + 
event.namespace );
+                               if( eventData ) {
+                                       event.handleObj.handler.call( this, 
event, eventData.prevVal );
+                               }
+
+                       } else {
+                               var self = this;
+
+                               $.each( $.data( this ), function( eventId, 
eventData ) {
+                                       if( eventId.indexOf( EVENT_ID ) !== 0 ) 
{
+                                               // Event is not an eachchange 
event.
+                                               return true;
+                                       }
+
+                                       var handlers = $.data( self, eventId 
).handlers;
+
+                                       for( var i = 0; i < handlers.length; 
i++ ) {
+                                               if( !alreadyTriggered( eventId, 
i ) ) {
+                                                       handlers[i].call( self, 
event, eventData.prevVal );
+
+                                                       triggeredHandlers.push( 
{
+                                                               id: eventId,
+                                                               index: i
+                                                       } );
+
+                                               }
+                                       }
+
+                               } );
+                       }
+
+                       return event;
+               }
+       };
+
+       var triggeredHandlers = [];
+
+       /**
+        * Checks whether a handler with a given event id has already been 
triggered.
+        *
+        * @param {string} eventId
+        * @param {number} index Numeric index within the list of handlers 
attached with the same
+        *        event id.
+        */
+       function alreadyTriggered( eventId, index ) {
+               for( var i = 0; i < triggeredHandlers.length; i++ ) {
+                       if( eventId === triggeredHandlers[i].id && index === 
triggeredHandlers[i].index ) {
+                               return true;
+                       }
+               }
+               return false;
+       }
+
+       /**
+        * Returns the value of a jQuery element or null if the element does 
not feature retrieving its
+        * value via .val().
+        *
+        * @param {jQuery} $elem
+        * @return {*}
+        */
+       function getValue( $elem ) {
+               // If the native element does not feature getting its value, an 
error is caused in the
+               // jQuery mechanism trying to retrieve the value.
+               try {
+                       return $elem.val();
+               } catch( e ) {
+                       return null;
+               }
+       }
+
+       /**
+        * Assigns a namespace to a string of one or more event names separated 
by a space character.
+        *
+        * @param {string} eventNames
+        * @param {string} namespace
+        * @return {string}
+        */
+       function assignNamespace( eventNames, namespace ) {
+               var names = eventNames.split( ' ' ),
+                       namespacedNames = [];
+
+               for( var i = 0; i < names.length; i++ ) {
+                       namespacedNames.push( names[i] + '.' + namespace );
+               }
+
+               return namespacedNames.join( ' ' );
+       }
+
+       /**
+        * Returns a string of on or more event names to be used for detecting 
any instant changes of an
+        * input box. This should be just 'input' in recent browsers.
+        *
+        * @return {string}
+        */
+       function getInputEvent() {
+               // IE (at least <= version 9) does not trigger input event when 
pressing backspace
+               // (version <= 8 does not support input event at all anyway)
+               if ( $.client.profile().name === 'msie' && 
$.client.profile().versionNumber >= 9 ) {
+                       return 'input keyup';
+               }
+
+               var fallbackEvents = 'keyup keydown blur cut paste mousedown 
mouseup mouseout',
+                       $input = $( '<input/>' ),
+                       supported = 'oninput' in $input[0];
+               return ( supported ) ? 'input' : fallbackEvents;
+       }
+
+}( jQuery ) );
diff --git a/ValueView/resources/jquery/jquery.inputAutoExpand.js 
b/ValueView/resources/jquery/jquery.inputAutoExpand.js
index ac9624e..d7fddc8 100644
--- a/ValueView/resources/jquery/jquery.inputAutoExpand.js
+++ b/ValueView/resources/jquery/jquery.inputAutoExpand.js
@@ -52,7 +52,7 @@
  * @option suppressNewLine {Boolean} Whether to suppress new-line characters.
  *         default: false
  *
- * @dependency jquery.eachchange
+ * @dependency jquery.event.special.eachchange
  *
  * @todo Make expandWidth and expandHeight work simultaneously.
  * @todo Destroy mechanism
@@ -166,7 +166,7 @@
 
                // set width on all important related events:
                $( this.input )
-               .eachchange( function( e, oldValue ) {
+               .on( 'eachchange', function( event, oldValue ) {
                        // NOTE/FIXME: won't be triggered if placeholder has 
changed (via JS) but not input text
 
                        // Got to check on each change since value might have 
been pasted or dragged into the
diff --git a/ValueView/tests/qunit/jquery/jquery.eachchange.tests.js 
b/ValueView/tests/qunit/jquery/jquery.eachchange.tests.js
deleted file mode 100644
index 6b18e44..0000000
--- a/ValueView/tests/qunit/jquery/jquery.eachchange.tests.js
+++ /dev/null
@@ -1,126 +0,0 @@
-/**
- * QUnit tests for 'eachchange' jQuery plugin
- *
- * @since 0.1
- * @file
- * @ingroup DataTypes
- *
- * @licence GNU GPL v2+
- * @author Daniel Werner
- */
-( function( $, QUnit ) {
-       'use strict';
-
-       // some helper functions for the tests:
-       var i = 0,
-               iIncr = function() {
-                       i++;
-               },
-               iReset = function() {
-                       i = 0;
-               },
-               iTriggerTest = function( subject, expected, description ) {
-                       subject.filter( 'input' ).trigger( 'eachchange' );
-                       QUnit.assert.equal(
-                               i,
-                               expected,
-                               description
-                       );
-               };
-
-       QUnit.module( 'jquery.eachchange', {
-               setup: function() {
-                       iReset();
-               },
-               teardown: function() {
-                       $( '.test_eachchange' ).remove();
-               }
-       } );
-
-       QUnit.test(
-               'Initialization',
-               function( assert ) {
-                       var $input = $( '<input/>', { 'class': 
'test_eachchange', type: 'text' } ),
-                               $inputNoType = $( '<input/>', { 'class': 
'test_eachchange' } ),
-                               $textarea = $( '<textarea/>', { 'class': 
'test_eachchange' } );
-
-                       assert.equal(
-                               $input.eachchange( iIncr ),
-                               $input,
-                               'Initialized "eachchange" on a text input 
element.'
-                       );
-
-                       assert.equal(
-                               $inputNoType.eachchange( iIncr ),
-                               $inputNoType,
-                               'Initialized "eachchange" on an input element 
that has no "type" attribute.'
-                       );
-
-                       assert.equal(
-                               $textarea.eachchange( iIncr ),
-                               $textarea,
-                               'Initialized "eachchange" on a textarea.'
-                       );
-               }
-       );
-
-       QUnit.test( 'jQuery.eachchange() basics', function( assert ) {
-               var subject = $( '<input/>', {
-                       'class': 'test_eachchange',
-                       'type': 'text',
-                       'name': 'test',
-                       'value': ''
-               } ).add( $( '<div/>', { 'class': 'test_eachchange' } ) ); // 
should always be ignored, otherwise some tests will fail.
-
-               assert.equal(
-                       subject.eachchange( iIncr ),
-                       subject,
-                       '"eachchange" initialized, returns the original jQuery 
object'
-               );
-
-               subject.eachchange( iIncr ); // assign second time
-
-               iTriggerTest(
-                       subject,
-                       2,
-                       '"eachchange" triggered, eachchange() was used twice on 
same object but should only be triggered once each.'
-               );
-
-               iReset();
-               subject.on( 'eachchange', iIncr );
-
-               iTriggerTest(
-                       subject,
-                       3,
-                       '"eachchange" added with jQuery.on(), should trigger 
three times now.'
-               );
-
-       } );
-
-       QUnit.test( 'jQuery.eachchange() on a jQuery set of two input 
elements', function( assert ) {
-               var subject = $( '<input/>', {
-                       'class': 'test_eachchange',
-                       'type': 'text',
-                       'name': 'test1',
-                       'value': ''
-               } ).add( $( '<input/>', {
-                       'class': 'test_eachchange',
-                       'type': 'text',
-                       'name': 'test2',
-                       'value': ''
-               } ) ); // should always be ignored, otherwise some tests will 
fail.
-
-               assert.equal(
-                       subject.eachchange( iIncr ),
-                       subject,
-                       '"eachchange" initialized, returns the original jQuery 
object'
-               );
-
-               iTriggerTest(
-                       subject,
-                       2,
-                       '"eachchange" triggered, eachchange() was used on two 
objects at the same time.'
-               );
-       } );
-
-}( jQuery, QUnit ) );
diff --git 
a/ValueView/tests/qunit/jquery/jquery.event.special.eachchange.tests.js 
b/ValueView/tests/qunit/jquery/jquery.event.special.eachchange.tests.js
new file mode 100644
index 0000000..e7308cd
--- /dev/null
+++ b/ValueView/tests/qunit/jquery/jquery.event.special.eachchange.tests.js
@@ -0,0 +1,216 @@
+/**
+ * @licence GNU GPL v2+
+ * @author H. Snater < mediaw...@snater.com >
+ * @author Daniel Werner
+ */
+( function( $, QUnit ) {
+       'use strict';
+
+       // Helper functions:
+       var i = 0,
+               iIncr = function() {
+                       i++;
+               },
+               iReset = function() {
+                       i = 0;
+               };
+
+       /**
+        * @param {Object} attributes
+        * @return {jQuery}
+        */
+       function generateInputElement( attributes ) {
+               return $( '<input/>', $.extend( {
+                       'class': 'test_eachchange',
+                       'type': 'text',
+                       'name': 'test',
+                       'value': ''
+               }, attributes ) );
+       }
+
+       QUnit.module( 'jquery.event.special.eachchange', {
+               setup: function() {
+                       iReset();
+               },
+               teardown: function() {
+                       $( '.test_eachchange' ).remove();
+               }
+       } );
+
+       QUnit.test(
+               'Initialization',
+               function( assert ) {
+                       var $input = $( '<input/>', { 'class': 
'test_eachchange', type: 'text' } ),
+                               $inputNoType = $( '<input/>', { 'class': 
'test_eachchange' } ),
+                               $textarea = $( '<textarea/>', { 'class': 
'test_eachchange' } ),
+                               $div = $( '<div/>', { 'class': 
'test_eachchange' } ),
+                               $object = $( {} );
+
+                       assert.equal(
+                               $input.on( 'eachchange', iIncr ),
+                               $input,
+                               'Initialized event on a text input element.'
+                       );
+
+                       assert.equal(
+                               $inputNoType.on( 'eachchange', iIncr ),
+                               $inputNoType,
+                               'Initialized event on an input element that has 
no "type" attribute.'
+                       );
+
+                       assert.equal(
+                               $textarea.on( 'eachchange', iIncr ),
+                               $textarea,
+                               'Initialized event on a textarea.'
+                       );
+
+                       assert.equal(
+                               $div.on( 'eachchange', iIncr ),
+                               $div,
+                               'Initialized event on a div element.'
+                       );
+
+                       assert.equal(
+                               $object.on( 'eachchange', iIncr ),
+                               $object,
+                               'Initialized event on a plain object.'
+                       );
+               }
+       );
+
+       QUnit.test( 'Triggering on a single input element', 2, function( assert 
) {
+               var $subject = generateInputElement( { 'value': 'a' } );
+
+               $subject.on( 'eachchange', iIncr );
+
+               // Assign a second time:
+               $subject.on( 'eachchange', function( event, previousValue ) {
+                       assert.equal(
+                               previousValue,
+                               'a',
+                               'Received previous value.'
+                       );
+
+                       iIncr();
+               } );
+
+               $subject.trigger( 'eachchange' );
+
+               assert.equal(
+                       i,
+                       2,
+                       'Event is triggered only once per assignment.'
+               );
+       } );
+
+       QUnit.test( 'Triggering with a namespace assigned', function( assert ) {
+               var $subject = generateInputElement();
+
+               $subject.on( 'eachchange.somenamespace', iIncr );
+               $subject.on( 'eachchange.othernamespace', iIncr );
+
+               $subject.trigger( 'eachchange.somenamespace' );
+
+               assert.equal(
+                       i,
+                       1,
+                       'Triggered "eachchange" handler with a specific 
namespace.'
+               );
+
+               $subject.trigger( 'eachchange' );
+
+               assert.equal(
+                       i,
+                       3,
+                       'Triggered "eachchange" handlers without a specific 
namespace.'
+               );
+       } );
+
+       QUnit.test( 'Triggering with the event assigned twice with the same 
namespace',
+               function( assert ) {
+                       var $subject = generateInputElement();
+
+                       $subject.on( 'eachchange.somenamespace', iIncr );
+                       $subject.on( 'eachchange.somenamespace', iIncr );
+
+                       $subject.trigger( 'eachchange.somenamespace' );
+
+                       assert.equal(
+                               i,
+                               2,
+                               'Triggered multiple "eachchange" handlers 
featuring the same namespace.'
+                       );
+
+                       $subject.trigger( 'eachchange' );
+
+                       assert.equal(
+                               i,
+                               4,
+                               'Triggered multiple "eachchange" handlers 
featuring the same namespace without '
+                                       + 'specifying the namespace.'
+                       );
+               }
+       );
+
+       QUnit.test( 'Triggering using a native browser event', function( assert 
) {
+               var $subject = generateInputElement();
+
+               $subject.on( 'eachchange', iIncr );
+
+               // Issuing "input" and "keydown" to trigger "eachchange" in all 
browsers:
+               $subject.trigger( $.Event( 'input' ) ).trigger( $.Event( 
'keydown' ) );
+
+               assert.ok(
+                       i > 0,
+                       'Triggered "eachchange" with a native browser event.'
+               );
+       } );
+
+       QUnit.test( 'Triggering on a set of two input elements', function( 
assert ) {
+               var $subject = generateInputElement().add( 
generateInputElement() );
+
+               $subject.on( 'eachchange', iIncr );
+
+               $subject.trigger( 'eachchange' );
+
+               assert.equal(
+                       i,
+                       2,
+                       'Triggered event on a set of two objects.'
+               );
+       } );
+
+       QUnit.test( 'Bubbling up the DOM tree', 1, function( assert ) {
+               var $subject = generateInputElement(),
+                       $parent = $( '<div/>' );
+
+               $parent
+               .append( $subject )
+               .on( 'eachchange', function( event, prevVal ) {
+                       assert.ok(
+                               true,
+                               'Event bubbled to parent DOM node.'
+                       );
+               } );
+
+               $subject.trigger( 'eachchange' );
+       } );
+
+       QUnit.test( 'Triggering event on an object that does not have a 
dedicated value',
+               1,
+               function( assert ) {
+                       var $subject = $( {} );
+
+                       $subject.on( 'eachchange', function( event, prevVal ) {
+                               assert.equal(
+                                       prevVal,
+                                       null,
+                                       'Event is triggered on object that does 
not have a dedicated value.'
+                               );
+                       } );
+
+                       $subject.trigger( 'eachchange' );
+               }
+       );
+
+}( jQuery, QUnit ) );

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

Gerrit-MessageType: newchange
Gerrit-Change-Id: I8e674b825f08c656e9bb78296d074d2c73cc01e6
Gerrit-PatchSet: 5
Gerrit-Project: mediawiki/extensions/DataValues
Gerrit-Branch: master
Gerrit-Owner: Henning Snater <henning.sna...@wikimedia.de>
Gerrit-Reviewer: Daniel Werner <daniel.a.r.wer...@gmail.com>
Gerrit-Reviewer: Tobias Gritschacher <tobias.gritschac...@wikimedia.de>
Gerrit-Reviewer: jenkins-bot

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to