Implement extension registration from an extension.json file
[mediawiki.git] / resources / src / mediawiki / mediawiki.debug.js
blobbdff99f7a1a92ac3142c829837afc418fe37cc73
1 ( function ( mw, $ ) {
2         'use strict';
4         var debug,
5                 hovzer = $.getFootHovzer();
7         /**
8          * Debug toolbar.
9          *
10          * Enabled server-side through `$wgDebugToolbar`.
11          *
12          * @class mw.Debug
13          * @singleton
14          * @author John Du Hart
15          * @since 1.19
16          */
17         debug = mw.Debug = {
18                 /**
19                  * Toolbar container element
20                  *
21                  * @property {jQuery}
22                  */
23                 $container: null,
25                 /**
26                  * Object containing data for the debug toolbar
27                  *
28                  * @property {Object}
29                  */
30                 data: {},
32                 /**
33                  * Initialize the debugging pane
34                  *
35                  * Shouldn't be called before the document is ready
36                  * (since it binds to elements on the page).
37                  *
38                  * @param {Object} [data] Defaults to 'debugInfo' from mw.config
39                  */
40                 init: function ( data ) {
42                         this.data = data || mw.config.get( 'debugInfo' );
43                         this.buildHtml();
45                         // Insert the container into the DOM
46                         hovzer.$.append( this.$container );
47                         hovzer.update();
49                         $( '.mw-debug-panelink' ).click( this.switchPane );
50                 },
52                 /**
53                  * Switch between panes
54                  *
55                  * Should be called with an HTMLElement as its thisArg,
56                  * because it's meant to be an event handler.
57                  *
58                  * TODO: Store cookie for last pane open.
59                  *
60                  * @param {jQuery.Event} e
61                  */
62                 switchPane: function ( e ) {
63                         var currentPaneId = debug.$container.data( 'currentPane' ),
64                                 requestedPaneId = $( this ).prop( 'id' ).slice( 9 ),
65                                 $currentPane = $( '#mw-debug-pane-' + currentPaneId ),
66                                 $requestedPane = $( '#mw-debug-pane-' + requestedPaneId ),
67                                 hovDone = false;
69                         function updateHov() {
70                                 if ( !hovDone ) {
71                                         hovzer.update();
72                                         hovDone = true;
73                                 }
74                         }
76                         // Skip hash fragment handling. Prevents screen from jumping.
77                         e.preventDefault();
79                         $( this ).addClass( 'current ' );
80                         $( '.mw-debug-panelink' ).not( this ).removeClass( 'current ' );
82                         // Hide the current pane
83                         if ( requestedPaneId === currentPaneId ) {
84                                 $currentPane.slideUp( updateHov );
85                                 debug.$container.data( 'currentPane', null );
86                                 return;
87                         }
89                         debug.$container.data( 'currentPane', requestedPaneId );
91                         if ( currentPaneId === undefined || currentPaneId === null ) {
92                                 $requestedPane.slideDown( updateHov );
93                         } else {
94                                 $currentPane.hide();
95                                 $requestedPane.show();
96                                 updateHov();
97                         }
98                 },
100                 /**
101                  * Construct the HTML for the debugging toolbar
102                  */
103                 buildHtml: function () {
104                         var $container, $bits, panes, id, gitInfo;
106                         $container = $( '<div id="mw-debug-toolbar" class="mw-debug" lang="en" dir="ltr"></div>' );
108                         $bits = $( '<div class="mw-debug-bits"></div>' );
110                         /**
111                          * Returns a jQuery element for a debug-bit div
112                          *
113                          * @ignore
114                          * @param {string} id
115                          * @return {jQuery}
116                          */
117                         function bitDiv( id ) {
118                                 return $( '<div>' ).prop( {
119                                         id: 'mw-debug-' + id,
120                                         className: 'mw-debug-bit'
121                                 } )
122                                 .appendTo( $bits );
123                         }
125                         /**
126                          * Returns a jQuery element for a pane link
127                          *
128                          * @ignore
129                          * @param {string} id
130                          * @param {string} text
131                          * @return {jQuery}
132                          */
133                         function paneLabel( id, text ) {
134                                 return $( '<a>' )
135                                         .prop( {
136                                                 className: 'mw-debug-panelabel',
137                                                 href: '#mw-debug-pane-' + id
138                                         } )
139                                         .text( text );
140                         }
142                         /**
143                          * Returns a jQuery element for a debug-bit div with a for a pane link
144                          *
145                          * @ignore
146                          * @param {string} id CSS id snippet. Will be prefixed with 'mw-debug-'
147                          * @param {string} text Text to show
148                          * @param {string} count Optional count to show
149                          * @return {jQuery}
150                          */
151                         function paneTriggerBitDiv( id, text, count ) {
152                                 if ( count ) {
153                                         text = text + ' (' + count + ')';
154                                 }
155                                 return $( '<div>' ).prop( {
156                                         id: 'mw-debug-' + id,
157                                         className: 'mw-debug-bit mw-debug-panelink'
158                                 } )
159                                 .append( paneLabel( id, text ) )
160                                 .appendTo( $bits );
161                         }
163                         paneTriggerBitDiv( 'console', 'Console', this.data.log.length );
165                         paneTriggerBitDiv( 'querylist', 'Queries', this.data.queries.length );
167                         paneTriggerBitDiv( 'debuglog', 'Debug log', this.data.debugLog.length );
169                         paneTriggerBitDiv( 'request', 'Request' );
171                         paneTriggerBitDiv( 'includes', 'PHP includes', this.data.includes.length );
173                         gitInfo = '';
174                         if ( this.data.gitRevision !== false ) {
175                                 gitInfo = '(' + this.data.gitRevision.slice( 0, 7 ) + ')';
176                                 if ( this.data.gitViewUrl !== false ) {
177                                         gitInfo = $( '<a>' )
178                                                 .attr( 'href', this.data.gitViewUrl )
179                                                 .text( gitInfo );
180                                 }
181                         }
183                         bitDiv( 'mwversion' )
184                                 .append( $( '<a href="//www.mediawiki.org/">MediaWiki</a>' ) )
185                                 .append( document.createTextNode( ': ' + this.data.mwVersion + ' ' ) )
186                                 .append( gitInfo );
188                         if ( this.data.gitBranch !== false ) {
189                                 bitDiv( 'gitbranch' ).text( 'Git branch: ' + this.data.gitBranch );
190                         }
192                         bitDiv( 'phpversion' )
193                                 .append( $( this.data.phpEngine === 'HHVM'
194                                         ? '<a href="http://hhvm.com/">HHVM</a>'
195                                         : '<a href="https://php.net/">PHP</a>'
196                                 ) )
197                                 .append( ': ' + this.data.phpVersion );
199                         bitDiv( 'time' )
200                                 .text( 'Time: ' + this.data.time.toFixed( 5 ) );
202                         bitDiv( 'memory' )
203                                 .text( 'Memory: ' + this.data.memory + ' (Peak: ' + this.data.memoryPeak + ')' );
205                         $bits.appendTo( $container );
207                         panes = {
208                                 console: this.buildConsoleTable(),
209                                 querylist: this.buildQueryTable(),
210                                 debuglog: this.buildDebugLogTable(),
211                                 request: this.buildRequestPane(),
212                                 includes: this.buildIncludesPane()
213                         };
215                         for ( id in panes ) {
216                                 if ( !panes.hasOwnProperty( id ) ) {
217                                         continue;
218                                 }
220                                 $( '<div>' )
221                                         .prop( {
222                                                 className: 'mw-debug-pane',
223                                                 id: 'mw-debug-pane-' + id
224                                         } )
225                                         .append( panes[id] )
226                                         .appendTo( $container );
227                         }
229                         this.$container = $container;
230                 },
232                 /**
233                  * Build the console panel
234                  */
235                 buildConsoleTable: function () {
236                         var $table, entryTypeText, i, length, entry;
238                         $table = $( '<table id="mw-debug-console">' );
240                         $( '<colgroup>' ).css( 'width', /* padding = */ 20 + ( 10 * /* fontSize = */ 11 ) ).appendTo( $table );
241                         $( '<colgroup>' ).appendTo( $table );
242                         $( '<colgroup>' ).css( 'width', 350 ).appendTo( $table );
244                         entryTypeText = function ( entryType ) {
245                                 switch ( entryType ) {
246                                         case 'log':
247                                                 return 'Log';
248                                         case 'warn':
249                                                 return 'Warning';
250                                         case 'deprecated':
251                                                 return 'Deprecated';
252                                         default:
253                                                 return 'Unknown';
254                                 }
255                         };
257                         for ( i = 0, length = this.data.log.length; i < length; i += 1 ) {
258                                 entry = this.data.log[i];
259                                 entry.typeText = entryTypeText( entry.type );
261                                 $( '<tr>' )
262                                         .append( $( '<td>' )
263                                                 .text( entry.typeText )
264                                                 .addClass( 'mw-debug-console-' + entry.type )
265                                         )
266                                         .append( $( '<td>' ).html( entry.msg ) )
267                                         .append( $( '<td>' ).text( entry.caller ) )
268                                         .appendTo( $table );
269                         }
271                         return $table;
272                 },
274                 /**
275                  * Build query list pane
276                  *
277                  * @return {jQuery}
278                  */
279                 buildQueryTable: function () {
280                         var $table, i, length, query;
282                         $table = $( '<table id="mw-debug-querylist"></table>' );
284                         $( '<tr>' )
285                                 .append( $( '<th>#</th>' ).css( 'width', '4em' )    )
286                                 .append( $( '<th>SQL</th>' ) )
287                                 .append( $( '<th>Time</th>' ).css( 'width', '8em'  ) )
288                                 .append( $( '<th>Call</th>' ).css( 'width', '18em' ) )
289                         .appendTo( $table );
291                         for ( i = 0, length = this.data.queries.length; i < length; i += 1 ) {
292                                 query = this.data.queries[i];
294                                 $( '<tr>' )
295                                         .append( $( '<td>' ).text( i + 1 ) )
296                                         .append( $( '<td>' ).text( query.sql ) )
297                                         .append( $( '<td class="stats">' ).text( ( query.time * 1000 ).toFixed( 4 ) + 'ms' ) )
298                                         .append( $( '<td>' ).text( query['function'] ) )
299                                 .appendTo( $table );
300                         }
302                         return $table;
303                 },
305                 /**
306                  * Build legacy debug log pane
307                  *
308                  * @return {jQuery}
309                  */
310                 buildDebugLogTable: function () {
311                         var $list, i, length, line;
312                         $list = $( '<ul>' );
314                         for ( i = 0, length = this.data.debugLog.length; i < length; i += 1 ) {
315                                 line = this.data.debugLog[i];
316                                 $( '<li>' )
317                                         .html( mw.html.escape( line ).replace( /\n/g, '<br />\n' ) )
318                                         .appendTo( $list );
319                         }
321                         return $list;
322                 },
324                 /**
325                  * Build request information pane
326                  *
327                  * @return {jQuery}
328                  */
329                 buildRequestPane: function () {
331                         function buildTable( title, data ) {
332                                 var $unit, $table, key;
334                                 $unit = $( '<div>' ).append( $( '<h2>' ).text( title ) );
336                                 $table = $( '<table>' ).appendTo( $unit );
338                                 $( '<tr>' )
339                                         .html( '<th>Key</th><th>Value</th>' )
340                                         .appendTo( $table );
342                                 for ( key in data ) {
343                                         if ( !data.hasOwnProperty( key ) ) {
344                                                 continue;
345                                         }
347                                         $( '<tr>' )
348                                                 .append( $( '<th>' ).text( key ) )
349                                                 .append( $( '<td>' ).text( data[key] ) )
350                                                 .appendTo( $table );
351                                 }
353                                 return $unit;
354                         }
356                         return $( '<div>' )
357                                 .text( this.data.request.method + ' ' + this.data.request.url )
358                                 .append( buildTable( 'Headers', this.data.request.headers ) )
359                                 .append( buildTable( 'Parameters', this.data.request.params ) );
360                 },
362                 /**
363                  * Build included files pane
364                  *
365                  * @return {jQuery}
366                  */
367                 buildIncludesPane: function () {
368                         var $table, i, length, file;
370                         $table = $( '<table>' );
372                         for ( i = 0, length = this.data.includes.length; i < length; i += 1 ) {
373                                 file = this.data.includes[i];
374                                 $( '<tr>' )
375                                         .append( $( '<td>' ).text( file.name ) )
376                                         .append( $( '<td class="nr">' ).text( file.size ) )
377                                         .appendTo( $table );
378                         }
380                         return $table;
381                 }
382         };
384 }( mediaWiki, jQuery ) );