r99891 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r99890‎ | r99891 | r99892 >
Date:17:05, 15 October 2011
Author:maxsem
Status:ok
Tags:
Comment:
Major rewrite, added support for generators, pageset and other tasty stuf
Modified paths:
  • /trunk/extensions/ApiSandbox/ApiSandbox.i18n.php (modified) (history)
  • /trunk/extensions/ApiSandbox/SpecialApiSandbox.php (modified) (history)
  • /trunk/extensions/ApiSandbox/ext.apiSandbox.js (modified) (history)

Diff [purge]

Index: trunk/extensions/ApiSandbox/SpecialApiSandbox.php
@@ -88,8 +88,14 @@
8989 . self::getSelect( 'query', $queryModules )
9090 . '</td></tr>
9191 </table>
92 -<div id="api-sandbox-further-inputs"></div>
9392 ';
 93+ $s .= '<div id="api-sandbox-main-inputs"></div>'
 94+ . $this->openFieldset( 'generic-parameters' )
 95+ . '<div id="api-sandbox-generic-inputs" class="mw-collapsible mw-collapsed"></div></fieldset>'
 96+ . $this->openFieldset( 'generator-parameters', array( 'style' => 'display: none;' ) )
 97+ . '<div id="api-sandbox-generator-inputs">foo</div></fieldset>
 98+';
 99+
