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