summaryrefslogtreecommitdiff
path: root/resources/src/mediawiki/mediawiki.debug.profile.js
diff options
context:
space:
mode:
Diffstat (limited to 'resources/src/mediawiki/mediawiki.debug.profile.js')
-rw-r--r--resources/src/mediawiki/mediawiki.debug.profile.js556
1 files changed, 0 insertions, 556 deletions
diff --git a/resources/src/mediawiki/mediawiki.debug.profile.js b/resources/src/mediawiki/mediawiki.debug.profile.js
deleted file mode 100644
index 04f7acd0..00000000
--- a/resources/src/mediawiki/mediawiki.debug.profile.js
+++ /dev/null
@@ -1,556 +0,0 @@
-/*!
- * JavaScript for the debug toolbar profiler, enabled through $wgDebugToolbar
- * and StartProfiler.php.
- *
- * @author Erik Bernhardson
- * @since 1.23
- */
-
-( function ( mw, $ ) {
- 'use strict';
-
- /**
- * @singleton
- * @class mw.Debug.profile
- */
- var profile = mw.Debug.profile = {
- /**
- * Object containing data for the debug toolbar
- *
- * @property ProfileData
- */
- data: null,
-
- /**
- * @property DOMElement
- */
- container: null,
-
- /**
- * Initializes the profiling pane.
- */
- init: function ( data, width, mergeThresholdPx, dropThresholdPx ) {
- data = data || mw.config.get( 'debugInfo' ).profile;
- profile.width = width || $(window).width() - 20;
- // merge events from same pixel(some events are very granular)
- mergeThresholdPx = mergeThresholdPx || 2;
- // only drop events if requested
- dropThresholdPx = dropThresholdPx || 0;
-
- if (
- !Array.prototype.map ||
- !Array.prototype.reduce ||
- !Array.prototype.filter ||
- !document.createElementNS ||
- !document.createElementNS.bind
- ) {
- profile.container = profile.buildRequiresBrowserFeatures();
- } else if ( data.length === 0 ) {
- profile.container = profile.buildNoData();
- } else {
- // Initialize createSvgElement (now that we know we have
- // document.createElementNS and bind)
- this.createSvgElement = document.createElementNS.bind( document, 'http://www.w3.org/2000/svg' );
-
- // generate a flyout
- profile.data = new ProfileData( data, profile.width, mergeThresholdPx, dropThresholdPx );
- // draw it
- profile.container = profile.buildSvg( profile.container );
- profile.attachFlyout();
- }
-
- return profile.container;
- },
-
- buildRequiresBrowserFeatures: function () {
- return $( '<div>' )
- .text( 'Certain browser features, including parts of ECMAScript 5 and document.createElementNS, are required for the profile visualization.' )
- .get( 0 );
- },
-
- buildNoData: function () {
- return $( '<div>' ).addClass( 'mw-debug-profile-no-data' )
- .text( 'No events recorded, ensure profiling is enabled in StartProfiler.php.' )
- .get( 0 );
- },
-
- /**
- * Creates DOM nodes appropriately namespaced for SVG.
- * Initialized in init after checking support
- *
- * @param string tag to create
- * @return DOMElement
- */
- createSvgElement: null,
-
- /**
- * @param DOMElement|undefined
- */
- buildSvg: function ( node ) {
- var container, group, i, g,
- timespan = profile.data.timespan,
- gapPerEvent = 38,
- space = 10.5,
- currentHeight = space,
- totalHeight = 0;
-
- profile.ratio = ( profile.width - space * 2 ) / ( timespan.end - timespan.start );
- totalHeight += gapPerEvent * profile.data.groups.length;
-
- if ( node ) {
- $( node ).empty();
- } else {
- node = profile.createSvgElement( 'svg' );
- node.setAttribute( 'version', '1.2' );
- node.setAttribute( 'baseProfile', 'tiny' );
- }
- node.style.height = totalHeight;
- node.style.width = profile.width;
-
- // use a container that can be transformed
- container = profile.createSvgElement( 'g' );
- node.appendChild( container );
-
- for ( i = 0; i < profile.data.groups.length; i++ ) {
- group = profile.data.groups[i];
- g = profile.buildTimeline( group );
-
- g.setAttribute( 'transform', 'translate( 0 ' + currentHeight + ' )' );
- container.appendChild( g );
-
- currentHeight += gapPerEvent;
- }
-
- return node;
- },
-
- /**
- * @param Object group of periods to transform into graphics
- */
- buildTimeline: function ( group ) {
- var text, tspan, line, i,
- sum = group.timespan.sum,
- ms = ' ~ ' + ( sum < 1 ? sum.toFixed( 2 ) : sum.toFixed( 0 ) ) + ' ms',
- timeline = profile.createSvgElement( 'g' );
-
- timeline.setAttribute( 'class', 'mw-debug-profile-timeline' );
-
- // draw label
- text = profile.createSvgElement( 'text' );
- text.setAttribute( 'x', profile.xCoord( group.timespan.start ) );
- text.setAttribute( 'y', 0 );
- text.textContent = group.name;
- timeline.appendChild( text );
-
- // draw metadata
- tspan = profile.createSvgElement( 'tspan' );
- tspan.textContent = ms;
- text.appendChild( tspan );
-
- // draw timeline periods
- for ( i = 0; i < group.periods.length; i++ ) {
- timeline.appendChild( profile.buildPeriod( group.periods[i] ) );
- }
-
- // full-width line under each timeline
- line = profile.createSvgElement( 'line' );
- line.setAttribute( 'class', 'mw-debug-profile-underline' );
- line.setAttribute( 'x1', 0 );
- line.setAttribute( 'y1', 28 );
- line.setAttribute( 'x2', profile.width );
- line.setAttribute( 'y2', 28 );
- timeline.appendChild( line );
-
- return timeline;
- },
-
- /**
- * @param Object period to transform into graphics
- */
- buildPeriod: function ( period ) {
- var node,
- head = profile.xCoord( period.start ),
- tail = profile.xCoord( period.end ),
- g = profile.createSvgElement( 'g' );
-
- g.setAttribute( 'class', 'mw-debug-profile-period' );
- $( g ).data( 'period', period );
-
- if ( head + 16 > tail ) {
- node = profile.createSvgElement( 'rect' );
- node.setAttribute( 'x', head );
- node.setAttribute( 'y', 8 );
- node.setAttribute( 'width', 2 );
- node.setAttribute( 'height', 9 );
- g.appendChild( node );
-
- node = profile.createSvgElement( 'rect' );
- node.setAttribute( 'x', head );
- node.setAttribute( 'y', 8 );
- node.setAttribute( 'width', ( period.end - period.start ) * profile.ratio || 2 );
- node.setAttribute( 'height', 6 );
- g.appendChild( node );
- } else {
- node = profile.createSvgElement( 'polygon' );
- node.setAttribute( 'points', pointList( [
- [ head, 8 ],
- [ head, 19 ],
- [ head + 8, 8 ],
- [ head, 8]
- ] ) );
- g.appendChild( node );
-
- node = profile.createSvgElement( 'polygon' );
- node.setAttribute( 'points', pointList( [
- [ tail, 8 ],
- [ tail, 19 ],
- [ tail - 8, 8 ],
- [ tail, 8 ]
- ] ) );
- g.appendChild( node );
-
- node = profile.createSvgElement( 'line' );
- node.setAttribute( 'x1', head );
- node.setAttribute( 'y1', 9 );
- node.setAttribute( 'x2', tail );
- node.setAttribute( 'y2', 9 );
- g.appendChild( node );
- }
-
- return g;
- },
-
- /**
- * @param Object
- */
- buildFlyout: function ( period ) {
- var contained, sum, ms, mem, i,
- node = $( '<div>' );
-
- for ( i = 0; i < period.contained.length; i++ ) {
- contained = period.contained[i];
- sum = contained.end - contained.start;
- ms = '' + ( sum < 1 ? sum.toFixed( 2 ) : sum.toFixed( 0 ) ) + ' ms';
- mem = formatBytes( contained.memory );
-
- $( '<div>' ).text( contained.source.name )
- .append( $( '<span>' ).text( ' ~ ' + ms + ' / ' + mem ).addClass( 'mw-debug-profile-meta' ) )
- .appendTo( node );
- }
-
- return node;
- },
-
- /**
- * Attach a hover flyout to all .mw-debug-profile-period groups.
- */
- attachFlyout: function () {
- // for some reason addClass and removeClass from jQuery
- // arn't working on svg elements in chrome <= 33.0 (possibly more)
- var $container = $( profile.container ),
- addClass = function ( node, value ) {
- var current = node.getAttribute( 'class' ),
- list = current ? current.split( ' ' ) : false,
- idx = list ? list.indexOf( value ) : -1;
-
- if ( idx === -1 ) {
- node.setAttribute( 'class', current ? ( current + ' ' + value ) : value );
- }
- },
- removeClass = function ( node, value ) {
- var current = node.getAttribute( 'class' ),
- list = current ? current.split( ' ' ) : false,
- idx = list ? list.indexOf( value ) : -1;
-
- if ( idx !== -1 ) {
- list.splice( idx, 1 );
- node.setAttribute( 'class', list.join( ' ' ) );
- }
- },
- // hide all tipsy flyouts
- hide = function () {
- $container.find( '.mw-debug-profile-period.tipsy-visible' )
- .each( function () {
- removeClass( this, 'tipsy-visible' );
- $( this ).tipsy( 'hide' );
- } );
- };
-
- $container.find( '.mw-debug-profile-period' ).tipsy( {
- fade: true,
- gravity: function () {
- return $.fn.tipsy.autoNS.call( this ) + $.fn.tipsy.autoWE.call( this );
- },
- className: 'mw-debug-profile-tipsy',
- center: false,
- html: true,
- trigger: 'manual',
- title: function () {
- return profile.buildFlyout( $( this ).data( 'period' ) ).html();
- }
- } ).on( 'mouseenter', function () {
- hide();
- addClass( this, 'tipsy-visible' );
- $( this ).tipsy( 'show' );
- } );
-
- $container.on( 'mouseleave', function ( event ) {
- var $from = $( event.relatedTarget ),
- $to = $( event.target );
- // only close the tipsy if we are not
- if ( $from.closest( '.tipsy' ).length === 0 &&
- $to.closest( '.tipsy' ).length === 0 &&
- $to.get( 0 ).namespaceURI !== 'http://www.w4.org/2000/svg'
- ) {
- hide();
- }
- } ).on( 'click', function () {
- // convenience method for closing
- hide();
- } );
- },
-
- /**
- * @return number the x co-ordinate for the specified timestamp
- */
- xCoord: function ( msTimestamp ) {
- return ( msTimestamp - profile.data.timespan.start ) * profile.ratio;
- }
- };
-
- function ProfileData( data, width, mergeThresholdPx, dropThresholdPx ) {
- // validate input data
- this.data = data.map( function ( event ) {
- event.periods = event.periods.filter( function ( period ) {
- return period.start && period.end
- && period.start < period.end
- // period start must be a reasonable ms timestamp
- && period.start > 1000000;
- } );
- return event;
- } ).filter( function ( event ) {
- return event.name && event.periods.length > 0;
- } );
-
- // start and end time of the data
- this.timespan = this.data.reduce( function ( result, event ) {
- return event.periods.reduce( periodMinMax, result );
- }, periodMinMax.initial() );
-
- // transform input data
- this.groups = this.collate( width, mergeThresholdPx, dropThresholdPx );
-
- return this;
- }
-
- /**
- * There are too many unique events to display a line for each,
- * so this does a basic grouping.
- */
- ProfileData.groupOf = function ( label ) {
- var pos, prefix = 'Profile section ended by close(): ';
- if ( label.indexOf( prefix ) === 0 ) {
- label = label.slice( prefix.length );
- }
-
- pos = [ '::', ':', '-' ].reduce( function ( result, separator ) {
- var pos = label.indexOf( separator );
- if ( pos === -1 ) {
- return result;
- } else if ( result === -1 ) {
- return pos;
- } else {
- return Math.min( result, pos );
- }
- }, -1 );
-
- if ( pos === -1 ) {
- return label;
- } else {
- return label.slice( 0, pos );
- }
- };
-
- /**
- * @return Array list of objects with `name` and `events` keys
- */
- ProfileData.groupEvents = function ( events ) {
- var group, i,
- groups = {};
-
- // Group events together
- for ( i = events.length - 1; i >= 0; i-- ) {
- group = ProfileData.groupOf( events[i].name );
- if ( groups[group] ) {
- groups[group].push( events[i] );
- } else {
- groups[group] = [events[i]];
- }
- }
-
- // Return an array of groups
- return Object.keys( groups ).map( function ( group ) {
- return {
- name: group,
- events: groups[group]
- };
- } );
- };
-
- ProfileData.periodSorter = function ( a, b ) {
- if ( a.start === b.start ) {
- return a.end - b.end;
- }
- return a.start - b.start;
- };
-
- ProfileData.genMergePeriodReducer = function ( mergeThresholdMs ) {
- return function ( result, period ) {
- if ( result.length === 0 ) {
- // period is first result
- return [{
- start: period.start,
- end: period.end,
- contained: [period]
- }];
- }
- var last = result[result.length - 1];
- if ( period.end < last.end ) {
- // end is contained within previous
- result[result.length - 1].contained.push( period );
- } else if ( period.start - mergeThresholdMs < last.end ) {
- // neighbors within merging distance
- result[result.length - 1].end = period.end;
- result[result.length - 1].contained.push( period );
- } else {
- // period is next result
- result.push( {
- start: period.start,
- end: period.end,
- contained: [period]
- } );
- }
- return result;
- };
- };
-
- /**
- * Collect all periods from the grouped events and apply merge and
- * drop transformations
- */
- ProfileData.extractPeriods = function ( events, mergeThresholdMs, dropThresholdMs ) {
- // collect the periods from all events
- return events.reduce( function ( result, event ) {
- if ( !event.periods.length ) {
- return result;
- }
- result.push.apply( result, event.periods.map( function ( period ) {
- // maintain link from period to event
- period.source = event;
- return period;
- } ) );
- return result;
- }, [] )
- // sort combined periods
- .sort( ProfileData.periodSorter )
- // Apply merge threshold. Original periods
- // are maintained in the `contained` property
- .reduce( ProfileData.genMergePeriodReducer( mergeThresholdMs ), [] )
- // Apply drop threshold
- .filter( function ( period ) {
- return period.end - period.start > dropThresholdMs;
- } );
- };
-
- /**
- * runs a callback on all periods in the group. Only valid after
- * groups.periods[0..n].contained are populated. This runs against
- * un-transformed data and is better suited to summing or other
- * stat collection
- */
- ProfileData.reducePeriods = function ( group, callback, result ) {
- return group.periods.reduce( function ( result, period ) {
- return period.contained.reduce( callback, result );
- }, result );
- };
-
- /**
- * Transforms this.data grouping by labels, merging neighboring
- * events in the groups, and drops events and groups below the
- * display threshold. Groups are returned sorted by starting time.
- */
- ProfileData.prototype.collate = function ( width, mergeThresholdPx, dropThresholdPx ) {
- // ms to pixel ratio
- var ratio = ( this.timespan.end - this.timespan.start ) / width,
- // transform thresholds to ms
- mergeThresholdMs = mergeThresholdPx * ratio,
- dropThresholdMs = dropThresholdPx * ratio;
-
- return ProfileData.groupEvents( this.data )
- // generate data about the grouped events
- .map( function ( group ) {
- // Cleaned periods from all events
- group.periods = ProfileData.extractPeriods( group.events, mergeThresholdMs, dropThresholdMs );
- // min and max timestamp per group
- group.timespan = ProfileData.reducePeriods( group, periodMinMax, periodMinMax.initial() );
- // ms from first call to end of last call
- group.timespan.length = group.timespan.end - group.timespan.start;
- // collect the un-transformed periods
- group.timespan.sum = ProfileData.reducePeriods( group, function ( result, period ) {
- result.push( period );
- return result;
- }, [] )
- // sort by start time
- .sort( ProfileData.periodSorter )
- // merge overlapping
- .reduce( ProfileData.genMergePeriodReducer( 0 ), [] )
- // sum
- .reduce( function ( result, period ) {
- return result + period.end - period.start;
- }, 0 );
-
- return group;
- }, this )
- // remove groups that have had all their periods filtered
- .filter( function ( group ) {
- return group.periods.length > 0;
- } )
- // sort events by first start
- .sort( function ( a, b ) {
- return ProfileData.periodSorter( a.timespan, b.timespan );
- } );
- };
-
- // reducer to find edges of period array
- function periodMinMax( result, period ) {
- if ( period.start < result.start ) {
- result.start = period.start;
- }
- if ( period.end > result.end ) {
- result.end = period.end;
- }
- return result;
- }
-
- periodMinMax.initial = function () {
- return { start: Number.POSITIVE_INFINITY, end: Number.NEGATIVE_INFINITY };
- };
-
- function formatBytes( bytes ) {
- var i, sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
- if ( bytes === 0 ) {
- return '0 Bytes';
- }
- i = parseInt( Math.floor( Math.log( bytes ) / Math.log( 1024 ) ), 10 );
- return Math.round( bytes / Math.pow( 1024, i ), 2 ) + ' ' + sizes[i];
- }
-
- // turns a 2d array into a point list for svg
- // polygon points attribute
- // ex: [[1,2],[3,4],[4,2]] = '1,2 3,4 4,2'
- function pointList( pairs ) {
- return pairs.map( function ( pair ) {
- return pair.join( ',' );
- } ).join( ' ' );
- }
-}( mediaWiki, jQuery ) );