r112756 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r112755‎ | r112756 | r112757 >
Date:01:34, 1 March 2012
Author:krinkle
Status:ok
Tags:
Comment:
[ApiSandbox] move modules resources into ./modules
Modified paths:
  • /trunk/extensions/ApiSandbox/ApiSandbox.php (modified) (history)
  • /trunk/extensions/ApiSandbox/ext.apiSandbox.css (deleted) (history)
  • /trunk/extensions/ApiSandbox/ext.apiSandbox.js (deleted) (history)
  • /trunk/extensions/ApiSandbox/modules (added) (history)
  • /trunk/extensions/ApiSandbox/modules/ext.apiSandbox.css (added) (history)
  • /trunk/extensions/ApiSandbox/modules/ext.apiSandbox.js (added) (history)

Diff [purge]

Index: trunk/extensions/ApiSandbox/ext.apiSandbox.js
@@ -1,671 +0,0 @@
2 -/*global jQuery, mediaWiki*/
3 -/*jslint regexp: true, browser: true, continue: true, sloppy: true, white: true, forin: true, plusplus: true */
4 -( function ( $, mw, undefined ) {
5 -
6 - var mainRequest, genericRequest, generatorRequest, queryRequest, // UiBuilder objects
7 - // Caches
8 - paramInfo, namespaceOptions,
9 - // page elements
10 - $format, $action, $query, $queryRow, $help, $mainContainer, $genericContainer,
11 - $generatorContainer, $queryContainer, $generatorBox, $form, $submit, $requestUrl, $requestPost,
12 - $output, $postRow, $buttonsContainer, $examplesButton, $examplesContent, $pageScroll;
13 -
14 -
15 - /** Local utility functions **/
16 -
17 - // get the first element in a list that is "scrollable"
18 - // depends on browser and skin (i.e. body or html)
19 - function getScrollableElement( /* selectors, .. */ ) {
20 - var i, argLen, el, $el, canScroll;
21 - for ( i = 0, argLen = arguments.length; i < argLen; i += 1 ) {
22 - el = arguments[i];
23 - $el = $(el);
24 - if ( $el.scrollTop() > 0 ) {
25 - return el;
26 - } else {
27 - $el.scrollTop( 1 );
28 - canScroll = $el.scrollTop() > 0;
29 - $el.scrollTop( 0 );
30 - if ( canScroll ) {
31 - return el;
32 - }
33 - }
34 - }
35 - return [];
36 - }
37 -
38 - function showLoading( $element ) {
39 - $element.html(
40 - mw.html.element( 'img',
41 - { src: mw.config.get( 'stylepath' ) + '/common/images/spinner.gif', alt: '' } )
42 - + mw.html.escape( mw.msg( 'apisb-loading' )
43 - )
44 - );
45 - }
46 -
47 - function showLoadError( $element, message ) {
48 - $element.html(
49 - mw.html.element( 'span', { 'class': 'error' }, mw.msg( message ) )
50 - );
51 - }
52 -
53 - function getParamInfo( what, loadCallback, completeCallback, errorCallback ) {
54 - var needed, param, subParam;
55 -
56 - needed = {};
57 - for ( param in what ) {
58 - if ( paramInfo[param] === undefined ) {
59 - needed[param] = what[param];
60 - } else if ( typeof needed[param] === 'object' ) {
61 - for ( subParam in param ) {
62 - if ( paramInfo[param][subParam] === undefined ) {
63 - needed[param][subParam] = what[param][subParam];
64 - }
65 - }
66 - } else {
67 - needed[param] = what[param];
68 - }
69 - }
70 - if ( $.isEmptyObject( needed ) ) {
71 - completeCallback();
72 - } else {
73 - loadCallback();
74 - needed.format = 'json';
75 - needed.action = 'paraminfo';
76 - $.getJSON(
77 - mw.util.wikiScript( 'api' ),
78 - needed,
79 - function ( data ) {
80 - var prop, i, info;
81 -
82 - if ( data.error || !data.paraminfo ) {
83 - errorCallback();
84 - return;
85 - }
86 - for ( prop in data.paraminfo ) {
87 - if ( paramInfo[prop] === undefined ) {
88 - paramInfo[prop] = data.paraminfo[prop];
89 - } else {
90 - for ( i = 0; i < data.paraminfo[prop].length; i++ ) {
91 - info = data.paraminfo[prop][i];
92 - if ( !paramInfo[prop][info.name] ) {
93 - paramInfo[prop][info.name] = info;
94 - }
95 - }
96 - }
97 - }
98 - completeCallback();
99 - }
100 - ).error( errorCallback );
101 - }
102 - }
103 -
104 - function resetUI() {
105 - $( '.api-sandbox-builder' ).each( function () {
106 - $( this ).data( 'builder' ).createInputs();
107 - } );
108 - }
109 -
110 - /**
111 - * @context {Element}
112 - * @param e {jQuery.Event}
113 - */
114 - function exampleClick( e ) {
115 - var link, params, i, pieces, key, value, $el, splitted, j;
116 - e.preventDefault();
117 -
118 - resetUI();
119 - link = $( this ).data( 'exampleLink' ).replace( /^.*?\?/, '' );
120 - params = link.split( '&' );
121 - for ( i = 0; i < params.length; i++ ) {
122 - pieces = params[i].split( '=' );
123 - if ( pieces.length === 1 ) { // checkbox
124 - $( '#param-' + pieces[0] ).prop( 'checked', true );
125 - } else {
126 - key = pieces[0];
127 - value = decodeURIComponent( pieces.slice( 1 ).join( '=' ) );
128 - if ( $.inArray( key, [ 'action', 'format', 'list', 'prop', 'meta' ] ) !== -1 ) {
129 - continue;
130 - }
131 - $el = $( '#param-' + key );
132 - if ( !$el.length ) {
133 - continue;
134 - }
135 - switch ( $el[0].nodeName.toLowerCase() ) {
136 - case 'select':
137 - if ( $el.attr( 'multiple' ) ) {
138 - splitted = value.split( '|' );
139 - for ( j = 0; j < splitted.length; j++ ) {
140 - $el.children( 'option[value="' + mw.html.escape( splitted[j] ) + '"]' )
141 - .prop( 'selected', true );
142 - }
143 - } else {
144 - $el.children( 'option[value="' + mw.html.escape( value ) + '"]' )
145 - .prop( 'selected', true );
146 - }
147 - break;
148 - case 'input':
149 - if ( $el.attr( 'type' ) === 'checkbox' ) {
150 - $( '#param-' + key ).prop( 'checked', true );
151 - } else {
152 - $el.val( value );
153 - }
154 - break;
155 - default:
156 - continue;
157 - }
158 - }
159 - }
160 - }
161 -
162 - function updateExamples( info ) {
163 - var i, $list, urlRegex, count, href, text, match, prefix, $prefix, linkText;
164 -
165 - if ( info.allexamples === undefined ) {
166 - return;
167 - }
168 - // on 1.18, convert everything into 1.19 format
169 - if ( info.allexamples.length > 0 && typeof info.allexamples[0] === 'string' ) {
170 - for ( i = 0; i < info.allexamples.length; i++ ) {
171 - info.allexamples[i] = { '*': info.allexamples[i] };
172 - }
173 - }
174 - $examplesContent.hide().html( '' );
175 - $list = $( '<ul>' );
176 - urlRegex = /api.php\?\S+/m;
177 - count = 0;
178 - for ( i = 0; i < info.allexamples.length; i++ ) {
179 - href = '';
180 - text = '';
181 - while ( i < info.allexamples.length && info.allexamples[i].description === undefined ) {
182 - match = urlRegex.exec( info.allexamples[i]['*'] );
183 - if ( match ) {
184 - href = match[0];
185 - break;
186 - } else {
187 - text += '\n' + info.allexamples[i]['*'];
188 - }
189 - i++;
190 - }
191 - if ( !href ) {
192 - href = info.allexamples[i]['*'];
193 - }
194 - if ( !text ) {
195 - text = info.allexamples[i].description !== undefined ? info.allexamples[i].description : href;
196 - }
197 - prefix = text.replace( /[^\n]*$/, '' );
198 - $prefix = prefix.length ? $( '<b>' ).text( prefix ) : [];
199 - linkText = text.replace( /^.*\n/, '' );
200 - $( '<li>' )
201 - .append( $prefix )
202 - .append(
203 - $( '<a>' )
204 - .attr( 'href', '#' )
205 - .data( 'exampleLink', href )
206 - .text( linkText )
207 - .click( exampleClick )
208 - ).appendTo( $list );
209 - count++;
210 - }
211 - $examplesButton.button( 'option', 'text', mw.msg( count === 1 ? 'apisb-example' : 'apisb-examples' ) );
212 - $list.appendTo( $examplesContent );
213 - if ( count ) {
214 - $examplesButton.show();
215 - } else {
216 - $examplesButton.hide();
217 - }
218 - }
219 -
220 - function updateQueryInfo( action, query ) {
221 - var data,
222 - isQuery = action === 'query';
223 -
224 - if ( action === '' || ( isQuery && query === '' ) ) {
225 - $submit.button( 'option', 'disabled', true );
226 - return;
227 - }
228 - query = query.replace( /^.*=/, '' );
229 - data = {};
230 - if ( isQuery ) {
231 - data.querymodules = query;
232 - } else {
233 - data.modules = action;
234 - }
235 - getParamInfo( data,
236 - function () {
237 - showLoading( $mainContainer );
238 - $submit.button( 'option', 'disabled', true );
239 - $examplesContent.hide();
240 - },
241 - function () {
242 - var info;
243 - if ( isQuery ) {
244 - info = paramInfo.querymodules[query];
245 - } else {
246 - info = paramInfo.modules[action];
247 - }
248 - mainRequest = new UiBuilder( $mainContainer, info, '' );
249 - mainRequest.setHelp( $help );
250 - $submit.button( 'option', 'disabled', false );
251 - updateExamples( info );
252 - },
253 - function () {
254 - $submit.button( 'option', 'disabled', false );
255 - showLoadError( $mainContainer, 'apisb-load-error' );
256 - $examplesContent.hide();
257 - }
258 - );
259 - }
260 -
261 - /**
262 - * HTML-escapes and pretty-formats an API description string
263 - *
264 - * @param s {String} String to escape
265 - * @return {String}
266 - */
267 - function smartEscape( s ) {
268 - if ( !s ) {
269 - return ''; // @todo: fully verify paraminfo output
270 - }
271 - s = mw.html.escape( s );
272 - if ( s.indexOf( '\n ' ) >= 0 ) {
273 - // turns *-bulleted list into a HTML list
274 - s = s.replace( /^(.*?)((?:\n\s+\*?[^\n]*)+)(.*?)$/m, '$1<ul>$2</ul>$3' ); // outer tags
275 - s = s.replace( /\n\s+\*?([^\n]*)/g, '\n<li>$1</li>' ); // <li> around bulleted lines
276 - }
277 - s = s.replace( /\n(?!<)/, '\n<br/>' );
278 - s = s.replace( /(?:https?:)?\/\/[^\s<>]+/g, function ( s ) {
279 - // linkify URLs, input is already HTML-escaped above
280 - return '<a href="' + s + '">' + s + '</a>';
281 - } );
282 - return s;
283 - }
284 -
285 - /**
286 - * Updates UI after basic query parameters have been changed
287 - */
288 - function updateUI() {
289 - var a = $action.val(),
290 - q = $query.val(),
291 - isQuery = a === 'query';
292 - if ( isQuery ) {
293 - $queryRow.show();
294 - if ( q !== '' ) {
295 - $queryContainer.show();
296 - } else {
297 - $queryContainer.hide();
298 - }
299 - } else {
300 - $queryRow.hide();
301 - $queryContainer.hide();
302 - }
303 - $mainContainer.text( '' );
304 - $help.text( '' );
305 - updateQueryInfo( a, q );
306 - $generatorBox.hide();
307 - }
308 -
309 -
310 - /**
311 - * Constructor that creates inputs for a query and builds request data
312 - *
313 - * @constructor
314 - * @param $container {jQuery} Container to put UI into
315 - * @param info {Object} Query information
316 - * @param prefix {String} Additional prefix for parameter names
317 - * @param params {Object} Optional override for info.parameters
318 - */
319 - function UiBuilder( $container, info, prefix, params ) {
320 - this.$container = $container;
321 - this.info = info;
322 - this.prefix = prefix + info.prefix;
323 - this.params = params !== undefined ? params : info.parameters;
324 -
325 - $container.addClass( 'api-sandbox-builder' ).data( 'builder', this );
326 -
327 - this.createInputs();
328 - }
329 -
330 - UiBuilder.prototype = {
331 - /**
332 - * Creates inputs and places them into container
333 - */
334 - createInputs: function () {
335 - var $table, $tbody, i, length, param, name;
336 -
337 - $table = $( '<table class="api-sandbox-params mw-datatable"><thead><tr></tr></thead><tbody></tbody></table>' )
338 - .find( '> thead > tr' )
339 - .append( mw.html.element( 'th', { 'class': 'api-sandbox-params-label' }, mw.msg( 'apisb-params-param' ) ) )
340 - .append( mw.html.element( 'th', { 'class': 'api-sandbox-params-value' }, mw.msg( 'apisb-params-input' ) ) )
341 - .append( mw.html.element( 'th', {}, mw.msg( 'apisb-params-desc' ) ) )
342 - .end();
343 - $tbody = $table.find( '> tbody' )
344 - for ( i = 0, length = this.params.length; i < length; i += 1 ) {
345 - param = this.params[i];
346 - name = this.prefix + param.name;
347 -
348 - $( '<tr>' )
349 - .append(
350 - $( '<td class="api-sandbox-params-label"></td>' )
351 - .html( mw.html.element( 'label',
352 - { 'for': 'param-' + name }, name )
353 - )
354 - )
355 - .append( $( '<td class="api-sandbox-params-value"></td>' ).html( this.input( param, name ) ) )
356 - .append( $( '<td class="mw-content-ltr" dir="ltr">' ).html( smartEscape( param.description ) ) )
357 - .appendTo( $tbody );
358 - }
359 - this.$container.html( $table );
360 - },
361 -
362 - /**
363 - * Adds module help to a container
364 - * @param $container {jQuery} Container to use
365 - */
366 - setHelp: function ( $container ) {
367 - var linkHtml = '',
368 - descHtml = smartEscape( this.info.description );
369 - if ( this.info.helpurls && this.info.helpurls[0] ) {
370 - descHtml = descHtml + ' ';
371 - linkHtml = mw.msg( 'parentheses', mw.html.element( 'a', {
372 - 'target': '_blank',
373 - 'href': this.info.helpurls[0]
374 - }, mw.msg( 'apisb-docs-more' ) ) );
375 - }
376 - $container.html( descHtml ).append( linkHtml );
377 - },
378 -
379 - input: function ( param, name ) {
380 - var s, id, attributes,
381 - value = '';
382 - switch ( param.type ) {
383 - case 'limit':
384 - value = '10';
385 - // fall through:
386 - case 'user':
387 - case 'timestamp':
388 - case 'integer':
389 - case 'string':
390 - s = mw.html.element( 'input', {
391 - 'class': 'api-sandbox-input',
392 - 'id': 'param-' + name,
393 - 'value': value,
394 - 'type': 'text'
395 - } );
396 - break;
397 -
398 - case 'bool':
399 - // normalisation for later use
400 - param.type = 'boolean';
401 - // fall through:
402 - case 'boolean':
403 - s = mw.html.element( 'input', {
404 - 'id': 'param-' + name,
405 - 'type': 'checkbox'
406 - } );
407 - break;
408 -
409 - case 'namespace':
410 - param.type = namespaceOptions;
411 - // fall through:
412 - default:
413 - if ( typeof param.type === 'object' ) {
414 - id = 'param-' + name;
415 - attributes = { 'id': id };
416 - if ( param.multi !== undefined ) {
417 - attributes.multiple = true;
418 - s = this.select( param.type, attributes, false );
419 - } else {
420 - s = this.select( param.type, attributes, true );
421 - }
422 - } else {
423 - s = mw.html.element( 'code', {}, mw.msg( 'parentheses', param.type ) );
424 - }
425 - }
426 - return s;
427 - },
428 -
429 - select: function ( values, attributes, selected ) {
430 - var i, length, value, face, attrs,
431 - s = '';
432 -
433 - attributes['class'] = 'api-sandbox-input';
434 - if ( attributes.multiple === true ) {
435 - attributes.size = Math.min( values.length, 10 );
436 - }
437 - if ( !$.isArray( selected ) ) {
438 - if ( selected ) {
439 - s += mw.html.element( 'option', {
440 - value: '',
441 - selected: true
442 - }, mw.msg( 'apisb-select-value' ) );
443 - }
444 - selected = [];
445 - }
446 -
447 - for ( i = 0, length = values.length; i < length; i += 1 ) {
448 - value = typeof values[i] === 'object' ? values[i].key : values[i];
449 - face = typeof values[i] === 'object' ? values[i].value : values[i];
450 - attrs = { 'value': value };
451 -
452 - if ( $.inArray( value, selected ) >= 0 ) {
453 - attrs.selected = true;
454 - }
455 -
456 - s += mw.html.element( 'option', attrs, face );
457 - }
458 - s = mw.html.element( 'select', attributes, new mw.html.Raw( s ) );
459 - return s;
460 - },
461 -
462 - getRequestData: function () {
463 - var params = '', i, length, param, name, $node, value;
464 -
465 - for ( i = 0, length = this.params.length; i < length; i += 1 ) {
466 - param = this.params[i];
467 - name = this.prefix + param.name;
468 - $node = $( '#param-' + name );
469 - if ( param.type === 'boolean' ) {
470 - if ( $node.prop( 'checked' ) === true ) {
471 - params += '&' + name;
472 - }
473 - } else {
474 - value = $node.val();
475 - if ( value === undefined || value === null || value === '' ) {
476 - continue;
477 - }
478 - if ( $.isArray( value ) ) {
479 - value = value.join( '|' );
480 - }
481 - params += '&' + encodeURIComponent( name ) + '=' + encodeURIComponent( value );
482 - }
483 - }
484 - return params;
485 - }
486 - }; // end of UiBuilder.prototype
487 -
488 - /** When the dom is ready.. **/
489 -
490 - $( function () {
491 -
492 - $( '#api-sandbox-content' ).show();
493 -
494 - // init page elements
495 - $format = $( '#api-sandbox-format' );
496 - $action = $( '#api-sandbox-action' );
497 - $query = $( '#api-sandbox-query' );
498 - $queryRow = $( '#api-sandbox-query-row' );
499 - $help = $( '#api-sandbox-help' );
500 - $mainContainer = $( '#api-sandbox-main-inputs' );
501 - $generatorContainer = $( '#api-sandbox-generator-inputs' );
502 - $queryContainer = $( '#api-sandbox-query-inputs' );
503 - $generatorBox = $( '#api-sandbox-generator-parameters' );
504 - $requestUrl = $( '#api-sandbox-url' );
505 - $requestPost = $( '#api-sandbox-post' );
506 - $output = $( '#api-sandbox-output' );
507 - $postRow = $( '#api-sandbox-post-row' );
508 - $buttonsContainer = $( '#api-sandbox-buttons' );
509 - $examplesContent = $( '#api-sandbox-examples' );
510 - $pageScroll = $( getScrollableElement( 'body', 'html' ) );
511 - $form = $( '#api-sandbox-form' );
512 - $submit = $( '<button>' )
513 - .attr( 'type', 'submit' )
514 - .text( mw.msg( 'apisb-submit' ) )
515 - .appendTo( $buttonsContainer );
516 - $submit = $submit.clone( /*dataAndEvents=*/true, /*deep=*/true )
517 - .appendTo( '#api-sandbox-parameters' )
518 - .add( $submit )
519 - .button({ disabled: true });
520 -
521 - $examplesButton = $( '<button>' )
522 - .attr( 'type', 'button' )
523 - .text( mw.msg( 'apisb-examples' ) )
524 - .click( function ( e ) {
525 - $examplesContent.slideToggle();
526 - } )
527 - .button()
528 - .hide()
529 - .appendTo( $buttonsContainer );
530 -
531 - $( '<button>' )
532 - .attr( 'type', 'button' )
533 - .text( mw.msg( 'apisb-clear' ) )
534 - .click( function ( e ) {
535 - resetUI();
536 - } )
537 - .button()
538 - .appendTo( $buttonsContainer );
539 -
540 - // init caches
541 - paramInfo = { modules: {}, querymodules: {} };
542 - namespaceOptions = [];
543 -
544 - // build namespace cache
545 - $.each( mw.config.get( 'wgFormattedNamespaces' ), function( nsId, nsName ) {
546 - if ( Number( nsId ) >= 0 ) {
547 - if ( nsId === '0' ) {
548 - nsName = mw.msg( 'apisb-ns-main' );
549 - }
550 - namespaceOptions.push( {
551 - key: nsId,
552 - value: nsName
553 - } );
554 - }
555 - } );
556 -
557 - // load stuff we need from the beginning
558 - getParamInfo(
559 - {
560 - mainmodule: 1,
561 - pagesetmodule: 1,
562 - modules: 'query'
563 - },
564 - function () {},
565 - function () {
566 - paramInfo.mainmodule.parameters = paramInfo.mainmodule.parameters.slice( 2 ); // remove format and action
567 - paramInfo.modules.query.parameters = paramInfo.modules.query.parameters.slice( 3 );
568 - $genericContainer = $( '#api-sandbox-generic-inputs > div' );
569 - genericRequest = new UiBuilder( $genericContainer, paramInfo.mainmodule, '' );
570 - queryRequest = new UiBuilder( $queryContainer, paramInfo.modules.query, '',
571 - [].concat( paramInfo.modules.query.parameters, paramInfo.pagesetmodule.parameters )
572 - );
573 - },
574 - function () {}
575 - );
576 -
577 - $action.change( updateUI );
578 - $query.change( updateUI );
579 -
580 - $( '#param-generator' ).live( 'change', function () {
581 - var generator = $( '#param-generator' ).val();
582 - if ( generator === '' ) {
583 - $generatorBox.hide();
584 - } else {
585 - $generatorBox.show();
586 - getParamInfo(
587 - { querymodules: generator },
588 - function () { showLoading( $generatorContainer ); },
589 - function () {
590 - generatorRequest = new UiBuilder( $generatorContainer, paramInfo.querymodules[generator], 'g' );
591 - },
592 - function () {
593 - showLoadError( $generatorContainer, 'apisb-request-error' );
594 - }
595 - );
596 - }
597 - } );
598 -
599 -
600 - $form.submit( function ( e ) {
601 - var url, params, mustBePosted;
602 -
603 - // Prevent browser from submitting the form
604 - // and reloading the page to the action-url.
605 - // We're doing it with AJAX instead, below.
606 - e.preventDefault();
607 -
608 - if ( $submit.button( 'option', 'disabled' ) === true ) {
609 - return;
610 - }
611 -
612 - url = mw.util.wikiScript( 'api' ) + '?' + $.param({ action: $action.val() });
613 - params = mainRequest.getRequestData();
614 - mustBePosted = mainRequest.info.mustbeposted === '';
615 - if ( $action.val() === 'query' ) {
616 - url += '&' + $query.val();
617 - params += queryRequest.getRequestData();
618 - }
619 - url += '&format=' + $format.val();
620 -
621 - params += genericRequest.getRequestData();
622 - if ( $( '#param-generator' ).length && $( '#param-generator' ).val() ) {
623 - params += generatorRequest.getRequestData();
624 - }
625 -
626 - showLoading( $output );
627 - if ( mustBePosted ) {
628 - $requestUrl.val( url );
629 - if ( params.length > 0 ) {
630 - params = params.substr( 1 ); // remove leading &
631 - }
632 - $requestPost.val( params );
633 - $postRow.show();
634 - } else {
635 - $requestUrl.val( url + params );
636 - $postRow.hide();
637 - }
638 - url = url.replace( /(&format=[^&]+)/, '$1fm' );
639 - $.ajax({
640 - url: url,
641 - data: params,
642 - dataType: 'text',
643 - type: mustBePosted ? 'POST' : 'GET',
644 - success: function ( data ) {
645 - var match = data.match( /<pre>[\s\S]*<\/pre>/ );
646 - if ( $.isArray( match ) ) {
647 - var time = data.match( /<!-- Served .*?in (\d+(\.\d+)?) secs. -->/ );
648 - data = match[0];
649 - if ( $.isArray( time ) ) {
650 - data += '\n<br/>' + mw.html.escape( mw.msg( 'apisb-request-time', time[1] ) );
651 - }
652 - } else {
653 - // some actions don't honor user-specified format
654 - data = '<pre>' + mw.html.escape( data ) + '</pre>';
655 - }
656 - $output.html( data );
657 - },
658 - error: function () {
659 - showLoadError( $output, 'apisb-request-error' );
660 - },
661 - // either success or error
662 - complete: function () {
663 - $pageScroll.animate({ scrollTop: $('#api-sandbox-result').offset().top }, 400, function () {
664 - window.location.hash = '#api-sandbox-result';
665 - });
666 - }
667 - });
668 - });
669 -
670 - });
671 -
672 -}( jQuery, mediaWiki ) );
Index: trunk/extensions/ApiSandbox/ext.apiSandbox.css
@@ -1,94 +0,0 @@
2 -/* Buttons */
3 -
4 -#api-sandbox-buttons {
5 - float: right;
6 -}
7 -
8 -#api-sandbox-buttons button {
9 - float: left;
10 -}
11 -
12 -/* Options */
13 -
14 -.api-sandbox-options {
15 - width: 100%;
16 - table-layout: fixed;
17 -}
18 -
19 -.api-sandbox-options th {
20 - text-align: left;
21 -}
22 -
23 -.api-sandbox-options td,
24 -.api-sandbox-options th {
25 - vertical-align: top;
26 - padding: 3px 5px;
27 - width: 160px;
28 -}
29 -
30 -.api-sandbox-options select {
31 - width: 140px;
32 -}
33 -
34 -.api-sandbox-options .api-sandbox-docs-col {
35 - width: auto;
36 -}
37 -
38 -/* Params */
39 -
40 -.api-sandbox-params {
41 - width: 100%;
42 - table-layout: fixed;
43 -}
44 -
45 -.api-sandbox-params td,
46 -.api-sandbox-params th {
47 - vertical-align: top;
48 - padding: 5px 10px;
49 -}
50 -
51 -.api-sandbox-params-label {
52 - width: 150px;
53 - text-align: right;
54 -}
55 -
56 -.api-sandbox-params-value {
57 - width: 260px;
58 -}
59 -
60 -.api-sandbox-params input[type="text"],
61 -.api-sandbox-params select {
62 - padding: 3px 5px;
63 - width: 225px;
64 -}
65 -
66 -th.api-sandbox-params-label,
67 -th.api-sandbox-params-value {
68 - text-align: center;
69 -}
70 -
71 -/* Result */
72 -
73 -.api-sandbox-result-container {
74 - width: 100%;
75 -}
76 -
77 -.api-sandbox-result-label {
78 - width: 12em;
79 -}
80 -
81 -#api-sandbox-input {
82 - width: 17em;
83 -}
84 -
85 -#api-sandbox-url,
86 -#api-sandbox-post {
87 - width: 100%;
88 -}
89 -
90 -/* override enwiki's insane styles */
91 -div.mw-collapsible {
92 - border: none !important;
93 - text-align: left !important;
94 - font-size: 100% !important;
95 -}
Index: trunk/extensions/ApiSandbox/modules/ext.apiSandbox.js
@@ -0,0 +1,671 @@
 2+/*global jQuery, mediaWiki*/
 3+/*jslint regexp: true, browser: true, continue: true, sloppy: true, white: true, forin: true, plusplus: true */
 4+( function ( $, mw, undefined ) {
 5+
 6+ var mainRequest, genericRequest, generatorRequest, queryRequest, // UiBuilder objects
 7+ // Caches
 8+ paramInfo, namespaceOptions,
 9+ // page elements
 10+ $format, $action, $query, $queryRow, $help, $mainContainer, $genericContainer,
 11+ $generatorContainer, $queryContainer, $generatorBox, $form, $submit, $requestUrl, $requestPost,
 12+ $output, $postRow, $buttonsContainer, $examplesButton, $examplesContent, $pageScroll;
 13+
 14+
 15+ /** Local utility functions **/
 16+
 17+ // get the first element in a list that is "scrollable"
 18+ // depends on browser and skin (i.e. body or html)
 19+ function getScrollableElement( /* selectors, .. */ ) {
 20+ var i, argLen, el, $el, canScroll;
 21+ for ( i = 0, argLen = arguments.length; i < argLen; i += 1 ) {
 22+ el = arguments[i];
 23+ $el = $(el);
 24+ if ( $el.scrollTop() > 0 ) {
 25+ return el;
 26+ } else {
 27+ $el.scrollTop( 1 );
 28+ canScroll = $el.scrollTop() > 0;
 29+ $el.scrollTop( 0 );
 30+ if ( canScroll ) {
 31+ return el;
 32+ }
 33+ }
 34+ }
 35+ return [];
 36+ }
 37+
 38+ function showLoading( $element ) {
 39+ $element.html(
 40+ mw.html.element( 'img',
 41+ { src: mw.config.get( 'stylepath' ) + '/common/images/spinner.gif', alt: '' } )
 42+ + mw.html.escape( mw.msg( 'apisb-loading' )
 43+ )
 44+ );
 45+ }
 46+
 47+ function showLoadError( $element, message ) {
 48+ $element.html(
 49+ mw.html.element( 'span', { 'class': 'error' }, mw.msg( message ) )
 50+ );
 51+ }
 52+
 53+ function getParamInfo( what, loadCallback, completeCallback, errorCallback ) {
 54+ var needed, param, subParam;
 55+
 56+ needed = {};
 57+ for ( param in what ) {
 58+ if ( paramInfo[param] === undefined ) {
 59+ needed[param] = what[param];
 60+ } else if ( typeof needed[param] === 'object' ) {
 61+ for ( subParam in param ) {
 62+ if ( paramInfo[param][subParam] === undefined ) {
 63+ needed[param][subParam] = what[param][subParam];
 64+ }
 65+ }
 66+ } else {
 67+ needed[param] = what[param];
 68+ }
 69+ }
 70+ if ( $.isEmptyObject( needed ) ) {
 71+ completeCallback();
 72+ } else {
 73+ loadCallback();
 74+ needed.format = 'json';
 75+ needed.action = 'paraminfo';
 76+ $.getJSON(
 77+ mw.util.wikiScript( 'api' ),
 78+ needed,
 79+ function ( data ) {
 80+ var prop, i, info;
 81+
 82+ if ( data.error || !data.paraminfo ) {
 83+ errorCallback();
 84+ return;
 85+ }
 86+ for ( prop in data.paraminfo ) {
 87+ if ( paramInfo[prop] === undefined ) {
 88+ paramInfo[prop] = data.paraminfo[prop];
 89+ } else {
 90+ for ( i = 0; i < data.paraminfo[prop].length; i++ ) {
 91+ info = data.paraminfo[prop][i];
 92+ if ( !paramInfo[prop][info.name] ) {
 93+ paramInfo[prop][info.name] = info;
 94+ }
 95+ }
 96+ }
 97+ }
 98+ completeCallback();
 99+ }
 100+ ).error( errorCallback );
 101+ }
 102+ }
 103+
 104+ function resetUI() {
 105+ $( '.api-sandbox-builder' ).each( function () {
 106+ $( this ).data( 'builder' ).createInputs();
 107+ } );
 108+ }
 109+
 110+ /**
 111+ * @context {Element}
 112+ * @param e {jQuery.Event}
 113+ */
 114+ function exampleClick( e ) {
 115+ var link, params, i, pieces, key, value, $el, splitted, j;
 116+ e.preventDefault();
 117+
 118+ resetUI();
 119+ link = $( this ).data( 'exampleLink' ).replace( /^.*?\?/, '' );
 120+ params = link.split( '&' );
 121+ for ( i = 0; i < params.length; i++ ) {
 122+ pieces = params[i].split( '=' );
 123+ if ( pieces.length === 1 ) { // checkbox
 124+ $( '#param-' + pieces[0] ).prop( 'checked', true );
 125+ } else {
 126+ key = pieces[0];
 127+ value = decodeURIComponent( pieces.slice( 1 ).join( '=' ) );
 128+ if ( $.inArray( key, [ 'action', 'format', 'list', 'prop', 'meta' ] ) !== -1 ) {
 129+ continue;
 130+ }
 131+ $el = $( '#param-' + key );
 132+ if ( !$el.length ) {
 133+ continue;
 134+ }
 135+ switch ( $el[0].nodeName.toLowerCase() ) {
 136+ case 'select':
 137+ if ( $el.attr( 'multiple' ) ) {
 138+ splitted = value.split( '|' );
 139+ for ( j = 0; j < splitted.length; j++ ) {
 140+ $el.children( 'option[value="' + mw.html.escape( splitted[j] ) + '"]' )
 141+ .prop( 'selected', true );
 142+ }
 143+ } else {
 144+ $el.children( 'option[value="' + mw.html.escape( value ) + '"]' )
 145+ .prop( 'selected', true );
 146+ }
 147+ break;
 148+ case 'input':
 149+ if ( $el.attr( 'type' ) === 'checkbox' ) {
 150+ $( '#param-' + key ).prop( 'checked', true );
 151+ } else {
 152+ $el.val( value );
 153+ }
 154+ break;
 155+ default:
 156+ continue;
 157+ }
 158+ }
 159+ }
 160+ }
 161+
 162+ function updateExamples( info ) {
 163+ var i, $list, urlRegex, count, href, text, match, prefix, $prefix, linkText;
 164+
 165+ if ( info.allexamples === undefined ) {
 166+ return;
 167+ }
 168+ // on 1.18, convert everything into 1.19 format
 169+ if ( info.allexamples.length > 0 && typeof info.allexamples[0] === 'string' ) {
 170+ for ( i = 0; i < info.allexamples.length; i++ ) {
 171+ info.allexamples[i] = { '*': info.allexamples[i] };
 172+ }
 173+ }
 174+ $examplesContent.hide().html( '' );
 175+ $list = $( '<ul>' );
 176+ urlRegex = /api.php\?\S+/m;
 177+ count = 0;
 178+ for ( i = 0; i < info.allexamples.length; i++ ) {
 179+ href = '';
 180+ text = '';
 181+ while ( i < info.allexamples.length && info.allexamples[i].description === undefined ) {
 182+ match = urlRegex.exec( info.allexamples[i]['*'] );
 183+ if ( match ) {
 184+ href = match[0];
 185+ break;
 186+ } else {
 187+ text += '\n' + info.allexamples[i]['*'];
 188+ }
 189+ i++;
 190+ }
 191+ if ( !href ) {
 192+ href = info.allexamples[i]['*'];
 193+ }
 194+ if ( !text ) {
 195+ text = info.allexamples[i].description !== undefined ? info.allexamples[i].description : href;
 196+ }
 197+ prefix = text.replace( /[^\n]*$/, '' );
 198+ $prefix = prefix.length ? $( '<b>' ).text( prefix ) : [];
 199+ linkText = text.replace( /^.*\n/, '' );
 200+ $( '<li>' )
 201+ .append( $prefix )
 202+ .append(
 203+ $( '<a>' )
 204+ .attr( 'href', '#' )
 205+ .data( 'exampleLink', href )
 206+ .text( linkText )
 207+ .click( exampleClick )
 208+ ).appendTo( $list );
 209+ count++;
 210+ }
 211+ $examplesButton.button( 'option', 'text', mw.msg( count === 1 ? 'apisb-example' : 'apisb-examples' ) );
 212+ $list.appendTo( $examplesContent );
 213+ if ( count ) {
 214+ $examplesButton.show();
 215+ } else {
 216+ $examplesButton.hide();
 217+ }
 218+ }
 219+
 220+ function updateQueryInfo( action, query ) {
 221+ var data,
 222+ isQuery = action === 'query';
 223+
 224+ if ( action === '' || ( isQuery && query === '' ) ) {
 225+ $submit.button( 'option', 'disabled', true );
 226+ return;
 227+ }
 228+ query = query.replace( /^.*=/, '' );
 229+ data = {};
 230+ if ( isQuery ) {
 231+ data.querymodules = query;
 232+ } else {
 233+ data.modules = action;
 234+ }
 235+ getParamInfo( data,
 236+ function () {
 237+ showLoading( $mainContainer );
 238+ $submit.button( 'option', 'disabled', true );
 239+ $examplesContent.hide();
 240+ },
 241+ function () {
 242+ var info;
 243+ if ( isQuery ) {
 244+ info = paramInfo.querymodules[query];
 245+ } else {
 246+ info = paramInfo.modules[action];
 247+ }
 248+ mainRequest = new UiBuilder( $mainContainer, info, '' );
 249+ mainRequest.setHelp( $help );
 250+ $submit.button( 'option', 'disabled', false );
 251+ updateExamples( info );
 252+ },
 253+ function () {
 254+ $submit.button( 'option', 'disabled', false );
 255+ showLoadError( $mainContainer, 'apisb-load-error' );
 256+ $examplesContent.hide();
 257+ }
 258+ );
 259+ }
 260+
 261+ /**
 262+ * HTML-escapes and pretty-formats an API description string
 263+ *
 264+ * @param s {String} String to escape
 265+ * @return {String}
 266+ */
 267+ function smartEscape( s ) {
 268+ if ( !s ) {
 269+ return ''; // @todo: fully verify paraminfo output
 270+ }
 271+ s = mw.html.escape( s );
 272+ if ( s.indexOf( '\n ' ) >= 0 ) {
 273+ // turns *-bulleted list into a HTML list
 274+ s = s.replace( /^(.*?)((?:\n\s+\*?[^\n]*)+)(.*?)$/m, '$1<ul>$2</ul>$3' ); // outer tags
 275+ s = s.replace( /\n\s+\*?([^\n]*)/g, '\n<li>$1</li>' ); // <li> around bulleted lines
 276+ }
 277+ s = s.replace( /\n(?!<)/, '\n<br/>' );
 278+ s = s.replace( /(?:https?:)?\/\/[^\s<>]+/g, function ( s ) {
 279+ // linkify URLs, input is already HTML-escaped above
 280+ return '<a href="' + s + '">' + s + '</a>';
 281+ } );
 282+ return s;
 283+ }
 284+
 285+ /**
 286+ * Updates UI after basic query parameters have been changed
 287+ */
 288+ function updateUI() {
 289+ var a = $action.val(),
 290+ q = $query.val(),
 291+ isQuery = a === 'query';
 292+ if ( isQuery ) {
 293+ $queryRow.show();
 294+ if ( q !== '' ) {
 295+ $queryContainer.show();
 296+ } else {
 297+ $queryContainer.hide();
 298+ }
 299+ } else {
 300+ $queryRow.hide();
 301+ $queryContainer.hide();
 302+ }
 303+ $mainContainer.text( '' );
 304+ $help.text( '' );
 305+ updateQueryInfo( a, q );
 306+ $generatorBox.hide();
 307+ }
 308+
 309+
 310+ /**
 311+ * Constructor that creates inputs for a query and builds request data
 312+ *
 313+ * @constructor
 314+ * @param $container {jQuery} Container to put UI into
 315+ * @param info {Object} Query information
 316+ * @param prefix {String} Additional prefix for parameter names
 317+ * @param params {Object} Optional override for info.parameters
 318+ */
 319+ function UiBuilder( $container, info, prefix, params ) {
 320+ this.$container = $container;
 321+ this.info = info;
 322+ this.prefix = prefix + info.prefix;
 323+ this.params = params !== undefined ? params : info.parameters;
 324+
 325+ $container.addClass( 'api-sandbox-builder' ).data( 'builder', this );
 326+
 327+ this.createInputs();
 328+ }
 329+
 330+ UiBuilder.prototype = {
 331+ /**
 332+ * Creates inputs and places them into container
 333+ */
 334+ createInputs: function () {
 335+ var $table, $tbody, i, length, param, name;
 336+
 337+ $table = $( '<table class="api-sandbox-params mw-datatable"><thead><tr></tr></thead><tbody></tbody></table>' )
 338+ .find( '> thead > tr' )
 339+ .append( mw.html.element( 'th', { 'class': 'api-sandbox-params-label' }, mw.msg( 'apisb-params-param' ) ) )
 340+ .append( mw.html.element( 'th', { 'class': 'api-sandbox-params-value' }, mw.msg( 'apisb-params-input' ) ) )
 341+ .append( mw.html.element( 'th', {}, mw.msg( 'apisb-params-desc' ) ) )
 342+ .end();
 343+ $tbody = $table.find( '> tbody' )
 344+ for ( i = 0, length = this.params.length; i < length; i += 1 ) {
 345+ param = this.params[i];
 346+ name = this.prefix + param.name;
 347+
 348+ $( '<tr>' )
 349+ .append(
 350+ $( '<td class="api-sandbox-params-label"></td>' )
 351+ .html( mw.html.element( 'label',
 352+ { 'for': 'param-' + name }, name )
 353+ )
 354+ )
 355+ .append( $( '<td class="api-sandbox-params-value"></td>' ).html( this.input( param, name ) ) )
 356+ .append( $( '<td class="mw-content-ltr" dir="ltr">' ).html( smartEscape( param.description ) ) )
 357+ .appendTo( $tbody );
 358+ }
 359+ this.$container.html( $table );
 360+ },
 361+
 362+ /**
 363+ * Adds module help to a container
 364+ * @param $container {jQuery} Container to use
 365+ */
 366+ setHelp: function ( $container ) {
 367+ var linkHtml = '',
 368+ descHtml = smartEscape( this.info.description );
 369+ if ( this.info.helpurls && this.info.helpurls[0] ) {
 370+ descHtml = descHtml + ' ';
 371+ linkHtml = mw.msg( 'parentheses', mw.html.element( 'a', {
 372+ 'target': '_blank',
 373+ 'href': this.info.helpurls[0]
 374+ }, mw.msg( 'apisb-docs-more' ) ) );
 375+ }
 376+ $container.html( descHtml ).append( linkHtml );
 377+ },
 378+
 379+ input: function ( param, name ) {
 380+ var s, id, attributes,
 381+ value = '';
 382+ switch ( param.type ) {
 383+ case 'limit':
 384+ value = '10';
 385+ // fall through:
 386+ case 'user':
 387+ case 'timestamp':
 388+ case 'integer':
 389+ case 'string':
 390+ s = mw.html.element( 'input', {
 391+ 'class': 'api-sandbox-input',
 392+ 'id': 'param-' + name,
 393+ 'value': value,
 394+ 'type': 'text'
 395+ } );
 396+ break;
 397+
 398+ case 'bool':
 399+ // normalisation for later use
 400+ param.type = 'boolean';
 401+ // fall through:
 402+ case 'boolean':
 403+ s = mw.html.element( 'input', {
 404+ 'id': 'param-' + name,
 405+ 'type': 'checkbox'
 406+ } );
 407+ break;
 408+
 409+ case 'namespace':
 410+ param.type = namespaceOptions;
 411+ // fall through:
 412+ default:
 413+ if ( typeof param.type === 'object' ) {
 414+ id = 'param-' + name;
 415+ attributes = { 'id': id };
 416+ if ( param.multi !== undefined ) {
 417+ attributes.multiple = true;
 418+ s = this.select( param.type, attributes, false );
 419+ } else {
 420+ s = this.select( param.type, attributes, true );
 421+ }
 422+ } else {
 423+ s = mw.html.element( 'code', {}, mw.msg( 'parentheses', param.type ) );
 424+ }
 425+ }
 426+ return s;
 427+ },
 428+
 429+ select: function ( values, attributes, selected ) {
 430+ var i, length, value, face, attrs,
 431+ s = '';
 432+
 433+ attributes['class'] = 'api-sandbox-input';
 434+ if ( attributes.multiple === true ) {
 435+ attributes.size = Math.min( values.length, 10 );
 436+ }
 437+ if ( !$.isArray( selected ) ) {
 438+ if ( selected ) {
 439+ s += mw.html.element( 'option', {
 440+ value: '',
 441+ selected: true
 442+ }, mw.msg( 'apisb-select-value' ) );
 443+ }
 444+ selected = [];
 445+ }
 446+
 447+ for ( i = 0, length = values.length; i < length; i += 1 ) {
 448+ value = typeof values[i] === 'object' ? values[i].key : values[i];
 449+ face = typeof values[i] === 'object' ? values[i].value : values[i];
 450+ attrs = { 'value': value };
 451+
 452+ if ( $.inArray( value, selected ) >= 0 ) {
 453+ attrs.selected = true;
 454+ }
 455+
 456+ s += mw.html.element( 'option', attrs, face );
 457+ }
 458+ s = mw.html.element( 'select', attributes, new mw.html.Raw( s ) );
 459+ return s;
 460+ },
 461+
 462+ getRequestData: function () {
 463+ var params = '', i, length, param, name, $node, value;
 464+
 465+ for ( i = 0, length = this.params.length; i < length; i += 1 ) {
 466+ param = this.params[i];
 467+ name = this.prefix + param.name;
 468+ $node = $( '#param-' + name );
 469+ if ( param.type === 'boolean' ) {
 470+ if ( $node.prop( 'checked' ) === true ) {
 471+ params += '&' + name;
 472+ }
 473+ } else {
 474+ value = $node.val();
 475+ if ( value === undefined || value === null || value === '' ) {
 476+ continue;
 477+ }
 478+ if ( $.isArray( value ) ) {
 479+ value = value.join( '|' );
 480+ }
 481+ params += '&' + encodeURIComponent( name ) + '=' + encodeURIComponent( value );
 482+ }
 483+ }
 484+ return params;
 485+ }
 486+ }; // end of UiBuilder.prototype
 487+
 488+ /** When the dom is ready.. **/
 489+
 490+ $( function () {
 491+
 492+ $( '#api-sandbox-content' ).show();
 493+
 494+ // init page elements
 495+ $format = $( '#api-sandbox-format' );
 496+ $action = $( '#api-sandbox-action' );
 497+ $query = $( '#api-sandbox-query' );
 498+ $queryRow = $( '#api-sandbox-query-row' );
 499+ $help = $( '#api-sandbox-help' );
 500+ $mainContainer = $( '#api-sandbox-main-inputs' );
 501+ $generatorContainer = $( '#api-sandbox-generator-inputs' );
 502+ $queryContainer = $( '#api-sandbox-query-inputs' );
 503+ $generatorBox = $( '#api-sandbox-generator-parameters' );
 504+ $requestUrl = $( '#api-sandbox-url' );
 505+ $requestPost = $( '#api-sandbox-post' );
 506+ $output = $( '#api-sandbox-output' );
 507+ $postRow = $( '#api-sandbox-post-row' );
 508+ $buttonsContainer = $( '#api-sandbox-buttons' );
 509+ $examplesContent = $( '#api-sandbox-examples' );
 510+ $pageScroll = $( getScrollableElement( 'body', 'html' ) );
 511+ $form = $( '#api-sandbox-form' );
 512+ $submit = $( '<button>' )
 513+ .attr( 'type', 'submit' )
 514+ .text( mw.msg( 'apisb-submit' ) )
 515+ .appendTo( $buttonsContainer );
 516+ $submit = $submit.clone( /*dataAndEvents=*/true, /*deep=*/true )
 517+ .appendTo( '#api-sandbox-parameters' )
 518+ .add( $submit )
 519+ .button({ disabled: true });
 520+
 521+ $examplesButton = $( '<button>' )
 522+ .attr( 'type', 'button' )
 523+ .text( mw.msg( 'apisb-examples' ) )
 524+ .click( function ( e ) {
 525+ $examplesContent.slideToggle();
 526+ } )
 527+ .button()
 528+ .hide()
 529+ .appendTo( $buttonsContainer );
 530+
 531+ $( '<button>' )
 532+ .attr( 'type', 'button' )
 533+ .text( mw.msg( 'apisb-clear' ) )
 534+ .click( function ( e ) {
 535+ resetUI();
 536+ } )
 537+ .button()
 538+ .appendTo( $buttonsContainer );
 539+
 540+ // init caches
 541+ paramInfo = { modules: {}, querymodules: {} };
 542+ namespaceOptions = [];
 543+
 544+ // build namespace cache
 545+ $.each( mw.config.get( 'wgFormattedNamespaces' ), function( nsId, nsName ) {
 546+ if ( Number( nsId ) >= 0 ) {
 547+ if ( nsId === '0' ) {
 548+ nsName = mw.msg( 'apisb-ns-main' );
 549+ }
 550+ namespaceOptions.push( {
 551+ key: nsId,
 552+ value: nsName
 553+ } );
 554+ }
 555+ } );
 556+
 557+ // load stuff we need from the beginning
 558+ getParamInfo(
 559+ {
 560+ mainmodule: 1,
 561+ pagesetmodule: 1,
 562+ modules: 'query'
 563+ },
 564+ function () {},
 565+ function () {
 566+ paramInfo.mainmodule.parameters = paramInfo.mainmodule.parameters.slice( 2 ); // remove format and action
 567+ paramInfo.modules.query.parameters = paramInfo.modules.query.parameters.slice( 3 );
 568+ $genericContainer = $( '#api-sandbox-generic-inputs > div' );
 569+ genericRequest = new UiBuilder( $genericContainer, paramInfo.mainmodule, '' );
 570+ queryRequest = new UiBuilder( $queryContainer, paramInfo.modules.query, '',
 571+ [].concat( paramInfo.modules.query.parameters, paramInfo.pagesetmodule.parameters )
 572+ );
 573+ },
 574+ function () {}
 575+ );
 576+
 577+ $action.change( updateUI );
 578+ $query.change( updateUI );
 579+
 580+ $( '#param-generator' ).live( 'change', function () {
 581+ var generator = $( '#param-generator' ).val();
 582+ if ( generator === '' ) {
 583+ $generatorBox.hide();
 584+ } else {
 585+ $generatorBox.show();
 586+ getParamInfo(
 587+ { querymodules: generator },
 588+ function () { showLoading( $generatorContainer ); },
 589+ function () {
 590+ generatorRequest = new UiBuilder( $generatorContainer, paramInfo.querymodules[generator], 'g' );
 591+ },
 592+ function () {
 593+ showLoadError( $generatorContainer, 'apisb-request-error' );
 594+ }
 595+ );
 596+ }
 597+ } );
 598+
 599+
 600+ $form.submit( function ( e ) {
 601+ var url, params, mustBePosted;
 602+
 603+ // Prevent browser from submitting the form
 604+ // and reloading the page to the action-url.
 605+ // We're doing it with AJAX instead, below.
 606+ e.preventDefault();
 607+
 608+ if ( $submit.button( 'option', 'disabled' ) === true ) {
 609+ return;
 610+ }
 611+
 612+ url = mw.util.wikiScript( 'api' ) + '?' + $.param({ action: $action.val() });
 613+ params = mainRequest.getRequestData();
 614+ mustBePosted = mainRequest.info.mustbeposted === '';
 615+ if ( $action.val() === 'query' ) {
 616+ url += '&' + $query.val();
 617+ params += queryRequest.getRequestData();
 618+ }
 619+ url += '&format=' + $format.val();
 620+
 621+ params += genericRequest.getRequestData();
 622+ if ( $( '#param-generator' ).length && $( '#param-generator' ).val() ) {
 623+ params += generatorRequest.getRequestData();
 624+ }
 625+
 626+ showLoading( $output );
 627+ if ( mustBePosted ) {
 628+ $requestUrl.val( url );
 629+ if ( params.length > 0 ) {
 630+ params = params.substr( 1 ); // remove leading &
 631+ }
 632+ $requestPost.val( params );
 633+ $postRow.show();
 634+ } else {
 635+ $requestUrl.val( url + params );
 636+ $postRow.hide();
 637+ }
 638+ url = url.replace( /(&format=[^&]+)/, '$1fm' );
 639+ $.ajax({
 640+ url: url,
 641+ data: params,
 642+ dataType: 'text',
 643+ type: mustBePosted ? 'POST' : 'GET',
 644+ success: function ( data ) {
 645+ var match = data.match( /<pre>[\s\S]*<\/pre>/ );
 646+ if ( $.isArray( match ) ) {
 647+ var time = data.match( /<!-- Served .*?in (\d+(\.\d+)?) secs. -->/ );
 648+ data = match[0];
 649+ if ( $.isArray( time ) ) {
 650+ data += '\n<br/>' + mw.html.escape( mw.msg( 'apisb-request-time', time[1] ) );
 651+ }
 652+ } else {
 653+ // some actions don't honor user-specified format
 654+ data = '<pre>' + mw.html.escape( data ) + '</pre>';
 655+ }
 656+ $output.html( data );
 657+ },
 658+ error: function () {
 659+ showLoadError( $output, 'apisb-request-error' );
 660+ },
 661+ // either success or error
 662+ complete: function () {
 663+ $pageScroll.animate({ scrollTop: $('#api-sandbox-result').offset().top }, 400, function () {
 664+ window.location.hash = '#api-sandbox-result';
 665+ });
 666+ }
 667+ });
 668+ });
 669+
 670+ });
 671+
 672+}( jQuery, mediaWiki ) );
