Esanders has uploaded a new change for review.

  https://gerrit.wikimedia.org/r/322359

Change subject: build: Replace jshint/jscs with eslint
......................................................................

build: Replace jshint/jscs with eslint

Change-Id: I0dd25614768dcc3258aedb2e6e2721af159834c4
---
A .eslintrc.json
D .jscsrc
D .jshintignore
D .jshintrc
M Gruntfile.js
M jsduck.external.js
M modules/contributions/base.js
M modules/editor/editors/ext.flow.editors.none.js
M modules/editor/editors/visualeditor/ext.flow.editors.visualeditor.js
M modules/editor/editors/visualeditor/mw.flow.ve.Target.js
M modules/editor/editors/visualeditor/mw.flow.ve.UserCache.js
M 
modules/editor/editors/visualeditor/ui/actions/mw.flow.ve.ui.SwitchEditorAction.js
M 
modules/editor/editors/visualeditor/ui/contextitem/mw.flow.ve.ui.MentionContextItem.js
M 
modules/editor/editors/visualeditor/ui/inspectors/mw.flow.ve.ui.MentionInspector.js
M 
modules/editor/editors/visualeditor/ui/tools/mw.flow.ve.ui.MentionInspectorTool.js
M 
modules/editor/editors/visualeditor/ui/widgets/mw.flow.ve.ui.MentionTargetInputWidget.js
M modules/editor/ext.flow.editor.js
M modules/engine/components/board/base/flow-board-api-events.js
M modules/engine/components/board/base/flow-board-interactive-events.js
M modules/engine/components/board/base/flow-board-load-events.js
M modules/engine/components/board/base/flow-boardandhistory-base.js
M modules/engine/components/board/features/flow-board-loadmore.js
M modules/engine/components/board/features/flow-board-navigation.js
M modules/engine/components/board/features/flow-board-side-rail.js
M modules/engine/components/board/features/flow-board-switcheditor.js
M modules/engine/components/board/features/flow-board-visualeditor.js
M modules/engine/components/board/flow-board.js
M modules/engine/components/board/flow-boardhistory.js
M modules/engine/components/common/flow-component-events.js
M modules/engine/components/common/flow-component-menus.js
M modules/engine/components/flow-component.js
M modules/engine/components/flow-registry.js
M modules/engine/components/flow-undo-page.js
M modules/engine/misc/flow-api.js
M modules/engine/misc/flow-eventlog.js
M modules/engine/misc/flow-handlebars.js
M modules/engine/misc/jquery.conditionalScroll.js
M modules/engine/misc/mw-ui.enhance.js
M modules/engine/misc/mw-ui.modal.js
M modules/flow/dm/api/mw.flow.dm.APIHandler.js
M modules/flow/dm/mixins/mw.flow.dm.List.js
M modules/flow/dm/mw.flow.dm.Board.js
M modules/flow/dm/mw.flow.dm.Captcha.js
M modules/flow/dm/mw.flow.dm.Categories.js
M modules/flow/dm/mw.flow.dm.CategoryItem.js
M modules/flow/dm/mw.flow.dm.Content.js
M modules/flow/dm/mw.flow.dm.Post.js
M modules/flow/dm/mw.flow.dm.RevisionedContent.js
M modules/flow/dm/mw.flow.dm.Topic.js
M modules/flow/ui/mw.flow.ui.CancelConfirmDialog.js
M modules/flow/ui/mw.flow.ui.js
M modules/flow/ui/widgets/editor/editors/mw.flow.ui.AbstractEditorWidget.js
M modules/flow/ui/widgets/editor/editors/mw.flow.ui.VisualEditorWidget.js
M modules/flow/ui/widgets/editor/mw.flow.ui.CanNotEditWidget.js
M modules/flow/ui/widgets/editor/mw.flow.ui.EditorSwitcherWidget.js
M modules/flow/ui/widgets/editor/mw.flow.ui.EditorWidget.js
M modules/flow/ui/widgets/mw.flow.ui.BoardDescriptionWidget.js
M modules/flow/ui/widgets/mw.flow.ui.CaptchaWidget.js
M modules/flow/ui/widgets/mw.flow.ui.EditPostWidget.js
M modules/flow/ui/widgets/mw.flow.ui.EditTopicSummaryWidget.js
M modules/flow/ui/widgets/mw.flow.ui.NavigationWidget.js
M modules/flow/ui/widgets/mw.flow.ui.ReplyWidget.js
M modules/flow/ui/widgets/mw.flow.ui.SidebarExpandWidget.js
M modules/flow/ui/widgets/mw.flow.ui.TopicMenuSelectWidget.js
M modules/mw.flow.Initializer.js
M package.json
M tests/qunit/engine/misc/test_flow-handlebars.js
M tests/qunit/flow/dm/test_mw.flow.dm.Content.js
M tests/qunit/flow/dm/test_mw.flow.dm.Topic.js
69 files changed, 573 insertions(+), 535 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/Flow 
refs/changes/59/322359/1

diff --git a/.eslintrc.json b/.eslintrc.json
new file mode 100644
index 0000000..e25b9c3
--- /dev/null
+++ b/.eslintrc.json
@@ -0,0 +1,22 @@
+{
+       "extends": "wikimedia",
+       "env": {
+               "browser": true,
+               "jquery": true,
+               "qunit": true
+       },
+       "globals": {
+               "mw": true,
+               "ve": false,
+               "mediaWiki": false,
+               "OO": false,
+               "Handlebars": false,
+               "moment": false
+       },
+       "rules": {
+               "dot-notation": [ "error", { "allowKeywords": true } ],
+               "no-underscore-dangle": 0,
+               "camelcase": 0,
+               "no-use-before-define": 0
+       }
+}
diff --git a/.jscsrc b/.jscsrc
deleted file mode 100644
index 75a7c62..0000000
--- a/.jscsrc
+++ /dev/null
@@ -1,7 +0,0 @@
-{
-       "preset": "wikimedia",
-       "requireVarDeclFirst": null,
-       "jsDoc": null,
-       "disallowDanglingUnderscores": null,
-       "requireCamelCaseOrUpperCaseIdentifiers": null
-}
diff --git a/.jshintignore b/.jshintignore
deleted file mode 100644
index 0feb364..0000000
--- a/.jshintignore
+++ /dev/null
@@ -1,3 +0,0 @@
-modules/jquery.scroll.js
-node_modules
-vendor
diff --git a/.jshintrc b/.jshintrc
deleted file mode 100644
index 90d9c51..0000000
--- a/.jshintrc
+++ /dev/null
@@ -1,33 +0,0 @@
-{
-       "globals": {
-               "console":     true,
-               "jQuery":      true,
-               "JsDiff":      true,
-               "Hogan":       true,
-               "QUnit":       true,
-               "mw":          true,
-               "mediaWiki":   true,
-               "strictEqual": true,
-               "deepEqual":   true,
-               "ok":          true,
-               "sinon":       true,
-               "ve":          true,
-               "Handlebars":  true,
-               "initStorer":  true,
-               "OO":          true,
-               "moment":      true,
-               "VisualEditorSupportCheck": true
-       },
-       "browser":  true,  // document, navigator, etc.
-       "curly":    true,  // requres curly braces around loops and conditionals
-       "devel":    true,  // console, alert, etc.
-       "eqeqeq":   true,  // prohibits == and !=
-       "es3":      false, // needed to catch foo.new object keys, but disabled 
because of "static" keyword
-       "forin":    false, // make for-in loops require hasOwnProperty check
-       "onevar":   true,  // only one var declaration per function
-       "supernew": true,  // suppress warnings about "weird" object 
constructions
-       "trailing": true,  // disallow trailing whitespace
-       "undef" :   true,   // prohibits the use of undefined variables
-       "unused":   "vars"  // complain about unused variables but not arguments
-       // "white":    true  // enforce Crockford rules
-}
diff --git a/Gruntfile.js b/Gruntfile.js
index 2bde11d..40bb70a 100644
--- a/Gruntfile.js
+++ b/Gruntfile.js
@@ -4,15 +4,14 @@
  * @package Flow
  */
 
-/*jshint node:true */
+/* eslint-env node: */
 module.exports = function ( grunt ) {
        grunt.loadNpmTasks( 'grunt-banana-checker' );
-       grunt.loadNpmTasks( 'grunt-contrib-jshint' );
        grunt.loadNpmTasks( 'grunt-contrib-watch' );
-       grunt.loadNpmTasks( 'grunt-jscs' );
+       grunt.loadNpmTasks( 'grunt-eslint' );
        grunt.loadNpmTasks( 'grunt-jsonlint' );
-       grunt.loadNpmTasks( 'grunt-tyops' );
        grunt.loadNpmTasks( 'grunt-stylelint' );
+       grunt.loadNpmTasks( 'grunt-tyops' );
 
        grunt.initConfig( {
                tyops: {
@@ -25,27 +24,11 @@
                                '!build/typos.json'
                        ]
                },
-               jshint: {
-                       options: {
-                               jshintrc: true
-                       },
+               eslint: {
                        all: [
-                               '*.js',
-                               'modules/**/*.js',
-                               'tests/qunit/**/*.js'
+                               '**/*.js',
+                               '!{node_modules,vendor,docs}/**/*.js'
                        ]
-               },
-               jscs: {
-                       fix: {
-                               options: {
-                                       config: true,
-                                       fix: true
-                               },
-                               src: '<%= jshint.all %>'
-                       },
-                       main: {
-                               src: '<%= jshint.all %>'
-                       }
                },
                stylelint: {
                        options: {
@@ -61,9 +44,9 @@
                },
                watch: {
                        files: [
-                               '.{csslintrc,jscsrc,jshintignore,jshintrc}',
-                               '<%= jshint.all %>',
-                               '<%= csslint.all %>'
+                               '.{stylelintlintrc,eslintrc.json}',
+                               '<%= eslint.all %>',
+                               '<%= stylelintlint.all %>'
                        ],
                        tasks: 'test'
                },
@@ -75,8 +58,7 @@
                }
        } );
 
-       grunt.registerTask( 'lint', [ 'tyops', 'jshint', 'jscs:main', 
'stylelint', 'jsonlint', 'banana' ] );
-       grunt.registerTask( 'fix', 'jscs:fix' );
+       grunt.registerTask( 'lint', [ 'tyops', 'eslint', 'stylelint', 
'jsonlint', 'banana' ] );
        grunt.registerTask( 'test', 'lint' );
        grunt.registerTask( 'default', 'test' );
 };
diff --git a/jsduck.external.js b/jsduck.external.js
index 9684cc1..86428bb 100644
--- a/jsduck.external.js
+++ b/jsduck.external.js
@@ -56,4 +56,3 @@
  * Source: <http://handlebarsjs.com/reference.html#base-SafeString>
  * @class Handlebars.SafeString
  */
-
diff --git a/modules/contributions/base.js b/modules/contributions/base.js
index 3f6ad96..646464d 100644
--- a/modules/contributions/base.js
+++ b/modules/contributions/base.js
@@ -28,4 +28,4 @@
        $( document ).ready( function () {
                $( '#bodyContent' ).one( 'click', '.flow-click-interactive', 
clickedFlowLink );
        } );
