From 960ecf8103a3b735dd8907ac44f6537a84bf32e0 Mon Sep 17 00:00:00 2001 From: Ed Sanders Date: Thu, 13 Aug 2015 17:39:24 +0100 Subject: [PATCH] Convert save checkboxes to OOUI widgets Anything with type=checkbox is coverted, and its label is found, either by for=id or by finding a wrapping label. All other inputs are stored in $otherFields for form building. Bug: T86617 Bug: T70572 Change-Id: I94376fef18d02d2058bb548c11ae17f3dec7268c --- .../ve.init.mw.DesktopArticleTarget.js | 115 ++++++++++++------ .../ve-mw/ui/dialogs/ve.ui.MWSaveDialog.js | 27 ++-- .../ui/styles/dialogs/ve.ui.MWSaveDialog.css | 13 +- 3 files changed, 98 insertions(+), 57 deletions(-) diff --git a/modules/ve-mw/init/targets/ve.init.mw.DesktopArticleTarget.js b/modules/ve-mw/init/targets/ve.init.mw.DesktopArticleTarget.js index aaee6f367c..f54f88799e 100644 --- a/modules/ve-mw/init/targets/ve.init.mw.DesktopArticleTarget.js +++ b/modules/ve-mw/init/targets/ve.init.mw.DesktopArticleTarget.js @@ -43,7 +43,9 @@ ve.init.mw.DesktopArticleTarget = function VeInitMwDesktopArticleTarget( config this.toolbarSetupDeferred = null; this.welcomeDialog = null; this.welcomeDialogPromise = null; - this.$checkboxes = null; + this.checkboxFields = null; + this.checkboxesByName = null; + this.$otherFields = null; // If this is true then #transformPage / #restorePage will not call pushState // This is to avoid adding a new history entry for the url we just got from onpopstate @@ -231,25 +233,68 @@ ve.init.mw.DesktopArticleTarget.prototype.loadSuccess = function ( response ) { // Parent method ve.init.mw.DesktopArticleTarget.super.prototype.loadSuccess.apply( this, arguments ); - var data = response ? response.visualeditor : {}; + var $checkboxes, defaults, + target = this, + data = response ? response.visualeditor : {}; + + this.checkboxFields = []; + this.checkboxesByName = {}; + this.$otherFields = $( [] ); + if ( data.checkboxes ) { - this.$checkboxes = $( ve.getObjectValues( data.checkboxes ).join( '' ) ); - // Populate checkboxes with default values for minor and watch - this.$checkboxes - .filter( '#wpMinoredit' ) - .prop( 'checked', mw.user.options.get( 'minordefault' ) ) - .end() - .filter( '#wpWatchthis' ) - .prop( 'checked', - mw.user.options.get( 'watchdefault' ) || - ( mw.user.options.get( 'watchcreations' ) && !this.pageExists ) || - data.watched === '' - ); + defaults = { + wpMinoredit: !!mw.user.options.get( 'minordefault' ), + wpWatchthis: !!mw.user.options.get( 'watchdefault' ) || + ( !!mw.user.options.get( 'watchcreations' ) && !this.pageExists ) || + data.watched === '' + }; + + $checkboxes = $( '
' ).html( ve.getObjectValues( data.checkboxes ).join( '' ) ); + $checkboxes.find( 'input[type=checkbox]' ).each( function () { + var $label, title, checkbox, + $this = $( this ), + name = $this.attr( 'name' ), + id = $this.attr( 'id' ); + + if ( !name ) { + // This really shouldn't happen.. + return; + } + + // Label with for=id + if ( id ) { + $label = $checkboxes.find( 'label[for=' + id + ']' ); + } + // Label wrapped input + if ( !$label ) { + $label = $this.closest( 'label' ); + } + if ( $label ) { + title = $label.attr( 'title' ); + $label.find( 'a' ).attr( 'target', '_blank' ); + } + checkbox = new OO.ui.CheckboxInputWidget( { + value: $this.attr( 'value' ), + selected: name && defaults[name] + } ); + // HACK: CheckboxInputWidget doesn't support access keys + checkbox.$input.attr( 'accesskey', $( this ).attr( 'accesskey' ) ); + target.checkboxFields.push( + new OO.ui.FieldLayout( checkbox, { + align: 'inline', + label: $label ? $label.contents() : undefined, + title: title + } ) + ); + target.checkboxesByName[name] = checkbox; + } ); + this.$otherFields = $checkboxes.find( 'input[type!=checkbox]' ); } }; /** * Handle the watch button being toggled on/off. + * * @param {jQuery.Event} e Event object whih triggered the event * @param {string} actionPerformed 'watch' or 'unwatch' */ @@ -257,12 +302,13 @@ ve.init.mw.DesktopArticleTarget.prototype.onWatchToggle = function ( e, actionPe if ( !this.active && !this.activating ) { return; } - this.$checkboxes.filter( '#wpWatchthis' ) - .prop( 'checked', - mw.user.options.get( 'watchdefault' ) || - ( mw.user.options.get( 'watchcreations' ) && !this.pageExists ) || + if ( this.checkboxesByName.wpWatchthis ) { + this.checkboxesByName.wpWatchthis.setSelected( + !!mw.user.options.get( 'watchdefault' ) || + ( !!mw.user.options.get( 'watchcreations' ) && !this.pageExists ) || actionPerformed === 'watch' ); + } }; /** @@ -614,10 +660,7 @@ ve.init.mw.DesktopArticleTarget.prototype.saveComplete = function ( // Just checking for mw.page.watch is not enough because in Firefox // there is Object.prototype.watch... if ( mw.page.hasOwnProperty( 'watch' ) ) { - watchChecked = this.saveDialog.$saveOptions - .find( '.ve-ui-mwSaveDialog-checkboxes' ) - .find( '#wpWatchthis' ) - .prop( 'checked' ); + watchChecked = this.checkboxesByName.wpWatchthis && this.checkboxesByName.wpWatchthis.isSelected(); mw.page.watch.updateWatchLink( $( '#ca-watch a, #ca-unwatch a' ), watchChecked ? 'unwatch' : 'watch' @@ -764,20 +807,23 @@ ve.init.mw.DesktopArticleTarget.prototype.editSource = function () { * @inheritdoc */ ve.init.mw.DesktopArticleTarget.prototype.getSaveFields = function () { - var checkboxFields = {}; + var name, fieldValues = {}; - this.$checkboxes - .each( function () { - var $this = $( this ); - // We can't just use $this.val() because .val() always returns the value attribute of - // a checkbox even when it's unchecked - if ( $this.prop( 'name' ) && ( $this.prop( 'type' ) !== 'checkbox' || $this.prop( 'checked' ) ) ) { - checkboxFields[$this.prop( 'name' )] = $this.val(); - } - } ); + for ( name in this.checkboxesByName ) { + if ( this.checkboxesByName[name].isSelected() ) { + fieldValues[name] = this.checkboxesByName[name].getValue(); + } + } + this.$otherFields.each( function () { + var $this = $( this ), + name = $this.prop( 'name' ); + if ( name ) { + fieldValues[name] = $this.val(); + } + } ); return ve.extendObject( - checkboxFields, + fieldValues, ve.init.mw.DesktopArticleTarget.super.prototype.getSaveFields.call( this ) ); }; @@ -886,7 +932,8 @@ ve.init.mw.DesktopArticleTarget.prototype.openSaveDialog = function () { windowAction.open( 'mwSave', { target: this, editSummary: this.initialEditSummary, - $checkboxes: this.$checkboxes + checkboxFields: this.checkboxFields, + checkboxesByName: this.checkboxesByName } ); this.initialEditSummary = undefined; }; diff --git a/modules/ve-mw/ui/dialogs/ve.ui.MWSaveDialog.js b/modules/ve-mw/ui/dialogs/ve.ui.MWSaveDialog.js index 26b03728dc..a67db3770f 100644 --- a/modules/ve-mw/ui/dialogs/ve.ui.MWSaveDialog.js +++ b/modules/ve-mw/ui/dialogs/ve.ui.MWSaveDialog.js @@ -27,6 +27,7 @@ ve.ui.MWSaveDialog = function VeUiMwSaveDialog( config ) { this.messages = {}; this.setupDeferred = $.Deferred(); this.target = null; + this.checkboxesByName = null; // Initialization this.$element.addClass( 've-ui-mwSaveDialog' ); @@ -292,8 +293,9 @@ ve.ui.MWSaveDialog.prototype.reset = function () { // Reset summary input this.editSummaryInput.setValue( '' ); // Uncheck minoredit - this.$saveOptions.find( '.ve-ui-mwSaveDialog-checkboxes' ) - .find( '#wpMinoredit' ).prop( 'checked', false ); + if ( this.checkboxesByName.wpMinoredit ) { + this.checkboxesByName.wpMinoredit.setSelected( false ); + } // Clear the diff this.$reviewViewer.empty(); }; @@ -303,19 +305,14 @@ ve.ui.MWSaveDialog.prototype.reset = function () { * * This method is safe to call even when the dialog hasn't been initialized yet. * - * @param {jQuery} $checkboxes jQuery collection of checkboxes + * @param {OO.ui.FieldLayout[]} checkboxFields Checkbox fields */ -ve.ui.MWSaveDialog.prototype.setupCheckboxes = function ( $checkboxes ) { +ve.ui.MWSaveDialog.prototype.setupCheckboxes = function ( checkboxFields ) { var dialog = this; this.setupDeferred.done( function () { - dialog.$saveOptions.find( '.ve-ui-mwSaveDialog-checkboxes' ) - .empty() - .append( $checkboxes ) - .find( 'a' ) - .attr( 'target', '_blank' ) - .end() - .find( 'input' ) - .prop( 'tabIndex', 0 ); + checkboxFields.forEach( function ( field ) { + dialog.$saveCheckboxes.append( field.$element ); + } ); } ); }; @@ -388,8 +385,9 @@ ve.ui.MWSaveDialog.prototype.initialize = function () { ); } ); + this.$saveCheckboxes = $( '
' ).addClass( 've-ui-mwSaveDialog-checkboxes' ); this.$saveOptions = $( '
' ).addClass( 've-ui-mwSaveDialog-options' ).append( - $( '
' ).addClass( 've-ui-mwSaveDialog-checkboxes' ), + this.$saveCheckboxes, this.editSummaryCountLabel.$element ); this.$saveMessages = $( '
' ); @@ -477,7 +475,8 @@ ve.ui.MWSaveDialog.prototype.getSetupProcess = function ( data ) { if ( data.editSummary !== undefined ) { this.setEditSummary( data.editSummary ); } - this.setupCheckboxes( data.$checkboxes ); + this.setupCheckboxes( data.checkboxFields ); + this.checkboxesByName = data.checkboxesByName || {}; // Old messages should not persist this.clearAllMessages(); this.swapPanel( 'save' ); diff --git a/modules/ve-mw/ui/styles/dialogs/ve.ui.MWSaveDialog.css b/modules/ve-mw/ui/styles/dialogs/ve.ui.MWSaveDialog.css index 5756a5ecc2..3d1f158e4e 100644 --- a/modules/ve-mw/ui/styles/dialogs/ve.ui.MWSaveDialog.css +++ b/modules/ve-mw/ui/styles/dialogs/ve.ui.MWSaveDialog.css @@ -35,17 +35,12 @@ .ve-ui-mwSaveDialog-checkboxes { margin-right: 3.25em; /* Hack to prevent overlap on edit summary count */ - line-height: 3em; - padding: 0 0.75em; + padding: 0.2em 0.75em 0 0.75em; } -.ve-ui-mwSaveDialog-checkboxes label { - padding-right: 0.75em; - vertical-align: middle; -} - -.ve-ui-mwSaveDialog-checkboxes input { - vertical-align: middle; +.ve-ui-mwSaveDialog-checkboxes .oo-ui-fieldLayout { + display: inline-block; + margin: 0 1.5em 0 0; } .ve-ui-mwSaveDialog-editSummary-count {