jenkins-bot has submitted this change and it was merged.
Change subject: Refactor code to be more modular
......................................................................
Refactor code to be more modular
Bug: T507
Change-Id: I414c0b51401597f8acfec472c030c6f2e7d8639f
---
M .jshintrc
M ImageMetrics.php
D resources/ext.imageMetrics.loader.js
R resources/head.js
A resources/loader.js
R resources/logger/LoadingTimeLogger.js
A resources/logger/Logger.js
R tests/qunit/logger/LoadingTimeLogger.test.js
8 files changed, 212 insertions(+), 136 deletions(-)
Approvals:
Gilles: Looks good to me, approved
jenkins-bot: Verified
diff --git a/.jshintrc b/.jshintrc
index c6a531b..bda7504 100644
--- a/.jshintrc
+++ b/.jshintrc
@@ -22,10 +22,12 @@
"predef": [
"window",
+ "document",
"Geo",
"QUnit",
"jQuery",
"mediaWiki",
- "mediaWikiLoadStart"
+ "mediaWikiLoadStart",
+ "OO"
]
}
diff --git a/ImageMetrics.php b/ImageMetrics.php
index 2898b3e..a494eab 100644
--- a/ImageMetrics.php
+++ b/ImageMetrics.php
@@ -38,21 +38,27 @@
$wgResourceModules += array(
'ext.imageMetrics' => array(
- 'scripts' => 'ext.imageMetrics.js',
+ 'scripts' => array(
+ 'logger/Logger.js',
+ 'logger/LoadingTimeLogger.js',
+ ),
'localBasePath' => __DIR__ . '/resources',
'remoteExtPath' => 'ImageMetrics/resources',
- 'dependencies' => 'schema.ImageMetricsLoadingTime',
+ 'dependencies' => array(
+ 'oojs',
+ 'schema.ImageMetricsLoadingTime',
+ ),
'targets' => array( 'desktop', 'mobile' ),
),
'ext.imageMetrics.head' => array(
- 'scripts' => 'ext.imageMetrics.head.js',
+ 'scripts' => 'head.js',
'localBasePath' => __DIR__ . '/resources',
'remoteExtPath' => 'ImageMetrics/resources',
'targets' => array( 'desktop', 'mobile' ),
'position' => 'top',
),
'ext.imageMetrics.loader' => array(
- 'scripts' => 'ext.imageMetrics.loader.js',
+ 'scripts' => 'loader.js',
'localBasePath' => __DIR__ . '/resources',
'remoteExtPath' => 'ImageMetrics/resources',
'targets' => array( 'desktop', 'mobile' ),
@@ -77,8 +83,12 @@
*/
$wgHooks[ 'ResourceLoaderGetConfigVars' ][] = function ( &$vars ) {
global $wgImageMetricsSamplingFactor,
$wgImageMetricsLoggedinSamplingFactor;
- $vars[ 'wgImageMetricsSamplingFactor' ] = $wgImageMetricsSamplingFactor;
- $vars[ 'wgImageMetricsLoggedinSamplingFactor' ] =
$wgImageMetricsLoggedinSamplingFactor;
+ $vars[ 'wgImageMetrics' ] = array(
+ 'samplingFactor' => array(
+ 'image' => $wgImageMetricsSamplingFactor,
+ 'imageLoggedin' =>
$wgImageMetricsLoggedinSamplingFactor,
+ ),
+ );
return true;
};
@@ -90,7 +100,7 @@
$wgHooks['ResourceLoaderTestModules'][] = function ( array &$testModules,
ResourceLoader &$resourceLoader ) {
$testModules['qunit']['ext.imageMetrics.tests'] = array(
'scripts' => array(
- 'tests/qunit/ext.imageMetrics.test.js',
+ 'tests/qunit/logger/LoadingTimeLogger.test.js',
),
'dependencies' => array(
'ext.imageMetrics',
diff --git a/resources/ext.imageMetrics.loader.js
b/resources/ext.imageMetrics.loader.js
deleted file mode 100644
index e6ae00c..0000000
--- a/resources/ext.imageMetrics.loader.js
+++ /dev/null
@@ -1,35 +0,0 @@
-/**
- * JavaScript module for image-related metrics.
- * @see https://mediawiki.org/wiki/Extension:ImageMetrics
- *
- * @licence GNU GPL v2 or later
- * @author Tisza Gergő <[email protected]>
- */
-( function ( mw, $ ) {
- 'use strict';
-
- var factor = mw.config.get( 'wgImageMetricsSamplingFactor', false ),
- loggedinFactor = mw.config.get(
'wgImageMetricsLoggedinSamplingFactor', false );
-
- if ( !mw.user.isAnon() && loggedinFactor ) {
- factor = loggedinFactor;
- }
-
- /**
- * Makes a random decision (based on the sampling factor configuration
setting) whether the current
- * request should be logged.
- * @return {boolean}
- */
- function isInSample() {
- if ( !$.isNumeric( factor ) || factor < 1 ) {
- return false;
- }
- return Math.floor( Math.random() * factor ) === 0;
- }
-
- if ( isInSample() ) {
- mw.loader.using( 'ext.imageMetrics', function () {
- mw.ImageMetrics.install( factor );
- } );
- }
-} ( mediaWiki, jQuery ) );
diff --git a/resources/ext.imageMetrics.head.js b/resources/head.js
similarity index 91%
rename from resources/ext.imageMetrics.head.js
rename to resources/head.js
index 8b30709..a0c9278 100644
--- a/resources/ext.imageMetrics.head.js
+++ b/resources/head.js
@@ -1,5 +1,6 @@
/**
* JavaScript module for image-related metrics.
+ * Top-loaded on every request to measure timing of events which might happen
before normal script load.
* @see https://mediawiki.org/wiki/Extension:ImageMetrics
*
* @licence GNU GPL v2 or later
diff --git a/resources/loader.js b/resources/loader.js
new file mode 100644
index 0000000..4004604
--- /dev/null
+++ b/resources/loader.js
@@ -0,0 +1,46 @@
+/**
+ * JavaScript module for image-related metrics.
+ * This module will be loaded on every request to perform sampling and load
the real module if needed.
+ * @see https://mediawiki.org/wiki/Extension:ImageMetrics
+ *
+ * @licence GNU GPL v2 or later
+ * @author Tisza Gergő <[email protected]>
+ */
+( function ( mw, $ ) {
+ 'use strict';
+
+ var logImage,
+ config = mw.config.get( 'wgImageMetrics', { samplingFactor: {}
} ),
+ imageFactor = config.samplingFactor.image,
+ loggedinImageFactor = config.samplingFactor.imageLoggedin;
+
+ /**
+ * Makes a random decision (based on samplingRatio) whether an event
should be logged.
+ * Returns true with 1/samplingRatio probability, or false if
samplingRatio is not a number or smaller than 1.
+ * @param {number|boolean} samplingRatio
+ * @return {boolean}
+ */
+ function isInSample( samplingRatio ) {
+ if ( !$.isNumeric( samplingRatio ) || samplingRatio < 1 ) {
+ return false;
+ }
+ return Math.floor( Math.random() * samplingRatio ) === 0;
+ }
+
+
+ if ( !mw.user.isAnon() && loggedinImageFactor ) {
+ imageFactor = loggedinImageFactor;
+ }
+
+ logImage = isInSample( imageFactor );
+
+ if ( !logImage ) {
+ return;
+ }
+
+ mw.loader.using( 'ext.imageMetrics', function () {
+ if ( logImage ) {
+ mw.imageMetrics.LoadingTimeLogger.install( imageFactor
);
+ }
+ } );
+} ( mediaWiki, jQuery ) );
diff --git a/resources/ext.imageMetrics.js
b/resources/logger/LoadingTimeLogger.js
similarity index 67%
rename from resources/ext.imageMetrics.js
rename to resources/logger/LoadingTimeLogger.js
index a2d31be..442a9c6 100644
--- a/resources/ext.imageMetrics.js
+++ b/resources/logger/LoadingTimeLogger.js
@@ -5,7 +5,7 @@
* @licence GNU GPL v2 or later
* @author Tisza Gergő <[email protected]>
*/
-( function ( mw, $ ) {
+( function ( mw, $, oo ) {
'use strict';
/**
@@ -18,42 +18,40 @@
*/
/**
- * @class ImageMetrics
+ * @class mw.imageMetrics.LoadingTimeLogger
+ * @extends mw.imageMetrics.Logger
* @constructor
* @param {number} samplingFactor sampling factor
- * @param {Object} performance window.performance
* @param {Object} location window.location
* @param {Object} mwConfig mw.config
* @param {Object} geo window.Geo
* @param {Object} eventLog mw.eventLog
+ * @param {Object} performance window.performance
*/
- function ImageMetrics( samplingFactor, performance, location, mwConfig,
geo, eventLog ) {
- this.samplingFactor = samplingFactor;
+ function LoadingTimeLogger(
+ /* inherited arguments */
+ samplingFactor, location, mwConfig, geo, eventLog,
+ /* custom arguments */
+ performance
+ ) {
+ LoadingTimeLogger.parent.call( this, samplingFactor, location,
mwConfig, geo, eventLog );
/** @property {Object} performance window.performance */
this.performance = performance;
-
- /** @property {Object} location window.location */
- this.location = location;
-
- /** @property {Object} mwConfig mw.config */
- this.mwConfig = mwConfig;
-
- /** @property {Object} geo window.Geo */
- this.geo = geo;
-
- /** @property {Object} eventLog mw.eventLog */
- this.eventLog = eventLog;
}
+ oo.inheritClass( LoadingTimeLogger, mw.imageMetrics.Logger );
+
+ LoadingTimeLogger.prototype.schema = 'ImageMetricsLoadingTime';
/**
* Factory function to take care of dependency injection.
* @static
* @param {number} samplingFactor sampling factor
- * @return {ImageMetrics}
+ * @return {mw.imageMetrics.LoadingTimeLogger}
*/
- ImageMetrics.create = function( samplingFactor ) {
- return new ImageMetrics( samplingFactor, window.performance,
window.location, mw.config, window.Geo, mw.eventLog );
+ LoadingTimeLogger.create = function( samplingFactor ) {
+ return new LoadingTimeLogger( samplingFactor, window.location,
mw.config, window.Geo,
+ mw.eventLog, window.performance );
};
/**
@@ -61,52 +59,50 @@
* @static
* @param {number} samplingFactor sampling factor
*/
- ImageMetrics.install = function( samplingFactor ) {
- var imageMetrics = ImageMetrics.create( samplingFactor );
+ LoadingTimeLogger.install = function( samplingFactor ) {
+ var logger = LoadingTimeLogger.create( samplingFactor );
$( window ).load( function () {
- imageMetrics.log();
+ logger.collect();
} );
+ };
+
+ /**
+ * Collects image metrics data and logs it via EventLogging.
+ */
+ LoadingTimeLogger.prototype.collect = function() {
+ var $file,
+ data = {};
+
+ data.imageType = 'filepage-main'; // the only supported
measurement type ATM
+ $file = $( '#file' ).find( 'img' ); // more efficient than
'#file img'
+
+ if ( !$file.length ) {
+ return;
+ }
+
+ this.addNavigationTimingData( data );
+ this.addResourceTimingData( data, $file );
+ this.addOtherData( data, $file );
+
+ this.log( data );
};
/**
* @property {Object<integer, string>} navigationTypes map of
NavigationTiming.type constants to
* human-readable strings
*/
- ImageMetrics.prototype.navigationTypes = {
+ LoadingTimeLogger.prototype.navigationTypes = {
0: 'navigate',
1: 'reload',
2: 'back_forward'
};
/**
- * Adds information provided by MediaWiki.
- * @param {Object} data
- * @param {jQuery} $file jQuery object containing the img element
- */
- ImageMetrics.prototype.addMediaWikiData = function ( data, $file ) {
- if ( this.geo && typeof this.geo.country === 'string' ) {
- data.country = this.geo.country;
- }
- if ( $file.attr( 'alt' ) ) {
- data.fileType = $file.attr( 'alt' ).split( '.' ).pop();
- }
- data.isAnon = this.mwConfig.get( 'wgUserId' ) === null;
- };
-
- /**
- * Adds timing data from the image onload event.
- * @param {Object} data
- */
- ImageMetrics.prototype.addOnloadData = function ( data ) {
- data.fallbackFullLoadingTime = mw.imageMetricsLoadTime;
- };
-
- /**
* Adds navigation type (reload, back etc) to the log data from the
NavigationTiming API.
* @param {Object} data
*/
- ImageMetrics.prototype.addNavigationTimingData = function ( data ) {
+ LoadingTimeLogger.prototype.addNavigationTimingData = function ( data )
{
if ( this.performance.navigation &&
this.performance.navigation.type in this.navigationTypes ) {
data.navigationType =
this.navigationTypes[this.performance.navigation.type];
}
@@ -118,7 +114,7 @@
* @param {jQuery} $file jQuery object containing the img element
* @return {PerformanceResourceTiming|boolean} timing object or false
if not supported
*/
- ImageMetrics.prototype.getResourceTiming = function ( $file ) {
+ LoadingTimeLogger.prototype.getResourceTiming = function ( $file ) {
var url, timing;
if ( !this.performance || !this.performance.getEntriesByName ) {
@@ -143,7 +139,7 @@
* @param {Object} data
* @param {jQuery} $file jQuery object containing the img element
*/
- ImageMetrics.prototype.addResourceTimingData = function ( data, $file )
{
+ LoadingTimeLogger.prototype.addResourceTimingData = function ( data,
$file ) {
var timing = this.getResourceTiming( $file );
if ( timing ) {
@@ -154,30 +150,16 @@
};
/**
- * Collects image metrics data and logs it via EventLogging.
+ * Adds non-ResourceTiming/NavigationTimning-based information.
+ * @param {Object} data
+ * @param {jQuery} $file jQuery object containing the img element
*/
- ImageMetrics.prototype.log = function() {
- var $file,
- data = {};
-
- data.samplingFactor = this.samplingFactor;
-
- data.isHttps = this.location.protocol === 'https:';
-
- data.imageType = 'filepage-main'; // the only supported
measurement type ATM
- $file = $( '#file' ).find( 'img' ); // more efficient than
'#file img'
-
- if ( !$file.length ) {
- return;
+ LoadingTimeLogger.prototype.addOtherData = function ( data, $file ) {
+ if ( $file.attr( 'alt' ) ) {
+ data.fileType = $file.attr( 'alt' ).split( '.' ).pop();
}
-
- this.addMediaWikiData( data, $file );
- this.addOnloadData( data );
- this.addNavigationTimingData( data );
- this.addResourceTimingData( data, $file );
-
- this.eventLog.logEvent( 'ImageMetricsLoadingTime', data );
+ data.fallbackFullLoadingTime = mw.imageMetricsLoadTime;
};
- mw.ImageMetrics = ImageMetrics;
-} ( mediaWiki, jQuery ) );
+ mw.imageMetrics.LoadingTimeLogger = LoadingTimeLogger;
+} ( mediaWiki, jQuery, OO ) );
diff --git a/resources/logger/Logger.js b/resources/logger/Logger.js
new file mode 100644
index 0000000..891a855
--- /dev/null
+++ b/resources/logger/Logger.js
@@ -0,0 +1,70 @@
+/**
+ * JavaScript module for image-related metrics.
+ * @see https://mediawiki.org/wiki/Extension:Logger
+ *
+ * @licence GNU GPL v2 or later
+ * @author Tisza Gergő <[email protected]>
+ */
+( function ( mw, $ ) {
+ 'use strict';
+
+ /**
+ * Abstract parent class for metrics tasks.
+ * @class mw.imageMetrics.Logger
+ * @constructor
+ * @param {number} samplingFactor sampling factor
+ * @param {Object} location window.location
+ * @param {Object} mwConfig mw.config
+ * @param {Object} geo window.Geo
+ * @param {Object} eventLog mw.eventLog
+ */
+ function Logger( samplingFactor, location, mwConfig, geo, eventLog ) {
+ this.samplingFactor = samplingFactor;
+
+ /** @property {Object} location window.location */
+ this.location = location;
+
+ /** @property {Object} mwConfig mw.config */
+ this.mwConfig = mwConfig;
+
+ /** @property {Object} geo window.Geo */
+ this.geo = geo;
+
+ /** @property {Object} eventLog mw.eventLog */
+ this.eventLog = eventLog;
+ }
+
+ /**
+ * EventLogging schema name.
+ * @type {string}
+ */
+ Logger.prototype.schema = null;
+
+ /**
+ * Factory function to take care of dependency injection.
+ * @static
+ * @param {number} samplingFactor sampling factor
+ * @return {mw.imageMetrics.Logger}
+ */
+ Logger.create = function( samplingFactor ) {
+ return new Logger( samplingFactor, window.location, mw.config,
window.Geo, mw.eventLog );
+ };
+
+ /**
+ * Collects image metrics data and logs it via EventLogging.
+ */
+ Logger.prototype.log = function( data ) {
+ data = $.extend( {}, data );
+ data.samplingFactor = this.samplingFactor;
+ data.isHttps = this.location.protocol === 'https:';
+ data.isAnon = this.mwConfig.get( 'wgUserId' ) === null;
+ if ( this.geo && typeof this.geo.country === 'string' ) {
+ data.country = this.geo.country;
+ }
+
+ this.eventLog.logEvent( this.schema, data );
+ };
+
+ mw.imageMetrics = {};
+ mw.imageMetrics.Logger = Logger;
+} ( mediaWiki, jQuery ) );
diff --git a/tests/qunit/ext.imageMetrics.test.js
b/tests/qunit/logger/LoadingTimeLogger.test.js
similarity index 79%
rename from tests/qunit/ext.imageMetrics.test.js
rename to tests/qunit/logger/LoadingTimeLogger.test.js
index 82e799a..60b3036 100644
--- a/tests/qunit/ext.imageMetrics.test.js
+++ b/tests/qunit/logger/LoadingTimeLogger.test.js
@@ -1,23 +1,23 @@
( function ( mw, $ ) {
'use strict';
- QUnit.module( 'ImageMetrics', QUnit.newMwEnvironment() );
+ QUnit.module( 'mw.imageMetrics.LoadingTimeLogger',
QUnit.newMwEnvironment() );
- function createImageMetrics( sandbox, options ) {
- var imageMetrics,
+ function createLoadingTimeLogger( sandbox, options ) {
+ var logger,
logEvent = sandbox.stub(),
config = new mw.Map( options.config || {} );
- imageMetrics = new mw.ImageMetrics( options.samplingFactor,
options.performance || {}, options.location || {}, config,
- options.geo || {}, { logEvent: logEvent } );
+ logger = new mw.imageMetrics.LoadingTimeLogger(
options.samplingFactor, options.location || {}, config,
+ options.geo || {}, { logEvent: logEvent },
options.performance || {} );
options.logEvent = logEvent;
- return imageMetrics;
+ return logger;
}
QUnit.test( 'Constructor sanity test', 1, function ( assert ) {
- var imageMetrics = createImageMetrics( this.sandbox, {} );
- assert.ok( imageMetrics, 'Object created' );
+ var logger = createLoadingTimeLogger( this.sandbox, {} );
+ assert.ok( logger, 'Object created' );
} );
QUnit.test( 'Minimal logging scenario', 12, function ( assert ) {
@@ -26,10 +26,10 @@
samplingFactor: 1,
config: {}
},
- imageMetrics = createImageMetrics( this.sandbox,
options );
+ logger = createLoadingTimeLogger( this.sandbox, options
);
$( '#qunit-fixture' ).append( '<div id="file"><img
alt="Foo.jpg" /></div>' );
- imageMetrics.log();
+ logger.collect();
data = options.logEvent.firstCall.args[1];
assert.strictEqual( data.samplingFactor, 1, 'samplingFactor is
logged correctly' );
@@ -45,8 +45,8 @@
assert.strictEqual( data.fetchDelay, undefined, 'fetchDelay is
not logged when Resource Timing API is not available' );
options.config.wgUserId = 1;
- imageMetrics = createImageMetrics( this.sandbox, options );
- imageMetrics.log();
+ logger = createLoadingTimeLogger( this.sandbox, options );
+ logger.collect();
data = options.logEvent.firstCall.args[1];
assert.strictEqual( data.isAnon, false, 'isAnon is logged
correctly' );
} );
@@ -57,10 +57,10 @@
samplingFactor: 1,
geo: { country: 'London' }
},
- imageMetrics = createImageMetrics( this.sandbox,
options );
+ logger = createLoadingTimeLogger( this.sandbox, options
);
$( '#qunit-fixture' ).append( '<div id="file"><img
alt="Foo.jpg" /></div>' );
- imageMetrics.log();
+ logger.collect();
data = options.logEvent.firstCall.args[1];
assert.strictEqual( data.country, 'London', 'country is logged
correctly' );
@@ -74,10 +74,10 @@
performance: { navigation: { type: 0 } },
samplingFactor: 1
},
- imageMetrics = createImageMetrics( this.sandbox,
options );
+ logger = createLoadingTimeLogger( this.sandbox, options
);
$( '#qunit-fixture' ).append( '<div id="file"><img
alt="Foo.jpg" /></div>' );
- imageMetrics.log();
+ logger.collect();
data = options.logEvent.firstCall.args[1];
assert.strictEqual( data.navigationType, 'navigate',
'navigationType is logged correctly' );
@@ -94,10 +94,10 @@
}] ) },
config: { wgImageMetricsSamplingFactor: 1 }
},
- imageMetrics = createImageMetrics( this.sandbox,
options );
+ logger = createLoadingTimeLogger( this.sandbox, options
);
$( '#qunit-fixture' ).append( '<div id="file"><img
alt="Foo.jpg" /></div>' );
- imageMetrics.log();
+ logger.collect();
data = options.logEvent.firstCall.args[1];
assert.strictEqual( data.ownLoadingTime, 111, 'ownLoadingTime
is logged correctly' );
@@ -110,10 +110,10 @@
performance: { navigation: { type: 0 } },
samplingFactor: 1
},
- imageMetrics = createImageMetrics( this.sandbox,
options );
+ logger = createLoadingTimeLogger( this.sandbox, options
);
$( '#qunit-fixture' ).append( '<div id="file"></div>' );
- imageMetrics.log();
+ logger.collect();
assert.strictEqual( options.logEvent.called, false, 'logEvent
was not called' );
} );
--
To view, visit https://gerrit.wikimedia.org/r/182762
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: I414c0b51401597f8acfec472c030c6f2e7d8639f
Gerrit-PatchSet: 3
Gerrit-Project: mediawiki/extensions/ImageMetrics
Gerrit-Branch: master
Gerrit-Owner: Gergő Tisza <[email protected]>
Gerrit-Reviewer: Gergő Tisza <[email protected]>
Gerrit-Reviewer: Gilles <[email protected]>
Gerrit-Reviewer: MarkTraceur <[email protected]>
Gerrit-Reviewer: jenkins-bot <>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits