Divec has uploaded a new change for review.
https://gerrit.wikimedia.org/r/84997
Change subject: WIP:EventSequencer listeners at event loop start/end
......................................................................
WIP:EventSequencer listeners at event loop start/end
Implement two new types of event listener, both triggered only once per
event loop iteration.
ve.EventSequencer.js
* onLoop( f ) to trigger a listener at the start of the event loop
* afterLoop( f ) to trigger a listener at the end of the event loop
Change-Id: Iee9d4e5599836f52840ac34deca3408b1e72c4c3
---
M modules/ve/ve.EventSequencer.js
1 file changed, 103 insertions(+), 4 deletions(-)
git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/VisualEditor
refs/changes/97/84997/1
diff --git a/modules/ve/ve.EventSequencer.js b/modules/ve/ve.EventSequencer.js
index e34d8d1..e78c385 100644
--- a/modules/ve/ve.EventSequencer.js
+++ b/modules/ve/ve.EventSequencer.js
@@ -19,9 +19,13 @@
* *after* the keypress listener (i.e. in the 'wrong' order). EventSequencer
* ensures that this does not happen.
*
- * All listeners receive the jQuery event as an argument. If an on-event
+ * All these listeners receive the jQuery event as an argument. If an on-event
* listener needs to pass information to a corresponding after-event listener,
* it can do so by adding properties into the jQuery event itself.
+ *
+ * There are also 'onLoop' and 'afterLoop' listeners, which only fire once per
+ * Javascript event loop iteration, respectively before and after all the
+ * other listeners fire.
*
* @class ve.EventSequencer
*/
@@ -81,6 +85,26 @@
this.afterListenersForEvent[eventName] = [];
this.eventHandlers[eventName] = makeEventHandler( eventName );
}
+
+ /**
+ * @property {Function[]}
+ */
+ this.onLoopListeners = [];
+
+ /**
+ * @property {Function[]}
+ */
+ this.afterLoopListeners = [];
+
+ /**
+ * @property {boolean}
+ */
+ this.doneOnLoop = false;
+
+ /**
+ * @property {number}
+ */
+ this.afterLoopTimeoutId = null;
};
/**
@@ -110,6 +134,15 @@
/**
+ * Add listeners to be fired at the start of the Javascript event loop
iteration
+ * @method
+ * @param {Function[]} listeners Listeners that take no arguments
+ */
+ve.EventSequencer.prototype.onLoop = function ( listeners ) {
+ Array.prototype.push.apply( this.onLoopListeners, listeners );
+};
+
+/**
* Add listeners to be fired just before the browser native action
* @method
* @param {Object.<string,Function>} listeners Function for each event
@@ -120,7 +153,6 @@
this.onListenersForEvent[eventName].push( listeners[eventName]
);
}
};
-
/**
* Add listeners to be fired as soon as possible after the native action
@@ -135,6 +167,15 @@
};
/**
+ * Add listeners to be fired at the end of the Javascript event loop iteration
+ * @method
+ * @param {Function[]} listeners Listeners that take no arguments
+ */
+ve.EventSequencer.prototype.afterLoop = function ( listeners ) {
+ Array.prototype.push.apply( this.afterLoopListeners, listeners );
+};
+
+/**
* Generic listener method which does the sequencing
* @private
* @method
@@ -144,6 +185,10 @@
ve.EventSequencer.prototype.onEvent = function ( eventName, ev ) {
var i, len, onListener, afterListener, pendingCall;
this.runPendingCalls();
+ if ( ! this.doneOnLoop ) {
+ this.doneOnLoop = true;
+ this.doOnLoop();
+ }
// Length cache 'len' is required, as an onListener could add another
onListener
for ( i = 0, len = this.onListenersForEvent[eventName].length; i < len;
i++ ) {
onListener = this.onListenersForEvent[eventName][i];
@@ -160,23 +205,77 @@
// Must wrap everything in a function call, to create the
required closure.
pendingCall = { 'func': afterListener, 'id': null, 'ev': ev,
'eventName': eventName };
/*jshint loopfunc:true */
- ( function ( pendingCall, ev ) {
+ // Should put the loop into the setTimeout. But len < 2 usually
so it's not so bad
+ ( function ( pendingCall, ev, me ) {
var id = setTimeout( function () {
if ( pendingCall.id === null ) {
// clearTimeout seems not always to
work immediately
return;
}
+ ve.EventSequencer.resetAfterLoopTimeout( me );
pendingCall.id = null;
pendingCall.func( ev );
} );
pendingCall.id = id;
- } )( pendingCall, ev );
+ } )( pendingCall, ev, this );
/*jshint loopfunc:false */
this.pendingCalls.push( pendingCall );
}
};
/**
+ * Call each onLoopListener once
+ * @private
+ * @method
+ */
+ve.EventSequencer.prototype.doOnLoop = function () {
+ var i, len, onLoopListener;
+ // Length cache 'len' is required, as the functions called may add
another listener
+ for ( i = 0, len = this.onLoopListeners.length; i < len; i++ ) {
+ onLoopListener = this.onLoopListeners[i];
+ onLoopListener();
+ }
+};
+
+/**
+ * Call each afterLoopListener once, unless the setTimeout is already cancelled
+ * @private
+ * @method
+ * @param {number} myTimeoutId The calling setTimeout id
+ */
+ve.EventSequencer.prototype.doAfterLoop = function ( myTimeoutId ) {
+ var i, len, afterLoopListener;
+
+ if ( this.afterLoopTimeoutId !== myTimeoutId ) {
+ // cancelled; do nothing
+ return;
+ }
+ this.afterLoopTimeoutId = null;
+
+ // Length cache 'len' is required, as the functions called may add
another listener
+ for ( i = 0, len = this.afterLoopListeners.length; i < len; i++ ) {
+ afterLoopListener = this.afterLoopListeners[i];
+ afterLoopListener();
+ }
+};
+
+/**
+ * Cancel any pending setTimeout for the doAfterLoop, then set a new one
+ * @private
+ * @function
+ * @param {me} {ve.EventSequencer} The calling EventSequencer
+ */
+ve.EventSequencer.resetAfterLoopTimeout = function ( me ) {
+ var timeoutId;
+ if ( me.afterLoopTimeoutId !== null ) {
+ clearTimeout( me.afterLoopTimeoutId );
+ }
+ timeoutId = setTimeout( function () {
+ me.doAfterLoop( timeoutId );
+ } );
+};
+
+/**
* Run any pending listeners, and clear the pending queue
* @private
* @method
--
To view, visit https://gerrit.wikimedia.org/r/84997
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: Iee9d4e5599836f52840ac34deca3408b1e72c4c3
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/VisualEditor
Gerrit-Branch: master
Gerrit-Owner: Divec <[email protected]>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits