add JS api, feedback libs from UW into core.
authorNeil Kandalgaonkar <neilk@users.mediawiki.org>
Fri, 9 Dec 2011 04:48:39 +0000 (04:48 +0000)
committerNeil Kandalgaonkar <neilk@users.mediawiki.org>
Fri, 9 Dec 2011 04:48:39 +0000 (04:48 +0000)
resources/Resources.php
resources/mediawiki/mediawiki.api.category.js [new file with mode: 0644]
resources/mediawiki/mediawiki.api.edit.js [new file with mode: 0644]
resources/mediawiki/mediawiki.api.js [new file with mode: 0644]
resources/mediawiki/mediawiki.api.parse.js [new file with mode: 0644]
resources/mediawiki/mediawiki.api.titleblacklist.js [new file with mode: 0644]
resources/mediawiki/mediawiki.feedback.js [new file with mode: 0644]

index 5f10a71..d6d1310 100644 (file)
@@ -497,10 +497,45 @@ return array(
                'debugScripts' => 'resources/mediawiki/mediawiki.log.js',
                'debugRaw' => false,
        ),
+       'mediawiki.api' => array(
+               'scripts' => 'resources/mediawiki/mediawiki.api.js',
+       ),
+       'mediawiki.api.category' => array( 
+               'scripts' => 'resources/mediawiki/mediawiki.api.category.js',
+               'dependencies' => array( 
+                       'mediawiki.api',
+                       'mediawiki.Title' 
+               ),
+       ),
+       'mediawiki.api.edit' => array( 
+               'scripts' => 'resources/mediawiki/mediawiki.api.edit.js',
+               'dependencies' => array( 
+                       'mediawiki.api',
+                       'mediawiki.Title' 
+               ),
+       ),
+       'mediawiki.api.parse' => array( 
+               'scripts' => 'resources/mediawiki/mediawiki.api.parse.js',
+               'dependencies' => 'mediawiki.api',
+       ),
+       'mediawiki.api.titleblacklist' => array( 
+               'scripts' => 'resources/mediawiki/mediawiki.api.titleblacklist.js',
+               'dependencies' => array( 
+                       'mediawiki.api',
+                       'mediawiki.Title' 
+               ),
+       ),
        'mediawiki.debug' => array(
                'scripts' => 'resources/mediawiki/mediawiki.debug.js',
                'styles' => 'resources/mediawiki/mediawiki.debug.css',
        ),
+       'mediawiki.feedback' => array(
+               'scripts' => 'resources/mediawiki/mediawiki.feedback.js',
+               'dependencies' => array( 
+                       'mediawiki.api.edit', 
+                       'mediawiki.Title' 
+               ),
+       ),
        'mediawiki.htmlform' => array(
                'scripts' => 'resources/mediawiki/mediawiki.htmlform.js',
        ),
