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

Reply via email to