Daniel Werner has uploaded a new change for review.

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


Change subject: Moves the jQuery.NativeEventHandler from Wikibase\Lib into 
ValueView extension
......................................................................

Moves the jQuery.NativeEventHandler from Wikibase\Lib into ValueView extension

Introduces a few minor changes while moving this:
- Gets rid of all Wikibase/MediaWiki dependencies in tests.
- Some cleanup done in test files, moved basic test definition in separate file.
- Some coding style changes, most notably multi-var statements replacing 
single-var.
- Cleanup in comments and documentation.

Change-Id: Ia207fdea460a64142cef3dc427d364db353183d3
---
M ValueView/ValueView.resources.mw.php
M ValueView/ValueView.tests.qunit.php
M ValueView/resources/jquery.ui/jquery.ui.inputextender.js
A ValueView/resources/jquery/jquery.nativeEventHandler.js
A ValueView/tests/qunit/jquery.NativeEventHandler/NativeEventHandler.test.js
A 
ValueView/tests/qunit/jquery.NativeEventHandler/NativeEventHandler.test.testDefinition.js
A 
ValueView/tests/qunit/jquery.NativeEventHandler/NativeEventHandler.testsOnObject.js
A 
ValueView/tests/qunit/jquery.NativeEventHandler/NativeEventHandler.testsOnWidget.js
8 files changed, 746 insertions(+), 45 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/DataValues 
refs/changes/48/79148/1

diff --git a/ValueView/ValueView.resources.mw.php 
b/ValueView/ValueView.resources.mw.php
index 0286208..1a556f2 100644
--- a/ValueView/ValueView.resources.mw.php
+++ b/ValueView/ValueView.resources.mw.php
@@ -71,6 +71,12 @@
                        ),
                ),
 
+               'jquery.nativeEventHandler' => $moduleTemplate + array(
+                       'scripts' => array(
+                               'jquery/jquery.nativeEventHandler.js',
+                       )
+               ),
+
                'jquery.eachchange' => $moduleTemplate + array(
                        'scripts' => array(
                                'jquery/jquery.eachchange.js'
diff --git a/ValueView/ValueView.tests.qunit.php 
b/ValueView/ValueView.tests.qunit.php
index e909c30..82692ac 100644
--- a/ValueView/ValueView.tests.qunit.php
+++ b/ValueView/ValueView.tests.qunit.php
@@ -54,6 +54,18 @@
                        ),
                ),
 