94100 $s .= Html::element( 'input',
95101 array(
96102 'type' => 'button',
@@ -111,7 +117,7 @@
112118 foreach ( $params[$type][ApiBase::PARAM_TYPE] as $module ) {
113119 $res[] = array(
114120 'value' => "$type=$module",
115 - 'text' => /* &nbsp; */ "\xc2\xa0\xc2\xa0\xc2\xa0$type=$module",
 121+ 'text' => /* &nbsp; x 3 */ "\xc2\xa0\xc2\xa0\xc2\xa0$type=$module",
116122 );
117123 }
118124 sort( $res );
@@ -174,10 +180,11 @@
175181
176182 /**
177183 * @param $name string
 184+ * @param $attribs Array
178185 * @return string
179186 */
180 - private function openFieldset( $name ) {
181 - return "\n" . Html::openElement( 'fieldset', array( 'id' => "api-sandbox-$name" ) )
 187+ private function openFieldset( $name, $attribs = array() ) {
 188+ return "\n" . Html::openElement( 'fieldset', array( 'id' => "api-sandbox-$name" ) + $attribs )
182189 . "\n\t" . Html::rawElement( 'legend', array(), wfMessage( "apisb-$name" )->parse() )
183190 . "\n";
184191 }
Index: trunk/extensions/ApiSandbox/ext.apiSandbox.js
@@ -1,4 +1,139 @@
22 jQuery( function( $ ) {
 3+ /**
 4+ * Class that creates inputs for a query and builds request data
 5+ *
 6+ * Constructor
 7+ * @param $container {jQuery} Container to put UI into
 8+ * @param info {Object} Query information
 9+ * @param prefix {String} Additional prefix for parameter names
 10+ */
 11+ function UiBuilder( $container, info, prefix ) {
 12+ this.$container = $container;
 13+ this.info = info;
 14+ this.prefix = prefix + info.prefix;
 15+
 16+ this.createInputs();
 17+ }
 18+
 19+ UiBuilder.prototype = {
 20+ /**
 21+ * Creates inputs and places them into container
 22+ */
 23+ createInputs: function() {
 24+ var s = '<table class="api-sandbox-options">\n<tbody>';
 25+ for ( var i = 0; i < this.info.parameters.length; i++ ) {
 26+ var param = this.info.parameters[i],
 27+ name = this.prefix + param.name;
 28+
 29+ s += '<tr><td class="api-sandbox-label"><label for="param-' + name + '">' + name + '=</label></td>'
 30+ + '<td class="api-sandbox-value">' + this.input( param, name )
 31+ + '</td><td>' + smartEscape( param.description ) + '</td></tr>';
 32+ }
 33+ s += '\n</tbody>\n</table>\n';
 34+ this.$container.html( s );
 35+ this.$container.show();
 36+ },
 37+
 38+ /**
 39+ * Adds module help to a container
 40+ * @param $container {jQuery} Container to use
 41+ */
 42+ setHelp: function( $container ) {
 43+ var desc = smartEscape( this.info.description );
 44+ if ( isset( this.info.helpurls ) && isset( this.info.helpurls[0] ) && this.info.helpurls[0] ) {
 45+ desc = desc.replace( /^([^\r\n\.]*)/,
 46+ '<a target="_blank" href="' + mw.html.escape( this.info.helpurls[0] ) + '">$1</a>'
 47+ );
 48+ }
 49+ $container.html( desc );
 50+ },
 51+
 52+ input: function( param, name ) {
 53+ var s,
 54+ value = '';
 55+ switch ( param.type ) {
 56+ case 'limit':
 57+ value = 10;
 58+ case 'user':
 59+ case 'timestamp':
 60+ case 'integer':
 61+ case 'string':
 62+ s = '<input class="api-sandbox-input" id="param-' + name + '" value="' + value + '"/>';
 63+ break;
 64+ case 'bool':
 65+ param.type = 'boolean'; // normalisation for later use
 66+ case 'boolean':
 67+ s = '<input id="param-' + name + '" type="checkbox"/>';
 68+ break;
 69+ case 'namespace':
 70+ param.type = namespaces;
 71+ default:
 72+ if ( typeof param.type == 'object' ) {
 73+ var id = 'param-' + name,
 74+ attributes = { 'id': id };
 75+ if ( isset( param.multi ) ) {
 76+ attributes.multiple = 'multiple';
 77+ s = this.select( param.type, attributes, false );
 78+ } else {
 79+ s = this.select( param.type, attributes, true );
 80+ }
 81+ } else {
 82+ s = mw.html.element( 'code', [], mw.msg( 'parentheses', param.type ) );
 83+ }
 84+ }
 85+ return s;
 86+ },
 87+
 88+ select: function( values, attributes, selected ) {
 89+ attributes['class'] = 'api-sandbox-input';
 90+ if ( isset( attributes.multiple ) ) {
 91+ attributes['size'] = Math.min( values.length, 10 );
 92+ }
 93+ var s = '';
 94+ if ( typeof selected != 'array' ) {
 95+ if ( selected ) {
 96+ s += mw.html.element( 'option', { value: '', selected: 'selected' }, mw.msg( 'apisb-select-value' ) );
 97+ }
 98+ selected = [];
 99+ }
 100+ for ( var i = 0; i < values.length; i++ ) {
 101+ var value = typeof values[i] == 'object' ? values[i].key : values[i],
 102+ face = typeof values[i] == 'object' ? values[i].value : values[i],
 103+ attrs = { 'value': value };
 104+ if ( $.inArray( value, selected ) >= 0 ) {
 105+ attrs.selected = 'selected';
 106+ }
 107+ s += '\n' + mw.html.element( 'option', attrs, face );
 108+ }
 109+ s = mw.html.element( 'select', attributes, new mw.html.Raw( s ) );
 110+ return s;
 111+ },
 112+
 113+ getRequestData: function() {
 114+ var params = '';
 115+ for ( var i = 0; i < this.info.parameters.length; i++ ) {
 116+ var param = this.info.parameters[i],
 117+ name = this.prefix + param.name,
 118+ $node = $( '#param-' + name );
 119+ if ( param.type == 'boolean' ) {
 120+ if ( $node.is( ':checked' ) ) {
 121+ params += '&' + name;
 122+ }
 123+ } else {
 124+ var value = $node.val();
 125+ if ( !isset( value ) || value == '' || value == null ) {
 126+ continue;
 127+ }
 128+ if ( $.isArray( value ) ) {
 129+ value = value.join( '|' );
 130+ }
 131+ params += '&' + name + '=' + encodeURIComponent( value );
 132+ }
 133+ }
 134+ return params;
 135+ }
 136+ } // end of UiBuilder.prototype
 137+
3138 var $content = $( '#api-sandbox-content' );
4139 if ( !$content.length ) {
5140 return;
@@ -11,18 +146,23 @@
12147 $query = $( '#api-sandbox-query' ),
13148 $queryRow = $( '#api-sandbox-query-row' ),
14149 $help = $( '#api-sandbox-help' ),
15 - $further = $( '#api-sandbox-further-inputs' ),
 150+ $mainContainer = $( '#api-sandbox-main-inputs' ),
 151+ $genericContainer, // will be set later
 152+ $generatorContainer = $( '#api-sandbox-generator-inputs' ),
16153 $submit = $( '#api-sandbox-submit' ),
17154 $requestUrl = $( '#api-sandbox-url' ),
18155 $requestPost = $( '#api-sandbox-post' ),
19156 $output = $( '#api-sandbox-output' ),
20157 $postRow = $( '#api-sandbox-post-row' );
21158
 159+ // UiBuilder objects
 160+ var mainQuery,
 161+ genericQuery,
 162+ generatorQuery;
 163+
22164 // cached stuff
23 - var actionCache = [],
24 - propCache = [],
25 - namespaces = [],
26 - currentInfo = {};
 165+ var paramInfo = { modules: {}, querymodules: {} },
 166+ namespaces = [];
27167
28168 // load namespaces
29169 $.getJSON( mw.util.wikiScript( 'api' ),
@@ -40,43 +180,58 @@
41181 namespaces.push( { key: id, value: ns } );
42182 }
43183 } else {
44 - showLoadError( $further, 'apisb-namespaces-error' );
 184+ showLoadError( $mainContainer, 'apisb-namespaces-error' );
45185 }
46186 }
47187 );
48188
49 - $action.change( updateBasics );
50 - $query.change( updateBasics );
 189+ // load stuff we need from the beginning
 190+ getParamInfo( { mainmodule: 1, pagesetmodule: 1, modules: 'query' },
 191+ function() {},
 192+ function() {
 193+ paramInfo.mainmodule.parameters.shift(); // remove format
 194+ paramInfo.mainmodule.parameters.shift(); // ...and action
 195+ $genericContainer = $( '#api-sandbox-generic-inputs > div' );
 196+ genericQuery = new UiBuilder( $genericContainer, paramInfo.mainmodule, '' );
 197+ },
 198+ function() {}
 199+ );
51200
 201+ $action.change( updateUI );
 202+ $query.change( updateUI );
 203+
 204+ $( '#param-generator' ).live( 'change', function() {
 205+ var generator = $( '#param-generator' ).val();
 206+ if ( generator == '' ) {
 207+ $generatorContainer.hide();
 208+ } else {
 209+ getParamInfo( { querymodules: generator },
 210+ function() { showLoading( $generatorContainer ); },
 211+ function() {
 212+ generatorQuery = new UiBuilder( $generatorContainer, paramInfo.querymodules[generator], 'g' );
 213+ },
 214+ function() {}
 215+ );
 216+ }
 217+ } );
 218+
52219 $submit.click( function() {
53 - var url = mw.util.wikiScript( 'api' ) + '?action=' + $action.val(),
54 - info = currentInfo; // in case it changes later
 220+ var url = mw.util.wikiScript( 'api' ) + '?action=' + $action.val();
55221 if ( $action.val() == 'query' ) {
56222 url += '&' + $query.val();
57223 }
58224 url += '&format=' + $format.val();
59 - var params = '';
60 - for ( var i = 0; i < info.parameters.length; i++ ) {
61 - var param = info.parameters[i],
62 - name = info.prefix + param.name,
63 - $node = $( '#param-' + name );
64 - if ( param.type == 'boolean' ) {
65 - if ( $node.is( ':checked' ) ) {
66 - params += '&' + name;
67 - }
68 - } else {
69 - var value = $node.val();
70 - if ( !isset( value ) || value == '' || value == null ) {
71 - continue;
72 - }
73 - if ( $.isArray( value ) ) {
74 - value = value.join( '|' );
75 - }
76 - params += '&' + name + '=' + encodeURIComponent( value );
77 - }
 225+
 226+ var params = mainQuery.getRequestData(),
 227+ mustBePosted = isset( mainQuery.info.mustbeposted );
 228+
 229+ params += genericQuery.getRequestData();
 230+ if ( $( '#param-generator' ).val() != '' ) {
 231+ params += generatorQuery.getRequestData();
78232 }
 233+
79234 showLoading( $output );
80 - if ( isset( info.mustbeposted ) ) {
 235+ if ( mustBePosted ) {
81236 $requestUrl.val( url );
82237 $requestPost.val( params );
83238 $postRow.show();
@@ -89,7 +244,7 @@
90245 url: url,
91246 data: params,
92247 dataType: 'text',
93 - type: isset( info.mustbeposted ) ? 'POST' : 'GET',
 248+ type: mustBePosted ? 'POST' : 'GET',
94249 success: function( data ) {
95250 var match = data.match( /<pre>[\s\S]*<\/pre>/ );
96251 if ( $.isArray( match ) ) {
@@ -114,72 +269,118 @@
115270 return typeof x != 'undefined';
116271 }
117272
118 - function showLoading( element ) {
119 - element.html(
 273+ /**
 274+ * Merges several objects into one that gets returned
 275+ */
 276+ function merge( /* ... */ ) {
 277+ var res = {};
 278+ for ( var i = 0; i < arguments.length; i++ ) {
 279+ $.extend( res, arguments[i] );
 280+ }
 281+ return res;
 282+ }
 283+
 284+ function showLoading( $element ) {
 285+ $element.html(
120286 mw.html.element( 'img',
121287 { src: mw.config.get( 'stylepath' ) + '/common/images/spinner.gif', alt: '' } )
122288 + mw.html.escape( mw.msg( 'apisb-loading' ) ) );
123289 }
124290
125 - function showLoadError( element, message ) {
126 - element.html( mw.html.element( 'span', { 'class': 'error' }, mw.msg( message ) ) );
 291+ function showLoadError( $element, message ) {
 292+ $element.html( mw.html.element( 'span', { 'class': 'error' }, mw.msg( message ) ) );
127293 }
128294
129 - function parseParamInfo( data ) {
130 - $further.text( '' );
131 - if ( !isset( data.paraminfo )
132 - || ( !isset( data.paraminfo.modules ) && !isset( data.paraminfo.querymodules ) )
133 - )
134 - {
135 - showLoadError( $further, 'apisb-load-error' );
136 - return;
 295+ function getParamInfo( what, loadCallback, completeCallback, errorCallback ) {
 296+ var needed = {};
 297+ for ( var param in what ) {
 298+ if ( !isset( paramInfo[param] ) ) {
 299+ needed[param] = what[param];
 300+ } else if (typeof needed[param] == 'object' ) {
 301+ for ( var subParam in param ) {
 302+ if ( !isset( paramInfo[param][subParam] ) ) {
 303+ needed[param][subParam] = what[param][subParam];
 304+ }
 305+ }
 306+ } else {
 307+ needed[param] = what[param];
 308+ }
137309 }
138 - if ( isset( data.paraminfo.modules ) ) {
139 - actionCache[data.paraminfo.modules[0].name] = data.paraminfo.modules[0];
140 - createInputs( actionCache[data.paraminfo.modules[0].name] );
 310+ if ( $.isEmptyObject( needed ) ) {
 311+ completeCallback();
141312 } else {
142 - propCache[data.paraminfo.querymodules[0].name] = data.paraminfo.querymodules[0];
143 - createInputs( propCache[data.paraminfo.querymodules[0].name] );
 313+ loadCallback();
 314+ needed.format = 'json';
 315+ needed.action = 'paraminfo';
 316+ $.getJSON(
 317+ mw.util.wikiScript( 'api' ),
 318+ needed,
 319+ function( data ) {
 320+ if ( data.error || !data.paraminfo ) {
 321+ errorCallback();
 322+ return;
 323+ }
 324+ for ( var prop in data.paraminfo ) {
 325+ if ( !isset( paramInfo[prop] ) ) {
 326+ paramInfo[prop] = data.paraminfo[prop];
 327+ } else {
 328+ for ( var i = 0; i < data.paraminfo[prop].length; i++ ) {
 329+ var info = data.paraminfo[prop][i];
 330+ if ( !paramInfo[prop][info.name] ) {
 331+ paramInfo[prop][info.name] = info;
 332+ }
 333+ }
 334+ }
 335+ }
 336+ completeCallback();
 337+ }
 338+ ).error( errorCallback );
144339 }
145 - $submit.removeAttr( 'disabled' );
146340 }
147341
148 - function getQueryInfo( action, query ) {
 342+ function updateQueryInfo( action, query ) {
149343 var isQuery = action == 'query';
150344 if ( action == '-' || ( isQuery && query == '-' ) ) {
151345 $submit.attr( 'disabled', 'disabled' );
152346 return;
153347 }
154348 query = query.replace( /^.*=/, '' );
155 - var cached;
156 - if ( isQuery ) {
157 - cached = propCache[query];
 349+ //if ( typeof cached != 'object' ) { // stupid FF adds watch() everywhere
 350+ data = {};
 351+ if (isQuery ) {
 352+ data.querymodules = query;
158353 } else {
159 - cached = actionCache[action];
 354+ data.modules = action;
160355 }
161 - if ( typeof cached != 'object' ) { // stupid FF adds watch() everywhere
162 - showLoading( $further );
163 - var data = {
164 - format: 'json',
165 - action: 'paraminfo'
166 - };
167 - if (isQuery ) {
168 - data.querymodules = query;
169 - } else {
170 - data.modules = action;
 356+ getParamInfo( data,
 357+ function() {
 358+ showLoading( $mainContainer );
 359+ $submit.attr( 'disabled', 'disabled' );
 360+ },
 361+ function() {
 362+ var info;
 363+ if ( isQuery ) {
 364+ info = merge( paramInfo.querymodules[query], paramInfo.modules.query, paramInfo.pagesetmodule );
 365+ } else {
 366+ info = paramInfo.modules[action];
 367+ }
 368+ mainQuery = new UiBuilder( $mainContainer, info, '' );
 369+ mainQuery.setHelp( $help );
 370+ $submit.removeAttr( 'disabled' );
 371+ },
 372+ function() {
 373+ $submit.removeAttr( 'disabled' );
 374+ showLoadError( $mainContainer, 'apisb-load-error' );
171375 }
172 - $submit.attr( 'disabled', 'disabled' );
173 - $.getJSON(
174 - mw.config.get( 'wgScriptPath' ) + '/api' + mw.config.get( 'wgScriptExtension' ),
175 - data,
176 - parseParamInfo
177 - );
178 - } else {
179 - $submit.removeAttr( 'disabled' );
180 - createInputs( cached );
181 - }
 376+ );
182377 }
183378
 379+ /**
 380+ * HTML-escapes and pretty-formats an API description string
 381+ *
 382+ * @param s {String} String to escape
 383+ * @return {String}
 384+ */
184385 function smartEscape( s ) {
185386 s = mw.html.escape( s );
186387 if ( s.indexOf( '\n ' ) >= 0 ) {
@@ -190,101 +391,24 @@
191392 return s;
192393 }
193394
194 - function createInputs( info ) {
195 - currentInfo = info;
196 - var desc = smartEscape( info.description );
197 - if ( isset( info.helpurls ) && isset( info.helpurls[0] ) && info.helpurls[0] ) {
198 - desc = desc.replace( /^([^\r\n\.]*)/,
199 - '<a target="_blank" href="' + mw.html.escape( info.helpurls[0] ) + '">$1</a>'
200 - );
201 - }
202 - $help.html( desc );
203 - var s = '<table class="api-sandbox-options">\n<tbody>';
204 - for ( var i = 0; i < info.parameters.length; i++ ) {
205 - var param = info.parameters[i],
206 - name = info.prefix + param.name;
207 -
208 - s += '<tr><td class="api-sandbox-label"><label for="param-' + name + '">' + name + '=</label></td>'
209 - + '<td class="api-sandbox-value">' + input( param, name )
210 - + '</td><td>' + smartEscape( param.description ) + '</td></tr>';
211 - }
212 - s += '\n</tbody>\n</table>\n';
213 - $further.html( s );
214 - }
215 -
216 - function input( param, name ) {
217 - var s,
218 - value = '';
219 - switch ( param.type ) {
220 - case 'limit':
221 - value = 10;
222 - case 'user':
223 - case 'timestamp':
224 - case 'integer':
225 - case 'string':
226 - s = '<input class="api-sandbox-input" id="param-' + name + '" value="' + value + '"/>';
227 - break;
228 - case 'bool':
229 - param.type = 'boolean'; // normalisation for later use
230 - case 'boolean':
231 - s = '<input id="param-' + name + '" type="checkbox"/>';
232 - break;
233 - case 'namespace':
234 - param.type = namespaces;
235 - default:
236 - if ( typeof param.type == 'object' ) {
237 - var id = 'param-' + name,
238 - attributes = { 'id': id };
239 - if ( isset( param.multi ) ) {
240 - attributes.multiple = 'multiple';
241 - s = select( param.type, attributes, false );
242 - } else {
243 - s = select( param.type, attributes, true );
244 - }
245 - } else {
246 - s = mw.html.element( 'code', [], mw.msg( 'parentheses', param.type ) );
247 - }
248 - }
249 - return s;
250 - }
251 -
252 - function select( values, attributes, selected ) {
253 - attributes['class'] = 'api-sandbox-input';
254 - if ( isset( attributes.multiple ) ) {
255 - attributes['size'] = Math.min( values.length, 10 );
256 - }
257 - var s = '';
258 - if ( typeof selected != 'array' ) {
259 - if ( selected ) {
260 - s += mw.html.element( 'option', { value: '', selected: 'selected' }, mw.msg( 'apisb-select-value' ) );
261 - }
262 - selected = [];
263 - }
264 - for ( var i = 0; i < values.length; i++ ) {
265 - var value = typeof values[i] == 'object' ? values[i].key : values[i],
266 - face = typeof values[i] == 'object' ? values[i].value : values[i],
267 - attrs = { 'value': value };
268 - if ( $.inArray( value, selected ) >= 0 ) {
269 - attrs.selected = 'selected';
270 - }
271 - s += '\n' + mw.html.element( 'option', attrs, face );
272 - }
273 - s = mw.html.element( 'select', attributes, new mw.html.Raw( s ) );
274 - return s;
275 - }
276 -
277 - function updateBasics() {
 395+ /**
 396+ * Updates UI after basic query parameters have been changed
 397+ */
 398+ function updateUI() {
278399 var a = $action.val(),
279400 q = $query.val(),
280401 isQuery = a == 'query';
281402 if ( isQuery ) {
282403 $queryRow.show();
 404+ $( '#api-sandbox-generator-parameters' ).show();
283405 } else {
284406 $queryRow.hide();
 407+ $( '#api-sandbox-generator-parameters' ).hide();
285408 }
286 - $further.text( '' );
 409+ $mainContainer.text( '' );
287410 $help.text( '' );
288 - getQueryInfo( a, q );
 411+ updateQueryInfo( a, q );
 412+ $generatorContainer.hide();
289413 }
290414
291415 });
\ No newline at end of file
Index: trunk/extensions/ApiSandbox/ApiSandbox.i18n.php
@@ -31,6 +31,8 @@
3232 'apisb-query-prop' => 'Properties',
3333 'apisb-query-list' => 'Lists',
3434 'apisb-query-meta' => 'Meta information',
 35+ 'apisb-generic-parameters'=> 'Generic parameters',
 36+ 'apisb-generator-parameters'=> 'Generator',
3537 );
3638
3739 /** Message documentation (Message documentation)

Follow-up revisions

RevisionCommit summaryAuthorDate
r108170Followup r99891, clean up javascriptjohnduhart19:33, 5 January 2012

Status & tagging log