Property changes on: trunk/extensions/ApiSandbox/modules/ext.apiSandbox.js
___________________________________________________________________
Added: svn:eol-style
1673 + native
Index: trunk/extensions/ApiSandbox/modules/ext.apiSandbox.css
@@ -0,0 +1,94 @@
 2+/* Buttons */
 3+
 4+#api-sandbox-buttons {
 5+ float: right;
 6+}
 7+
 8+#api-sandbox-buttons button {
 9+ float: left;
 10+}
 11+
 12+/* Options */
 13+
 14+.api-sandbox-options {
 15+ width: 100%;
 16+ table-layout: fixed;
 17+}
 18+
 19+.api-sandbox-options th {
 20+ text-align: left;
 21+}
 22+
 23+.api-sandbox-options td,
 24+.api-sandbox-options th {
 25+ vertical-align: top;
 26+ padding: 3px 5px;
 27+ width: 160px;
 28+}
 29+
 30+.api-sandbox-options select {
 31+ width: 140px;
 32+}
 33+
 34+.api-sandbox-options .api-sandbox-docs-col {
 35+ width: auto;
 36+}
 37+
 38+/* Params */
 39+
 40+.api-sandbox-params {
 41+ width: 100%;
 42+ table-layout: fixed;
 43+}
 44+
 45+.api-sandbox-params td,
 46+.api-sandbox-params th {
 47+ vertical-align: top;
 48+ padding: 5px 10px;
 49+}
 50+
 51+.api-sandbox-params-label {
 52+ width: 150px;
 53+ text-align: right;
 54+}
 55+
 56+.api-sandbox-params-value {
 57+ width: 260px;
 58+}
 59+
 60+.api-sandbox-params input[type="text"],
 61+.api-sandbox-params select {
 62+ padding: 3px 5px;
 63+ width: 225px;
 64+}
 65+
 66+th.api-sandbox-params-label,
 67+th.api-sandbox-params-value {
 68+ text-align: center;
 69+}
 70+
 71+/* Result */
 72+
 73+.api-sandbox-result-container {
 74+ width: 100%;
 75+}
 76+
 77+.api-sandbox-result-label {
 78+ width: 12em;
 79+}
 80+
 81+#api-sandbox-input {
 82+ width: 17em;
 83+}
 84+
 85+#api-sandbox-url,
 86+#api-sandbox-post {
 87+ width: 100%;
 88+}
 89+
 90+/* override enwiki's insane styles */
 91+div.mw-collapsible {
 92+ border: none !important;
 93+ text-align: left !important;
 94+ font-size: 100% !important;
 95+}
Property changes on: trunk/extensions/ApiSandbox/modules/ext.apiSandbox.css
___________________________________________________________________
Added: svn:eol-style
196 + native
Index: trunk/extensions/ApiSandbox/ApiSandbox.php
@@ -24,8 +24,8 @@
2525 $wgResourceModules['ext.apiSandbox'] = array(
2626 'scripts' => 'ext.apiSandbox.js',
2727 'styles' => 'ext.apiSandbox.css',
28 - 'localBasePath' => dirname( __FILE__ ),
29 - 'remoteExtPath' => 'ApiSandbox',
 28+ 'localBasePath' => $dir . '/modules',
 29+ 'remoteExtPath' => 'ApiSandbox/modules',
3030 'messages' => array(
3131 'apisb-loading',
3232 'apisb-load-error',

Status & tagging log