diff --git a/resources/mediawiki/mediawiki.api.category.js b/resources/mediawiki/mediawiki.api.category.js
new file mode 100644 (file)
index 0000000..269d852
--- /dev/null
@@ -0,0 +1,106 @@
+// library to assist with API calls on categories
+
+( function( mw, $ ) {
+
+       $.extend( mw.Api.prototype, { 
+               /**
+                * Determine if a category exists
+                * @param {mw.Title} 
+                * @param {Function} callback to pass boolean of category's existence
+                * @param {Function} optional callback to run if api error
+                * @return ajax call object
+                */
+               isCategory: function( title, callback, err ) {
+                       var params = {
+                               'prop': 'categoryinfo',
+                               'titles': title.toString()
+                       };
+
+                       var ok = function( data ) {
+                               var exists = false;
+                               if ( data.query && data.query.pages ) {
+                                       $.each( data.query.pages, function( id, page ) {
+                                               if ( page.categoryinfo ) {
+                                                       exists = true;
+                                               }
+                                       } );
+                               }
+                               callback( exists );
+                       };
+
+                       return this.get( params, { ok: ok, err: err } );
+
+               },
+
+               /**
+                * Get a list of categories that match a certain prefix. 
+                *   e.g. given "Foo", return "Food", "Foolish people", "Foosball tables" ...
+                * @param {String} prefix to match
+                * @param {Function} callback to pass matched categories to
+                * @param {Function} optional callback to run if api error
+                * @return ajax call object
+                */
+               getCategoriesByPrefix: function( prefix, callback, err ) {              
+
+                       // fetch with allpages to only get categories that have a corresponding description page.
+                       var params = {
+                               'list': 'allpages',
+                               'apprefix': prefix,
+                               'apnamespace': mw.config.get('wgNamespaceIds').category
+                       };
+
+                       var ok = function( data ) {
+                               var texts = [];
+                               if ( data.query && data.query.allpages ) { 
+                                       $.each( data.query.allpages, function( i, category ) {
+                                               texts.push( new mw.Title( category.title ).getNameText() ); 
+                                       } );
+                               }
+                               callback( texts );
+                       };
+
+                       return this.get( params, { ok: ok, err: err } );
+
+               },
+
+
+               /**
+                * Get the categories that a particular page on the wiki belongs to
+                * @param {mw.Title}
+                * @param {Function} callback to pass categories to (or false, if title not found)
+                * @param {Function} optional callback to run if api error
+                * @param {Boolean} optional asynchronousness (default = true = async)
+                * @return ajax call object 
+                */
+               getCategories: function( title, callback, err, async ) {
+                       var params = {
+                               prop: 'categories',
+                               titles: title.toString()
+                       };
+                       if ( async === undefined ) {
+                               async = true;
+                       }
+
+                       var ok = function( data ) {
+                               var ret = false;
+                               if ( data.query && data.query.pages ) {
+                                       $.each( data.query.pages, function( id, page ) {
+                                               if ( page.categories ) {
+                                                       if ( typeof ret !== 'object' ) { 
+                                                               ret = [];
+                                                       }
+                                                       $.each( page.categories, function( i, cat ) { 
+                                                               ret.push( new mw.Title( cat.title ) ); 
+                                                       } );
+                                               }
+                                       } );
+                               }
+                               callback( ret );
+                       };
+
+                       return this.get( params, { ok: ok, err: err, async: async } );
+
+               }
+
+       } );
+} )( window.mediaWiki, jQuery );
diff --git a/resources/mediawiki/mediawiki.api.edit.js b/resources/mediawiki/mediawiki.api.edit.js
new file mode 100644 (file)
index 0000000..db8a6a3
--- /dev/null
@@ -0,0 +1,117 @@
+// library to assist with edits
+
+( function( mw, $, undefined ) {
+
+       // cached token so we don't have to keep fetching new ones for every single post
+       var cachedToken = null;
+
+       $.extend( mw.Api.prototype, { 
+
+               /* Post to API with edit token. If we have no token, get one and try to post.
+                * If we have a cached token try using that, and if it fails, blank out the
+                * cached token and start over.
+                * 
+                * @param params API parameters
+                * @param ok callback for success
+                * @param err (optional) error callback
+                */
+               postWithEditToken: function( params, ok, err ) {
+                       var api = this;
+                       if ( cachedToken === null ) {
+                               // We don't have a valid cached token, so get a fresh one and try posting.
+                               // We do not trap any 'badtoken' or 'notoken' errors, because we don't want
+                               // an infinite loop. If this fresh token is bad, something else is very wrong.
+                               var useTokenToPost = function( token ) {
+                                       params.token = token; 
+                                       api.post( params, ok, err );
+                               };
+                               api.getEditToken( useTokenToPost, err );
+                       } else {
+                               // We do have a token, but it might be expired. So if it is 'bad' then
+                               // start over with a new token.
+                               params.token = cachedToken;
+                               var getTokenIfBad = function( code, result ) {
+                                       if ( code === 'badtoken' )  {
+                                               cachedToken = null; // force a new token
+                                               api.postWithEditToken( params, ok, err );
+                                       } else {
+                                               err( code, result );
+                                       }
+                               };
+                               api.post( params, { 'ok' : ok, 'err' : getTokenIfBad });
+                       }
+               },
+       
+               /**
+                * Api helper to grab an edit token
+                *
+                * token callback has signature ( String token )
+                * error callback has signature ( String code, Object results, XmlHttpRequest xhr, Exception exception )
+                * Note that xhr and exception are only available for 'http_*' errors
+                *  code may be any http_* error code (see mw.Api), or 'token_missing'
+                *
+                * @param {Function} received token callback
+                * @param {Function} error callback
+                */
+               getEditToken: function( tokenCallback, err ) {
+                       var api = this;
+
+                       var parameters = {                      
+                               'prop': 'info',
+                               'intoken': 'edit',
+                               /* we need some kind of dummy page to get a token from. This will return a response 
+                                  complaining that the page is missing, but we should also get an edit token */
+                               'titles': 'DummyPageForEditToken'
+                       };
+
+                       var ok = function( data ) {
+                               var token;
+                               $.each( data.query.pages, function( i, page ) {
+                                       if ( page['edittoken'] ) {
+                                               token = page['edittoken'];
+                                               return false;
+                                       }
+                               } );
+                               if ( token !== undefined ) {
+                                       cachedToken = token;
+                                       tokenCallback( token );
+                               } else {
+                                       err( 'token-missing', data );
+                               }
+                       };
+
+                       var ajaxOptions = {
+                               'ok': ok,
+                               'err': err,
+                               // Due to the API assuming we're logged out if we pass the callback-parameter,
+                               // we have to disable jQuery's callback system, and instead parse JSON string,
+                               // by setting 'jsonp' to false.
+                               'jsonp': false
+                       };
+
+                       api.get( parameters, ajaxOptions );
+               },
+
+               /**
+                * Create a new section of the page.
+                * @param {mw.Title|String} target page
+                * @param {String} header
+                * @param {String} wikitext message
+                * @param {Function} success handler
+                * @param {Function} error handler
+                */
+               newSection: function( title, header, message, ok, err ) {
+                       var params = {
+                               action: 'edit',
+                               section: 'new',
+                               format: 'json',
+                               title: title.toString(),
+                               summary: header,
+                               text: message
+                       };
+                       this.postWithEditToken( params, ok, err );
+               }
+
+        } ); // end extend
+
+} )( window.mediaWiki, jQuery );
diff --git a/resources/mediawiki/mediawiki.api.js b/resources/mediawiki/mediawiki.api.js
new file mode 100644 (file)
index 0000000..e093b59
--- /dev/null
@@ -0,0 +1,208 @@
+/* mw.Api objects represent the API of a particular MediaWiki server. */       
+
+( function( mw, $j, undefined ) {
+       
+       /**
+        * Represents the API of a particular MediaWiki server.
+        *
+        * Required options: 
+        *   url - complete URL to API endpoint. Usually equivalent to wgServer + wgScriptPath + '/api.php'
+        *
+        * Other options:
+        *   can override the parameter defaults and ajax default options.
+        *      XXX document!
+        *  
+        * ajax options can also be overriden on every get() or post()
+        * 
+        * @param options {Mixed} can take many options, but must include at minimum the API url.
+        */
+       mw.Api = function( options ) {
+
+               // make sure we at least have a URL endpoint for the API
+               if ( options.url === undefined ) {
+                       throw new Error( 'Configuration error - needs url property' );
+               }
+
+               this.url = options.url;
+
+               /* We allow people to omit these default parameters from API requests */
+               // there is very customizable error handling here, on a per-call basis
+               // wondering, would it be simpler to make it easy to clone the api object, change error handling, and use that instead?
+               this.defaults = {
+                       parameters: {
+                               action: 'query',
+                               format: 'json'
+                       },
+
+                       ajax: {
+                               // force toString if we got a mw.Uri object
+                               url: new String( this.url ),  
+
+                               /* default function for success and no API error */
+                               ok: function() {},
+
+                               // caller can supply handlers for http transport error or api errors
+                               err: function( code, result ) {
+                                       mw.log( "mw.Api error: " + code, 'debug' );
+                               },
+
+                               timeout: 30000, /* 30 seconds */
+
+                               dataType: 'json'
+
+                       }
+               };
+
+
+               if ( options.parameters ) {
+                       $j.extend( this.defaults.parameters, options.parameters );
+               }
+
+               if ( options.ajax ) { 
+                       $j.extend( this.defaults.ajax, options.ajax );
+               }
+       };
+
+       mw.Api.prototype = {
+
+               /**
+                * For api queries, in simple cases the caller just passes a success callback.
+                * In complex cases they pass an object with a success property as callback and probably other options.
+                * Normalize the argument so that it's always the latter case.
+                * 
+                * @param {Object|Function} ajax properties, or just a success function
+                * @return Function
+                */
+               normalizeAjaxOptions: function( arg ) {
+                       if ( typeof arg === 'function' ) {
+                               var ok = arg;
+                               arg = { 'ok': ok };
+                       }
+                       if (! arg.ok ) {
+                               throw Error( "ajax options must include ok callback" );
+                       }
+                       return arg;
+               },
+
+               /**
+                * Perform API get request
+                *
+                * @param {Object} request parameters 
+                * @param {Object|Function} ajax properties, or just a success function
+                */     
+               get: function( parameters, ajaxOptions ) {
+                       ajaxOptions = this.normalizeAjaxOptions( ajaxOptions );
+                       ajaxOptions.type = 'GET';
+                       this.ajax( parameters, ajaxOptions );
+               },
+
+               /**
+                * Perform API post request
+                * TODO post actions for nonlocal will need proxy 
+                * 
+                * @param {Object} request parameters 
+                * @param {Object|Function} ajax properties, or just a success function
+                */
+               post: function( parameters, ajaxOptions ) {
+                       ajaxOptions = this.normalizeAjaxOptions( ajaxOptions );
+                       ajaxOptions.type = 'POST';
+                       this.ajax( parameters, ajaxOptions );
+               },
+
+               /**
+                * Perform the API call. 
+                * 
+                * @param {Object} request parameters 
+                * @param {Object} ajax properties
+                */
+               ajax: function( parameters, ajaxOptions ) {
+                       parameters = $j.extend( {}, this.defaults.parameters, parameters );
+                       ajaxOptions = $j.extend( {}, this.defaults.ajax, ajaxOptions );
+
+                       // Some deployed MediaWiki >= 1.17 forbid periods in URLs, due to an IE XSS bug
+                       // So let's escape them here. See bug #28235
+                       // This works because jQuery accepts data as a query string or as an Object
+                       ajaxOptions.data = $j.param( parameters ).replace( /\./g, '%2E' );
+               
+                       ajaxOptions.error = function( xhr, textStatus, exception ) {
+                               ajaxOptions.err( 'http', { xhr: xhr, textStatus: textStatus, exception: exception } );
+                       };
+
+                       
+                       /* success just means 200 OK; also check for output and API errors */
+                       ajaxOptions.success = function( result ) {
+                               if ( result === undefined || result === null || result === '' ) {
+                                       ajaxOptions.err( "ok-but-empty", "OK response but empty result (check HTTP headers?)" );
+                               } else if ( result.error ) {
+                                       var code = result.error.code === undefined ? 'unknown' : result.error.code;
+                                       ajaxOptions.err( code, result );
+                               } else { 
+                                       ajaxOptions.ok( result );
+                               }
+                       };
+
+                       $j.ajax( ajaxOptions );
+
+               }
+
+       };
+
+       /**
+        * This is a list of errors we might receive from the API.
+        * For now, this just documents our expectation that there should be similar messages
+        * available.
+        */
+       mw.Api.errors = [
+               /* occurs when POST aborted - jQuery 1.4 can't distinguish abort or lost connection from 200 OK + empty result */
+               'ok-but-empty',
+
+               // timeout
+               'timeout',
+
+               /* really a warning, but we treat it like an error */
+               'duplicate',
+               'duplicate-archive',
+
+               /* upload succeeded, but no image info. 
+                  this is probably impossible, but might as well check for it */
+               'noimageinfo',
+
+               /* remote errors, defined in API */
+               'uploaddisabled',
+               'nomodule',
+               'mustbeposted',
+               'badaccess-groups',
+               'stashfailed',
+               'missingresult',
+               'missingparam',
+               'invalid-file-key',
+               'copyuploaddisabled',
+               'mustbeloggedin',
+               'empty-file',
+               'file-too-large',
+               'filetype-missing',
+               'filetype-banned',
+               'filename-tooshort',
+               'illegal-filename',
+               'verification-error',
+               'hookaborted',
+               'unknown-error',
+               'internal-error',
+               'overwrite',
+               'badtoken',
+               'fetchfileerror',
+               'fileexists-shared-forbidden'
+       ];
+
+       /**
+        * This is a list of warnings we might receive from the API.
+        * For now, this just documents our expectation that there should be similar messages
+        * available.
+        */
+
+       mw.Api.warnings = [
+               'duplicate',
+               'exists'
+       ];
+
+}) ( window.mediaWiki, jQuery );
diff --git a/resources/mediawiki/mediawiki.api.parse.js b/resources/mediawiki/mediawiki.api.parse.js
new file mode 100644 (file)
index 0000000..7bcc4bb
--- /dev/null
@@ -0,0 +1,29 @@
+// library to assist with action=parse, that is, get rendered HTML of wikitext
+
+( function( mw, $ ) {
+
+       $.extend( mw.Api.prototype, { 
+               /**
+                * Parse wikitext into HTML
+                * @param {String} wikitext
+                * @param {Function} callback to which to pass success HTML
+                * @param {Function} callback if error (optional)
+                */
+               parse: function( wikiText, useHtml, error ) {
+                       var params = {
+                               text: wikiText,
+                               action: 'parse'
+                       };
+                       var ok = function( data ) {
+                               if ( data && data.parse && data.parse.text && data.parse.text['*'] ) {
+                                       useHtml( data.parse.text['*'] );
+                               } 
+                       };
+                       this.get( params, ok, error );
+               }
+
+
+       } ); // end extend
+} )( window.mediaWiki, jQuery );
+
+
diff --git a/resources/mediawiki/mediawiki.api.titleblacklist.js b/resources/mediawiki/mediawiki.api.titleblacklist.js
new file mode 100644 (file)
index 0000000..95bbc4f
--- /dev/null
@@ -0,0 +1,48 @@
+// library to assist with API calls on titleblacklist
+
+( function( mw, $ ) {
+
+       // cached token so we don't have to keep fetching new ones for every single post
+       var cachedToken = null;
+
+       $.extend( mw.Api.prototype, { 
+               /**
+                * @param {mw.Title} 
+                * @param {Function} callback to pass false on Title not blacklisted, or error text when blacklisted
+                * @param {Function} optional callback to run if api error
+                * @return ajax call object
+                */
+               isBlacklisted: function( title, callback, err ) {
+                       var params = {
+                               'action': 'titleblacklist',
+                               'tbaction': 'create',
+                               'tbtitle': title.toString()
+                       };
+
+                       var ok = function( data ) {
+                               // this fails open (if nothing valid is returned by the api, allows the title)
+                               // also fails open when the API is not present, which will be most of the time.
+                               if ( data.titleblacklist && data.titleblacklist.result && data.titleblacklist.result == 'blacklisted') {
+                                       var result;
+                                       if ( data.titleblacklist.reason ) {
+                                               result = {
+                                                       reason: data.titleblacklist.reason,
+                                                       line: data.titleblacklist.line,
+                                                       message: data.titleblacklist.message
+                                               };
+                                       } else {
+                                               mw.log("mw.Api.titleblacklist::isBlacklisted> no reason data for blacklisted title", 'debug');
+                                               result = { reason: "Blacklisted, but no reason supplied", line: "Unknown" };
+                                       }
+                                       callback( result );
+                               } else {
+                                       callback ( false );
+                               }
+                       };
+
+                       return this.get( params, ok, err );
+
+               }
+
+       } );
+} )( window.mediaWiki, jQuery );
diff --git a/resources/mediawiki/mediawiki.feedback.js b/resources/mediawiki/mediawiki.feedback.js
new file mode 100644 (file)
index 0000000..6c7a997
--- /dev/null
@@ -0,0 +1,138 @@
+( function( mw, $, undefined ) {
+
+       /**
+        * Thingy for collecting user feedback on a wiki page
+        * @param {mw.Api}  api properly configured to talk to this wiki
+        * @param {mw.Title} the title of the page where you collect feedback
+        * @param {id} a string identifying this feedback form to separate it from others on the same page
+        */
+       mw.Feedback = function( api, feedbackTitle ) {
+               var _this = this;
+               this.api = api;
+               this.feedbackTitle = feedbackTitle;
+               this.setup();
+       };
+
+       mw.Feedback.prototype = {
+               setup: function() {
+                       var _this = this;
+
+                       // Set up buttons for dialog box. We have to do it the hard way since the json keys are localized
+                       _this.buttons = {};
+                       _this.buttons[ gM( 'mwe-upwiz-feedback-cancel' ) ] = function() { _this.cancel(); };
+                       _this.buttons[ gM( 'mwe-upwiz-feedback-submit' ) ] = function() { _this.submit(); };
+                               
+                       var $feedbackPageLink = $j( '<a></a>' ).attr( { 'href': _this.feedbackTitle.getUrl(), 'target': '_blank' } );
+                       this.$dialog = 
+                               $( '<div style="position:relative;"></div>' ).append( 
+                                       $( '<div class="mwe-upwiz-feedback-mode mwe-upwiz-feedback-form"></div>' ).append( 
+                                               $( '<div style="margin-top:0.4em;"></div>' ).append( 
+                                                       $( '<small></small>' ).msg( 'mwe-upwiz-feedback-note', 
+                                                                                   _this.feedbackTitle.getNameText(), 
+                                                                                   $feedbackPageLink ) 
+                                               ),
+                                               $( '<div style="margin-top:1em;"></div>' ).append( 
+                                                       gM( 'mwe-upwiz-feedback-subject' ), 
+                                                       $( '<br/>' ), 
+                                                       $( '<input type="text" class="mwe-upwiz-feedback-subject" name="subject" maxlength="60" style="width:99%;"/>' ) 
+                                               ),
+                                               $( '<div style="margin-top:0.4em;"></div>' ).append( 
+                                                       gM( 'mwe-upwiz-feedback-message' ), 
+                                                       $( '<br/>' ), 
+                                                       $( '<textarea name="message" class="mwe-upwiz-feedback-message" style="width:99%;" rows="5" cols="60"></textarea>' ) 
+                                               )
+                                       ),
+                                       $( '<div class="mwe-upwiz-feedback-mode mwe-upwiz-feedback-submitting" style="text-align:center;margin:3em 0;"></div>' ).append( 
+                                               gM( 'mwe-upwiz-feedback-adding' ), 
+                                               $( '<br/>' ), 
+                                               $( '<img src="http://upload.wikimedia.org/wikipedia/commons/4/42/Loading.gif" />' ) 
+                                       ),
+                                       $( '<div class="mwe-upwiz-feedback-mode mwe-upwiz-feedback-error" style="position:relative;"></div>' ).append( 
+                                               $( '<div class="mwe-upwiz-feedback-error-msg style="color:#990000;margin-top:0.4em;"></div>' )
+
+                                       )
+                               ).dialog({
+                                       width: 500,
+                                       autoOpen: false,
+                                       title: gM( 'mwe-upwiz-feedback-title' ),
+                                       modal: true,
+                                       buttons: _this.buttons
+                               }); 
+
+                       this.subjectInput = this.$dialog.find( 'input.mwe-upwiz-feedback-subject' ).get(0);
+                       this.messageInput = this.$dialog.find( 'textarea.mwe-upwiz-feedback-message' ).get(0);
+                       this.displayForm();             
+               },
+
+               display: function( s ) {
+                       this.$dialog.dialog( { buttons:{} } ); // hide the buttons
+                       this.$dialog.find( '.mwe-upwiz-feedback-mode' ).hide(); // hide everything
+                       this.$dialog.find( '.mwe-upwiz-feedback-' + s ).show(); // show the desired div 
+               },
+
+               displaySubmitting: function() { 
+                       this.display( 'submitting' );
+               },
+
+               displayForm: function( contents ) {
+                       this.subjectInput.value = (contents && contents.subject) ? contents.subject : '';
+                       this.messageInput.value = (contents && contents.message) ? contents.message : '';
+                                               
+                       this.display( 'form' ); 
+                       this.$dialog.dialog( { buttons: this.buttons } ); // put the buttons back
+               },
+
+               displayError: function( message ) {
+                       this.display( 'error' );
+                       this.$dialog.find( '.mwe-upwiz-feedback-error-msg' ).msg( message ); 
+               },
+
+               cancel: function() { 
+                       this.$dialog.dialog( 'close' );
+               },
+
+               submit: function() {
+                       var _this = this;
+
+                       // get the values to submit
+                       var subject = this.subjectInput.value;
+
+                       var message = "<small>User agent: " + navigator.userAgent + "</small>\n\n"
+                                + this.messageInput.value;
+                       if ( message.indexOf( '~~~' ) == -1 ) {
+                               message += " ~~~~";
+                       }
+
+                       this.displaySubmitting();
+
+                       var ok = function( result ) {
+                               if ( result.edit !== undefined ) {
+                                       if ( result.edit.result === 'Success' ) {
+                                               _this.$dialog.dialog( 'close' ); // edit complete, close dialog box
+                                       } else {
+                                               _this.displayError( 'mwe-upwiz-feedback-error1' ); // unknown API result
+                                       }
+                               } else {
+                                       displayError( 'mwe-upwiz-feedback-error2' ); // edit failed
+                               }
+                       };
+
+                       var err = function( code, info ) {
+                               displayError( 'mwe-upwiz-feedback-error3' ); // ajax request failed
+                       };
+               
+                       this.api.newSection( this.feedbackTitle, subject, message, ok, err );
+
+               }, // close submit button function
+
+
+               launch: function( contents ) {
+                       this.displayForm( contents );
+                       this.$dialog.dialog( 'open' );
+                       this.subjectInput.focus();
+               }       
+
+       };
+
+
+} )( window.mediaWiki, jQuery );