-} )( jQuery, mediaWiki );
+}( jQuery, mediaWiki ) );
diff --git a/modules/editor/editors/ext.flow.editors.none.js 
b/modules/editor/editors/ext.flow.editors.none.js
index e42a650..d44c536 100644
--- a/modules/editor/editors/ext.flow.editors.none.js
+++ b/modules/editor/editors/ext.flow.editors.none.js
@@ -11,10 +11,10 @@
         * @param {string} [content='']
         */
        mw.flow.editors.none = function ( $node, content ) {
+               var $editor = $node.closest( '.flow-editor' );
+
                // Parent constructor
                mw.flow.editors.none.parent.call( this );
-
-               var $editor = $node.closest( '.flow-editor' );
 
                // node the editor is associated with.
                this.$node = $node;
diff --git 
a/modules/editor/editors/visualeditor/ext.flow.editors.visualeditor.js 
b/modules/editor/editors/visualeditor/ext.flow.editors.visualeditor.js
index 60b1655..0863fdd 100644
--- a/modules/editor/editors/visualeditor/ext.flow.editors.visualeditor.js
+++ b/modules/editor/editors/visualeditor/ext.flow.editors.visualeditor.js
@@ -162,6 +162,8 @@
        };
 
        mw.flow.editors.visualeditor.prototype.moveCursorToEnd = function () {
+               var data, cursorPos;
+
                if ( !this.target ) {
                        this.initCallbacks.push( function () {
                                this.moveCursorToEnd();
@@ -169,8 +171,8 @@
                        return;
                }
 
-               var data = this.target.surface.getModel().getDocument().data,
-                       cursorPos = data.getNearestContentOffset( 
data.getLength(), -1 );
+               data = this.target.surface.getModel().getDocument().data;
+               cursorPos = data.getNearestContentOffset( data.getLength(), -1 
);
 
                this.target.surface.getModel().setSelection( new ve.Range( 
cursorPos ) );
        };
@@ -194,10 +196,9 @@
        // Static methods
 
        mw.flow.editors.visualeditor.static.isSupported = function () {
-               /* jshint newcap: false */
-
                var isMobileTarget = ( mw.config.get( 'skin' ) === 'minerva' );
 
+               /* global VisualEditorSupportCheck */
                return !!(
                        !isMobileTarget &&
                        mw.loader.getState( 'ext.visualEditor.core' ) &&
diff --git a/modules/editor/editors/visualeditor/mw.flow.ve.Target.js 
b/modules/editor/editors/visualeditor/mw.flow.ve.Target.js
index a587e81..1f8d795 100644
--- a/modules/editor/editors/visualeditor/mw.flow.ve.Target.js
+++ b/modules/editor/editors/visualeditor/mw.flow.ve.Target.js
@@ -72,7 +72,7 @@
        // These tools aren't available so don't bother generating them
        mw.flow.ve.Target.prototype.generateCitationFeatures = function () {};
 
-       mw.flow.ve.Target.prototype.attachToolbar = function ( surface ) {
+       mw.flow.ve.Target.prototype.attachToolbar = function () {
                this.$element.after( this.getToolbar().$element );
        };
 
diff --git a/modules/editor/editors/visualeditor/mw.flow.ve.UserCache.js 
b/modules/editor/editors/visualeditor/mw.flow.ve.UserCache.js
index 2aa104c..36e1716 100644
--- a/modules/editor/editors/visualeditor/mw.flow.ve.UserCache.js
+++ b/modules/editor/editors/visualeditor/mw.flow.ve.UserCache.js
@@ -46,12 +46,13 @@
                );
                return xhr
                        .then( function ( data ) {
+                               // The parent class wants data like { query: { 
pages: { userid: { data } } } }
+                               var i, len, user, newData = {};
+
                                if ( !data.query || !data.query.users ) {
                                        return data;
                                }
 
-                               // The parent class wants data like { query: { 
pages: { userid: { data } } } }
-                               var i, len, user, newData = {};
                                for ( i = 0, len = data.query.users.length; i < 
len; i++ ) {
                                        user = data.query.users[ i ];
                                        // Parent class needs .title
@@ -78,10 +79,10 @@
         * @param {string|string[]} usernames One or more user names
         */
        mw.flow.ve.UserCache.prototype.setAsExisting = function ( usernames ) {
+               var i, len, cacheData = {};
                if ( typeof usernames === 'string' ) {
                        usernames = [ usernames ];
                }
-               var i, len, cacheData = {};
                for ( i = 0, len = usernames.length; i < len; i++ ) {
                        cacheData[ usernames[ i ] ] = { missing: false, 
invalid: false };
                }
diff --git 
a/modules/editor/editors/visualeditor/ui/actions/mw.flow.ve.ui.SwitchEditorAction.js
 
b/modules/editor/editors/visualeditor/ui/actions/mw.flow.ve.ui.SwitchEditorAction.js
index 74d2078..b611ae5 100644
--- 
a/modules/editor/editors/visualeditor/ui/actions/mw.flow.ve.ui.SwitchEditorAction.js
+++ 
b/modules/editor/editors/visualeditor/ui/actions/mw.flow.ve.ui.SwitchEditorAction.js
@@ -1,60 +1,60 @@
 ( function ( mw, OO, ve ) {
 
-/**
- * Action to switch from VisualEditor to the Wikitext editing interface
- * within Flow.
- *
- * @class
- * @extends ve.ui.Action
- *
- * @constructor
- * @param {ve.ui.Surface} surface Surface to act on
- */
-mw.flow.ve.ui.SwitchEditorAction = function MwFlowVeUiSwitchEditorAction( 
surface ) {
-       // Parent constructor
-       ve.ui.Action.call( this, surface );
-};
+       /**
+        * Action to switch from VisualEditor to the Wikitext editing interface
+        * within Flow.
+        *
+        * @class
+        * @extends ve.ui.Action
+        *
+        * @constructor
+        * @param {ve.ui.Surface} surface Surface to act on
+        */
+       mw.flow.ve.ui.SwitchEditorAction = function 
MwFlowVeUiSwitchEditorAction( surface ) {
+               // Parent constructor
+               ve.ui.Action.call( this, surface );
+       };
 
-/* Inheritance */
+       /* Inheritance */
 
-OO.inheritClass( mw.flow.ve.ui.SwitchEditorAction, ve.ui.Action );
+       OO.inheritClass( mw.flow.ve.ui.SwitchEditorAction, ve.ui.Action );
 
-/* Static Properties */
+       /* Static Properties */
 
-/**
- * Name of this action
- *
- * @static
- * @property
- */
-mw.flow.ve.ui.SwitchEditorAction.static.name = 'flowSwitchEditor';
+       /**
+        * Name of this action
+        *
+        * @static
+        * @property
+        */
+       mw.flow.ve.ui.SwitchEditorAction.static.name = 'flowSwitchEditor';
 
-/**
- * List of allowed methods for the action.
- *
- * @static
- * @property
- */
-mw.flow.ve.ui.SwitchEditorAction.static.methods = [ 'switch' ];
+       /**
+        * List of allowed methods for the action.
+        *
+        * @static
+        * @property
+        */
+       mw.flow.ve.ui.SwitchEditorAction.static.methods = [ 'switch' ];
 
-/* Methods */
+       /* Methods */
 
-/**
- * Switch to wikitext editing.
- *
- * @method
- */
-mw.flow.ve.ui.SwitchEditorAction.prototype.switch = function () {
-       var $editor = this.surface.$element.closest( '.flow-editor' );
-       if ( $editor.length ) {
-               // Old editor
-               mw.flow.editor.switchEditor( $editor.find( 'textarea' ), 'none' 
);
-       } else {
-               // New editor
-               this.surface.emit( 'switchEditor' );
-       }
-};
+       /**
+        * Switch to wikitext editing.
+        *
+        * @method
+        */
+       mw.flow.ve.ui.SwitchEditorAction.prototype.switch = function () {
+               var $editor = this.surface.$element.closest( '.flow-editor' );
+               if ( $editor.length ) {
+                       // Old editor
+                       mw.flow.editor.switchEditor( $editor.find( 'textarea' 
), 'none' );
+               } else {
+                       // New editor
+                       this.surface.emit( 'switchEditor' );
+               }
+       };
 
-ve.ui.actionFactory.register( mw.flow.ve.ui.SwitchEditorAction );
+       ve.ui.actionFactory.register( mw.flow.ve.ui.SwitchEditorAction );
 
 }( mediaWiki, OO, ve ) );
diff --git 
a/modules/editor/editors/visualeditor/ui/contextitem/mw.flow.ve.ui.MentionContextItem.js
 
b/modules/editor/editors/visualeditor/ui/contextitem/mw.flow.ve.ui.MentionContextItem.js
index 906a67e..5e73bdf 100644
--- 
a/modules/editor/editors/visualeditor/ui/contextitem/mw.flow.ve.ui.MentionContextItem.js
+++ 
b/modules/editor/editors/visualeditor/ui/contextitem/mw.flow.ve.ui.MentionContextItem.js
@@ -44,7 +44,7 @@
        /**
         * Returns a short description emphasizing the relevant data (currently 
just the user name)
         *
-        * @return string User name
+        * @return {string} User name
         */
        mw.flow.ve.ui.MentionContextItem.prototype.getDescription = function () 
{
                var key = 
mw.flow.ve.ui.MentionInspector.static.templateParameterKey;
diff --git 
a/modules/editor/editors/visualeditor/ui/inspectors/mw.flow.ve.ui.MentionInspector.js
 
b/modules/editor/editors/visualeditor/ui/inspectors/mw.flow.ve.ui.MentionInspector.js
index 5af5d50..1f87abb 100644
--- 
a/modules/editor/editors/visualeditor/ui/inspectors/mw.flow.ve.ui.MentionInspector.js
+++ 
b/modules/editor/editors/visualeditor/ui/inspectors/mw.flow.ve.ui.MentionInspector.js
@@ -276,6 +276,7 @@
         *
         * @param {Object} [data] Inspector initial data
         * @param {boolean} [data.selectAt] Select the '@' symbol to the left 
of the fragment
+        * @return {OO.ui.Process}
         */
        mw.flow.ve.ui.MentionInspector.prototype.getSetupProcess = function ( 
data ) {
                return 
mw.flow.ve.ui.MentionInspector.parent.prototype.getSetupProcess.call( this, 
data )
diff --git 
a/modules/editor/editors/visualeditor/ui/tools/mw.flow.ve.ui.MentionInspectorTool.js
 
b/modules/editor/editors/visualeditor/ui/tools/mw.flow.ve.ui.MentionInspectorTool.js
index 45d9d27..d746472 100644
--- 
a/modules/editor/editors/visualeditor/ui/tools/mw.flow.ve.ui.MentionInspectorTool.js
+++ 
b/modules/editor/editors/visualeditor/ui/tools/mw.flow.ve.ui.MentionInspectorTool.js
@@ -29,7 +29,8 @@
        /**
         * Checks whether the model represents a user mention
         *
-        * @return boolean
+        * @param {ve.dm.Model} model
+        * @return {boolean}
         */
        mw.flow.ve.ui.MentionInspectorTool.static.isCompatibleWith = function ( 
model ) {
                return model instanceof ve.dm.MWTransclusionNode &&
diff --git 
a/modules/editor/editors/visualeditor/ui/widgets/mw.flow.ve.ui.MentionTargetInputWidget.js
 
b/modules/editor/editors/visualeditor/ui/widgets/mw.flow.ve.ui.MentionTargetInputWidget.js
index bf622fa..55b9c6c 100644
--- 
a/modules/editor/editors/visualeditor/ui/widgets/mw.flow.ve.ui.MentionTargetInputWidget.js
+++ 
b/modules/editor/editors/visualeditor/ui/widgets/mw.flow.ve.ui.MentionTargetInputWidget.js
@@ -1,4 +1,4 @@
-( function ( $, mw, OO, ve ) {
+( function ( $, mw, OO ) {
        'use strict';
 
        /**
@@ -146,4 +146,4 @@
                        this.username = item.getData();
                }
        };
-}( jQuery, mediaWiki, OO, ve ) );
+}( jQuery, mediaWiki, OO ) );
diff --git a/modules/editor/ext.flow.editor.js 
b/modules/editor/ext.flow.editor.js
index ab2e7a3..451ac16 100644
--- a/modules/editor/ext.flow.editor.js
+++ b/modules/editor/ext.flow.editor.js
@@ -66,14 +66,13 @@
                 * @return {jQuery.Promise} Will resolve once editor instance 
is loaded
                 */
                load: function ( $node, content ) {
-                       /**
-                        * When calling load(), loadEditor() may not yet have 
completed loading the
-                        * dependencies. To make sure it doesn't break, this 
will in interval,
-                        * check for it and only start loading once 
initialization is complete.
-                        *
-                        * @private
-                        */
-                       var tryLoad = function ( $node, content ) {
+                       var interval,
+                               deferred = $.Deferred();
+
+                       // When calling load(), loadEditor() may not yet have 
completed loading the
+                       // dependencies. To make sure it doesn't break, this 
will in interval,
+                       // check for it and only start loading once 
initialization is complete.
+                       function tryLoad( $node, content ) {
                                if ( mw.flow.editor.editor === null ) {
                                        return;
                                } else {
@@ -86,8 +85,8 @@
                                }
 
                                deferred.resolve();
-                       },
-                       deferred = $.Deferred(),
+                       }
+
                        interval = setInterval( $.proxy( tryLoad, this, $node, 
content ), 10 );
 
                        return deferred.promise();
@@ -155,6 +154,7 @@
                        $node.data( 'flow-editor', 
mw.flow.editor.editors.length )
                                .closest( '.flow-editor' ).addClass( 
'flow-editor-' + mw.flow.editor.editor.static.name );
 
+                       // eslint-disable-next-line new-cap
                        mw.flow.editor.editors.push( new mw.flow.editor.editor( 
$node, content ) );
                        return mw.flow.editor.getEditor( $node );
                },
@@ -224,13 +224,14 @@
                        return mw.loader.using( 'ext.flow.editors.' + 
desiredEditor )
 
                                .then( function () {
+                                       var content, oldFormat, newFormat;
+
                                        if ( !mw.flow.editors[ desiredEditor 
].static.isSupported() ) {
                                                return $.Deferred().reject( 
'editor-not-supported' );
                                        }
 
-                                       var content = editor.getRawContent(),
-                                               oldFormat = 
editor.constructor.static.format,
-                                               newFormat;
+                                       content = editor.getRawContent();
+                                       oldFormat = 
editor.constructor.static.format;
 
                                        mw.flow.editor.editor = 
mw.flow.editors[ desiredEditor ];
                                        newFormat = 
mw.flow.editor.editor.static.format;
diff --git a/modules/engine/components/board/base/flow-board-api-events.js 
b/modules/engine/components/board/base/flow-board-api-events.js
index cc6c45a..c00d06c 100644
--- a/modules/engine/components/board/base/flow-board-api-events.js
+++ b/modules/engine/components/board/base/flow-board-api-events.js
@@ -10,7 +10,7 @@
         * @constructor
         * @param {jQuery} $container
         */
-       function FlowBoardComponentApiEventsMixin( $container ) {
+       function FlowBoardComponentApiEventsMixin() {
                // Bind event callbacks
                this.bindNodeHandlers( 
FlowBoardComponentApiEventsMixin.UI.events );
        }
@@ -136,7 +136,7 @@
         * @param {jqXHR} jqxhr
         * @return {jQuery.Promise}
         */
-       FlowBoardComponentApiEventsMixin.UI.events.apiHandlers.board = function 
( info, data, jqxhr ) {
+       FlowBoardComponentApiEventsMixin.UI.events.apiHandlers.board = function 
( info, data ) {
                var $rendered,
                        flowBoard = info.component,
                        dfd = $.Deferred();
@@ -172,7 +172,7 @@
         * @param {jqXHR} jqxhr
         * @return {jQuery.Promise}
         */
-       FlowBoardComponentApiEventsMixin.UI.events.apiHandlers.submitTopicTitle 
= function ( info, data, jqxhr ) {
+       FlowBoardComponentApiEventsMixin.UI.events.apiHandlers.submitTopicTitle 
= function ( info, data ) {
                if ( info.status !== 'done' ) {
                        // Error will be displayed by default & edit conflict 
handled, nothing else to wrap up
                        return $.Deferred().resolve().promise();
@@ -193,7 +193,7 @@
         * @param {jqXHR} jqxhr
         * @return {jQuery.Promise}
         */
-       FlowBoardComponentApiEventsMixin.UI.events.apiHandlers.watchItem = 
function ( info, data, jqxhr ) {
+       FlowBoardComponentApiEventsMixin.UI.events.apiHandlers.watchItem = 
function ( info, data ) {
                var watchUrl, unwatchUrl,
                        watchType, watchLinkTemplate, $newLink,
                        $target = $( this ),
@@ -321,17 +321,19 @@
         * @return {jQuery.Promise} return.return
         */
        function _genModerateHandler( action, successCallback ) {
-               return function ( info, data, jqxhr ) {
+               return function ( info, data ) {
+                       var $form, revisionId, $target, flowBoard,
+                               $this = $( this );
+
                        if ( info.status !== 'done' ) {
                                // Error will be displayed by default, nothing 
else to wrap up
                                return $.Deferred().resolve().promise();
                        }
 
-                       var $this = $( this ),
-                               $form = $this.closest( 'form' ),
-                               revisionId = data.flow[ action 
].committed.topic[ 'post-revision-id' ],
-                               $target = $form.data( 'flow-dialog-owner' ) || 
$form,
-                               flowBoard = mw.flow.getPrototypeMethod( 
'board', 'getInstanceByElement' )( $this );
+                       $form = $this.closest( 'form' );
+                       revisionId = data.flow[ action ].committed.topic[ 
'post-revision-id' ];
+                       $target = $form.data( 'flow-dialog-owner' ) || $form;
+                       flowBoard = mw.flow.getPrototypeMethod( 'board', 
'getInstanceByElement' )( $this );
 
                        // @todo: add 3rd argument (target selector); there's 
no need to refresh entire topic if only post was moderated
                        return _flowBoardComponentRefreshTopic( $target, 
data.flow[ action ].workflow )
diff --git 
a/modules/engine/components/board/base/flow-board-interactive-events.js 
b/modules/engine/components/board/base/flow-board-interactive-events.js
index 76ec59e..9bba197 100644
--- a/modules/engine/components/board/base/flow-board-interactive-events.js
+++ b/modules/engine/components/board/base/flow-board-interactive-events.js
@@ -9,7 +9,7 @@
         * @extends FlowComponent
         * @constructor
         */
-       function FlowBoardComponentInteractiveEventsMixin( $container ) {
+       function FlowBoardComponentInteractiveEventsMixin() {
                this.bindNodeHandlers( 
FlowBoardComponentInteractiveEventsMixin.UI.events );
        }
        OO.initClass( FlowBoardComponentInteractiveEventsMixin );
@@ -28,6 +28,7 @@
         * Toggles collapse state
         *
         * @param {Event} event
+        * @return {jQuery.Promise}
         */
        
FlowBoardComponentInteractiveEventsMixin.UI.events.interactiveHandlers.collapserCollapsibleToggle
 = function ( event ) {
                var $target = $( this ).closest( '.flow-element-collapsible' ),
diff --git a/modules/engine/components/board/base/flow-board-load-events.js 
b/modules/engine/components/board/base/flow-board-load-events.js
index 0663413..a5092d7 100644
--- a/modules/engine/components/board/base/flow-board-load-events.js
+++ b/modules/engine/components/board/base/flow-board-load-events.js
@@ -9,7 +9,7 @@
         * @extends FlowComponent
         * @constructor
         */
-       function FlowBoardComponentLoadEventsMixin( $container ) {
+       function FlowBoardComponentLoadEventsMixin() {
                this.bindNodeHandlers( 
FlowBoardComponentLoadEventsMixin.UI.events );
        }
        OO.initClass( FlowBoardComponentLoadEventsMixin );
diff --git a/modules/engine/components/board/base/flow-boardandhistory-base.js 
b/modules/engine/components/board/base/flow-boardandhistory-base.js
index d906238..3a48051 100644
--- a/modules/engine/components/board/base/flow-boardandhistory-base.js
+++ b/modules/engine/components/board/base/flow-boardandhistory-base.js
@@ -4,12 +4,14 @@
  */
 
 ( function ( $, mw ) {
+       var inTopicNamespace;
+
        /**
         *
         * @param {jQuery} $container
         * @constructor
         */
-       function FlowBoardAndHistoryComponentBase( $container ) {
+       function FlowBoardAndHistoryComponentBase() {
                this.bindNodeHandlers( 
FlowBoardAndHistoryComponentBase.UI.events );
        }
        OO.initClass( FlowBoardAndHistoryComponentBase );
@@ -156,7 +158,7 @@
 
                // Render the modal itself with mw-ui-modal
                modal = mw.Modal( {
-                       open:  $( 
mw.flow.TemplateEngine.processTemplateGetFragment( template, params ) 
).children(),
+                       open: $( 
mw.flow.TemplateEngine.processTemplateGetFragment( template, params ) 
).children(),
                        disableCloseOnOutsideClick: true
                } );
 
@@ -262,5 +264,5 @@
        }
        FlowBoardAndHistoryComponentBase.static.inTopicNamespace = 
flowBoardInTopicNamespace;
 
-       var inTopicNamespace = mw.config.get( 'wgNamespaceNumber' ) === 
mw.config.get( 'wgNamespaceIds' ).topic;
+       inTopicNamespace = mw.config.get( 'wgNamespaceNumber' ) === 
mw.config.get( 'wgNamespaceIds' ).topic;
 }( jQuery, mediaWiki ) );
diff --git a/modules/engine/components/board/features/flow-board-loadmore.js 
b/modules/engine/components/board/features/flow-board-loadmore.js
index 9ef1acd..405ae4a 100644
--- a/modules/engine/components/board/features/flow-board-loadmore.js
+++ b/modules/engine/components/board/features/flow-board-loadmore.js
@@ -9,7 +9,7 @@
         * @this FlowBoardComponent
         * @constructor
         */
-       function FlowBoardComponentLoadMoreFeatureMixin( $container ) {
+       function FlowBoardComponentLoadMoreFeatureMixin() {
                /** Stores a reference to each topic element currently on the 
page */
                this.renderedTopics = {};
                /** Stores a list of all topics titles by ID */
@@ -86,7 +86,8 @@
                // 2a. Topic is not rendered; do we know about this topic ID?
                if ( flowBoard.topicTitlesById[ topicId ] === undefined ) {
                        // We don't. Abort!
-                       return flowBoard.debug( 'Unknown topicId', arguments );
+                       flowBoard.debug( 'Unknown topicId', arguments );
+                       return;
                }
 
                // 2b. Load that topic and jump to it
@@ -132,7 +133,7 @@
                                _scrollWithoutInfinite();
                        } )
                        // On fail, render an error
-                       .fail( function ( code, data ) {
+                       .fail( function ( code ) {
                                flowBoard.debug( true, 'Failed to load topics: 
' + code );
                                // Failed fetching the new data to be displayed.
                                // @todo render the error at topic position and 
scroll to it
@@ -159,7 +160,7 @@
         * @param {Object} queryMap
         * @param {FlowBoardComponent} info.component
         */
-       function flowBoardComponentLoadMoreFeatureBoardApiPreHandler( event, 
info, queryMap ) {
+       function flowBoardComponentLoadMoreFeatureBoardApiPreHandler( event, 
info ) {
                // Backup the topic data
                info.component.renderedTopicsBackup = 
info.component.renderedTopics;
                info.component.topicTitlesByIdBackup = 
info.component.topicTitlesById;
@@ -181,9 +182,8 @@
         * @param {FlowBoardComponent} info.component
         * @param {Object} data
         * @param {jqXHR} jqxhr
-        * @return {jQuery.Promise}
         */
-       function flowBoardComponentLoadMoreFeatureBoardApiCallback( info, data, 
jqxhr ) {
+       function flowBoardComponentLoadMoreFeatureBoardApiCallback( info ) {
                if ( info.status !== 'done' ) {
                        // Failed; restore the topic data
                        info.component.renderedTopics = 
info.component.renderedTopicsBackup;
@@ -204,20 +204,25 @@
         * @param {FlowBoardComponent} info.component
         * @param {Object} data
         * @param {jqXHR} jqxhr
+        * @return {jQuery.Promise}
         */
-       function flowBoardComponentLoadMoreFeatureTopicsApiCallback( info, 
data, jqxhr ) {
+       function flowBoardComponentLoadMoreFeatureTopicsApiCallback( info, data 
) {
+               var scrollTarget,
+                       $scrollContainer,
+                       topicsData,
+                       readingTopicPosition,
+                       $this = $( this ),
+                       $target = info.$target,
+                       flowBoard = info.component;
+
                if ( info.status !== 'done' ) {
                        // Error will be displayed by default, nothing else to 
wrap up
                        return $.Deferred().resolve().promise();
                }
 
-               var $this = $( this ),
-                       $target = info.$target,
-                       flowBoard = info.component,
-                       scrollTarget = $this.data( 'flow-scroll-target' ),
-                       $scrollContainer = $.findWithParent( $this, $this.data( 
'flow-scroll-container' ) ),
-                       topicsData = data.flow[ 'view-topiclist' 
].result.topiclist,
-                       readingTopicPosition;
+               scrollTarget = $this.data( 'flow-scroll-target' );
+               $scrollContainer = $.findWithParent( $this, $this.data( 
'flow-scroll-container' ) );
+               topicsData = data.flow[ 'view-topiclist' ].result.topiclist;
 
                if ( scrollTarget === 'window' && flowBoard.readingTopicId ) {
                        // Store the current position of the topic you are 
reading
@@ -321,7 +326,7 @@
                        } );
                } else {
                        $target = $.findWithParent( $button, 
scrollTargetSelector );
-                       $target.on( 'scroll.flow-load-more', $.throttle( 50, 
function ( evt ) {
+                       $target.on( 'scroll.flow-load-more', $.throttle( 50, 
function () {
                                
_flowBoardComponentLoadMoreFeatureInfiniteScrollCheck.call( board, 
$scrollContainer, $target );
                        } ) );
                }
@@ -415,15 +420,16 @@
         * @param {jQuery} $calculationContainer Container to do scroll 
calculations on (height, scrollTop, offset, etc.)
         */
        function _flowBoardComponentLoadMoreFeatureInfiniteScrollCheck( 
$searchContainer, $calculationContainer ) {
+               var calculationContainerHeight, calculationContainerScroll, 
calculationContainerThreshold;
                if ( this.infiniteScrollDisabled ) {
                        // This happens when the topic navigation is used to 
jump to a topic
                        // We should not infinite-load anything when we are 
scrolling to a topic
                        return;
                }
 
-               var calculationContainerHeight = $calculationContainer.height(),
-                       calculationContainerScroll = 
$calculationContainer.scrollTop(),
-                       calculationContainerThreshold = ( 
$calculationContainer.offset() || { top: calculationContainerScroll } ).top;
+               calculationContainerHeight = $calculationContainer.height();
+               calculationContainerScroll = $calculationContainer.scrollTop();
+               calculationContainerThreshold = ( 
$calculationContainer.offset() || { top: calculationContainerScroll } ).top;
 
                // Find load more buttons within our search container, and they 
must be visible
                $searchContainer.find( this.$loadMoreNodes ).filter( ':visible' 
).each( function () {
@@ -460,13 +466,15 @@
         * @private
         */
        function _flowBoardComponentLoadMoreFeatureRenderTopics( flowBoard, 
topicsData, forceShowLoadMore, $insertAt, scrollTarget, scrollContainer, 
scrollTemplate ) {
+               var i, j, $topic, topicId,
+                       $allRendered = $( [] ),
+                       toInsert = [];
+
                if ( !topicsData.roots.length ) {
                        flowBoard.debug( 'No topics returned from API', 
arguments );
                        return;
                }
 
-               /** @private
-                */
                function _createRevPagination( $target ) {
                        if ( !topicsData.links.pagination.fwd && 
!topicsData.links.pagination.rev ) {
                                return;
@@ -494,8 +502,6 @@
                        );
                }
 
-               /** @private
-                */
                function _createFwdPagination( $target ) {
                        if ( forceShowLoadMore || 
topicsData.links.pagination.fwd ) {
                                // Add the load more to the end of the stack
@@ -542,10 +548,6 @@
 
                        return $newTopics;
                }
-
-               var i, j, $topic, topicId,
-                       $allRendered = $( [] ),
-                       toInsert = [];
 
                for ( i = 0; i < topicsData.roots.length; i++ ) {
                        topicId = topicsData.roots[ i ];
diff --git a/modules/engine/components/board/features/flow-board-navigation.js 
b/modules/engine/components/board/features/flow-board-navigation.js
index 0b65358..9dfb1e2 100644
--- a/modules/engine/components/board/features/flow-board-navigation.js
+++ b/modules/engine/components/board/features/flow-board-navigation.js
@@ -10,7 +10,7 @@
         * @param {jQuery} $container
         * @this FlowComponent
         */
-       function FlowBoardComponentBoardHeaderFeatureMixin( $container ) {
+       function FlowBoardComponentBoardHeaderFeatureMixin() {
                // Bind element handlers
                this.bindNodeHandlers( 
FlowBoardComponentBoardHeaderFeatureMixin.UI.events );
 
diff --git a/modules/engine/components/board/features/flow-board-side-rail.js 
b/modules/engine/components/board/features/flow-board-side-rail.js
index d80a996..2342f0a 100644
--- a/modules/engine/components/board/features/flow-board-side-rail.js
+++ b/modules/engine/components/board/features/flow-board-side-rail.js
@@ -9,7 +9,7 @@
         * @this FlowComponent
         * @constructor
         */
-       function FlowBoardComponentSideRailFeatureMixin( $container ) {
+       function FlowBoardComponentSideRailFeatureMixin() {
                // Bind element handlers
                this.bindNodeHandlers( 
FlowBoardComponentSideRailFeatureMixin.UI.events );
        }
@@ -33,7 +33,7 @@
         *
         * @param {Event} event
         */
-       function FlowBoardComponentSideRailFeatureMixinLoadCallback( event ) {
+       function FlowBoardComponentSideRailFeatureMixinLoadCallback() {
                if ( mw.user.options.get( 'flow-side-rail-state' ) === 
'collapsed' ) {
                        $( '.flow-component' ).addClass( 'expanded' );
                }
@@ -49,7 +49,7 @@
         *
         * @param {Event} event
         */
-       function FlowBoardComponentSideRailFeatureMixinToggleCallback( event ) {
+       function FlowBoardComponentSideRailFeatureMixinToggleCallback() {
                var boardIsExpanded = $( '.flow-component' ).toggleClass( 
'expanded' ).hasClass( 'expanded' ),
                        sideRailState = boardIsExpanded ? 'collapsed' : 
'expanded';
 
diff --git 
a/modules/engine/components/board/features/flow-board-switcheditor.js 
b/modules/engine/components/board/features/flow-board-switcheditor.js
index bc6b574..056d32e 100644
--- a/modules/engine/components/board/features/flow-board-switcheditor.js
+++ b/modules/engine/components/board/features/flow-board-switcheditor.js
@@ -10,7 +10,7 @@
         * @this FlowComponent
         * @constructor
         */
-       function FlowBoardComponentSwitchEditorFeatureMixin( $container ) {
+       function FlowBoardComponentSwitchEditorFeatureMixin() {
                // Bind element handlers
                this.bindNodeHandlers( 
FlowBoardComponentSwitchEditorFeatureMixin.UI.events );
        }
diff --git 
a/modules/engine/components/board/features/flow-board-visualeditor.js 
b/modules/engine/components/board/features/flow-board-visualeditor.js
index 0fcb319..163c298 100644
--- a/modules/engine/components/board/features/flow-board-visualeditor.js
+++ b/modules/engine/components/board/features/flow-board-visualeditor.js
@@ -10,7 +10,7 @@
         * @constructor
         *
         */
-       function FlowBoardComponentVisualEditorFeatureMixin( $container ) {
+       function FlowBoardComponentVisualEditorFeatureMixin() {
        }
 
        // This is not really VE-specific, but I'm not sure where best to put 
it.
@@ -18,7 +18,8 @@
        /**
         * Finds topic authors for the given node
         *
-        * @return Array List of usernames
+        * @param {jQuery} $node
+        * @return {Array} List of usernames
         */
        function flowVisualEditorGetTopicPosters( $node ) {
                var $topic = $node.closest( '.flow-topic' ),
diff --git a/modules/engine/components/board/flow-board.js 
b/modules/engine/components/board/flow-board.js
index cd87321..93d32e5 100644
--- a/modules/engine/components/board/flow-board.js
+++ b/modules/engine/components/board/flow-board.js
@@ -70,17 +70,19 @@
         * @return {boolean|jQuery}
         */
        function flowBoardComponentReinitializeContainer( $container ) {
+               var $retObj, $header, $boardNavigation, $board;
+
                if ( $container === false ) {
                        return false;
                }
 
                // Trigger this on FlowBoardAndHistoryComponentBase
                // @todo use EventEmitter to do this?
-               var $retObj = 
FlowBoardComponent.parent.prototype.reinitializeContainer.call( this, 
$container ),
+               $retObj = 
FlowBoardComponent.parent.prototype.reinitializeContainer.call( this, 
$container );
                // Find any new (or previous) elements
-                       $header = $container.find( '.flow-board-header' 
).addBack().filter( '.flow-board-header:first' ),
-                       $boardNavigation = $container.find( 
'.flow-board-navigation' ).addBack().filter( '.flow-board-navigation:first' ),
-                       $board = $container.find( '.flow-board' 
).addBack().filter( '.flow-board:first' );
+               $header = $container.find( '.flow-board-header' 
).addBack().filter( '.flow-board-header:first' );
+               $boardNavigation = $container.find( '.flow-board-navigation' 
).addBack().filter( '.flow-board-navigation:first' );
+               $board = $container.find( '.flow-board' ).addBack().filter( 
'.flow-board:first' );
 
                if ( $retObj === false ) {
                        return false;
diff --git a/modules/engine/components/board/flow-boardhistory.js 
b/modules/engine/components/board/flow-boardhistory.js
index 0e8df7c..7c1683e 100644
--- a/modules/engine/components/board/flow-boardhistory.js
+++ b/modules/engine/components/board/flow-boardhistory.js
@@ -12,7 +12,7 @@
         * @constructor
         * @param {jQuery} $container
         */
-       function FlowBoardHistoryComponent( $container ) {
+       function FlowBoardHistoryComponent() {
                this.bindNodeHandlers( FlowBoardHistoryComponent.UI.events );
        }
        OO.initClass( FlowBoardHistoryComponent );
@@ -39,13 +39,15 @@
         * @param {jqXHR} jqxhr
         * @return {jQuery.Promise}
         */
-       function flowBoardHistoryModerationCallback( info, data, jqxhr ) {
+       function flowBoardHistoryModerationCallback( info ) {
+               var flowBoardHistory;
+
                if ( info.status !== 'done' ) {
                        // Error will be displayed by default, nothing else to 
wrap up
                        return $.Deferred().resolve().promise();
                }
 
-               var flowBoardHistory = mw.flow.getPrototypeMethod( 
'boardHistory', 'getInstanceByElement' )( $( this ) );
+               flowBoardHistory = mw.flow.getPrototypeMethod( 'boardHistory', 
'getInstanceByElement' )( $( this ) );
 
                // Clear the form so we can refresh without the confirmation 
dialog
                flowBoardHistory.emitWithReturn( 'cancelForm', $( this 
).closest( 'form' ) );
@@ -65,7 +67,7 @@
         * @param {Object} data
         * @return {jQuery.Promise}
         */
-       FlowBoardHistoryComponent.UI.events.apiHandlers.lockTopic = function ( 
info, data ) {
+       FlowBoardHistoryComponent.UI.events.apiHandlers.lockTopic = function ( 
info ) {
                if ( info.status !== 'done' ) {
                        // Error will be displayed by default & edit conflict 
handled, nothing else to wrap up
                        return $.Deferred().resolve().promise();
diff --git a/modules/engine/components/common/flow-component-events.js 
b/modules/engine/components/common/flow-component-events.js
index 6b3559d..1684a74 100644
--- a/modules/engine/components/common/flow-component-events.js
+++ b/modules/engine/components/common/flow-component-events.js
@@ -13,6 +13,7 @@
         * @class
         * @extends OO.EventEmitter
         * @constructor
+        * @param {jQuery} $container Container
         */
        function FlowComponentEventsMixin( $container ) {
                var self = this;
@@ -229,7 +230,7 @@
         *
         * @param {string} code
         * @param {Object} result
-        * @return string
+        * @return {string}
         */
        function flowGetApiErrorMessage( code, result ) {
                if ( result.error && result.error.info ) {
@@ -427,6 +428,8 @@
         * @todo Perhaps use name="flow-load-handler" for performance in older 
browsers
         */
        function flowMakeContentInteractiveCallback( $container ) {
+               var component;
+
                if ( !$container.jquery ) {
                        $container = $container.$container;
                }
@@ -437,7 +440,7 @@
                }
 
                // Get the FlowComponent
-               var component = mw.flow.getPrototypeMethod( 'component', 
'getInstanceByElement' )( $container );
+               component = mw.flow.getPrototypeMethod( 'component', 
'getInstanceByElement' )( $container );
 
                // Find all load-handlers and trigger them
                $container.find( '.flow-load-interactive' ).add( 
$container.filter( '.flow-load-interactive' ) ).each( function () {
@@ -481,9 +484,7 @@
        }
        FlowComponentEventsMixin.eventHandlers.makeContentInteractive = 
flowMakeContentInteractiveCallback;
 
-       /**
-        * Triggers load handlers.
-        */
+       // Triggers load handlers
        function flowLoadHandlerCallback( handlerName, args, context ) {
                args = $.isArray( args ) ? args : ( args ? [ args ] : [] );
                context = context || this;
@@ -531,30 +532,35 @@
        /**
         * Triggers both API and interactive handlers.
         * To manually trigger a handler on an element, you can use 
extraParameters via $el.trigger.
+        *
         * @param {Event} event
         * @param {Object} [extraParameters]
         * @param {string} [extraParameters.interactiveHandler]
         * @param {string} [extraParameters.apiHandler]
         */
        function flowInteractiveHandlerCallback( event, extraParameters ) {
+               var args, $context, interactiveHandlerName, apiHandlerName;
+
                // Only trigger with enter key & no modifier keys, if keypress
                if ( event.type === 'keypress' && ( event.charCode !== 13 || 
event.metaKey || event.shiftKey || event.ctrlKey || event.altKey ) ) {
                        return;
                }
 
-               var args = Array.prototype.slice.call( arguments, 0 ),
-                       $context = $( event.currentTarget || 
event.delegateTarget || event.target ),
-                       // Have either of these been forced via trigger 
extraParameters?
-                       interactiveHandlerName = ( extraParameters || {} 
).interactiveHandler || $context.data( 'flow-interactive-handler' ),
-                       apiHandlerName = ( extraParameters || {} ).apiHandler 
|| $context.data( 'flow-api-handler' );
+               args = Array.prototype.slice.call( arguments, 0 );
+               $context = $( event.currentTarget || event.delegateTarget || 
event.target );
+               // Have either of these been forced via trigger extraParameters?
+               interactiveHandlerName = ( extraParameters || {} 
).interactiveHandler || $context.data( 'flow-interactive-handler' );
+               apiHandlerName = ( extraParameters || {} ).apiHandler || 
$context.data( 'flow-api-handler' );
 
-               return flowExecuteInteractiveHandler.call( this, args, 
$context, interactiveHandlerName, apiHandlerName );
+               flowExecuteInteractiveHandler.call( this, args, $context, 
interactiveHandlerName, apiHandlerName );
        }
        FlowComponentEventsMixin.eventHandlers.interactiveHandler = 
flowInteractiveHandlerCallback;
        FlowComponentEventsMixin.eventHandlers.apiRequest = 
flowInteractiveHandlerCallback;
 
        /**
         * Triggers both API and interactive handlers, on focus.
+        *
+        * @param {Event} event
         */
        function flowInteractiveHandlerFocusCallback( event ) {
                var args = Array.prototype.slice.call( arguments, 0 ),
@@ -562,7 +568,7 @@
                        interactiveHandlerName = $context.data( 
'flow-interactive-handler-focus' ),
                        apiHandlerName = $context.data( 
'flow-api-handler-focus' );
 
-               return flowExecuteInteractiveHandler.call( this, args, 
$context, interactiveHandlerName, apiHandlerName );
+               flowExecuteInteractiveHandler.call( this, args, $context, 
interactiveHandlerName, apiHandlerName );
        }
        FlowComponentEventsMixin.eventHandlers.interactiveHandlerFocus = 
flowInteractiveHandlerFocusCallback;
 
@@ -585,19 +591,22 @@
         *
         * Additionally:
         * * data-flow-eventlog-forward: Selectors to forward funnel data to
+        *
+        * @param {Event} event
         */
        function flowEventLogCallback( event ) {
+               var $context, data, component, $promise, eventInstance, key, 
value;
+
                // Only trigger with enter key & no modifier keys, if keypress
                if ( event.type === 'keypress' && ( event.charCode !== 13 || 
event.metaKey || event.shiftKey || event.ctrlKey || event.altKey ) ) {
                        return;
                }
 
-               var $context = $( event.currentTarget ),
-                       data = $context.data(),
-                       component = mw.flow.getPrototypeMethod( 'component', 
'getInstanceByElement' )( $context ),
-                       $promise = data.flowInteractiveHandlerPromise || 
$.Deferred().resolve().promise(),
-                       eventInstance = {},
-                       key, value;
+               $context = $( event.currentTarget );
+               data = $context.data();
+               component = mw.flow.getPrototypeMethod( 'component', 
'getInstanceByElement' )( $context );
+               $promise = data.flowInteractiveHandlerPromise || 
$.Deferred().resolve().promise();
+               eventInstance = {};
 
                // Fetch loggable data: everything prefixed flowEventlog except
                // flowEventLogForward and flowEventLogSchema
@@ -636,7 +645,7 @@
         * When the whole class has been instantiated fully (after every 
constructor has been called).
         * @param {FlowComponent} component
         */
-       function flowEventsMixinInstantiationComplete( component ) {
+       function flowEventsMixinInstantiationComplete() {
                $( window ).trigger( 'scroll.flow-window-scroll' );
        }
        FlowComponentEventsMixin.eventHandlers.instantiationComplete = 
flowEventsMixinInstantiationComplete;
@@ -814,7 +823,8 @@
                mw.tooltip.show(
                        $tooltipTarget,
                        // tooltipTarget will not always be part of a 
FlowBoardComponent
-                       $( mw.flow.TemplateEngine.processTemplateGetFragment(
+                       $(
+                               
mw.flow.TemplateEngine.processTemplateGetFragment(
                                        'flow_tooltip_subscribed.partial',
                                        {
                                                unsubscribe: false,
@@ -869,7 +879,7 @@
         *
         * @param {jQuery} $node
         * @param {string} selector
-        * @return jQuery
+        * @return {jQuery}
         */
        function _flowFindUpward( $node, selector ) {
                // first check if result can already be found inside $node
diff --git a/modules/engine/components/common/flow-component-menus.js 
b/modules/engine/components/common/flow-component-menus.js
index 45ec362..82b97a8 100644
--- a/modules/engine/components/common/flow-component-menus.js
+++ b/modules/engine/components/common/flow-component-menus.js
@@ -93,6 +93,7 @@
         * Allows you to open a flow-menu from a secondary click handler 
elsewhere.
         * Uses data-flow-menu-target="< foo .flow-menu"
         * @param {Event} event
+        * @return {jQuery.Promise}
         */
        function flowComponentMenusFeatureElementMenuToggleCallback( event ) {
                var $this = $( this ),
diff --git a/modules/engine/components/flow-component.js 
b/modules/engine/components/flow-component.js
index a0f55d8..de26948 100644
--- a/modules/engine/components/flow-component.js
+++ b/modules/engine/components/flow-component.js
@@ -55,8 +55,10 @@
         * @param {boolean} [isError=true]
         */
        mw.flow.debug = FlowComponent.prototype.debug = function ( isError ) {
+               var args;
+               /* eslint-disable no-console */
                if ( window.console ) {
-                       var args = Array.prototype.slice.call( arguments, 0 );
+                       args = Array.prototype.slice.call( arguments, 0 );
 
                        if ( typeof isError === 'boolean' ) {
                                args.shift();
@@ -68,12 +70,13 @@
 
                        if ( isError && console.error ) {
                                // If console.error is supported, send that, 
because it gives a stack trace
-                               return console.error.apply( console, args );
+                               console.error.apply( console, args );
                        }
 
                        // Otherwise, use console.log
                        console.log.apply( console, args );
                }
+               /* eslint-enable no-console */
        };
 
        /**
@@ -175,13 +178,14 @@
                        '*',
                        { flowSpawnedBy: this.$container, flowSpawnedFrom: $el 
},
                        function ( event ) {
+                               var i, $nodes;
                                // Let's forward these events in an unusual 
way, similar to how jQuery propagates events...
                                // First, only take the very first, top-level 
event, as the rest of the propagation is handled elsewhere
                                if ( event.target === this ) {
                                        // Get all the parent nodes of our 
target,
                                        // but do not include any nodes we will 
already be bubbling up to (eg. body)
-                                       var $nodes = 
$eventTarget.parents().addBack().not( $( this ).parents().addBack() ),
-                                               i = $nodes.length;
+                                       $nodes = 
$eventTarget.parents().addBack().not( $( this ).parents().addBack() );
+                                       i = $nodes.length;
 
                                        // For every node between $eventTarget 
and window that was not filtered out above...
                                        while ( i-- ) {
@@ -212,14 +216,15 @@
         * @private
         */
        function _eventForwardDispatch( event, container ) {
+               var i, ret, handleObj, matched, j,
+                       args, handlers, special,
+                       handlerQueue = [];
+
                // Make a writable jQuery.Event from the native event object
                event = jQuery.event.fix( event );
-
-               var i, ret, handleObj, matched, j,
-                       handlerQueue = [],
-                       args = Array.prototype.slice.call( arguments, 0 ),
-                       handlers = ( jQuery._data( this, 'events' ) || {} )[ 
event.type ] || [],
-                       special = jQuery.event.special[ event.type ] || {};
+               args = Array.prototype.slice.call( arguments, 0 );
+               handlers = ( jQuery._data( this, 'events' ) || {} )[ event.type 
] || [];
+               special = jQuery.event.special[ event.type ] || {};
 
                // Use the fix-ed jQuery.Event rather than the (read-only) 
native event
                args[ 0 ] = event;
diff --git a/modules/engine/components/flow-registry.js 
b/modules/engine/components/flow-registry.js
index 946b77d..80e9664 100644
--- a/modules/engine/components/flow-registry.js
+++ b/modules/engine/components/flow-registry.js
@@ -8,10 +8,10 @@
  */
 
 ( function ( $, mw ) {
+       var _componentRegistry = new OO.Registry();
+
        /** @class mw.flow */
        mw.flow = mw.flow || {}; // create mw.flow globally
-
-       var _componentRegistry = new OO.Registry();
 
        /**
         * Instantiate one or more new FlowComponents.
diff --git a/modules/engine/components/flow-undo-page.js 
b/modules/engine/components/flow-undo-page.js
index f91c6a5..c5be7e7 100644
--- a/modules/engine/components/flow-undo-page.js
+++ b/modules/engine/components/flow-undo-page.js
@@ -3,7 +3,7 @@
        $( document ).ready( function () {
 
                $( 'form[method=POST]' ).each( function ( index, form ) {
-                       $( form ).submit( function ( event ) {
+                       $( form ).submit( function () {
                                var $textarea = $( form ).find( 
'textarea.flow-editor-initialized' ),
                                        moduleName = $( form ).data( 'module' ),
                                        editorExist = mw.flow.editor.exists( 
$textarea ),
@@ -26,4 +26,4 @@
 
        } );
 
-} )( jQuery, mediaWiki );
+}( jQuery, mediaWiki ) );
diff --git a/modules/engine/misc/flow-api.js b/modules/engine/misc/flow-api.js
index 78f6ea2..1d2c8c4 100644
--- a/modules/engine/misc/flow-api.js
+++ b/modules/engine/misc/flow-api.js
@@ -1,6 +1,4 @@
 ( function ( mw, $ ) {
-       mw.flow = mw.flow || {}; // create mw.flow globally
-
        var apiTransformMap = {
                // Map of API submodule name, block name, and prefix name
                'moderate-post': [ 'topic_', 'mp' ],
@@ -19,13 +17,16 @@
                'edit-topic-summary': [ 'topicsummary_', 'ets' ]
        };
 
+       mw.flow = mw.flow || {}; // create mw.flow globally
+
        /**
         * Handles Flow API calls. Each FlowComponent has its own instance of 
FlowApi as component.Api,
         * so that it can store a workflowId and pageName permanently for 
simplicity.
+        *
+        * @constructor
+        * @param {Object} storageEngine
         * @param {string} [workflowId]
         * @param {string} [pageName]
-        * @return {FlowApi}
-        * @constructor
         */
        function FlowApi( storageEngine, workflowId, pageName ) {
                this.StorageEngine = storageEngine;
@@ -289,7 +290,7 @@
         * @param {Object} queryMap
         * @return {jQuery.Promise}
         */
-       function flowApiRequestFromNode( node, queryMap ) {
+       function flowApiRequestFromNode( node ) {
                var $node = $( node );
 
                if ( $node.is( 'a' ) ) {
@@ -312,15 +313,16 @@
         * @return {undefined|jQuery.Promise}
         */
        function flowApiAbortOldRequestFromNode( $node, queryMap, 
startNewMethod ) {
+               var str, prevApiCall, newApiCall;
+
                $node = $( $node );
 
                // transform flow_* params into (nt_*, rep_*, ...)
                queryMap = flowApiTransformMap( queryMap );
 
                // If this anchor already has a request in flight, abort it
-               var str = 'flow-api-query-temp-' + queryMap.action + '-' + 
queryMap.submodule,
-                       prevApiCall = $node.data( str ),
-                       newApiCall;
+               str = 'flow-api-query-temp-' + queryMap.action + '-' + 
queryMap.submodule;
+               prevApiCall = $node.data( str );
 
                // If a previous API call was found, let's abort it
                if ( prevApiCall ) {
diff --git a/modules/engine/misc/flow-eventlog.js 
b/modules/engine/misc/flow-eventlog.js
index 33d73a2..cd3c48f 100644
--- a/modules/engine/misc/flow-eventlog.js
+++ b/modules/engine/misc/flow-eventlog.js
@@ -1,9 +1,19 @@
 ( function ( mw, $ ) {
+       var FlowEventLogRegistry = {
+               funnels: {},
+
+               /**
+                * Generates a unique id.
+                *
+                * @return {string}
+                */
+               generateFunnelId: mw.user.generateRandomSessionId
+       };
+
        /**
+        * @constructor
         * @param {string} schemaName Canonical schema name.
         * @param {Object} [eventInstance] Shared event instance data.
-        * @return {FlowEventLog}
-        * @constructor
         */
        function FlowEventLog( schemaName, eventInstance ) {
                this.schemaName = schemaName;
@@ -21,17 +31,6 @@
                }
                this.logEvent = logEvent;
        }
-
-       var FlowEventLogRegistry = {
-               funnels: {},
-
-               /**
-                * Generates a unique id.
-                *
-                * @return {string}
-                */
-               generateFunnelId: mw.user.generateRandomSessionId
-       };
 
        // Export
        /**
diff --git a/modules/engine/misc/flow-handlebars.js 
b/modules/engine/misc/flow-handlebars.js
index c3344a6..18abe27 100644
--- a/modules/engine/misc/flow-handlebars.js
+++ b/modules/engine/misc/flow-handlebars.js
@@ -3,13 +3,13 @@
  */
 
 ( function ( mw, $, moment, Handlebars ) {
-       mw.flow = mw.flow || {}; // create mw.flow globally
-
        var _tplcache = {},
                _timestamp = {
-               list: [],
-               currentIndex: 0
-       };
+                       list: [],
+                       currentIndex: 0
+               };
+
+       mw.flow = mw.flow || {}; // create mw.flow globally
 
        /**
         * Instantiates a FlowHandlebars instance for TemplateEngine.
@@ -17,7 +17,7 @@
         * @return {FlowHandlebars}
         * @constructor
         */
-       function FlowHandlebars( FlowStorageEngine ) {
+       function FlowHandlebars() {
                return this;
        }
 
@@ -86,10 +86,10 @@
        /**
         * A method to call helper functions from outside templates. This 
removes Handlebars.SafeString wrappers.
         * @param {string} helperName
-        * @param {...*} [args]
-        * @return mixed
+        * @param {...Mixed} [args]
+        * @return {Mixed}
         */
-       FlowHandlebars.prototype.callHelper = function ( helperName, args ) {
+       FlowHandlebars.prototype.callHelper = function ( helperName ) {
                var result = this[ helperName ].apply( this, 
Array.prototype.slice.call( arguments, 1 ) );
                if ( result && result.string ) {
                        return result.string;
@@ -189,7 +189,7 @@
         * @param {Object} [options]
         * @return {string}
         */
-       FlowHandlebars.prototype.l10n = function ( str /*, args..., options */ 
) {
+       FlowHandlebars.prototype.l10n = function ( str /* , args..., options */ 
) {
                // chop off str and options leaving just args
                var args = flowNormalizeL10nParameters( 
Array.prototype.slice.call( arguments, 1, -1 ) );
 
@@ -198,9 +198,11 @@
 
        /**
         * HTML-safe version of l10n.
+        *
+        * @param {string} str
         * @return {string|Handlebars.SafeString}
         */
-       FlowHandlebars.prototype.l10nParse = function ( str /*, args..., 
options */ ) {
+       FlowHandlebars.prototype.l10nParse = function ( str /* , args..., 
options */ ) {
                var args = flowNormalizeL10nParameters( 
Array.prototype.slice.call( arguments, 1, -1 ) );
 
                return FlowHandlebars.prototype.html(
@@ -232,13 +234,14 @@
         * @return {string}
         */
        FlowHandlebars.prototype.timestamp = function ( timestamp ) {
+               var guid, formatter;
+
                if ( isNaN( timestamp ) ) {
                        mw.flow.debug( '[timestamp] Invalid arguments', 
arguments );
                        return;
                }
 
-               var guid,
-                       formatter = moment( timestamp );
+               formatter = moment( timestamp );
 
                // Generate a GUID for this element to find it later
                guid = ( Math.random() + 1 ).toString( 36 ).substring( 2 );
@@ -348,7 +351,7 @@
         * @param {Object} options
         * @return {string}
         */
-       FlowHandlebars.prototype.workflowBlock = function ( context, options ) {
+       FlowHandlebars.prototype.workflowBlock = function ( context ) {
                return FlowHandlebars.prototype.html( 
FlowHandlebars.prototype.processTemplate(
                        'flow_block_' + context.type + ( context[ 
'block-action-template' ] || '' ),
                        context
@@ -362,7 +365,7 @@
         * @param {Object} options
         * @return {string}
         */
-       FlowHandlebars.prototype.postBlock = function ( context, revision, 
options ) {
+       FlowHandlebars.prototype.postBlock = function ( context, revision ) {
                return FlowHandlebars.prototype.html( 
FlowHandlebars.prototype.processTemplate(
                        'flow_post',
                        {
@@ -436,9 +439,9 @@
 
        /**
         * Runs a callback when user is anonymous
-        * @param array $options which must contain fn and inverse key mapping 
to functions.
         *
-        * @return mixed result of callback
+        * @param {Object} options which must contain fn and inverse key 
mapping to functions.
+        * @return {Mixed} result of callback
         */
        FlowHandlebars.prototype.ifAnonymous = function ( options ) {
                if ( mw.user.isAnon() ) {
@@ -449,9 +452,9 @@
 
        /**
         * Adds returnto parameter pointing to given Title to an existing URL
-        * @param string $title
         *
-        * @return string modified url
+        * @param {string} title
+        * @return {string} modified url
         */
        FlowHandlebars.prototype.linkWithReturnTo = function ( title ) {
                return mw.util.getUrl( title, {
@@ -534,6 +537,8 @@
         * @param {string} value
         * @param {string} operator supported values: 'or'
         * @param {string} value2
+        * @param {Object} options
+        * @return {string}
         */
        FlowHandlebars.prototype.ifCond = function ( value, operator, value2, 
options ) {
                if ( operator === 'or' ) {
diff --git a/modules/engine/misc/jquery.conditionalScroll.js 
b/modules/engine/misc/jquery.conditionalScroll.js
index 3a25abd..b09f871 100644
--- a/modules/engine/misc/jquery.conditionalScroll.js
+++ b/modules/engine/misc/jquery.conditionalScroll.js
@@ -6,6 +6,7 @@
         * 2. If el ends below viewport and fits into viewport, scrolls to put 
bottom of el at bottom of viewport.
         * 3. If el ends below viewport but is taller than the viewport, 
scrolls to put top of el at top of viewport.
         * @param {string|number} [speed='fast']
+        * @return {jQuery}
         */
        $.fn.conditionalScrollIntoView = function ( speed ) {
                speed = speed !== undefined ? speed : 'fast';
diff --git a/modules/engine/misc/mw-ui.enhance.js 
b/modules/engine/misc/mw-ui.enhance.js
index c4f5c85..7f86833 100644
--- a/modules/engine/misc/mw-ui.enhance.js
+++ b/modules/engine/misc/mw-ui.enhance.js
@@ -43,14 +43,14 @@
                }
 
                function onMwUiButtonBlur( event ) {
+                       var $el, $form, $siblings, $focused;
+
                        if ( event.target.className.indexOf( 'mw-ui-button' ) 
=== -1 ) {
                                // Not a button event
                                return;
                        }
 
-                       var $el       = $( event.target ),
-                               $form, $siblings, $focused;
-
+                       $el = $( event.target );
                        $form = $el.closest( 'form, .mw-ui-button-container' );
                        if ( $form.length ) {
                                // If this button is in a form, apply this to 
all the form's buttons.
@@ -171,6 +171,7 @@
                 * @param {jQuery|HTMLElement|string} [content] A jQuery set, 
an element, or a string of
                 *  HTML.  If omitted, first tries tooltipContentCallback, then 
target.title
                 * @param {Object} [options]
+                * @return {jQuery}
                 */
                function mwUiTooltipShow( target, content, options ) {
                        var $target = $( target ),
@@ -260,8 +261,7 @@
                        $tooltip
                                // Add the content to it
                                .find( '.flow-ui-tooltip-content' )
-                                       .empty()
-                                       [ insertFn ]( content )
+                                       .empty()[ insertFn ]( content )
                                        .end()
                                // Move this off-page before rendering it, so 
that we can calculate its real dimensions
                                // @todo use .parent() loop to check for 
z-index and + that to this if needed
@@ -423,17 +423,17 @@
 
                /**
                 * Event handler for mouse entering on a .flow-ui-tooltip-target
-                * @param {Event} event
+                * @param {jQuery.Event} event
                 */
-               function onMwUiTooltipFocus( event ) {
+               function onMwUiTooltipFocus() {
                        mw.tooltip.show( this );
                }
 
                /**
                 * Event handler for mouse leaving a .flow-ui-tooltip-target
-                * @param {Event} event
+                * @param {jQuery.Event} event
                 */
-               function onMwUiTooltipBlur( event ) {
+               function onMwUiTooltipBlur() {
                        mw.tooltip.hide( this );
                }
 
diff --git a/modules/engine/misc/mw-ui.modal.js 
b/modules/engine/misc/mw-ui.modal.js
index 7d14f5d..fe561d6 100644
--- a/modules/engine/misc/mw-ui.modal.js
+++ b/modules/engine/misc/mw-ui.modal.js
@@ -179,11 +179,12 @@
                return this;
        };
 
+       // eslint-disable-next-line valid-jsdoc
        /**
         * Changes the title of the modal.
         *
         * @param {string|null} title
-        * @return MwUiModal
+        * @return {MwUiModal}
         */
        MwUiModal.prototype.setTitle = function ( title ) {
                var $heading = this.getNode().find( this.headingSelector ),
@@ -213,6 +214,7 @@
 
        /**
         * @todo Implement data-mwui handlers, currently using data-flow
+        * @return {boolean}
         */
        MwUiModal.prototype.setInteractiveHandler = function () {
                return false;
@@ -220,6 +222,8 @@
 
        /**
         * Returns modal name.
+        *
+        * @return {string}
         */
        MwUiModal.prototype.getName = function () {
                return this.name;
@@ -274,7 +278,7 @@
         * @param {Object|HTMLElement|HTMLElement[]|jQuery|string} contents
         * @return {MwUiModal}
         */
-       MwUiModal.prototype.addSteps = function ( contents ) {
+       MwUiModal.prototype.addSteps = function () {
                return false;
        };
 
@@ -289,7 +293,7 @@
         * @param {HTMLElement|jQuery|string} contents
         * @return {MwUiModal}
         */
-       MwUiModal.prototype.setStep = function ( to, contents ) {
+       MwUiModal.prototype.setStep = function () {
                return false;
        };
 
@@ -300,7 +304,7 @@
         *
         * @return {Object}
         */
-       MwUiModal.prototype.getSteps = function ( to, contents ) {
+       MwUiModal.prototype.getSteps = function () {
                return {};
        };
 
@@ -367,7 +371,7 @@
         * @param {number|string} to
         * @return {MwUiModal|boolean} false if invalid step, MwUiModal on 
success
         */
-       MwUiModal.prototype.go = function ( to ) {
+       MwUiModal.prototype.go = function () {
                return false;
        };
 
diff --git a/modules/flow/dm/api/mw.flow.dm.APIHandler.js 
b/modules/flow/dm/api/mw.flow.dm.APIHandler.js
index 387d370..84c446e 100644
--- a/modules/flow/dm/api/mw.flow.dm.APIHandler.js
+++ b/modules/flow/dm/api/mw.flow.dm.APIHandler.js
@@ -86,11 +86,11 @@
         */
        mw.flow.dm.APIHandler.prototype.getCategories = function () {
                var params = {
-                               action: 'query',
-                               titles: this.page,
-                               generator: 'categories',
-                               gcllimit: 'max'
-                       };
+                       action: 'query',
+                       titles: this.page,
+                       generator: 'categories',
+                       gcllimit: 'max'
+               };
 
                return ( new mw.Api() ).get( $.extend( {}, this.requestParams, 
params ) )
                        .then( function ( response ) {
@@ -125,6 +125,7 @@
         * Send a request to get topic list
         *
         * @param {string} orderType Sort order type, 'newest' or 'updated'
+        * @param {Object} config Configuration
         * @cfg {string} [offset] Topic offset id or timestamp offset
         *  if given, the topic list will be returned with topics that
         *  are after (and including) the topic with the given uuid or
@@ -286,6 +287,7 @@
        /**
         * Get a post.
         *
+        * @param {string} topicId
         * @param {string} postId
         * @param {string} format
         * @return {jQuery.Promise} Promise that is resolved with the post 
revision data
@@ -315,12 +317,12 @@
         */
        mw.flow.dm.APIHandler.prototype.savePost = function ( topicId, postId, 
content, format, captcha ) {
                var params = {
-                               page: this.getTopicTitle( topicId ),
-                               epcontent: content,
-                               epformat: format,
-                               epprev_revision: this.currentRevision,
-                               eppostId: postId
-                       };
+                       page: this.getTopicTitle( topicId ),
+                       epcontent: content,
+                       epformat: format,
+                       epprev_revision: this.currentRevision,
+                       eppostId: postId
+               };
 
                this.addCaptcha( params, captcha );
 
@@ -360,11 +362,11 @@
         */
        mw.flow.dm.APIHandler.prototype.saveTopicSummary = function ( topicId, 
content, format, captcha ) {
                var params = {
-                               page: this.getTopicTitle( topicId ),
-                               etssummary: content,
-                               etsformat: format,
-                               etsprev_revision: this.currentRevision
-                       };
+                       page: this.getTopicTitle( topicId ),
+                       etssummary: content,
+                       etsformat: format,
+                       etsprev_revision: this.currentRevision
+               };
 
                this.addCaptcha( params, captcha );
 
@@ -384,10 +386,10 @@
         */
        mw.flow.dm.APIHandler.prototype.saveTopicTitle = function ( topicId, 
content, captcha ) {
                var params = {
-                               page: this.getTopicTitle( topicId ),
-                               etcontent: content,
-                               etprev_revision: this.currentRevision
-                       };
+                       page: this.getTopicTitle( topicId ),
+                       etcontent: content,
+                       etprev_revision: this.currentRevision
+               };
 
                this.addCaptcha( params, captcha );
 
@@ -407,10 +409,10 @@
         */
        mw.flow.dm.APIHandler.prototype.lockTopic = function ( topicId, 
moderationState, reasonMsgKey ) {
                var params = {
-                               page: this.getTopicTitle( topicId ),
-                               cotmoderationState: moderationState,
-                               cotreason: mw.msg( reasonMsgKey )
-                       };
+                       page: this.getTopicTitle( topicId ),
+                       cotmoderationState: moderationState,
+                       cotreason: mw.msg( reasonMsgKey )
+               };
 
                return this.postEdit( 'lock-topic', params )
                        .then( function ( data ) {
diff --git a/modules/flow/dm/mixins/mw.flow.dm.List.js 
b/modules/flow/dm/mixins/mw.flow.dm.List.js
index fd8c268..f0c7ff4 100644
--- a/modules/flow/dm/mixins/mw.flow.dm.List.js
+++ b/modules/flow/dm/mixins/mw.flow.dm.List.js
@@ -6,12 +6,8 @@
         * @mixin
         * @abstract
         * @constructor
-        * @param {Object} config Configuration options
         */
-       mw.flow.dm.List = function mwFlowDmList( config ) {
-               // Configuration initialization
-               config = config || {};
-
+       mw.flow.dm.List = function mwFlowDmList() {
                this.items = [];
 
                // Store references to items by their ids
@@ -138,6 +134,7 @@
                }
        };
 
+       // eslint-disable-next-line valid-jsdoc
        /**
         * Add items
         *
@@ -196,6 +193,7 @@
                return this;
        };
 
+       // eslint-disable-next-line valid-jsdoc
        /**
         * Remove items
         *
@@ -236,9 +234,11 @@
                return this;
        };
 
+       // eslint-disable-next-line valid-jsdoc
        /**
         * Clear all items
         *
+        * @chainable
         * @fires clear
         */
        mw.flow.dm.List.prototype.clearItems = function () {
diff --git a/modules/flow/dm/mw.flow.dm.Board.js 
b/modules/flow/dm/mw.flow.dm.Board.js
index fec6d2c..5ebf3f8 100644
--- a/modules/flow/dm/mw.flow.dm.Board.js
+++ b/modules/flow/dm/mw.flow.dm.Board.js
@@ -1,4 +1,4 @@
-( function ( $ ) {
+( function () {
        /**
         * Flow Board
         *
@@ -177,7 +177,7 @@
        /**
         * Set board description
         *
-        * @param {mw.flow.dm.BoardDescription} Board description
+        * @param {mw.flow.dm.BoardDescription} desc Board description
         * @fires descriptionChange
         */
        mw.flow.dm.Board.prototype.setDescription = function ( desc ) {
@@ -212,7 +212,7 @@
        /**
         * Set board sort order, 'newest' or 'updated'
         *
-        * @param {string} Board sort order
+        * @param {string} order Board sort order
         * @fires sortOrderChange
         */
        mw.flow.dm.Board.prototype.setSortOrder = function ( order ) {
@@ -224,6 +224,8 @@
 
        /**
         * Get the last offset for the API's offsetId
+        *
+        * @return {string}
         */
        mw.flow.dm.Board.prototype.getOffsetId = function () {
                var topics = this.getItems();
@@ -235,6 +237,8 @@
 
        /**
         * Get the last offset for the API's offset timestamp
+        *
+        * @return {number}
         */
        mw.flow.dm.Board.prototype.getOffset = function () {
                var topics = this.getItems();
@@ -254,4 +258,4 @@
                this.clearItems();
                this.emit( 'reset', order );
        };
-}( jQuery ) );
+}() );
diff --git a/modules/flow/dm/mw.flow.dm.Captcha.js 
b/modules/flow/dm/mw.flow.dm.Captcha.js
index 87886ce..1151b47 100644
--- a/modules/flow/dm/mw.flow.dm.Captcha.js
+++ b/modules/flow/dm/mw.flow.dm.Captcha.js
@@ -1,4 +1,4 @@
-( function ( $ ) {
+( function () {
        /**
         * Data model for a (potential) CAPTCHA.  This is always used; it will 
just return false for
         * isRequired() if no user interaction is required.
@@ -67,4 +67,4 @@
        mw.flow.dm.Captcha.prototype.getRenderingInformation = function () {
                return this.renderingInformation;
        };
-}( jQuery ) );
+}() );
diff --git a/modules/flow/dm/mw.flow.dm.Categories.js 
b/modules/flow/dm/mw.flow.dm.Categories.js
index b002788..9a05c3d 100644
--- a/modules/flow/dm/mw.flow.dm.Categories.js
+++ b/modules/flow/dm/mw.flow.dm.Categories.js
@@ -1,4 +1,4 @@
-( function ( $ ) {
+( function () {
        /**
         * Flow Board
         *
@@ -23,4 +23,4 @@
 
        OO.mixinClass( mw.flow.dm.Categories, OO.EventEmitter );
        OO.mixinClass( mw.flow.dm.Categories, mw.flow.dm.List );
-}( jQuery ) );
+}() );
diff --git a/modules/flow/dm/mw.flow.dm.CategoryItem.js 
b/modules/flow/dm/mw.flow.dm.CategoryItem.js
index fa4372a..2fa577d 100644
--- a/modules/flow/dm/mw.flow.dm.CategoryItem.js
+++ b/modules/flow/dm/mw.flow.dm.CategoryItem.js
@@ -1,4 +1,4 @@
-( function ( $ ) {
+( function () {
        /**
         * Flow Board
         *
@@ -40,4 +40,4 @@
        mw.flow.dm.CategoryItem.prototype.exists = function () {
                return this.categoryExists;
        };
-}( jQuery ) );
+}() );
diff --git a/modules/flow/dm/mw.flow.dm.Content.js 
b/modules/flow/dm/mw.flow.dm.Content.js
index 71e738b..9af8527 100644
--- a/modules/flow/dm/mw.flow.dm.Content.js
+++ b/modules/flow/dm/mw.flow.dm.Content.js
@@ -1,4 +1,4 @@
-( function ( $ ) {
+( function () {
        /**
         * Flow Content class
         *
@@ -61,6 +61,7 @@
         * @fires contentChange
         */
        mw.flow.dm.Content.prototype.set = function ( representations ) {
+               var format;
                this.defaultFormat = null;
                this.contentRepresentations = {};
 
@@ -68,7 +69,7 @@
                        this.defaultFormat = representations.format;
                        this.contentRepresentations[ this.defaultFormat ] = 
representations.content;
 
-                       for ( var format in representations ) {
+                       for ( format in representations ) {
                                if ( representations.hasOwnProperty( format ) ) 
{
                                        this.contentRepresentations[ format ] = 
representations[ format ];
                                }
@@ -77,4 +78,4 @@
 
                this.emit( 'contentChange' );
        };
-}( jQuery ) );
+}() );
diff --git a/modules/flow/dm/mw.flow.dm.Post.js 
b/modules/flow/dm/mw.flow.dm.Post.js
index 7080af3..2168a86 100644
--- a/modules/flow/dm/mw.flow.dm.Post.js
+++ b/modules/flow/dm/mw.flow.dm.Post.js
@@ -1,4 +1,4 @@
-( function ( $ ) {
+( function () {
        /**
         * Flow Post
         *
@@ -97,4 +97,4 @@
                return this.replyIds;
        };
 
-}( jQuery ) );
+}() );
diff --git a/modules/flow/dm/mw.flow.dm.RevisionedContent.js 
b/modules/flow/dm/mw.flow.dm.RevisionedContent.js
index 9a01d03..7260481 100644
--- a/modules/flow/dm/mw.flow.dm.RevisionedContent.js
+++ b/modules/flow/dm/mw.flow.dm.RevisionedContent.js
@@ -185,6 +185,8 @@
 
        /**
         * @see mw.flow.dm.Content
+        * @param {string} format
+        * @return {string}
         */
        mw.flow.dm.RevisionedContent.prototype.getContent = function ( format ) 
{
                return this.content.get( format );
@@ -192,9 +194,10 @@
 
        /**
         * @see mw.flow.dm.Content
+        * @param {Object} representations
         */
        mw.flow.dm.RevisionedContent.prototype.setContent = function ( 
representations ) {
-               return this.content.set( representations );
+               this.content.set( representations );
        };
 
        /**
@@ -240,7 +243,7 @@
        /**
         * Set the revision change type
         *
-        * @param {string} Revision change type
+        * @param {string} type Revision change type
         */
        mw.flow.dm.RevisionedContent.prototype.setChangeType = function ( type 
) {
                if ( type !== undefined && this.changeType !== type ) {
diff --git a/modules/flow/dm/mw.flow.dm.Topic.js 
b/modules/flow/dm/mw.flow.dm.Topic.js
index 8c51d03..e109677 100644
--- a/modules/flow/dm/mw.flow.dm.Topic.js
+++ b/modules/flow/dm/mw.flow.dm.Topic.js
@@ -160,7 +160,7 @@
        /**
         * Set the topic summary
         *
-        * @param {string} Topic summary
+        * @param {string} summary Topic summary
         * @fires summary
         */
        mw.flow.dm.Topic.prototype.setSummary = function ( summary ) {
diff --git a/modules/flow/ui/mw.flow.ui.CancelConfirmDialog.js 
b/modules/flow/ui/mw.flow.ui.CancelConfirmDialog.js
index 42205d6..3297cb5 100644
--- a/modules/flow/ui/mw.flow.ui.CancelConfirmDialog.js
+++ b/modules/flow/ui/mw.flow.ui.CancelConfirmDialog.js
@@ -1,4 +1,4 @@
-( function ( $ ) {
+( function () {
        /**
         * Dialog for confirming with the user if they reall want to cancel
         * the edit.
@@ -43,4 +43,4 @@
 
        mw.flow.ui.windowFactory.register( mw.flow.ui.CancelConfirmDialog );
 
-}( jQuery ) );
+}() );
diff --git a/modules/flow/ui/mw.flow.ui.js b/modules/flow/ui/mw.flow.ui.js
index 6649dee..657402c 100644
--- a/modules/flow/ui/mw.flow.ui.js
+++ b/modules/flow/ui/mw.flow.ui.js
@@ -2,4 +2,3 @@
 
 mw.flow.ui.windowFactory = new OO.Factory();
 mw.flow.ui.windowManager = new OO.ui.WindowManager( { factory: 
mw.flow.ui.windowFactory } );
-
diff --git 
a/modules/flow/ui/widgets/editor/editors/mw.flow.ui.AbstractEditorWidget.js 
b/modules/flow/ui/widgets/editor/editors/mw.flow.ui.AbstractEditorWidget.js
index 5ca7dbf..31daaef 100644
--- a/modules/flow/ui/widgets/editor/editors/mw.flow.ui.AbstractEditorWidget.js
+++ b/modules/flow/ui/widgets/editor/editors/mw.flow.ui.AbstractEditorWidget.js
@@ -1,4 +1,4 @@
-( function ( $ ) {
+( function () {
        /**
         * Flow abstract editor widget.
         *
@@ -206,4 +206,4 @@
                return this.initialContent !== this.getContent();
        };
 
-}( jQuery ) );
+}() );
diff --git 
a/modules/flow/ui/widgets/editor/editors/mw.flow.ui.VisualEditorWidget.js 
b/modules/flow/ui/widgets/editor/editors/mw.flow.ui.VisualEditorWidget.js
index 0d1c224..d638e83 100644
--- a/modules/flow/ui/widgets/editor/editors/mw.flow.ui.VisualEditorWidget.js
+++ b/modules/flow/ui/widgets/editor/editors/mw.flow.ui.VisualEditorWidget.js
@@ -42,6 +42,7 @@
        mw.flow.ui.VisualEditorWidget.static.isSupported = function () {
                var isMobileTarget = ( mw.config.get( 'skin' ) === 'minerva' );
 
+               /* global VisualEditorSupportCheck */
                return !!(
                        !isMobileTarget &&
                        mw.loader.getState( 'ext.visualEditor.core' ) &&
@@ -81,6 +82,7 @@
         * Create a VE surface with the provided content in it.
         *
         * @param {string} content HTML to put in the surface (body only)
+        * @return {jQuery.Promise} Promise which resolves when the surface is 
ready
         */
        mw.flow.ui.VisualEditorWidget.prototype.createSurface = function ( 
content ) {
                var widget = this,
diff --git a/modules/flow/ui/widgets/editor/mw.flow.ui.CanNotEditWidget.js 
b/modules/flow/ui/widgets/editor/mw.flow.ui.CanNotEditWidget.js
index 1c75dd6..85fa27a 100644
--- a/modules/flow/ui/widgets/editor/mw.flow.ui.CanNotEditWidget.js
+++ b/modules/flow/ui/widgets/editor/mw.flow.ui.CanNotEditWidget.js
@@ -144,7 +144,7 @@
         * Check if the specified group is required to edit and they lack it.
         *
         * @param {string} groupName
-        * @return True if and only if the group is both required to edit and 
missing
+        * @return {boolean} The group is both required to edit and missing
         */
        mw.flow.ui.CanNotEditWidget.prototype.isMissingRequiredGroup = function 
( groupName ) {
                var isGroupRequired = $.inArray( groupName, 
this.restrictionEdit ) !== -1,
diff --git a/modules/flow/ui/widgets/editor/mw.flow.ui.EditorSwitcherWidget.js 
b/modules/flow/ui/widgets/editor/mw.flow.ui.EditorSwitcherWidget.js
index eb118fc..e055350 100644
--- a/modules/flow/ui/widgets/editor/mw.flow.ui.EditorSwitcherWidget.js
+++ b/modules/flow/ui/widgets/editor/mw.flow.ui.EditorSwitcherWidget.js
@@ -219,6 +219,11 @@
         * @fires switch
         */
        mw.flow.ui.EditorSwitcherWidget.prototype.switchEditor = function ( 
name ) {
+               var promise, switchingDeferred,
+                       oldName, oldEditor, oldContent, oldFormat,
+                       newEditor, newFormat,
+                       widget = this;
+
                if ( !this.isEditorAvailable( name ) ) {
                        return $.Deferred().reject().promise();
                }
@@ -241,15 +246,13 @@
                        return $.Deferred().reject().promise();
                }
 
-               var promise,
-                       switchingDeferred = $.Deferred(),
-                       oldName = this.activeEditorName,
-                       oldEditor = this.getActiveEditor(),
-                       oldContent = oldEditor && oldEditor.getContent() || 
this.initialContent,
-                       oldFormat = this.contentFormat,
-                       newEditor = this.getEditor( name ),
-                       newFormat = newEditor.getFormat(),
-                       widget = this;
+               switchingDeferred = $.Deferred();
+               oldName = this.activeEditorName;
+               oldEditor = this.getActiveEditor();
+               oldContent = oldEditor && oldEditor.getContent() || 
this.initialContent;
+               oldFormat = this.contentFormat;
+               newEditor = this.getEditor( name );
+               newFormat = newEditor.getFormat();
 
                this.switchingPromise = promise = switchingDeferred.promise();
                this.switchingPromise.newEditorName = name;
@@ -388,6 +391,9 @@
         * @return {jQuery.Promise} Promise resolved when new content has been 
set
         */
        mw.flow.ui.EditorSwitcherWidget.prototype.setContent = function ( 
content, contentFormat ) {
+               var promise, settingDeferred,
+                       widget = this;
+
                if ( this.settingPromise ) {
                        // TODO handle this more gracefully
                        return $.Deferred().reject();
@@ -401,9 +407,7 @@
                        return $.Deferred().resolve().promise();
                }
 
-               var promise,
-                       settingDeferred = $.Deferred(),
-                       widget = this;
+               settingDeferred = $.Deferred();
 
                // Setting this.settingPromise prevents switchEditor() from 
changing the active editor
                // while we convert
diff --git a/modules/flow/ui/widgets/editor/mw.flow.ui.EditorWidget.js 
b/modules/flow/ui/widgets/editor/mw.flow.ui.EditorWidget.js
index 112f3af..0a040fa 100644
--- a/modules/flow/ui/widgets/editor/mw.flow.ui.EditorWidget.js
+++ b/modules/flow/ui/widgets/editor/mw.flow.ui.EditorWidget.js
@@ -293,6 +293,8 @@
         * @return {jQuery.Promise} Promise resolved when editor switch is done
         */
        mw.flow.ui.EditorWidget.prototype.activate = function () {
+               var switchPromise, editor, widget;
+
                if ( this.isActive() ) {
                        this.bindBeforeUnloadHandler();
                        return $.Deferred().resolve().promise();
@@ -300,9 +302,9 @@
 
                // Doesn't call editorSwitcherWidget.activate() because we want 
to
                // evaluate the user preference as late as possible
-               var switchPromise,
-                       editor = this.initialEditor || mw.user.options.get( 
'flow-editor' ),
-                       widget = this;
+               editor = this.initialEditor || mw.user.options.get( 
'flow-editor' );
+               widget = this;
+
                if ( editor === 'none' ) {
                        editor = 'wikitext';
                }
diff --git a/modules/flow/ui/widgets/mw.flow.ui.BoardDescriptionWidget.js 
b/modules/flow/ui/widgets/mw.flow.ui.BoardDescriptionWidget.js
index 8df24b1..4fc458a 100644
--- a/modules/flow/ui/widgets/mw.flow.ui.BoardDescriptionWidget.js
+++ b/modules/flow/ui/widgets/mw.flow.ui.BoardDescriptionWidget.js
@@ -208,7 +208,7 @@
         * Respond to editor save event. Save the content and display the new 
description.
         *
         * @param {string} content Content to save
-        * @param {string} contentFormat Format of content
+        * @param {string} format Format of content
         * @fires saveContent
         */
        mw.flow.ui.BoardDescriptionWidget.prototype.onEditorSaveContent = 
function ( content, format ) {
diff --git a/modules/flow/ui/widgets/mw.flow.ui.CaptchaWidget.js 
b/modules/flow/ui/widgets/mw.flow.ui.CaptchaWidget.js
index f71e059..4b90ce1 100644
--- a/modules/flow/ui/widgets/mw.flow.ui.CaptchaWidget.js
+++ b/modules/flow/ui/widgets/mw.flow.ui.CaptchaWidget.js
@@ -6,6 +6,7 @@
         * @extends OO.ui.Widget
         *
         * @constructor
+        * @param {mw.flow.dm.Captcha} model
         * @param {Object} [config]
         */
        mw.flow.ui.CaptchaWidget = function mwFlowUiCaptchaWidget( model, 
config ) {
diff --git a/modules/flow/ui/widgets/mw.flow.ui.EditPostWidget.js 
b/modules/flow/ui/widgets/mw.flow.ui.EditPostWidget.js
index ad8cdba..9242862 100644
--- a/modules/flow/ui/widgets/mw.flow.ui.EditPostWidget.js
+++ b/modules/flow/ui/widgets/mw.flow.ui.EditPostWidget.js
@@ -124,6 +124,9 @@
 
        /**
         * Respond to editor save
+        *
+        * @param {string} content Content
+        * @param {string} format Format
         */
        mw.flow.ui.EditPostWidget.prototype.onEditorSaveContent = function ( 
content, format ) {
                var widget = this,
diff --git a/modules/flow/ui/widgets/mw.flow.ui.EditTopicSummaryWidget.js 
b/modules/flow/ui/widgets/mw.flow.ui.EditTopicSummaryWidget.js
index 8779a82..76460a1 100644
--- a/modules/flow/ui/widgets/mw.flow.ui.EditTopicSummaryWidget.js
+++ b/modules/flow/ui/widgets/mw.flow.ui.EditTopicSummaryWidget.js
@@ -123,6 +123,9 @@
 
        /**
         * Respond to editor save
+        *
+        * @param {string} content Content
+        * @param {string} format Format
         */
        mw.flow.ui.EditTopicSummaryWidget.prototype.onEditorSaveContent = 
function ( content, format ) {
                var widget = this,
diff --git a/modules/flow/ui/widgets/mw.flow.ui.NavigationWidget.js 
b/modules/flow/ui/widgets/mw.flow.ui.NavigationWidget.js
index 8edc8c0..4d5ee83 100644
--- a/modules/flow/ui/widgets/mw.flow.ui.NavigationWidget.js
+++ b/modules/flow/ui/widgets/mw.flow.ui.NavigationWidget.js
@@ -119,7 +119,7 @@
 
                if ( isScrolledDown ) {
                        // TODO use binary search
-                       $( '.flow-topic' ).each( function ( index, element ) {
+                       $( '.flow-topic' ).each( function () {
                                if ( isElementInView( $( this ) ) ) {
                                        topicId = $( this ).data( 'flowId' );
                                        return false;
diff --git a/modules/flow/ui/widgets/mw.flow.ui.ReplyWidget.js 
b/modules/flow/ui/widgets/mw.flow.ui.ReplyWidget.js
index 2148ea6..e5c36ee 100644
--- a/modules/flow/ui/widgets/mw.flow.ui.ReplyWidget.js
+++ b/modules/flow/ui/widgets/mw.flow.ui.ReplyWidget.js
@@ -118,6 +118,9 @@
 
        /**
         * Respond to editor save
+        *
+        * @param {string} content Content
+        * @param {string} format Format
         */
        mw.flow.ui.ReplyWidget.prototype.onEditorSaveContent = function ( 
content, format ) {
                var widget = this,
@@ -179,6 +182,8 @@
 
        /**
         * Check if the widget is expandable
+        *
+        * @return {boolean}
         */
        mw.flow.ui.ReplyWidget.prototype.isExpandable = function () {
                return this.expandable;
@@ -186,6 +191,8 @@
 
        /**
         * Check if the widget is expanded
+        *
+        * @return {boolean}
         */
        mw.flow.ui.ReplyWidget.prototype.isExpanded = function () {
                return this.expanded;
diff --git a/modules/flow/ui/widgets/mw.flow.ui.SidebarExpandWidget.js 
b/modules/flow/ui/widgets/mw.flow.ui.SidebarExpandWidget.js
index ebe6679..d5c0081 100644
--- a/modules/flow/ui/widgets/mw.flow.ui.SidebarExpandWidget.js
+++ b/modules/flow/ui/widgets/mw.flow.ui.SidebarExpandWidget.js
@@ -1,4 +1,4 @@
-( function ( $ ) {
+( function () {
        /**
         * Flow sidebar expand widget
         *
@@ -64,7 +64,7 @@
 
                        // Change the preference
                        siderailState = this.collapsed ? 'collapsed' : 
'expanded';
-                       if ( !mw.user.isAnon() &&  mw.user.options.get( 
'flow-side-rail-state' ) !== siderailState ) {
+                       if ( !mw.user.isAnon() && mw.user.options.get( 
'flow-side-rail-state' ) !== siderailState ) {
                                // update the user preferences; no preferences 
for anons
 
                                new mw.Api().saveOption( 
'flow-side-rail-state', siderailState );
@@ -84,4 +84,4 @@
        mw.flow.ui.SidebarExpandWidget.prototype.isCollapsed = function () {
                return this.collapsed;
        };
-}( jQuery ) );
+}() );
diff --git a/modules/flow/ui/widgets/mw.flow.ui.TopicMenuSelectWidget.js 
b/modules/flow/ui/widgets/mw.flow.ui.TopicMenuSelectWidget.js
index aa3bc32..9f5369c 100644
--- a/modules/flow/ui/widgets/mw.flow.ui.TopicMenuSelectWidget.js
+++ b/modules/flow/ui/widgets/mw.flow.ui.TopicMenuSelectWidget.js
@@ -1,4 +1,4 @@
-( function ( $ ) {
+( function () {
        /**
         * Flow topic list widget
         *
@@ -76,6 +76,8 @@
        /**
         * Respond to scrolling of the menu. If we are close to the
         * bottom, call for more topics.
+        *
+        * @return {boolean} False to prevent default event
         */
        mw.flow.ui.TopicMenuSelectWidget.prototype.onMenuScroll = function () {
                var actualHeight, naturalHeight, scrollTop, isNearBottom;
@@ -122,7 +124,7 @@
                var widget = this;
 
                this.loadingMoreTopics = true;
-               this.system.fetchMoreTopics()
+               return this.system.fetchMoreTopics()
                        .then( function ( hasMoreTopicsInApi ) {
                                widget.noMoreTopics = !hasMoreTopicsInApi;
                                if ( widget.noMoreTopics ) {
@@ -230,4 +232,4 @@
                
mw.flow.ui.TopicMenuSelectWidget.parent.prototype.removeItems.call( this, items 
);
        };
 
-}( jQuery ) );
+}() );
diff --git a/modules/mw.flow.Initializer.js b/modules/mw.flow.Initializer.js
index 54acaf6..93dc61a 100644
--- a/modules/mw.flow.Initializer.js
+++ b/modules/mw.flow.Initializer.js
@@ -599,12 +599,12 @@
                                uri = new mw.Uri( href ),
                                replyTo = uri.query.topic_postId,
                                $topic = $( this ).closest( '.flow-topic' ),
-                                       placeholder = mw.msg( 
'flow-reply-topic-title-placeholder', $topic.find( '.flow-topic-title' 
).text().trim() ),
-                                       // replyTo can refer to a post ID or a 
topic ID
-                                       // For posts, the ReplyWidget should go 
in .flow-replies
-                                       // For topics, it's directly inside the 
topic
-                                       $targetContainer = $( '#flow-post-' + 
replyTo + ' > .flow-replies, #flow-topic-' + replyTo ),
-                                       $existingWidget = 
$targetContainer.children( '.flow-ui-replyWidget' );
+                               placeholder = mw.msg( 
'flow-reply-topic-title-placeholder', $topic.find( '.flow-topic-title' 
).text().trim() ),
+                               // replyTo can refer to a post ID or a topic ID
+                               // For posts, the ReplyWidget should go in 
.flow-replies
+                               // For topics, it's directly inside the topic
+                               $targetContainer = $( '#flow-post-' + replyTo + 
' > .flow-replies, #flow-topic-' + replyTo ),
+                               $existingWidget = $targetContainer.children( 
'.flow-ui-replyWidget' );
 
                        // Check that there's not already a reply widget 
existing in the same place
                        if ( $existingWidget.length > 0 ) {
@@ -656,7 +656,7 @@
         * @param {string} [action] Lock action 'lock' or 'unlock'. If not 
given, the action
         *  is assumed as summary only.
         */
-       mw.flow.Initializer.prototype.startEditTopicSummary = function ( 
isFullBoard, topicId, action  ) {
+       mw.flow.Initializer.prototype.startEditTopicSummary = function ( 
isFullBoard, topicId, action ) {
                var editTopicSummaryWidget,
                        self = this,
                        $topic = $( '#flow-topic-' + topicId ),
@@ -770,17 +770,6 @@
         * @return {mw.flow.ui.EditorWidget}
         */
        mw.flow.Initializer.prototype.createEditorWidget = function ( 
$domToReplace, content, saveMsgKey ) {
-               function handleFailure( errorCode, errorObj ) {
-                       captchaWidget.model.update( errorCode, errorObj );
-
-                       if ( !captchaWidget.model.isRequired() ) {
-                               error.setLabel( new OO.ui.HtmlSnippet( 
errorObj.error && errorObj.error.info || errorObj.exception ) );
-                               error.toggle( true );
-                       }
-
-                       editor.popPending();
-               }
-
                var $wrapper,
                        isProbablyEditable = mw.config.get( 
'wgIsProbablyEditable' ),
                        anonWarning = new mw.flow.ui.AnonWarningWidget( {
@@ -801,6 +790,17 @@
                                confirmLeave: !!mw.user.options.get( 
'useeditwarning' )
                        } );
 
+               function handleFailure( errorCode, errorObj ) {
+                       captchaWidget.model.update( errorCode, errorObj );
+
+                       if ( !captchaWidget.model.isRequired() ) {
+                               error.setLabel( new OO.ui.HtmlSnippet( 
errorObj.error && errorObj.error.info || errorObj.exception ) );
+                               error.toggle( true );
+                       }
+
+                       editor.popPending();
+               }
+
                error.toggle( false );
                editor.toggle( true );
                anonWarning.toggle( mw.user.isAnon() );
diff --git a/package.json b/package.json
index b1bff8f..ce094e4 100644
--- a/package.json
+++ b/package.json
@@ -8,11 +8,11 @@
     "doc": "jsduck"
   },
   "devDependencies": {
+    "eslint-config-wikimedia": "0.3.0",
     "grunt": "1.0.1",
     "grunt-banana-checker": "0.5.0",
-    "grunt-contrib-jshint": "1.0.0",
     "grunt-contrib-watch": "1.0.0",
-    "grunt-jscs": "2.8.0",
+    "grunt-eslint": "19.0.0",
     "grunt-jsonlint": "1.0.7",
     "grunt-stylelint": "0.6.0",
     "grunt-tyops": "0.1.0",
diff --git a/tests/qunit/engine/misc/test_flow-handlebars.js 
b/tests/qunit/engine/misc/test_flow-handlebars.js
index 6c88391..004e929 100644
--- a/tests/qunit/engine/misc/test_flow-handlebars.js
+++ b/tests/qunit/engine/misc/test_flow-handlebars.js
@@ -1,153 +1,153 @@
 ( function ( $ ) {
-QUnit.module( 'ext.flow: Handlebars helpers', {
-       setup: function () {
-               var stub = this.sandbox.stub( mw.template, 'get' ),
-                       stubUser;
+       QUnit.module( 'ext.flow: Handlebars helpers', {
+               setup: function () {
+                       var stub = this.sandbox.stub( mw.template, 'get' ),
+                               stubUser;
 
-               stub.withArgs( 'ext.flow.templating', 'foo.handlebars' 
).returns( {
-                       render: function ( data ) {
-                               return data && data.val ? '<div>Magic.</div>' : 
'Stubbed.';
-                       }
-               } );
-               this.handlebarsProto = mw.flow.FlowHandlebars.prototype;
-               this.handlebarsProto._qunit_helper_test = function ( a, b ) {
-                       return a + b;
-               };
+                       stub.withArgs( 'ext.flow.templating', 'foo.handlebars' 
).returns( {
+                               render: function ( data ) {
+                                       return data && data.val ? 
'<div>Magic.</div>' : 'Stubbed.';
+                               }
+                       } );
+                       this.handlebarsProto = mw.flow.FlowHandlebars.prototype;
+                       this.handlebarsProto._qunit_helper_test = function ( a, 
b ) {
+                               return a + b;
+                       };
 
-               // Stub user
-               stubUser = this.sandbox.stub( mw.user, 'isAnon' );
-               stubUser.onCall( 0 ).returns( true );
-               stubUser.onCall( 1 ).returns( false );
-               this.opts = {
-                       fn: function () {
-                               return 'ok';
-                       },
-                       inverse: function () {
-                               return 'nope';
-                       }
-               };
-       }
-} );
-
-QUnit.test( 'Handlebars.prototype.processTemplate', 1, function ( assert ) {
-       assert.strictEqual( this.handlebarsProto.processTemplate( 'foo', { val: 
'Hello' } ),
-               '<div>Magic.</div>', 'Getting a template works.' );
-} );
-
-QUnit.test( 'Handlebars.prototype.processTemplateGetFragment', 1, function ( 
assert ) {
-       assert.strictEqual( this.handlebarsProto.processTemplateGetFragment( 
'foo', { val: 'Hello' } ).childNodes.length,
-               1, 'Return a fragment with the div child node' );
-} );
-
-QUnit.test( 'Handlebars.prototype.getTemplate', 2, function ( assert ) {
-       assert.strictEqual( this.handlebarsProto.getTemplate( 'foo' )(), 
'Stubbed.', 'Getting a template works.' );
-       assert.strictEqual( this.handlebarsProto.getTemplate( 'foo' )(), 
'Stubbed.', 'Getting a template from cache works.' );
-} );
-
-// Helpers
-QUnit.test( 'Handlebars.prototype.callHelper', 1, function ( assert ) {
-       assert.strictEqual( this.handlebarsProto.callHelper( 
'_qunit_helper_test', 1, 2 ),
-               3, 'Check the helper was called.' );
-} );
-
-QUnit.test( 'Handlebars.prototype.eachPost', 3, function ( assert ) {
-       var ctx = {
-               posts: {
-                       1: [ 300 ],
-                       // Purposely points to a missing revision to deal with 
edge case
-                       2: [ 500 ]
-               },
-               revisions: {
-                       300: { content: 'a' }
+                       // Stub user
+                       stubUser = this.sandbox.stub( mw.user, 'isAnon' );
+                       stubUser.onCall( 0 ).returns( true );
+                       stubUser.onCall( 1 ).returns( false );
+                       this.opts = {
+                               fn: function () {
+                                       return 'ok';
+                               },
+                               inverse: function () {
+                                       return 'nope';
+                               }
+                       };
                }
-       };
+       } );
 
-       assert.deepEqual( this.handlebarsProto.eachPost( ctx, 1, {} ), { 
content: 'a' }, 'Matches given id.' );
-       assert.deepEqual( this.handlebarsProto.eachPost( ctx, 1, this.opts ), 
'ok', 'Runs fn when given.' );
-       assert.deepEqual( this.handlebarsProto.eachPost( ctx, 2, {} ), { 
content: null }, 'Missing revision id.' );
-} );
+       QUnit.test( 'Handlebars.prototype.processTemplate', 1, function ( 
assert ) {
+               assert.strictEqual( this.handlebarsProto.processTemplate( 
'foo', { val: 'Hello' } ),
+                       '<div>Magic.</div>', 'Getting a template works.' );
+       } );
 
-QUnit.test( 'Handlebars.prototype.ifCond', 8, function ( assert ) {
-       assert.strictEqual( this.handlebarsProto.ifCond( 'foo', '===', 'bar', 
this.opts ), 'nope', 'not equal' );
-       assert.strictEqual( this.handlebarsProto.ifCond( 'foo', '===', 'foo', 
this.opts ), 'ok', 'equal' );
-       assert.strictEqual( this.handlebarsProto.ifCond( true, 'or', false, 
this.opts ), 'ok', 'true || false' );
-       assert.strictEqual( this.handlebarsProto.ifCond( true, 'or', true, 
this.opts ), 'ok', 'true || true' );
-       assert.strictEqual( this.handlebarsProto.ifCond( false, 'or', false, 
this.opts ), 'nope', 'false || false' );
-       assert.strictEqual( this.handlebarsProto.ifCond( false, 'monkeypunch', 
this.opts ), '', 'Unknown operator' );
-       assert.strictEqual( this.handlebarsProto.ifCond( 'foo', '!==', 'foo', 
this.opts ), 'nope' );
-       assert.strictEqual( this.handlebarsProto.ifCond( 'foo', '!==', 'bar', 
this.opts ), 'ok' );
-} );
+       QUnit.test( 'Handlebars.prototype.processTemplateGetFragment', 1, 
function ( assert ) {
+               assert.strictEqual( 
this.handlebarsProto.processTemplateGetFragment( 'foo', { val: 'Hello' } 
).childNodes.length,
+                       1, 'Return a fragment with the div child node' );
+       } );
 
-QUnit.test( 'Handlebars.prototype.ifAnonymous', 2, function () {
-       strictEqual( this.handlebarsProto.ifAnonymous( this.opts ), 'ok', 'User 
should be anonymous first time.' );
-       strictEqual( this.handlebarsProto.ifAnonymous( this.opts ), 'nope', 
'User should be logged in on second call.' );
-} );
+       QUnit.test( 'Handlebars.prototype.getTemplate', 2, function ( assert ) {
+               assert.strictEqual( this.handlebarsProto.getTemplate( 'foo' 
)(), 'Stubbed.', 'Getting a template works.' );
+               assert.strictEqual( this.handlebarsProto.getTemplate( 'foo' 
)(), 'Stubbed.', 'Getting a template from cache works.' );
+       } );
 
-QUnit.test( 'Handlebars.prototype.concat', 2, function () {
-       strictEqual( this.handlebarsProto.concat( 'a', 'b', 'c', this.opts ), 
'abc', 'Check concat working fine.' );
-       strictEqual( this.handlebarsProto.concat( this.opts ), '', 'Without 
arguments.' );
-} );
+       // Helpers
+       QUnit.test( 'Handlebars.prototype.callHelper', 1, function ( assert ) {
+               assert.strictEqual( this.handlebarsProto.callHelper( 
'_qunit_helper_test', 1, 2 ),
+                       3, 'Check the helper was called.' );
+       } );
 
-QUnit.test( 'Handlebars.prototype.progressiveEnhancement', 5, function () {
-       var opts = $.extend( { hash: { type: 'insert', target: 'abc', id: 'def' 
} }, this.opts ),
-               $div = $( document.createElement( 'div' ) );
+       QUnit.test( 'Handlebars.prototype.eachPost', 3, function ( assert ) {
+               var ctx = {
+                       posts: {
+                               1: [ 300 ],
+                               // Purposely points to a missing revision to 
deal with edge case
+                               2: [ 500 ]
+                       },
+                       revisions: {
+                               300: { content: 'a' }
+                       }
+               };
 
-       // Render script tag
-       strictEqual(
-               this.handlebarsProto.progressiveEnhancement( opts ).string,
-               '<scr' + 'ipt' +
-                       ' 
type="text/x-handlebars-template-progressive-enhancement"' +
-                       ' data-type="' + opts.hash.type + '"' +
-                       ' data-target="' + opts.hash.target + '"' +
-                       ' id="' + opts.hash.id + '">' +
-                       'ok' +
-                       '</scr' + 'ipt>',
-               'Should output exact replica of script tag.'
-       );
+               assert.deepEqual( this.handlebarsProto.eachPost( ctx, 1, {} ), 
{ content: 'a' }, 'Matches given id.' );
+               assert.deepEqual( this.handlebarsProto.eachPost( ctx, 1, 
this.opts ), 'ok', 'Runs fn when given.' );
+               assert.deepEqual( this.handlebarsProto.eachPost( ctx, 2, {} ), 
{ content: null }, 'Missing revision id.' );
+       } );
 
-       // Replace itself: no target (default to self), no type (default to 
insert)
-       $div.empty().append( this.handlebarsProto.processTemplateGetFragment(
-               Handlebars.compile( 
'{{#progressiveEnhancement}}hello{{/progressiveEnhancement}}' )
-       ) );
-       strictEqual(
-               $div.html(),
-               'hello',
-               'progressiveEnhancement should be processed in template string.'
-       );
+       QUnit.test( 'Handlebars.prototype.ifCond', 8, function ( assert ) {
+               assert.strictEqual( this.handlebarsProto.ifCond( 'foo', '===', 
'bar', this.opts ), 'nope', 'not equal' );
+               assert.strictEqual( this.handlebarsProto.ifCond( 'foo', '===', 
'foo', this.opts ), 'ok', 'equal' );
+               assert.strictEqual( this.handlebarsProto.ifCond( true, 'or', 
false, this.opts ), 'ok', 'true || false' );
+               assert.strictEqual( this.handlebarsProto.ifCond( true, 'or', 
true, this.opts ), 'ok', 'true || true' );
+               assert.strictEqual( this.handlebarsProto.ifCond( false, 'or', 
false, this.opts ), 'nope', 'false || false' );
+               assert.strictEqual( this.handlebarsProto.ifCond( false, 
'monkeypunch', this.opts ), '', 'Unknown operator' );
+               assert.strictEqual( this.handlebarsProto.ifCond( 'foo', '!==', 
'foo', this.opts ), 'nope' );
+               assert.strictEqual( this.handlebarsProto.ifCond( 'foo', '!==', 
'bar', this.opts ), 'ok' );
+       } );
 
-       // Replace a target entirely: target + type=replace
-       $div.empty().append( this.handlebarsProto.processTemplateGetFragment(
-               Handlebars.compile( '{{#progressiveEnhancement target="~ 
.pgetest" type="replace"}}hello{{/progressiveEnhancement}}<div 
class="pgetest">foo</div>' )
-       ) );
-       strictEqual(
-               $div.html(),
-               'hello',
-               'progressiveEnhancement should replace target node.'
-       );
+       QUnit.test( 'Handlebars.prototype.ifAnonymous', 2, function () {
+               strictEqual( this.handlebarsProto.ifAnonymous( this.opts ), 
'ok', 'User should be anonymous first time.' );
+               strictEqual( this.handlebarsProto.ifAnonymous( this.opts ), 
'nope', 'User should be logged in on second call.' );
+       } );
 
-       // Insert before a target: target + type=insert
-       $div.empty().append(
-               this.handlebarsProto.processTemplateGetFragment(
-                       Handlebars.compile( '{{#progressiveEnhancement 
target="~ .pgetest" type="insert"}}hello{{/progressiveEnhancement}}<div 
class="pgetest">foo</div>' )
-               )
-       );
-       strictEqual(
-               $div.html(),
-               'hello<div class="pgetest">foo</div>',
-               'progressiveEnhancement should insert before target.'
-       );
+       QUnit.test( 'Handlebars.prototype.concat', 2, function () {
+               strictEqual( this.handlebarsProto.concat( 'a', 'b', 'c', 
this.opts ), 'abc', 'Check concat working fine.' );
+               strictEqual( this.handlebarsProto.concat( this.opts ), '', 
'Without arguments.' );
+       } );
 
-       // Replace target's content: target + type=content
-       $div.empty().append(
-               this.handlebarsProto.processTemplateGetFragment(
-                       Handlebars.compile( '{{#progressiveEnhancement 
target="~ .pgetest" type="content"}}hello{{/progressiveEnhancement}}<div 
class="pgetest">foo</div>' )
-               )
-       );
-       strictEqual(
-               $div.html(),
-               '<div class="pgetest">hello</div>',
-               'progressiveEnhancement should replace target content.'
-       );
-} );
+       QUnit.test( 'Handlebars.prototype.progressiveEnhancement', 5, function 
() {
+               var opts = $.extend( { hash: { type: 'insert', target: 'abc', 
id: 'def' } }, this.opts ),
+                       $div = $( document.createElement( 'div' ) );
+
+               // Render script tag
+               strictEqual(
+                       this.handlebarsProto.progressiveEnhancement( opts 
).string,
+                       '<scr' + 'ipt' +
+                               ' 
type="text/x-handlebars-template-progressive-enhancement"' +
+                               ' data-type="' + opts.hash.type + '"' +
+                               ' data-target="' + opts.hash.target + '"' +
+                               ' id="' + opts.hash.id + '">' +
+                               'ok' +
+                               '</scr' + 'ipt>',
+                       'Should output exact replica of script tag.'
+               );
+
+               // Replace itself: no target (default to self), no type 
(default to insert)
+               $div.empty().append( 
this.handlebarsProto.processTemplateGetFragment(
+                       Handlebars.compile( 
'{{#progressiveEnhancement}}hello{{/progressiveEnhancement}}' )
+               ) );
+               strictEqual(
+                       $div.html(),
+                       'hello',
+                       'progressiveEnhancement should be processed in template 
string.'
+               );
+
+               // Replace a target entirely: target + type=replace
+               $div.empty().append( 
this.handlebarsProto.processTemplateGetFragment(
+                       Handlebars.compile( '{{#progressiveEnhancement 
target="~ .pgetest" type="replace"}}hello{{/progressiveEnhancement}}<div 
class="pgetest">foo</div>' )
+               ) );
+               strictEqual(
+                       $div.html(),
+                       'hello',
+                       'progressiveEnhancement should replace target node.'
+               );
+
+               // Insert before a target: target + type=insert
+               $div.empty().append(
+                       this.handlebarsProto.processTemplateGetFragment(
+                               Handlebars.compile( '{{#progressiveEnhancement 
target="~ .pgetest" type="insert"}}hello{{/progressiveEnhancement}}<div 
class="pgetest">foo</div>' )
+                       )
+               );
+               strictEqual(
+                       $div.html(),
+                       'hello<div class="pgetest">foo</div>',
+                       'progressiveEnhancement should insert before target.'
+               );
+
+               // Replace target's content: target + type=content
+               $div.empty().append(
+                       this.handlebarsProto.processTemplateGetFragment(
+                               Handlebars.compile( '{{#progressiveEnhancement 
target="~ .pgetest" type="content"}}hello{{/progressiveEnhancement}}<div 
class="pgetest">foo</div>' )
+                       )
+               );
+               strictEqual(
+                       $div.html(),
+                       '<div class="pgetest">hello</div>',
+                       'progressiveEnhancement should replace target content.'
+               );
+       } );
 
 }( jQuery ) );
diff --git a/tests/qunit/flow/dm/test_mw.flow.dm.Content.js 
b/tests/qunit/flow/dm/test_mw.flow.dm.Content.js
index 96c7527..9c0bf34 100644
--- a/tests/qunit/flow/dm/test_mw.flow.dm.Content.js
+++ b/tests/qunit/flow/dm/test_mw.flow.dm.Content.js
@@ -4,12 +4,12 @@
 
 QUnit.test( 'Stores different content representations (formats)', function ( 
assert ) {
        var content = new mw.flow.dm.Content( {
-                       content: 'content in default format (wikitext, for 
instance)',
-                       format: 'wikitext',
-                       html: 'content in html format',
-                       plaintext: 'content in plaintext format',
-                       someNewFormat: 'content in some new format'
-               } );
+               content: 'content in default format (wikitext, for instance)',
+               format: 'wikitext',
+               html: 'content in html format',
+               plaintext: 'content in plaintext format',
+               someNewFormat: 'content in some new format'
+       } );
 
        assert.equal( content.get( 'html' ), 'content in html format' );
        assert.equal( content.get( 'wikitext' ), 'content in default format 
(wikitext, for instance)' );
diff --git a/tests/qunit/flow/dm/test_mw.flow.dm.Topic.js 
b/tests/qunit/flow/dm/test_mw.flow.dm.Topic.js
index 1525b8a..6066a3b 100644
--- a/tests/qunit/flow/dm/test_mw.flow.dm.Topic.js
+++ b/tests/qunit/flow/dm/test_mw.flow.dm.Topic.js
@@ -3,12 +3,13 @@
 /* Tests */
 
 QUnit.test( 'Load topics', function ( assert ) {
-       var i, j, ilen, jlen, topic, result, operation,
+       var i, j, ilen, jlen, topic, result, operation, cases,
                executeOperation = function ( obj, operation, params ) {
                        return obj[ operation ].apply( obj, params );
                },
-               expectCount = 0,
-               cases = [
+               expectCount = 0;
+
+       cases = [
                {
                        args: {
                                id: 'sgl9yjs9nwgmc7l7',

-- 
To view, visit https://gerrit.wikimedia.org/r/322359
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I0dd25614768dcc3258aedb2e6e2721af159834c4
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/Flow
Gerrit-Branch: master
Gerrit-Owner: Esanders <esand...@wikimedia.org>

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to