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