+               'jquery.nativeEventHandler.tests' => array(
+                       'scripts' => array(
+                               
"$bp/jquery.NativeEventHandler/NativeEventHandler.test.js",
+                               
"$bp/jquery.NativeEventHandler/NativeEventHandler.test.testDefinition.js",
+                               
"$bp/jquery.NativeEventHandler/NativeEventHandler.testsOnObject.js",
+                               
"$bp/jquery.NativeEventHandler/NativeEventHandler.testsOnWidget.js",
+                       ),
+                       'dependencies' => array(
+                               'jquery.nativeEventHandler',
+                       ),
+               ),
+
                'jquery.eachchange.tests' => array(
                        'scripts' => array(
                                "$bp/jquery/jquery.eachchange.tests.js",
diff --git a/ValueView/resources/jquery.ui/jquery.ui.inputextender.js 
b/ValueView/resources/jquery.ui/jquery.ui.inputextender.js
index e897667..1df5bc2 100644
--- a/ValueView/resources/jquery.ui/jquery.ui.inputextender.js
+++ b/ValueView/resources/jquery.ui/jquery.ui.inputextender.js
@@ -20,19 +20,13 @@
  *         hidden when the associated input element is empty.
  *         Default value: true
  *
- * @option {string} [contentAnimationEvents] One or more events (separated 
with spaces) which imply
- *         that the input extenders extension's content is about to be 
animated. Those events should
- *         give a jQuery.AnimationEvent object as their event object. If this 
is the case and the
- *         event bubbles up to the input extender's extension node, then this 
will trigger the
- *         "contentanimation" event on the widget node.
- *
- * @event animation: Triggered at the beginning of an animation of the input's 
extension.
- *        (1) {jQuery.AnimationEvent} animationEvent
- *
- * @event contentanimation: Triggered at the beginning of an animation of the 
extender's
- *        extension content. Depends on the "contentAnimationEvents" option.
- *        (1) {jQuery.AnimationEvent} animationEvent The animation event gets 
passed on from the
- *            event within the input extender's extension causing the 
"contentanimation" event.
+ * @event animationstep: While the input extender's extension is being 
animated, this event is
+ *        triggered on each animation step. The event forwards the parameters 
received from the
+ *        animation's "step" callback. However, when the animation is 
finished, the event is
+ *        triggered without the second and third parameter.
+ *        (1) {jQuery.Event}
+ *        (2) {number|undefined} [now]
+ *        (3) {jQuery.Tween|undefined} [tween]
  *
  * @dependency jQuery.Widget
  * @dependency jQuery.eachchange
@@ -83,7 +77,6 @@
                 */
                options: {
                        content: [],
-                       contentAnimationEvents: '',
                        initCallback: null,
                        hideWhenInputEmpty: true,
                        position: {
@@ -225,7 +218,7 @@
                /**
                 * Shows the extension.
                 *
-                * @param {Function} [callback] Invoked as soon as the 
extension's show animation is done.
+                * @param {Function} [callback] Invoked as soon as the contents 
are visible.
                 */
                showExtension: function( callback ) {
                        if( !this._isExtended ) {
@@ -237,7 +230,7 @@
                /**
                 * Hides the extension.
                 *
-                * @param {Function} [callback] Invoked as soon as the 
extension's hide animation is done.
+                * @param {Function} [callback] Invoked as soon as the contents 
are hidden.
                 */
                hideExtension: function( callback ) {
                        if( this._isExtended ) {
@@ -280,6 +273,12 @@
 
                /**
                 * Draws the widget.
+                *
+                * @param {Function} [callback] For private usage only.
+                * TODO: Get rid of "callback", introduce some sort of 
"animationstart" event instead,
+                *  offering an object allowing to register callback that will 
be given into the animation's
+                *  options. show/hideExtension can then do a .one() event 
registration for that one and
+                *  register their callback there.
                 */
                draw: function( /* private: */ callback ) {
                        this.element[ this._isExtended ? 'addClass' : 
'removeClass' ](
@@ -301,6 +300,7 @@
                                this.$extension = $extension = 
this._buildExtension();
                                $extension.appendTo( $( 'body' ) );
 
+                               // TODO
                                if( $.isFunction( this.options.initCallback ) ) 
{
                                        $extension.show();
                                        this.options.initCallback.call( this, 
$extension );
@@ -337,43 +337,37 @@
                                this.$extension.css( 'opacity', '1' );
                        }
 
-                       this.$extension.stop( true ).animateWithEvent(
-                               'extensionexpansion',
-                               'fadeIn',
-                               {
-                                       duration: 150,
-                                       complete: function() {
-                                               if( $.isFunction( callback ) ) {
-                                                       callback();
-                                               }
-                                       }
+                       this.$extension.stop( true ).fadeIn( {
+                               duration: 150,
+                               step: function( now, tween ) {
+                                       self._trigger( 'animationstep', null, [ 
now, tween ] );
                                },
-                               function( animationEvent ) {
-                                       self._trigger( 'animation', 
animationEvent );
+                               complete: function() {
+                                       if( $.isFunction( callback ) ) {
+                                               callback();
+                                       }
+                                       self._trigger( 'animationstep' );
                                }
-                       );
+                       } );
                        inputExtendersWithVisibleExtension.add( this );
                },
 
                _drawExtensionRemoval: function( callback ) {
                        var self = this;
 
-                       this.$extension.stop( true ).animateWithEvent(
-                               'extensionremoval',
-                               'fadeOut',
-                               {
-                                       duration: 150,
-                                       complete: function() {
-                                               
inputExtendersWithVisibleExtension.remove( self );
-                                               if( $.isFunction( callback ) ) {
-                                                       callback();
-                                               }
-                                       }
+                       this.$extension.stop( true ).fadeOut( {
+                               duration: 150,
+                               step: function( now, tween ) {
+                                       self._trigger( 'animationstep', null, [ 
now, tween ] );
                                },
-                               function( animationEvent ) {
-                                       self._trigger( 'animation', 
animationEvent );
+                               complete: function() {
+                                       
inputExtendersWithVisibleExtension.remove( this );
+                                       if( $.isFunction( callback ) ) {
+                                               callback();
+                                       }
+                                       self._trigger( 'animationstep' );
                                }
-                       );
+                       } );
                },
 
                /**
@@ -416,8 +410,10 @@
                                        self.showExtension();
                                }
                        } )
-                       .on( this.options.contentAnimationEvents, function( 
animationEvent ) {
-                               self._trigger( 'contentanimation', 
animationEvent );
+                       // TODO: move this out of here, toggler is not even 
used/known to this widget:
+                       .on( 'toggleranimationstep.' + this.widgetName, 
function( event, now, tween ) {
+                               self._reposition();
+                               self._trigger( 'animationstep', null, [ now, 
tween ] );
                        } )
                        .on( 'keydown.' + this.widgetName, function( event ) {
                                // Take care of tabbing out of the extension 
again:
diff --git a/ValueView/resources/jquery/jquery.nativeEventHandler.js 
b/ValueView/resources/jquery/jquery.nativeEventHandler.js
new file mode 100644
index 0000000..0cca3e5
--- /dev/null
+++ b/ValueView/resources/jquery/jquery.nativeEventHandler.js
@@ -0,0 +1,224 @@
+/**
+ * @file
+ *
+ * @licence GNU GPL v2+
+ * @author Daniel Werner < [email protected] >
+ *
+ * Returns a function (outer function) which executes some given logic and 
does additional event
+ * handling for a given event. The event handling is separated in up to three 
steps and handles
+ * advanced features offered by jQuery. The following steps (handlers) in 
detail:
+ *
+ * - initial handler: Executes some initial logic which allows to cancel the 
whole process. All
+ *   parameters given to the outer function are available as well as the 
related jQuery.Event
+ *   object. The jQuery.Event object can be
+ *   used to cancel all further event handlers (jQuery.Event.cancel()) or to 
prevent only the
+ *   custom handlers to be
+ *   executed (jQuery.Event.stopImmediatePropagation() or prevent only the 
native handler to be
+ *   executed (jQuery.Event.preventDefault()). jQuery.Event.handlerArgs can be 
set to an array
+ *   to define all arguments propagated to all handlers if not defined 
otherwise by one of the
+ *   other ...HandlerArgs fields. jQuery.Event.customHandlerArgs can be set to 
an array to
+ *   define all arguments propagated to custom handlers, the jQuery.Event 
object itself will
+ *   always be given to the custom handlers. jQuery.Event.nativeHandlerArgs 
can be set to an
+ *   array which acts as equivalent for the native handler.
+ *
+ * - custom handlers: These are all the handlers registered to the object with 
jQuery.fn.on().
+ *   If not prevented by the initial handler, they will be executed right 
after the initial
+ *   handler. By default this will get the same arguments as the initial 
handler, except if the
+ *   initial handler has explicitly set jQuery.Event.customHandlerArgs.
+ *
+ * - native handler: Is the handler which executes the actual logic of the 
outer function which
+ *   should also be what the defined event is all about.
+ *
+ * The native handler return value will be taken as return value for the outer 
function. If the
+ * native handler never gets called, the return value of the outer function 
can either be the
+ * last return value given by any custom handler (as long as the return value 
was not undefined)
+ * or - if the custom handlers aren't called, if there are no custom handlers 
registered, if
+ * 'allowCustomResult' is set to false or if returning of custom values is not 
supported by the
+ * responsible event handler (which is the case when used within widgets) - 
the initial
+ * handler's return value.
+ *
+ * The context the handlers are called in is usually the one of the outer 
function. The only
+ * exception is for custom handlers while the system is used within jQuery 
widgets. In those
+ * custom handlers, the context will be the widget's subject DOM node.
+ *
+ * NOTE: The native handler is available as 'nativeHandler' property of the 
returned function.
+ *       The initial handler is available as 'initialHandler' property (just 
an empty function
+ *       if not provided)
+ *
+ * @version 0.1
+ *
+ * @param eventName String
+ * @param fn Function|Object if this is a function, then it will be taken as 
native handler and
+ *        will be executed after custom event handlers are executed.
+ *        If this is an object, this can hold properties defining the native 
as well as the
+ *        initial handler as well as additional options for changing the event 
handling
+ *        behavior. The following properties can be given (see full 
description about the
+ *        different handlers above):
+ *        - initially: should be a function representing the initial handler
+ *        - natively: the actual native handler, 'function which makes the 
event happen'
+ *        - allowCustomResult: if set to true, custom handlers result values 
will be returned
+ *          by the outer function if the native handler won't be called 
(because of
+ *          jQuery.preventDefault)
+ *
+ * @return Function
+ *
+ * @example
+ * <code>
+ * // Will focus the element and return true if focus has been set, false if 
the process failed.
+ * // Will trigger 'focus' event if focus isn't set already.
+ * SomeConstructor.prototype.focus = $.NativeEventHandler( 'focus', {
+ *     // event: jQuery.Event which will be triggered after this if 
event.stopPropagation() not called
+ *     // The other arguments are those who were given to the public, outer 
focus() function
+ *     initially: function( event, highlight, someInternal ) {
+ *         if( this.hasFocus() ) {
+ *             event.cancel(); // focus is set already, stop everything...
+ *             return true; // ... and let the outer focus() function return 
true
+ *         }
+ *         event.customHandlerArgs = [ highlight ]; // don't give the 
someInternal arg to custom event handlers
+ *         return false; // will be returned by outer focus() if custom 
handlers call event.preventDefault()
+ *     },
+ *     natively: function( event, highlight, somethingInternal ) {
+ *         // this will only be called after 'focus' event was called and 
default wasn't prevented
+ *         doSomething();
+ *         return true; // final return value for outer focus()
+ *     }
+ * } )
+ * </code>
+ */
+jQuery.NativeEventHandler = ( function( $  ) {
+       'use strict';
+
+       var NEH_OPTIONS = [
+               'natively',
+               'nativeHandler',
+               'initially',
+               'initialHandler',
+               'allowCustomResult'
+       ];
+
+       return function( eventName, fn ) {
+               var initialFn = function() {},
+                       allowCustomResult = false;
+
+               if( !$.isFunction( fn ) ) { // not just a native handler but 
additional callbacks/options
+                       // check for spelling errors in definition object
+                       $.each( fn, function( key, elem ) {
+                               if( $.inArray( key, NEH_OPTIONS ) === -1 ) {
+                                       throw new Error( 'Unknown native event 
handler option "' + key + '"' );
+                               }
+                       } );
+
+                       // get options
+                       allowCustomResult = fn.allowCustomResult !== undefined
+                               ? fn.allowCustomResult
+                               : allowCustomResult;
+
+                       // get handlers
+                       initialFn = fn.initially || fn.initialHandler || 
initialFn;
+                       fn = fn.natively || fn.nativeHandler;
+
+                       // make sure we have a native handler or fail
+                       if( !$.isFunction( fn ) ) {
+                               throw new Error( 'No native handler function 
provided' );
+                       }
+               }
+
+               /**
+                * The returned function handling all the stages of handlers.
+                * 1. initial, 2. custom, 3. native
+                *
+                * @return {*}
+                */
+               var handler = function() {
+                       var event = $.Event( eventName, {
+                               handlerArgs: false,
+                               customHandlerArgs: false,
+                               nativeHandlerArgs: false,
+                               cancel: function() {
+                                       event.stopImmediatePropagation();
+                                       event.preventDefault();
+                               }
+                       } );
+                       var args = $( arguments ).toArray();
+
+                       // Does all the preparation and can cancel the whole 
thing:
+                       var ret = handler.initialHandler.apply( this, [ event 
].concat( args ) );
+
+                       var defaultPreventedByWidget = false;
+
+                       // Store this so custom callbacks can't interfere with 
internal default prevention:
+                       var defaultPreventedEarly = event.isDefaultPrevented();
+
+                       // Allow for different arguments for custom/native 
event handlers:
+                       var handlerArgs = event.handlerArgs || args;
+                       var customHandlerArgs = event.customHandlerArgs || 
handlerArgs;
+                       var nativeHandlerArgs = event.nativeHandlerArgs || 
handlerArgs;
+
+                       // Don't reveal this to custom handlers!
+                       event.handlerArgs = event.customHandlerArgs = 
event.nativeHandlerArgs = event.cancel
+                               = undefined;
+
+                       // Trigger all registered events (custom handlers).
+                       // This might be prevented by the initial handler for 
some reason.
+                       if( !event.isImmediatePropagationStopped() ) {
+                               if( $.Widget && ( this instanceof $.Widget ) ) {
+                                       // Use the $.Widget's native trigger 
mechanisms.
+                                       // $.Widget._trigger will use its own 
event but return false if prevented.
+                                       // Also, context of custom handlers 
will be the DOM node rather than the widget.
+                                       defaultPreventedByWidget = 
!this._trigger( event.type, null, customHandlerArgs );
+                                       // TODO: attach our own event as some 
field of the widget's event
+                               } else {
+                                       // Don't use trigger(); it might end up 
in an endless loop since it would try to
+                                       // execute a function named after the 
event in the object.
+                                       $( this ).triggerHandler( event, 
customHandlerArgs );
+                               }
+
+                               // If desired for this event, let custom 
handlers last return value overwrite
+                               // initial handlers one.
+                               if( allowCustomResult && event.result !== 
undefined ) {
+                                       ret = event.result;
+                               }
+                       }
+
+                       // Initial handler and custom handlers can prevent 
native event from being executed.
+                       if( !defaultPreventedEarly
+                               && ( !defaultPreventedByWidget && 
!event.isDefaultPrevented() )
+                       ) {
+                               // Call native handler for the event.
+                               // Give event as first argument just like 
jQuery does for custom handlers!
+                               var nativeRet = handler.nativeHandler.apply( 
this, [ event ].concat( nativeHandlerArgs ) );
+
+                               // If native handler returns undefined, return 
previously gathered return value.
+                               ret = nativeRet !== undefined ? nativeRet : 
ret; // might be the same value
+                       }
+
+                       return ret; // whatever the initial or custom 
handler(s) returned last (ignoring undefined)
+               };
+
+               /**
+                * @type Function
+                * @since 0.1
+                *
+                * @param event {jQuery.Event} the event which is about to be 
triggered
+                * @param args {Array} arguments which will be applied to the 
native handler as well as to
+                *        the custom callbacks (except if the return value is 
an array in which case these
+                *        values will be used for custom callbacks).
+                * @return {undefined|Boolean|Array} if undefined or true the 
native handler as well as all
+                *         custom callbacks will be executed. If false, the 
whole event will be cancelled.
+                *         If an array is returned, its contents will be 
applied to the custom callbacks as
+                *         parameters, the native handler will still receive 
the arguments of the args array.
+                */
+               handler.initialHandler = initialFn;
+
+               /**
+                * Holds the pure functionality of the native event handler.
+                *
+                * @type Function
+                * @since 0.1
+                */
+               handler.nativeHandler = fn; // for outside world and inheritance
+
+               return handler;
+       };
+
+}( jQuery ) );
diff --git 
a/ValueView/tests/qunit/jquery.NativeEventHandler/NativeEventHandler.test.js 
b/ValueView/tests/qunit/jquery.NativeEventHandler/NativeEventHandler.test.js
new file mode 100644
index 0000000..f9b35b7
--- /dev/null
+++ b/ValueView/tests/qunit/jquery.NativeEventHandler/NativeEventHandler.test.js
@@ -0,0 +1,304 @@
+/**
+ * @file
+ * @licence GNU GPL v2+
+ * @author Daniel Werner < [email protected] >
+ */
+jQuery.NativeEventHandler.test = ( function ( $, QUnit ) {
+       'use strict';
+
+/**
+ * Will execute NativeEventHandler tests based on a given test definition.
+ * @see jQuery.NativeEventHandler.test.testDefinition
+ *
+ * @since 0.1
+ *
+ * @param {Object} testDefinition
+ */
+return function( testDefinition ) {
+       QUnit.module( 'jQuery.NativeEventHandler using ' + 
testDefinition.eventSystem );
+
+       // TEST HELPERS:
+
+       var NEH_STAGE = {
+               INITIAL: 1,
+               CUSTOM: 2,
+               NATIVE: 4
+       };
+       var testResult = 0;
+       function initialHandler() {
+               testResult |= NEH_STAGE.INITIAL;
+       }
+       function customHandler() {
+               testResult |= NEH_STAGE.CUSTOM;
+       }
+       function nativeHandler() {
+               testResult |= NEH_STAGE.NATIVE;
+       }
+       function testNEH( exceptedFlag, comment ) {
+               QUnit.assert.equal(
+                       testResult,
+                       exceptedFlag,
+                       comment
+               );
+               testResult = 0;
+       }
+       var newTestBody = testDefinition.newTestBody;
+       var supportsCustomResults = testDefinition.supportsCustomResults;
+
+       // ACTUAL TESTS:
+
+       QUnit.test( 'Simple native event', function( assert ) {
+               var TEST_EVENT = 'run';
+               var testBody = newTestBody(); // 'Class' which we define our 
test function in
+               var ret;
+
+               testBody[ TEST_EVENT ] = $.NativeEventHandler( TEST_EVENT, 
nativeHandler );
+
+               assert.ok(
+                       $.isFunction( testBody[ TEST_EVENT ] ),
+                       'Returns a function'
+               );
+
+               assert.ok(
+                       $.isFunction( testBody[ TEST_EVENT ].nativeHandler ),
+                       'Reference to inner native handler function stored'
+               );
+
+               // register some custom event
+               testBody.one( TEST_EVENT, customHandler );
+               testBody[ TEST_EVENT ](); // CALL!
+               testNEH(
+                       NEH_STAGE.NATIVE + NEH_STAGE.CUSTOM,
+                       'custom and native events were called after registering 
event with jQuery.one()'
+               );
+
+               testBody[ TEST_EVENT ](); // CALL!
+               testNEH(
+                       NEH_STAGE.NATIVE,
+                       'only native handler was called because no events are 
registered'
+               );
+
+               testBody.one( TEST_EVENT, function( event ) {
+                       customHandler();
+                       event.preventDefault(); // should prevent from calling 
native handler
+                       return 'foo';
+               } );
+               ret = testBody[ TEST_EVENT ](); // CALL!
+               testNEH(
+                       NEH_STAGE.CUSTOM,
+                       'only custom handlers are called after one of them 
requests jQuery.Event.preventDefault()'
+               );
+               assert.notEqual(
+                       ret,
+                       'foo', // shouldn't work because allowCustomResult not 
set to true!
+                       'calling event function has not returned value returned 
by native handler'
+               );
+
+               testBody.one( TEST_EVENT, function( event ) {
+                       event.stopImmediatePropagation();
+               } );
+               testBody.one( TEST_EVENT, customHandler );
+               testBody[ TEST_EVENT ](); // CALL!
+               testNEH(
+                       NEH_STAGE.NATIVE,
+                       'no further custom handlers were called after 
jQuery.Event.stopImmediatePropagation()'
+               );
+       } );
+
+
+       QUnit.test( 'Simple native event with initial handler, also allowing 
custom results', function( assert ) {
+               var TEST_EVENT = 'run';
+               var testBody = newTestBody();
+               var ret;
+
+               testBody[ TEST_EVENT ] = $.NativeEventHandler( TEST_EVENT, {
+                       allowCustomResult: true,
+                       initially: function( event, cancel ) {
+                               initialHandler();
+                               if( cancel ) { // for cancel test
+                                       event.cancel();
+                               }
+                               return NEH_STAGE.INITIAL;
+                       },
+                       natively: function( event ) {
+                               nativeHandler();
+                               return NEH_STAGE.NATIVE;
+                       }
+               } );
+
+               assert.ok(
+                       $.isFunction( testBody[ TEST_EVENT ].initialHandler ),
+                       'Reference to inner initial handler function stored'
+               );
+
+               ret = testBody[ TEST_EVENT ](); // CALL!
+               assert.equal(
+                       ret,
+                       NEH_STAGE.NATIVE,
+                       'calling event function returns value returned by 
native handler'
+               );
+               testNEH(
+                       NEH_STAGE.INITIAL + NEH_STAGE.NATIVE,
+                       'initial and native handlers were called (no event 
registered)'
+               );
+
+               // register some custom event
+               testBody.one( TEST_EVENT, customHandler );
+               testBody[ TEST_EVENT ](); // CALL!
+               testNEH(
+                       NEH_STAGE.INITIAL + NEH_STAGE.NATIVE + NEH_STAGE.CUSTOM,
+                       'initial, custom and native handlers were called'
+               );
+
+               ret = testBody[ TEST_EVENT ]( true ); // CALL!, argument 
triggers $.Event.cancel() test
+               assert.equal(
+                       ret,
+                       NEH_STAGE.INITIAL,
+                       'calling event function returns value returned by 
initial handler because of condition in initial handler'
+               );
+               testNEH(
+                       NEH_STAGE.INITIAL,
+                       'only initial handler was called, which then decided to 
cancel the whole event'
+               );
+
+               testBody.one( TEST_EVENT, function( event ) {
+                       customHandler();
+                       event.preventDefault();
+                       return NEH_STAGE.CUSTOM; // should be returned by event 
function because native handler suppressed above^^
+               } );
+               ret = testBody[ TEST_EVENT ](); // CALL!
+
+               if( supportsCustomResults ) {
+                       assert.equal(
+                               ret,
+                               NEH_STAGE.CUSTOM,
+                               'calling event function returns value returned 
by last custom handler because ' +
+                                       'default was prevented and custom 
results are supported by the event handler'
+                       );
+               } else {
+                       assert.equal(
+                               ret,
+                               NEH_STAGE.INITIAL,
+                               'calling event function returns value returned 
by initial handler even though ' +
+                                       'custom handler did prevent default and 
returned a custom value while default ' +
+                                       'has been prevented. The final output 
will be the native handler\'s return value'
+                       );
+               }
+               testNEH(
+                       NEH_STAGE.INITIAL + NEH_STAGE.CUSTOM,
+                       'only custom handlers are called after one of them 
requests jQuery.Event.preventDefault()'
+               );
+
+               testBody.one( TEST_EVENT, function( event ) {
+                       customHandler();
+                       return NEH_STAGE.CUSTOM; // should be returned by event 
function because native handler suppressed next!
+               } );
+               testBody.one( TEST_EVENT, function( event ) {
+                       return false;
+               } );
+               ret = testBody[ TEST_EVENT ](); // CALL!
+
+               assert.equal(
+                       ret,
+                       supportsCustomResults
+                               ? false // false returned by custom handler, 
also implies preventDefault!
+                               : NEH_STAGE.INITIAL, // false causes 
preventDefault() but outer function will have native handler's return value
+                       supportsCustomResults
+                               ? 'calling event function returns value 
returned by first custom handler even' +
+                                       'though it is false'
+                               : 'calling event function returns native 
handlers result instead of false even ' +
+                                       'though false was returned by custom 
handler. This is because custom results ' +
+                                       'are not supported by the event handler 
in use.'
+               );
+               testNEH(
+                       NEH_STAGE.INITIAL + NEH_STAGE.CUSTOM,
+                       'only custom handlers are called after second custom 
handler returned false'
+               );
+       } );
+
+
+       QUnit.test(
+               'Additional jQuery.Event members used for communicating between 
initial handler and outer function',
+               12, // make sure all tests are executed since we execute some 
tests from within event handlers!
+       function( assert ) {
+
+               var TEST_EVENT = 'run';
+               var testBody = newTestBody();
+               var newBasicHandlerTest = function( handlerType, 
numberOfAdditionalArgs ) {
+                               return function( event ) {
+                                       assert.equal(
+                                               this,
+                                               testBody[ handlerType + 
'HandlerContext' ],
+                                               handlerType + ' handler was 
called in the right context'
+                                       );
+                                       assert.ok(
+                                               event instanceof $.Event,
+                                               handlerType + ' handler 
callback gets jQuery.Event as first parameter'
+                                       );
+                                       assert.ok(
+                                               arguments.length === 
numberOfAdditionalArgs + 1, // + 1 for event arg
+                                               'all ' + numberOfAdditionalArgs 
+ ' arguments plus one for event object get passed on'
+                                       );
+                                       switch( handlerType ) { // will only 
set a flag that the handler was called
+                                               case 'initial': 
initialHandler(); break;
+                                               case 'native': nativeHandler(); 
break;
+                                               case 'custom': customHandler(); 
break;
+                                       }
+                               };
+                       };
+
+               testBody[ TEST_EVENT ] = $.NativeEventHandler( TEST_EVENT, {
+                       initially: function( event ) {
+                               newBasicHandlerTest( 'initial', 2 ).apply( 
this, arguments );
+
+                               assert.ok(
+                                       event.customHandlerArgs === false,
+                                       'jQuery.Event.customHandlerArgs is set 
to false'
+                               );
+
+                               assert.ok(
+                                       event.nativeHandlerArgs === false,
+                                       'jQuery.Event.customHandlerArgs is set 
to false'
+                               );
+
+                               event.customHandlerArgs = [ 1, 2, 3 ];
+                               event.nativeHandlerArgs = [ 1, 2, 3, 4, 5 ];
+                       },
+                       natively: newBasicHandlerTest( 'native', 5 )
+               } );
+
+               // register some custom event, execute newBasicHandlerTest 
tests there as well
+               testBody.one( TEST_EVENT, newBasicHandlerTest( 'custom', 3 ) );
+               testBody[ TEST_EVENT ]( 1, 2 ); // CALL!, give two parameters 
for test
+               testNEH(
+                       NEH_STAGE.INITIAL + NEH_STAGE.NATIVE + NEH_STAGE.CUSTOM,
+                       'initial, custom and native handlers were called'
+               );
+       } );
+
+
+       QUnit.test( 'Excepted errors', function( assert ) {
+               assert.throws(
+                       function() {
+                               $.NativeEventHandler( 'foo' );
+                       },
+                       'Can\'t create handler without function'
+               );
+
+               assert.throws(
+                       function() {
+                               $.NativeEventHandler( 'foo', {} );
+                       },
+                       'Can\'t create handler without function although 2nd 
parameter is set'
+               );
+
+               assert.throws(
+                       function() {
+                               $.NativeEventHandler( 'foo', { natively: 
$.noop, 'foo': 'test' } );
+                       },
+                       'Can\'t create handler with unknown key in 2nd 
parameter'
+               );
+       } );
+};
+
+}( jQuery, QUnit ) );
diff --git 
a/ValueView/tests/qunit/jquery.NativeEventHandler/NativeEventHandler.test.testDefinition.js
 
b/ValueView/tests/qunit/jquery.NativeEventHandler/NativeEventHandler.test.testDefinition.js
new file mode 100644
index 0000000..5af1d3d
--- /dev/null
+++ 
b/ValueView/tests/qunit/jquery.NativeEventHandler/NativeEventHandler.test.testDefinition.js
@@ -0,0 +1,50 @@
+/**
+ * @file
+ * @licence GNU GPL v2+
+ * @author Daniel Werner < [email protected] >
+ */
+jQuery.NativeEventHandler.test.testDefinition = ( function ( $, QUnit ) {
+       'use strict';
+
+       /**
+        * Test definition for running wb.tests.nativeEventHandlerTest with.
+        *
+        * @since 0.1
+        *
+        * @constructor
+        * @abstract
+        */
+       return {
+               /**
+                * Descriptive name of the event handler system which is used 
together with the
+                * NativeEventHandler in this test definition.
+                * @type {String}
+                */
+               eventSystem: '',
+
+               /**
+                * Whether custom results are supported by the event handler 
system in use. E.g. $.Widget's
+                * _trigger() does not allow for custom results while 
$.trigger() does.
+                * @type Boolean
+                */
+               supportsCustomResults: false,
+
+               /**
+                * Returns an Object which test functions factored by the 
NativeEventHandler can be attached
+                * to. The returned object also has a 'one' function which is 
equivalent to jQuery.one() and
+                * allows for listening to events which should be triggered by 
any NativeEventHandler
+                * defined on the test body Object returned by this.
+                *
+                * @return {Object} Will have the following fields:
+                *         - 'one' function to register custom event handler, 
will be removed after called once.
+                *         - 'initialHandlerContext' The object which should be 
the context for initial handler.
+                *         - 'customHandlerContext' The object which should be 
the context for custom handlers.
+                *         - 'nativeHandlerContext' The object which should be 
the context for native handler.
+                */
+               newWidgetTestBody: function() {
+                       throw new Error( '"newWidgetTestBody" has to be 
overwritten in specific test '
+                               + 'implementation' );
+               }
+       };
+
+}( jQuery, QUnit ) );
diff --git 
a/ValueView/tests/qunit/jquery.NativeEventHandler/NativeEventHandler.testsOnObject.js
 
b/ValueView/tests/qunit/jquery.NativeEventHandler/NativeEventHandler.testsOnObject.js
new file mode 100644
index 0000000..1f7c877
--- /dev/null
+++ 
b/ValueView/tests/qunit/jquery.NativeEventHandler/NativeEventHandler.testsOnObject.js
@@ -0,0 +1,50 @@
+/**
+ * @file
+ * @licence GNU GPL v2+
+ * @author Daniel Werner < [email protected] >
+ */
+( function ( $, QUnit, runTest, testDefinitionBase ) {
+       'use strict';
+
+       /**
+        * Test definition for running NativeEventHandler tests within a plain 
Object's environment.
+        * For triggering events on the object, $( obj ).trigger() will be used.
+        *
+        * @type Object
+        */
+       var testDefinition = $.extend( {}, testDefinitionBase, {
+               /**
+                * @see 
jQuery.NativeEventHandler.test.testDefinition.eventSystem
+                */
+               eventSystem: 'jQuery.fn.trigger',
+
+               /**
+                * @see 
jQuery.NativeEventHandler.test.testDefinition.supportsCustomResults
+                */
+               supportsCustomResults: true,
+
+               /**
+                * @see 
jQuery.NativeEventHandler.test.testDefinition.newWidgetTestBody
+                */
+               newTestBody: function() {
+                       var testBody = {
+                               one: function( eventType, fn ) {
+                                       $( this ).one( eventType, fn );
+                               }
+                       };
+                       testBody.initialHandlerContext
+                               = testBody.customHandlerContext
+                               = testBody.nativeHandlerContext
+                               = testBody;
+
+                       return testBody;
+               }
+       } );
+
+       runTest( testDefinition );
+}(
+       jQuery,
+       QUnit,
+       jQuery.NativeEventHandler.test,
+       jQuery.NativeEventHandler.test.testDefinition
+) );
diff --git 
a/ValueView/tests/qunit/jquery.NativeEventHandler/NativeEventHandler.testsOnWidget.js
 
b/ValueView/tests/qunit/jquery.NativeEventHandler/NativeEventHandler.testsOnWidget.js
new file mode 100644
index 0000000..e7af534
--- /dev/null
+++ 
b/ValueView/tests/qunit/jquery.NativeEventHandler/NativeEventHandler.testsOnWidget.js
@@ -0,0 +1,59 @@
+/**
+ * @file
+ * @licence GNU GPL v2+
+ * @author Daniel Werner < [email protected] >
+ */
+( function ( $, QUnit, runTest, testDefinitionBase ) {
+       'use strict';
+
+       /**
+        * Test definition for running NativeEventHandler tests within jQuery 
Widget environment,
+        * meaning, the jQuery.Widget's _trigger() function will be used to 
trigger events.
+        *
+        * @type Object
+        */
+       var testDefinition = $.extend( {}, testDefinitionBase, {
+               /**
+                * @see wb.tests.NativeEventHandlerTestDefinition.eventSystem
+                */
+               eventSystem: 'jQuery.Widget.prototype._trigger',
+
+               /**
+                * @see 
wb.tests.NativeEventHandlerTestDefinition.supportsCustomResults
+                */
+               supportsCustomResults: false,
+
+               /**
+                * @see 
wb.tests.NativeEventHandlerTestDefinition.newWidgetTestBody
+                */
+               newTestBody: function() {
+                       var TestWidget = function() {
+                               $.Widget.apply( this, arguments );
+                       };
+                       TestWidget.prototype = $.extend( new $.Widget(), {
+                               constructor: TestWidget,
+                               widgetName: 'neh_test_widget',
+                               widgetEventPrefix: 'neh_test_widget_'
+                       } );
+
+                       var testBody = new TestWidget( {}, $( '<div/>' ) );
+
+                       testBody.one = function( eventType, fn ) {
+                               // In widgets, event will have a prefix!
+                               testBody.element.one( 
testBody.widgetEventPrefix + eventType, fn );
+                       };
+                       testBody.initialHandlerContext = testBody;
+                       testBody.customHandlerContext = testBody.element[0];
+                       testBody.nativeHandlerContext = testBody;
+
+                       return testBody;
+               }
+       } );
+
+       runTest( testDefinition );
+}(
+       jQuery,
+       QUnit,
+       jQuery.NativeEventHandler.test,
+       jQuery.NativeEventHandler.test.testDefinition
+) );

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

Gerrit-MessageType: newchange
Gerrit-Change-Id: Ia207fdea460a64142cef3dc427d364db353183d3
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/DataValues
Gerrit-Branch: master
Gerrit-Owner: Daniel Werner <[email protected]>

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

Reply via email to