jenkins-bot has submitted this change and it was merged. ( https://gerrit.wikimedia.org/r/361921 )
Change subject: Inspectors for editing LanguageConverter markup ...................................................................... Inspectors for editing LanguageConverter markup Basic inspectors for editing. Changing from block to inline, or adding "hidden" or "describe" flags not supported. The UI layout for two-way and one-way rules could certainly be improved. Bug: T49411 Change-Id: I5ce29e4bf47abf509afde0a57f64b5d1189f5185 --- M extension.json M modules/ve-mw/i18n/en.json M modules/ve-mw/i18n/qqq.json M modules/ve-mw/ui/contextitems/ve.ui.MWLanguageVariantNodeContextItem.js A modules/ve-mw/ui/inspectors/ve.ui.MWLanguageVariantInspector.js 5 files changed, 938 insertions(+), 3 deletions(-) Approvals: jenkins-bot: Verified Jforrester: Looks good to me, approved diff --git a/extension.json b/extension.json index f986095..92aa105 100644 --- a/extension.json +++ b/extension.json @@ -1973,7 +1973,8 @@ "scripts": [ "modules/ve-mw/dm/nodes/ve.dm.MWLanguageVariantNode.js", "modules/ve-mw/ce/nodes/ve.ce.MWLanguageVariantNode.js", - "modules/ve-mw/ui/contextitems/ve.ui.MWLanguageVariantNodeContextItem.js" + "modules/ve-mw/ui/contextitems/ve.ui.MWLanguageVariantNodeContextItem.js", + "modules/ve-mw/ui/inspectors/ve.ui.MWLanguageVariantInspector.js" ], "styles": [ "modules/ve-mw/ui/styles/contextitems/ve.ui.MWLanguageVariantNodeContextItem.css", @@ -2004,7 +2005,24 @@ "visualeditor-mwlanguagevariantcontextitem-title-name", "visualeditor-mwlanguagevariantcontextitem-title-oneway", "visualeditor-mwlanguagevariantcontextitem-title-twoway", - "visualeditor-mwlanguagevariantcontextitem-title-unknown" + "visualeditor-mwlanguagevariantcontextitem-title-unknown", + "visualeditor-mwlanguagevariantinspector-disabled-placeholder", + "visualeditor-mwlanguagevariantinspector-filter-langs-label", + "visualeditor-mwlanguagevariantinspector-filter-langs-placeholder", + "visualeditor-mwlanguagevariantinspector-filter-text-label", + "visualeditor-mwlanguagevariantinspector-filter-text-placeholder", + "visualeditor-mwlanguagevariantinspector-oneway-add-button", + "visualeditor-mwlanguagevariantinspector-oneway-clear-button", + "visualeditor-mwlanguagevariantinspector-oneway-from-text-placeholder", + "visualeditor-mwlanguagevariantinspector-oneway-to-text-placeholder", + "visualeditor-mwlanguagevariantinspector-title-disabled", + "visualeditor-mwlanguagevariantinspector-title-filter", + "visualeditor-mwlanguagevariantinspector-title-name", + "visualeditor-mwlanguagevariantinspector-title-oneway", + "visualeditor-mwlanguagevariantinspector-title-twoway", + "visualeditor-mwlanguagevariantinspector-twoway-add-button", + "visualeditor-mwlanguagevariantinspector-twoway-clear-button", + "visualeditor-mwlanguagevariantinspector-twoway-text-placeholder" ], "targets": [ "desktop", diff --git a/modules/ve-mw/i18n/en.json b/modules/ve-mw/i18n/en.json index fb9af38..77362bc 100644 --- a/modules/ve-mw/i18n/en.json +++ b/modules/ve-mw/i18n/en.json @@ -343,6 +343,23 @@ "visualeditor-mwlanguagevariantcontextitem-title-oneway": "One-way conversion rule", "visualeditor-mwlanguagevariantcontextitem-title-twoway": "Language conversion rule", "visualeditor-mwlanguagevariantcontextitem-title-unknown": "Language variant", + "visualeditor-mwlanguagevariantinspector-disabled-placeholder": "Text protected from variant conversion", + "visualeditor-mwlanguagevariantinspector-filter-langs-label": "Languages", + "visualeditor-mwlanguagevariantinspector-filter-langs-placeholder": "Language code", + "visualeditor-mwlanguagevariantinspector-filter-text-label": "Contents", + "visualeditor-mwlanguagevariantinspector-filter-text-placeholder": "Filtered text", + "visualeditor-mwlanguagevariantinspector-oneway-add-button": "Add new case", + "visualeditor-mwlanguagevariantinspector-oneway-clear-button": "Remove case", + "visualeditor-mwlanguagevariantinspector-oneway-from-text-placeholder": "Source text", + "visualeditor-mwlanguagevariantinspector-oneway-to-text-placeholder": "Variant text", + "visualeditor-mwlanguagevariantinspector-title-disabled": "Variant conversion disabled", + "visualeditor-mwlanguagevariantinspector-title-filter": "Variant filter", + "visualeditor-mwlanguagevariantinspector-title-name": "Language name", + "visualeditor-mwlanguagevariantinspector-title-oneway": "One-way conversion rule", + "visualeditor-mwlanguagevariantinspector-title-twoway": "Language conversion rule", + "visualeditor-mwlanguagevariantinspector-twoway-add-button": "Add new case", + "visualeditor-mwlanguagevariantinspector-twoway-clear-button": "Remove case", + "visualeditor-mwlanguagevariantinspector-twoway-text-placeholder": "Variant text", "visualeditor-mwpredialog-title": "Preformatted plain text", "visualeditor-mwpredialog-convert": "Allow text styling", "visualeditor-mwsignature-tool": "Your signature", diff --git a/modules/ve-mw/i18n/qqq.json b/modules/ve-mw/i18n/qqq.json index 3012d58..ff85a1a 100644 --- a/modules/ve-mw/i18n/qqq.json +++ b/modules/ve-mw/i18n/qqq.json @@ -356,6 +356,23 @@ "visualeditor-mwlanguagevariantcontextitem-title-oneway": "Title shown at top of context item for a one-way language variant conversion rule", "visualeditor-mwlanguagevariantcontextitem-title-twoway": "Title shown at top of context item for a standard language variant conversion rule", "visualeditor-mwlanguagevariantcontextitem-title-unknown": "Title shown at top of context item for a language variant markup of unknown type", + "visualeditor-mwlanguagevariantinspector-disabled-placeholder": "Placeholder text shown when there are no contents in a language variant markup disabled region", + "visualeditor-mwlanguagevariantinspector-filter-langs-label": "Label shown above list of languages for a language variant filter", + "visualeditor-mwlanguagevariantinspector-filter-langs-placeholder": "Placeholder text shown in the language selection list", + "visualeditor-mwlanguagevariantinspector-filter-text-label": "Label shown above area used to edit the contents of a language variant filter", + "visualeditor-mwlanguagevariantinspector-filter-text-placeholder": "Placeholder text shown when there are no contents in a language variant filter", + "visualeditor-mwlanguagevariantinspector-oneway-add-button": "Label on button to add a new case to a one-way language conversion rule.", + "visualeditor-mwlanguagevariantinspector-oneway-clear-button": "Label on button to remove a case from a one-way language conversion rule.", + "visualeditor-mwlanguagevariantinspector-oneway-from-text-placeholder": "Placeholder for source text on a new one-way conversion rule case.", + "visualeditor-mwlanguagevariantinspector-oneway-to-text-placeholder": "Placeholder for destination text on a new one-way conversion rule case.", + "visualeditor-mwlanguagevariantinspector-title-disabled": "Title shown at top of inspector where language variant conversion is disabled.\n\nSee also:\n* {{msg-mw|Visualeditor-mwlanguagevariantcontextitem-title-disabled}}", + "visualeditor-mwlanguagevariantinspector-title-filter": "Title shown at top of inspector where language variants are filtered for conversion.\n\nSee also:\n* {{msg-mw|Visualeditor-mwlanguagevariantcontextitem-title-filter}}", + "visualeditor-mwlanguagevariantinspector-title-name": "Title shown at top of inspector where localized name of language is converted.\n\nSee also:\n* {{msg-mw|Visualeditor-mwlanguagevariantcontextitem-title-name}}", + "visualeditor-mwlanguagevariantinspector-title-oneway": "Title shown at top of inspector for a one-way language variant conversion rule.\n\nSee also:\n* {{msg-mw|Visualeditor-mwlanguagevariantcontextitem-title-oneway}}", + "visualeditor-mwlanguagevariantinspector-title-twoway": "Title shown at top of inspector for a standard language variant conversion rule.\n\nSee also:\n* {{msg-mw|Visualeditor-mwlanguagevariantcontextitem-title-twoway}}", + "visualeditor-mwlanguagevariantinspector-twoway-add-button": "Label on button to add a new case to a language conversion rule.", + "visualeditor-mwlanguagevariantinspector-twoway-clear-button": "Label on button to remove a case from a language conversion rule.", + "visualeditor-mwlanguagevariantinspector-twoway-text-placeholder": "Placeholder for text on a new conversion rule case.", "visualeditor-mwpredialog-title": "Title for the preformatted text dialog", "visualeditor-mwpredialog-convert": "Label for a button which will convert the preformatted text block into a form which can have text styles applied (e.g. bold, italic, links).", "visualeditor-mwsignature-tool": "Used as name of the tool for inserting signatures.", diff --git a/modules/ve-mw/ui/contextitems/ve.ui.MWLanguageVariantNodeContextItem.js b/modules/ve-mw/ui/contextitems/ve.ui.MWLanguageVariantNodeContextItem.js index f70ce25..7c5b07e 100644 --- a/modules/ve-mw/ui/contextitems/ve.ui.MWLanguageVariantNodeContextItem.js +++ b/modules/ve-mw/ui/contextitems/ve.ui.MWLanguageVariantNodeContextItem.js @@ -41,7 +41,7 @@ ve.dm.MWLanguageVariantHiddenNode ]; -ve.ui.MWLanguageVariantNodeContextItem.static.editable = false; +ve.ui.MWLanguageVariantNodeContextItem.static.commandName = 'mwLanguageVariant'; /* Methods */ @@ -180,6 +180,27 @@ } ); }; +/** + * @inheritdoc + */ +ve.ui.MWLanguageVariantNodeContextItem.prototype.getCommand = function () { + var type = this.model.getRuleType(), + cmdName = this.constructor.static.commandName + '-' + type; + return this.context.getSurface().commandRegistry.lookup( cmdName ); +}; + /* Registration */ ve.ui.contextItemFactory.register( ve.ui.MWLanguageVariantNodeContextItem ); + +[ 'disabled', 'filter', 'name', 'twoway', 'oneway' ].forEach( function ( type ) { + ve.ui.commandRegistry.register( + new ve.ui.Command( + 'mwLanguageVariant-' + type, 'window', 'open', + { + args: [ 'mwLanguageVariant-' + type ], + supportedSelections: [ 'linear' ] + } + ) + ); +} ); diff --git a/modules/ve-mw/ui/inspectors/ve.ui.MWLanguageVariantInspector.js b/modules/ve-mw/ui/inspectors/ve.ui.MWLanguageVariantInspector.js new file mode 100644 index 0000000..6d4e6b6 --- /dev/null +++ b/modules/ve-mw/ui/inspectors/ve.ui.MWLanguageVariantInspector.js @@ -0,0 +1,862 @@ +/*! + * VisualEditor UserInterface LanguageVariantInspector class. + * + * @copyright 2011-2017 VisualEditor Team and others; see http://ve.mit-license.org + */ + +/** + * Inspector for a ve.dm.MWLanguageVariantNode. + * + * @class + * @extends ve.ui.NodeInspector + * + * @constructor + * @param {Object} [config] Configuration options + */ +ve.ui.MWLanguageVariantInspector = function VeUiMWLanguageVariantInspector() { + // Parent constructor + ve.ui.MWLanguageVariantInspector.super.apply( this, arguments ); +}; + +/* Inheritance */ + +OO.inheritClass( ve.ui.MWLanguageVariantInspector, ve.ui.NodeInspector ); + +/* Static properties */ + +ve.ui.MWLanguageVariantInspector.static.name = 'mwLanguageVariant-disabled'; + +ve.ui.MWLanguageVariantInspector.static.title = OO.ui.deferMsg( + 'visualeditor-mwlanguagevariantinspector-title-disabled' +); + +ve.ui.MWLanguageVariantInspector.static.modelClasses = [ + ve.dm.MWLanguageVariantBlockNode, + ve.dm.MWLanguageVariantInlineNode, + ve.dm.MWLanguageVariantHiddenNode +]; + +ve.ui.MWLanguageVariantInspector.static.size = 'large'; + +ve.ui.MWLanguageVariantInspector.static.actions = [ + { + action: 'remove', + label: OO.ui.deferMsg( 'visualeditor-inspector-remove-tooltip' ), + flags: 'destructive', + modes: 'edit' + } +].concat( ve.ui.MWLanguageVariantInspector.super.static.actions ); + +ve.ui.MWLanguageVariantInspector.static.defaultVariantType = + 'mwLanguageVariantInline'; + +ve.ui.MWLanguageVariantInspector.static.includeCommands = null; + +// This is very similar to the exclude list in ve.ui.MWMediaDialog +ve.ui.MWLanguageVariantInspector.static.excludeCommands = [ + // No formatting + 'paragraph', + 'heading1', + 'heading2', + 'heading3', + 'heading4', + 'heading5', + 'heading6', + 'preformatted', + 'blockquote', + // TODO: Decide if tables tools should be allowed + 'tableCellHeader', + 'tableCellData', + // No structure + 'bullet', + 'bulletWrapOnce', + 'number', + 'numberWrapOnce', + 'indent', + 'outdent' +]; + +/** + * Get the import rules for embedded target widgets in this inspector. + * + * @see ve.ui.MWMediaDialog#getImportRules + * @return {Object} Import rules + */ +ve.ui.MWLanguageVariantInspector.static.getImportRules = function () { + return ve.extendObject( + ve.copy( ve.init.target.constructor.static.importRules ), + { + // TODO: We might want to include some of the + // conversion/sanitization done by ve.ui.MWMediaDialog + } + ); +}; + +/* Methods */ + +/** + * Return a valid `variantInfo` object which will be used when a new + * node of this subclass is inserted. For instance, + * ve.ui.MWLanguageVariantDisabledInspector will return the appropriate + * object to use when the equivalent of wikitext `-{}-` is inserted + * in the document. + * + * @method + * @return {Object} + */ +ve.ui.MWLanguageVariantInspector.prototype.getDefaultVariantInfo = function () { + throw new Error( 'getDefaultVariantInfo must be implemented by subclass' ); +}; + +/* eslint-disable no-unused-vars */ +/** + * Convert the current inspector state to new content which can be used + * to update the ve.dm.SurfaceFragment backing this inspector. + * + * @method + * @param {Object} An existing variantInfo object for this node, which will be + * mutated to update it with the latest content from this inspector. + * @return {string|Array} New content for the ve.dm.SurfaceFragment + * being inspected/updated. + */ +ve.ui.MWLanguageVariantInspector.prototype.getContentFromInspector = function ( variantInfo ) { + throw new Error( 'getContentFromInspector must be implemented by subclass' ); +}; +/* eslint-enable no-unused-vars */ + +// Helper functions for creating sub-document editors for embedded HTML + +/** + * Helper function to create a subdocument editor for HTML embedded in the + * language variant node. + * + * @method + * @param {String} [placeholder] Placeholder text for this editor. + * @return {ve.ui.TargetWidget} + */ +ve.ui.MWLanguageVariantInspector.prototype.createTextTarget = function ( placeholder ) { + return ve.init.target.createTargetWidget( { + tools: ve.init.target.constructor.static.toolbarGroups, + includeCommands: this.constructor.static.includeCommands, + excludeCommands: this.constructor.static.excludeCommands, + importRules: this.constructor.static.getImportRules(), + inDialog: this.constructor.static.name, + placeholder: placeholder || null + } ); +}; + +/** + * Helper function to initialize a ve.ui.TargetWidget with a given HTML + * string extracted from the language variant node. + * + * @method + * @param {ve.ui.TargetWidget} [textTarget] A subdocument editor widget + * created by ve.ui.MWLanguageVariantInspector#createTextTarget. + * @param {String} [htmlString] The HTML string extracted from this node. + * @return {ve.Document} The document model now backing the widget. + */ +ve.ui.MWLanguageVariantInspector.prototype.setupTextTargetDoc = function ( textTarget, htmlString ) { + var doc = this.variantNode.getDocument().newFromHtml( htmlString ); + textTarget.setDocument( doc ); + textTarget.initialize(); + return doc; +}; + +/** + * Helper function to serialize the document backing a `ve.ui.TargetWidget` + * back into HTML which can be embedded into the language variant node. + * This method needs to do a bit of hackery to remove unnecessary p-wrapping + * and (TODO) determine if an inline node needs to be converted to a + * block node or vice-versa. + * + * @method + * @param {ve.Document} The document backing an editor widget, as returned + * by ve.ui.MWLanguageVariantInspector#setupTextTargetDoc. + * @return {String} An HTML string appropriate for embedding into a + * language variant node. + */ +ve.ui.MWLanguageVariantInspector.prototype.getHtmlForDoc = function ( doc ) { + var surface = new ve.dm.Surface( doc ), + targetHtmlDoc; + + // Remove outermost p-wrapping, if present + try { + surface.change( + ve.dm.TransactionBuilder.static.newFromWrap( doc, new ve.Range( 0, doc.data.countNonInternalElements() ), [], [], [ { type: 'paragraph' } ], [] ) + ); + } catch ( e ) { + // Sometimes there is no p-wrapping, for example: "* foo" + // Sometimes there are multiple <p> tags in the output. + // That's okay: ignore the error and use what we've got. + } + // XXX return a flag to indicate whether contents are now inline or block? + targetHtmlDoc = ve.dm.converter.getDomFromModel( doc, false ); + return ve.properInnerHtml( targetHtmlDoc.body ); +}; + +// Inspector implementation + +/** + * Handle frame ready events. + * + * @method + */ +ve.ui.MWLanguageVariantInspector.prototype.initialize = function () { + // Parent method + ve.ui.MWLanguageVariantInspector.super.prototype.initialize.call( this ); + this.$content.addClass( 've-ui-mwLanguageVariantInspector-content' ); +}; + +/** + * @inheritdoc + */ +ve.ui.MWLanguageVariantInspector.prototype.getActionProcess = function ( action ) { + if ( action === 'remove' || action === 'insert' ) { + return new OO.ui.Process( function () { + this.close( { action: action } ); + }, this ); + } + return ve.ui.MWLanguageVariantInspector.super.prototype.getActionProcess.call( this, action ); +}; + +/** + * Handle the inspector being setup. + * + * @method + * @param {Object} [data] Inspector opening data + * @return {OO.ui.Process} + */ +ve.ui.MWLanguageVariantInspector.prototype.getSetupProcess = function ( data ) { + return ve.ui.MWLanguageVariantInspector.super.prototype.getSetupProcess.call( this, data ) + .next( function () { + this.getFragment().getSurface().pushStaging(); + + this.variantNode = this.getSelectedNode(); + if ( !this.variantNode ) { + this.getFragment().insertContent( [ + { + type: this.constructor.static.defaultVariantType, + attributes: { + variantInfo: this.getDefaultVariantInfo() + } + }, + { type: '/' + this.constructor.static.defaultVariantType } + ] ).select(); + this.variantNode = this.getSelectedNode(); + } + }, this ); +}; + +/** + * @inheritdoc + */ +ve.ui.MWLanguageVariantInspector.prototype.getTeardownProcess = function ( data ) { + data = data || {}; + return ve.ui.MWLanguageVariantInspector.super.prototype.getTeardownProcess.call( this, data ) + .first( function () { + var surfaceModel = this.getFragment().getSurface(), + newContent; + + if ( data.action === 'remove' ) { + surfaceModel.popStaging(); + // If popStaging removed the node then this will be a no-op + this.getFragment().removeContent(); + } else if ( data.action === 'done' ) { + // Edit language variant node + newContent = this.getContentFromInspector( + ve.copy( this.variantNode.getVariantInfo() ) + ); + if ( newContent[ 0 ].type === this.variantNode.getType() ) { + this.getFragment().changeAttributes( { + variantInfo: newContent[ 0 ].attributes.variantInfo + } ); + } else { + this.getFragment().removeContent(); + this.getFragment().insertContent( newContent ).select(); + this.variantNode = this.getSelectedNode(); + } + surfaceModel.applyStaging(); + } else { + surfaceModel.popStaging(); + } + + }, this ); +}; + +/* Subclasses */ + +/** + * Editor for "disabled" rules. + * + * @class + * @extends ve.ui.MWLanguageVariantInspector + * + * @constructor + * @param {Object} [config] Configuration options +*/ +ve.ui.MWLanguageVariantDisabledInspector = function VeUiMWLanguageVariantDisabledInspector() { + ve.ui.MWLanguageVariantDisabledInspector.super.apply( this, arguments ); +}; + +/* Inheritance */ + +OO.inheritClass( ve.ui.MWLanguageVariantDisabledInspector, ve.ui.MWLanguageVariantInspector ); + +/* Static properties */ + +ve.ui.MWLanguageVariantDisabledInspector.static.name = 'mwLanguageVariant-disabled'; + +ve.ui.MWLanguageVariantDisabledInspector.static.title = OO.ui.deferMsg( + 'visualeditor-mwlanguagevariantinspector-title-disabled' +); + +/* Methods */ + +ve.ui.MWLanguageVariantDisabledInspector.prototype.initialize = function () { + ve.ui.MWLanguageVariantDisabledInspector.super.prototype.initialize.call( this ); + this.textTarget = this.createTextTarget( OO.ui.msg( + 'visualeditor-mwlanguagevariantinspector-disabled-placeholder' + ) ); + this.form.$element.append( this.textTarget.$element ); +}; + +ve.ui.MWLanguageVariantDisabledInspector.prototype.getDefaultVariantInfo = function () { + return { disabled: { t: '' } }; +}; + +ve.ui.MWLanguageVariantDisabledInspector.prototype.getSetupProcess = function ( data ) { + return ve.ui.MWLanguageVariantDisabledInspector.super.prototype.getSetupProcess.call( this, data ).next( function () { + var variantInfo = this.variantNode.getVariantInfo(); + this.textTargetDoc = this.setupTextTargetDoc( + this.textTarget, + variantInfo.disabled.t + ); + }, this ); +}; + +ve.ui.MWLanguageVariantDisabledInspector.prototype.getContentFromInspector = function ( variantInfo ) { + // TODO should allow type to depend on targetHtmlDoc, maybe switch + // from inline to block. + var type = this.variantNode.getType(); + variantInfo.disabled.t = this.getHtmlForDoc( this.textTargetDoc ); + return [ + { + type: type, + attributes: { variantInfo: variantInfo } + }, + { + type: '/' + type + } + ]; +}; + +ve.ui.MWLanguageVariantDisabledInspector.prototype.getReadyProcess = function ( data ) { + return ve.ui.MWLanguageVariantDisabledInspector.super.prototype.getReadyProcess.call( this, data ) + .next( function () { + this.textTarget.focus(); + }, this ); +}; + +ve.ui.MWLanguageVariantDisabledInspector.prototype.getTeardownProcess = function ( data ) { + return ve.ui.MWLanguageVariantDisabledInspector.super.prototype.getTeardownProcess.call( this, data ) + .next( function () { + // Reset inspector + this.textTarget.clear(); + this.textTargetDoc = null; + }, this ); +}; + +/** + * Editor for "name" rules. + * + * @class + * @extends ve.ui.MWLanguageVariantInspector + * + * @constructor + * @param {Object} [config] Configuration options +*/ +ve.ui.MWLanguageVariantNameInspector = function VeUiMWLanguageVariantNameInspector() { + ve.ui.MWLanguageVariantNameInspector.super.apply( this, arguments ); +}; + +/* Inheritance */ + +OO.inheritClass( ve.ui.MWLanguageVariantNameInspector, ve.ui.MWLanguageVariantInspector ); + +/* Static properties */ + +ve.ui.MWLanguageVariantNameInspector.static.name = 'mwLanguageVariant-name'; + +ve.ui.MWLanguageVariantNameInspector.static.title = OO.ui.deferMsg( + 'visualeditor-mwlanguagevariantinspector-title-name' +); + +/* Methods */ + +ve.ui.MWLanguageVariantNameInspector.prototype.initialize = function () { + ve.ui.MWLanguageVariantNameInspector.super.prototype.initialize.call( this ); + this.languageInput = new ve.ui.LanguageInputWidget( { + dialogManager: this.manager.getSurface().getDialogs(), + dirInput: 'none' + } ); + this.form.$element.append( this.languageInput.$element ); +}; + +ve.ui.MWLanguageVariantNameInspector.prototype.getDefaultVariantInfo = function () { + return { name: { t: mw.config.get( 'wgUserVariant' ) || 'en' } }; +}; + +ve.ui.MWLanguageVariantNameInspector.prototype.getSetupProcess = function ( data ) { + return ve.ui.MWLanguageVariantNameInspector.super.prototype.getSetupProcess.call( this, data ) + .next( function () { + var variantInfo = this.variantNode.getVariantInfo(); + this.languageInput.setLangAndDir( + variantInfo.name.t, + 'auto' + ); + }, this ); +}; + +ve.ui.MWLanguageVariantNameInspector.prototype.getContentFromInspector = function ( variantInfo ) { + var type = this.variantNode.getType(); + variantInfo.name.t = this.languageInput.getLang(); + return [ + { + type: type, + attributes: { variantInfo: variantInfo } + }, + { + type: '/' + type + } + ]; +}; + +/** + * Editor for "filter" rules. + * + * @class + * @extends ve.ui.MWLanguageVariantInspector + * + * @constructor + * @param {Object} [config] Configuration options +*/ +ve.ui.MWLanguageVariantFilterInspector = function VeUiMWLanguageVariantFilterInspector() { + ve.ui.MWLanguageVariantFilterInspector.super.apply( this, arguments ); +}; + +/* Inheritance */ + +OO.inheritClass( ve.ui.MWLanguageVariantFilterInspector, ve.ui.MWLanguageVariantInspector ); + +/* Static properties */ + +ve.ui.MWLanguageVariantFilterInspector.static.name = 'mwLanguageVariant-filter'; + +ve.ui.MWLanguageVariantFilterInspector.static.title = OO.ui.deferMsg( + 'visualeditor-mwlanguagevariantinspector-title-filter' +); + +/* Methods */ + +ve.ui.MWLanguageVariantFilterInspector.prototype.initialize = function () { + ve.ui.MWLanguageVariantFilterInspector.super.prototype.initialize.call( this ); + this.textTarget = this.createTextTarget( OO.ui.msg( + 'visualeditor-mwlanguagevariantinspector-filter-text-placeholder' + ) ); + + this.langWidget = new OO.ui.TagMultiselectWidget( { + allowArbitary: false, + allowDisplayInvalidTags: true, + allowedValues: ve.init.platform.getLanguageCodes().sort(), + placeholder: OO.ui.msg( + 'visualeditor-mwlanguagevariantinspector-filter-langs-placeholder' + ), + icon: 'language' + } ); + this.langWidget.createTagItemWidget = function ( data, label ) { + var name = ve.init.platform.getLanguageName( data.toLowerCase() ); + label = label || ( name ? ( name + ' (' + data + ')' ) : data ); + return OO.ui.TagMultiselectWidget.prototype.createTagItemWidget.call( + this, data, label + ); + }; + + this.form.$element.append( + new OO.ui.FieldLayout( this.langWidget, { + align: 'top', + label: OO.ui.msg( 'visualeditor-mwlanguagevariantinspector-filter-langs-label' ) + } ).$element + ); + this.form.$element.append( + new OO.ui.FieldLayout( this.textTarget, { + align: 'top', + label: OO.ui.msg( 'visualeditor-mwlanguagevariantinspector-filter-text-label' ) + } ).$element + ); +}; + +ve.ui.MWLanguageVariantFilterInspector.prototype.getDefaultVariantInfo = function () { + return { filter: { l: [ mw.config.get( 'wgUserVariant' ) ], t: '' } }; +}; + +ve.ui.MWLanguageVariantFilterInspector.prototype.getSetupProcess = function ( data ) { + return ve.ui.MWLanguageVariantFilterInspector.super.prototype.getSetupProcess.call( this, data ).next( function () { + var variantInfo = this.variantNode.getVariantInfo(); + this.textTargetDoc = this.setupTextTargetDoc( + this.textTarget, + variantInfo.filter.t + ); + this.langWidget.setValue( variantInfo.filter.l ); + }, this ); +}; + +ve.ui.MWLanguageVariantFilterInspector.prototype.getContentFromInspector = function ( variantInfo ) { + // TODO should allow type to depend on targetHtmlDoc, maybe switch + // from inline to block. + var type = this.variantNode.getType(); + variantInfo.filter.t = this.getHtmlForDoc( this.textTargetDoc ); + variantInfo.filter.l = this.langWidget.getValue(); + return [ + { + type: type, + attributes: { variantInfo: variantInfo } + }, + { + type: '/' + type + } + ]; +}; + +ve.ui.MWLanguageVariantFilterInspector.prototype.getReadyProcess = function ( data ) { + return ve.ui.MWLanguageVariantFilterInspector.super.prototype.getReadyProcess.call( this, data ) + .next( function () { + this.textTarget.focus(); + }, this ); +}; + +ve.ui.MWLanguageVariantFilterInspector.prototype.getTeardownProcess = function ( data ) { + return ve.ui.MWLanguageVariantFilterInspector.super.prototype.getTeardownProcess.call( this, data ) + .next( function () { + // Reset inspector + this.langWidget.clearInput(); + this.langWidget.clearItems(); + this.textTarget.clear(); + this.textTargetDoc = null; + }, this ); +}; + +/** + * Editor for "two-way" rules. + * + * @class + * @extends ve.ui.MWLanguageVariantInspector + * + * @constructor + * @param {Object} [config] Configuration options +*/ +ve.ui.MWLanguageVariantTwoWayInspector = function VeUiMWLanguageVariantTwoWayInspector() { + ve.ui.MWLanguageVariantTwoWayInspector.super.apply( this, arguments ); +}; + +/* Inheritance */ + +OO.inheritClass( ve.ui.MWLanguageVariantTwoWayInspector, ve.ui.MWLanguageVariantInspector ); + +/* Static properties */ + +ve.ui.MWLanguageVariantTwoWayInspector.static.name = 'mwLanguageVariant-twoway'; + +ve.ui.MWLanguageVariantTwoWayInspector.static.title = OO.ui.deferMsg( + 'visualeditor-mwlanguagevariantinspector-title-twoway' +); + +/* Methods */ + +ve.ui.MWLanguageVariantTwoWayInspector.prototype.initialize = function () { + ve.ui.MWLanguageVariantTwoWayInspector.super.prototype.initialize.call( this ); + this.items = []; + this.layout = new OO.ui.FieldsetLayout( { } ); + this.form.$element.append( this.layout.$element ); + + this.addButton = new OO.ui.ButtonInputWidget( { + label: OO.ui.msg( 'visualeditor-mwlanguagevariantinspector-twoway-add-button' ), + icon: 'add' + } ); + this.form.$element.append( this.addButton.$element ); + + // Events + this.addButton.connect( this, { click: 'onAddButtonClick' } ); +}; + +ve.ui.MWLanguageVariantTwoWayInspector.prototype.getDefaultVariantInfo = function () { + return { twoway: [ { l: mw.config.get( 'wgUserVariant' ), t: '' } ] }; +}; + +ve.ui.MWLanguageVariantTwoWayInspector.prototype.getSetupProcess = function ( data ) { + return ve.ui.MWLanguageVariantTwoWayInspector.super.prototype.getSetupProcess.call( this, data ).next( function () { + var variantInfo = this.variantNode.getVariantInfo(); + this.layout.clearItems(); this.items = []; + variantInfo.twoway.forEach( function ( tw, idx ) { + this.items[ idx ] = this.createItem( tw.l, tw.t ); + this.layout.addItems( [ this.items[ idx ].layout ] ); + }, this ); + }, this ); +}; + +/** + * Create widgets corresponding to a given mapping given by this rule. + * @method + * @param {String} [lang] The language code for the content text. + * @param {String} [content] The HTML content text. + * @return {Object} An object containing the required widgets and backing + * documents for this mapping item. + */ +ve.ui.MWLanguageVariantTwoWayInspector.prototype.createItem = function ( lang, content ) { + var languageInput, textTarget, clearButton, layout, item; + + languageInput = new ve.ui.LanguageInputWidget( { + dialogManager: this.manager.getSurface().getDialogs(), + dirInput: 'none' + } ); + textTarget = this.createTextTarget( OO.ui.msg( + 'visualeditor-mwlanguagevariantinspector-twoway-text-placeholder' + ) ); + clearButton = new OO.ui.ButtonInputWidget( { + icon: 'clear', + iconTitle: OO.ui.deferMsg( + 'visualeditor-mwlanguagevariantinspector-twoway-clear-button' + ), + framed: false + } ); + layout = new OO.ui.FieldLayout( + new OO.ui.Widget( { + content: [ + new OO.ui.ActionFieldLayout( + languageInput, + clearButton + ), + textTarget + ] + } ), {} + ); + item = { + languageInput: languageInput, + textTarget: textTarget, + clearButton: clearButton, + layout: layout + }; + + // Initialize + item.textTargetDoc = this.setupTextTargetDoc( textTarget, content ); + languageInput.setLangAndDir( lang, 'auto' ); + clearButton.connect( this, { click: [ 'onClearButtonClick', item ] } ); + return item; +}; + +ve.ui.MWLanguageVariantTwoWayInspector.prototype.getContentFromInspector = function ( variantInfo ) { + // TODO should allow type to depend on targetHtmlDoc, maybe switch + // from inline to block. + var type = this.variantNode.getType(); + variantInfo.twoway = this.items.map( function ( item ) { + return { + l: item.languageInput.getLang(), + t: this.getHtmlForDoc( item.textTargetDoc ) + }; + }, this ); + return [ + { + type: type, + attributes: { variantInfo: variantInfo } + }, + { + type: '/' + type + } + ]; +}; + +/** + * Create a new mapping item in the inspector. + * @method + */ +ve.ui.MWLanguageVariantTwoWayInspector.prototype.onAddButtonClick = function () { + var idx = this.items.length; + this.items[ idx ] = this.createItem( mw.config.get( 'wgUserVariant' ), '' ); + this.layout.addItems( [ this.items[ idx ].layout ] ); +}; + +/** + * Remove a mapping item from the inspector. + * @method + */ +ve.ui.MWLanguageVariantTwoWayInspector.prototype.onClearButtonClick = function ( item ) { + var idx = this.items.indexOf( item ); + this.items.splice( idx, 1 ); + this.layout.removeItems( [ item.layout ] ); + item.clearButton.disconnect( this ); +}; + +/** + * Editor for "one-way" rules. + * + * @class + * @extends ve.ui.MWLanguageVariantInspector + * + * @constructor + * @param {Object} [config] Configuration options +*/ +ve.ui.MWLanguageVariantOneWayInspector = function VeUiMWLanguageVariantOneWayInspector() { + ve.ui.MWLanguageVariantOneWayInspector.super.apply( this, arguments ); +}; + +/* Inheritance */ + +OO.inheritClass( ve.ui.MWLanguageVariantOneWayInspector, ve.ui.MWLanguageVariantInspector ); + +/* Static properties */ + +ve.ui.MWLanguageVariantOneWayInspector.static.name = 'mwLanguageVariant-oneway'; + +ve.ui.MWLanguageVariantOneWayInspector.static.title = OO.ui.deferMsg( + 'visualeditor-mwlanguagevariantinspector-title-oneway' +); + +/* Methods */ + +ve.ui.MWLanguageVariantOneWayInspector.prototype.initialize = function () { + ve.ui.MWLanguageVariantOneWayInspector.super.prototype.initialize.call( this ); + this.items = []; + this.layout = new OO.ui.FieldsetLayout( { } ); + this.form.$element.append( this.layout.$element ); + + this.addButton = new OO.ui.ButtonInputWidget( { + label: OO.ui.msg( 'visualeditor-mwlanguagevariantinspector-oneway-add-button' ), + icon: 'add' + } ); + this.form.$element.append( this.addButton.$element ); + + // Events + this.addButton.connect( this, { click: 'onAddButtonClick' } ); +}; + +ve.ui.MWLanguageVariantOneWayInspector.prototype.getDefaultVariantInfo = function () { + return { oneway: [ { f: '', l: mw.config.get( 'wgUserVariant' ), t: '' } ] }; +}; + +ve.ui.MWLanguageVariantOneWayInspector.prototype.getSetupProcess = function ( data ) { + return ve.ui.MWLanguageVariantOneWayInspector.super.prototype.getSetupProcess.call( this, data ).next( function () { + var variantInfo = this.variantNode.getVariantInfo(); + this.layout.clearItems(); this.items = []; + variantInfo.oneway.forEach( function ( ow, idx ) { + this.items[ idx ] = this.createItem( ow.f, ow.l, ow.t ); + this.layout.addItems( [ this.items[ idx ].layout ] ); + }, this ); + }, this ); +}; + +/** + * Create widgets corresponding to a given mapping given by this rule. + * @method + * @param {String} [from] The HTML source text. + * @param {String} [lang] The language code for the destination text. + * @param {String} [to] The HTML destination text. + * @return {Object} An object containing the required widgets and backing + * documents for this mapping item. + */ +ve.ui.MWLanguageVariantOneWayInspector.prototype.createItem = function ( from, lang, to ) { + var fromTextTarget, languageInput, toTextTarget, clearButton, layout, item; + + fromTextTarget = this.createTextTarget( OO.ui.msg( + 'visualeditor-mwlanguagevariantinspector-oneway-from-text-placeholder' + ) ); + languageInput = new ve.ui.LanguageInputWidget( { + dialogManager: this.manager.getSurface().getDialogs(), + dirInput: 'none' + } ); + toTextTarget = this.createTextTarget( OO.ui.msg( + 'visualeditor-mwlanguagevariantinspector-oneway-to-text-placeholder' + ) ); + clearButton = new OO.ui.ButtonInputWidget( { + icon: 'clear', + iconTitle: OO.ui.deferMsg( + 'visualeditor-mwlanguagevariantinspector-oneway-clear-button' + ), + framed: false + } ); + layout = new OO.ui.FieldLayout( + new OO.ui.Widget( { + content: [ + new OO.ui.ActionFieldLayout( + fromTextTarget, + clearButton + ), + languageInput, + toTextTarget + ] + } ), {} + ); + item = { + fromTextTarget: fromTextTarget, + languageInput: languageInput, + toTextTarget: toTextTarget, + clearButton: clearButton, + layout: layout + }; + + // Initialize + item.fromTextTargetDoc = this.setupTextTargetDoc( fromTextTarget, from ); + item.toTextTargetDoc = this.setupTextTargetDoc( toTextTarget, to ); + languageInput.setLangAndDir( lang, 'auto' ); + clearButton.connect( this, { click: [ 'onClearButtonClick', item ] } ); + return item; +}; + +ve.ui.MWLanguageVariantOneWayInspector.prototype.getContentFromInspector = function ( variantInfo ) { + // TODO should allow type to depend on targetHtmlDoc, maybe switch + // from inline to block. + var type = this.variantNode.getType(); + variantInfo.oneway = this.items.map( function ( item ) { + return { + f: this.getHtmlForDoc( item.fromTextTargetDoc ), + l: item.languageInput.getLang(), + t: this.getHtmlForDoc( item.toTextTargetDoc ) + }; + }, this ); + return [ + { + type: type, + attributes: { variantInfo: variantInfo } + }, + { + type: '/' + type + } + ]; +}; + +/** + * Create a new mapping item in the inspector. + * @method + */ +ve.ui.MWLanguageVariantOneWayInspector.prototype.onAddButtonClick = function () { + var idx = this.items.length; + this.items[ idx ] = this.createItem( '', mw.config.get( 'wgUserVariant' ), '' ); + this.layout.addItems( [ this.items[ idx ].layout ] ); +}; + +/** + * Remove a mapping item from the inspector. + * @method + */ +ve.ui.MWLanguageVariantOneWayInspector.prototype.onClearButtonClick = function ( item ) { + var idx = this.items.indexOf( item ); + this.items.splice( idx, 1 ); + this.layout.removeItems( [ item.layout ] ); + item.clearButton.disconnect( this ); +}; + +/* Registration */ + +ve.ui.windowFactory.register( ve.ui.MWLanguageVariantDisabledInspector ); +ve.ui.windowFactory.register( ve.ui.MWLanguageVariantNameInspector ); +ve.ui.windowFactory.register( ve.ui.MWLanguageVariantFilterInspector ); +ve.ui.windowFactory.register( ve.ui.MWLanguageVariantTwoWayInspector ); +ve.ui.windowFactory.register( ve.ui.MWLanguageVariantOneWayInspector ); -- To view, visit https://gerrit.wikimedia.org/r/361921 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: merged Gerrit-Change-Id: I5ce29e4bf47abf509afde0a57f64b5d1189f5185 Gerrit-PatchSet: 20 Gerrit-Project: mediawiki/extensions/VisualEditor Gerrit-Branch: master Gerrit-Owner: C. Scott Ananian <canan...@wikimedia.org> Gerrit-Reviewer: C. Scott Ananian <canan...@wikimedia.org> Gerrit-Reviewer: Esanders <esand...@wikimedia.org> Gerrit-Reviewer: Jforrester <jforres...@wikimedia.org> Gerrit-Reviewer: Siebrand <siebr...@kitano.nl> Gerrit-Reviewer: jenkins-bot <> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits