Merge "Whitelist the <wbr> element."
[mediawiki.git] / resources / mediawiki / mediawiki.debug.js
blob986917a1dd5d7d3a764a951d8523a9ccd690bfde
1 /**
2  * JavaScript for the new debug toolbar, enabled through $wgDebugToolbar.
3  *
4  * @author John Du Hart
5  * @since 1.19
6  */
8 ( function ( mw, $ ) {
9         'use strict';
11         var debug,
12                 hovzer = $.getFootHovzer();
14         debug = mw.Debug = {
15                 /**
16                  * Toolbar container element
17                  *
18                  * @var {jQuery}
19                  */
20                 $container: null,
22                 /**
23                  * Object containing data for the debug toolbar
24                  *
25                  * @var {Object}
26                  */
27                 data: {},
29                 /**
30                  * Initializes the debugging pane.
31                  * Shouldn't be called before the document is ready
32                  * (since it binds to elements on the page).
33                  *
34                  * @param {Object} data, defaults to 'debugInfo' from mw.config
35                  */
36                 init: function ( data ) {
38                         this.data = data || mw.config.get( 'debugInfo' );
39                         this.buildHtml();
41                         // Insert the container into the DOM
42                         hovzer.$.append( this.$container );
43                         hovzer.update();
45                         $( '.mw-debug-panelink' ).click( this.switchPane );
46                 },
48                 /**
49                  * Switches between panes
50                  *
51                  * @todo Store cookie for last pane open
52                  * @context {Element}
53                  * @param {jQuery.Event} e
54                  */
55                 switchPane: function ( e ) {
56                         var currentPaneId = debug.$container.data( 'currentPane' ),
57                                 requestedPaneId = $(this).prop( 'id' ).substr( 9 ),
58                                 $currentPane = $( '#mw-debug-pane-' + currentPaneId ),
59                                 $requestedPane = $( '#mw-debug-pane-' + requestedPaneId ),
60                                 hovDone = false;
62                         function updateHov() {
63                                 if ( !hovDone ) {
64                                         hovzer.update();
65                                         hovDone = true;
66                                 }
67                         }
69                         // Skip hash fragment handling. Prevents screen from jumping.
70                         e.preventDefault();
72                         $( this ).addClass( 'current ');
73                         $( '.mw-debug-panelink' ).not( this ).removeClass( 'current ');
75                         // Hide the current pane
76                         if ( requestedPaneId === currentPaneId ) {
77                                 $currentPane.slideUp( updateHov );
78                                 debug.$container.data( 'currentPane', null );
79                                 return;
80                         }
82                         debug.$container.data( 'currentPane', requestedPaneId );
84                         if ( currentPaneId === undefined || currentPaneId === null ) {
85                                 $requestedPane.slideDown( updateHov );
86                         } else {
87                                 $currentPane.hide();
88                                 $requestedPane.show();
89                                 updateHov();
90                         }
91                 },
93                 /**
94                  * Constructs the HTML for the debugging toolbar
95                  */
96                 buildHtml: function () {
97                         var $container, $bits, panes, id, gitInfo;
99                         $container = $( '<div id="mw-debug-toolbar" class="mw-debug" lang="en" dir="ltr"></div>' );
101                         $bits = $( '<div class="mw-debug-bits"></div>' );
103                         /**
104                          * Returns a jQuery element for a debug-bit div
105                          *
106                          * @param id
107                          * @return {jQuery}
108                          */
109                         function bitDiv( id ) {
110                                 return $( '<div>' ).prop({
111                                         id: 'mw-debug-' + id,
112                                         className: 'mw-debug-bit'
113                                 })
114                                 .appendTo( $bits );
115                         }
117                         /**
118                          * Returns a jQuery element for a pane link
119                          *
120                          * @param id
121                          * @param text
122                          * @return {jQuery}
123                          */
124                         function paneLabel( id, text ) {
125                                 return $( '<a>' )
126                                         .prop({
127                                                 className: 'mw-debug-panelabel',
128                                                 href: '#mw-debug-pane-' + id
129                                         })
130                                         .text( text );
131                         }
133                         /**
134                          * Returns a jQuery element for a debug-bit div with a for a pane link
135                          *
136                          * @param id CSS id snippet. Will be prefixed with 'mw-debug-'
137                          * @param text Text to show
138                          * @param count Optional count to show
139                          * @return {jQuery}
140                          */
141                         function paneTriggerBitDiv( id, text, count ) {
142                                 if ( count ) {
143                                         text = text + ' (' + count + ')';
144                                 }
145                                 return $( '<div>' ).prop({
146                                         id: 'mw-debug-' + id,
147                                         className: 'mw-debug-bit mw-debug-panelink'
148                                 })
149                                 .append( paneLabel( id, text ) )
150                                 .appendTo( $bits );
151                         }
153                         paneTriggerBitDiv( 'console', 'Console', this.data.log.length );
155                         paneTriggerBitDiv( 'querylist', 'Queries', this.data.queries.length );
157                         paneTriggerBitDiv( 'debuglog', 'Debug log', this.data.debugLog.length );
159                         paneTriggerBitDiv( 'request', 'Request' );
161                         paneTriggerBitDiv( 'includes', 'PHP includes', this.data.includes.length );
163                         gitInfo = '';
164                         if ( this.data.gitRevision !== false ) {
165                                 gitInfo = '(' + this.data.gitRevision.substring( 0, 7 ) + ')';
166                                 if ( this.data.gitViewUrl !== false ) {
167                                         gitInfo = $( '<a>' )
168                                                 .attr( 'href', this.data.gitViewUrl )
169                                                 .text( gitInfo );
170                                 }
171                         }
173                         bitDiv( 'mwversion' )
174                                 .append( $( '<a href="//www.mediawiki.org/">MediaWiki</a>' ) )
175                                 .append( document.createTextNode( ': ' + this.data.mwVersion + ' ' ) )
176                                 .append( gitInfo );
178                         if ( this.data.gitBranch !== false ) {
179                                 bitDiv( 'gitbranch' ).text( 'Git branch: ' + this.data.gitBranch );
180                         }
182                         bitDiv( 'phpversion' )
183                                 .append( $( '<a href="//www.php.net/"></a>' ).text( 'PHP' ) )
184                                 .append( ': ' + this.data.phpVersion );
186                         bitDiv( 'time' )
187                                 .text( 'Time: ' + this.data.time.toFixed( 5 ) );
189                         bitDiv( 'memory' )
190                                 .text( 'Memory: ' + this.data.memory + ' (Peak: ' + this.data.memoryPeak + ')' );
192                         $bits.appendTo( $container );
194                         panes = {
195                                 console: this.buildConsoleTable(),
196                                 querylist: this.buildQueryTable(),
197                                 debuglog: this.buildDebugLogTable(),
198                                 request: this.buildRequestPane(),
199                                 includes: this.buildIncludesPane()
200                         };
202                         for ( id in panes ) {
203                                 if ( !panes.hasOwnProperty( id ) ) {
204                                         continue;
205                                 }
207                                 $( '<div>' )
208                                         .prop({
209                                                 className: 'mw-debug-pane',
210                                                 id: 'mw-debug-pane-' + id
211                                         })
212                                         .append( panes[id] )
213                                         .appendTo( $container );
214                         }
216                         this.$container = $container;
217                 },
219                 /**
220                  * Builds the console panel
221                  */
222                 buildConsoleTable: function () {
223                         var $table, entryTypeText, i, length, entry;
225                         $table = $( '<table id="mw-debug-console">' );
227                         $( '<colgroup>' ).css( 'width', /* padding = */ 20 + ( 10 * /* fontSize = */ 11 ) ).appendTo( $table );
228                         $( '<colgroup>' ).appendTo( $table );
229                         $( '<colgroup>' ).css( 'width', 350 ).appendTo( $table );
232                         entryTypeText = function ( entryType ) {
233                                 switch ( entryType ) {
234                                         case 'log':
235                                                 return 'Log';
236                                         case 'warn':
237                                                 return 'Warning';
238                                         case 'deprecated':
239                                                 return 'Deprecated';
240                                         default:
241                                                 return 'Unknown';
242                                 }
243                         };
245                         for ( i = 0, length = this.data.log.length; i < length; i += 1 ) {
246                                 entry = this.data.log[i];
247                                 entry.typeText = entryTypeText( entry.type );
249                                 $( '<tr>' )
250                                         .append( $( '<td>' )
251                                                 .text( entry.typeText )
252                                                 .addClass( 'mw-debug-console-' + entry.type )
253                                         )
254                                         .append( $( '<td>' ).html( entry.msg ) )
255                                         .append( $( '<td>' ).text( entry.caller ) )
256                                         .appendTo( $table );
257                         }
259                         return $table;
260                 },
262                 /**
263                  * Query list pane
264                  */
265                 buildQueryTable: function () {
266                         var $table, i, length, query;
268                         $table = $( '<table id="mw-debug-querylist"></table>' );
270                         $( '<tr>' )
271                                 .append( $( '<th>#</th>' ).css( 'width', '4em' )    )
272                                 .append( $( '<th>SQL</th>' ) )
273                                 .append( $( '<th>Time</th>' ).css( 'width', '8em'  ) )
274                                 .append( $( '<th>Call</th>' ).css( 'width', '18em' ) )
275                         .appendTo( $table );
277                         for ( i = 0, length = this.data.queries.length; i < length; i += 1 ) {
278                                 query = this.data.queries[i];
280                                 $( '<tr>' )
281                                         .append( $( '<td>' ).text( i + 1 ) )
282                                         .append( $( '<td>' ).text( query.sql ) )
283                                         .append( $( '<td class="stats">' ).text( ( query.time * 1000 ).toFixed( 4 ) + 'ms' ) )
284                                         .append( $( '<td>' ).text( query['function'] ) )
285                                 .appendTo( $table );
286                         }
289                         return $table;
290                 },
292                 /**
293                  * Legacy debug log pane
294                  */
295                 buildDebugLogTable: function () {
296                         var $list, i, length, line;
297                         $list = $( '<ul>' );
299                         for ( i = 0, length = this.data.debugLog.length; i < length; i += 1 ) {
300                                 line = this.data.debugLog[i];
301                                 $( '<li>' )
302                                         .html( mw.html.escape( line ).replace( /\n/g, '<br />\n' ) )
303                                         .appendTo( $list );
304                         }
306                         return $list;
307                 },
309                 /**
310                  * Request information pane
311                  */
312                 buildRequestPane: function () {
314                         function buildTable( title, data ) {
315                                 var $unit, $table, key;
317                                 $unit = $( '<div>' ).append( $( '<h2>' ).text( title ) );
319                                 $table = $( '<table>' ).appendTo( $unit );
321                                 $( '<tr>' )
322                                         .html( '<th>Key</th><th>Value</th>' )
323                                         .appendTo( $table );
325                                 for ( key in data ) {
326                                         if ( !data.hasOwnProperty( key ) ) {
327                                                 continue;
328                                         }
330                                         $( '<tr>' )
331                                                 .append( $( '<th>' ).text( key ) )
332                                                 .append( $( '<td>' ).text( data[key] ) )
333                                                 .appendTo( $table );
334                                 }
336                                 return $unit;
337                         }
339                         return $( '<div>' )
340                                 .text( this.data.request.method + ' ' + this.data.request.url )
341                                 .append( buildTable( 'Headers', this.data.request.headers ) )
342                                 .append( buildTable( 'Parameters', this.data.request.params ) );
343                 },
345                 /**
346                  * Included files pane
347                  */
348                 buildIncludesPane: function () {
349                         var $table, i, length, file;
351                         $table = $( '<table>' );
353                         for ( i = 0, length = this.data.includes.length; i < length; i += 1 ) {
354                                 file = this.data.includes[i];
355                                 $( '<tr>' )
356                                         .append( $( '<td>' ).text( file.name ) )
357                                         .append( $( '<td class="nr">' ).text( file.size ) )
358                                         .appendTo( $table );
359                         }
361                         return $table;
362                 }
363         };
365 }( mediaWiki, jQuery ) );