jenkins-bot has submitted this change and it was merged.

Change subject: First implementation of the support of the <pages> tag in the 
VisualEditor
......................................................................


First implementation of the support of the <pages> tag in the VisualEditor

Depends-On: I633c521859b59614835344a4644560ac933c6028
Depends-On: Ia3533d89a94f4451c45deb0d89770d358fb2612a
Change-Id: I876c902c7c8331617ddcdb23e3dc04c7f16224dd
---
M .jshintrc
M ProofreadPage.php
M i18n/en.json
M i18n/qqq.json
A modules/ve/node/ve.ce.MWPagesNode.js
A modules/ve/node/ve.dm.MWPagesNode.js
A modules/ve/node/ve.ui.MWPagesInspector.js
A modules/ve/node/ve.ui.MWPagesInspectorTool.js
8 files changed, 381 insertions(+), 4 deletions(-)

Approvals:
  Esanders: Looks good to me, approved
  jenkins-bot: Verified



diff --git a/.jshintrc b/.jshintrc
index 27e9c99..1a54bf2 100644
--- a/.jshintrc
+++ b/.jshintrc
@@ -2,7 +2,6 @@
        // Enforcing
        "bitwise": true,
        "eqeqeq": true,
-       "es3": true,
        "freeze": true,
        "latedef": true,
        "noarg": true,
@@ -15,6 +14,8 @@
        "browser": true,
 
        "globals": {
-               "mw": false
+               "mw": false,
+               "OO": false,
+               "ve": false
        }
 }
diff --git a/ProofreadPage.php b/ProofreadPage.php
index e22ff49..cadbc58 100644
--- a/ProofreadPage.php
+++ b/ProofreadPage.php
@@ -202,8 +202,27 @@
                'styles'  => 
'special/indexpages/ext.proofreadpage.special.indexpages.css',
                'dependencies' => array( 'ext.proofreadpage.base' )
        ),
+       'ext.proofreadpage.ve.node.pages' => $prpResourceTemplate + array(
+               'scripts' => array(
+                       've/node/ve.dm.MWPagesNode.js',
+                       've/node/ve.ce.MWPagesNode.js',
+                       've/node/ve.ui.MWPagesInspector.js',
+                       've/node/ve.ui.MWPagesInspectorTool.js'
+               ),
+               'dependencies' => array( 'ext.visualEditor.mwcore' ),
+               'messages' => array(
+                       
'proofreadpage-visualeditor-node-pages-inspector-tooltip',
+                       'proofreadpage-visualeditor-node-pages-inspector-title',
+                       
'proofreadpage-visualeditor-node-pages-inspector-description',
+                       
'proofreadpage-visualeditor-node-pages-inspector-indexselector-yes',
+                       
'proofreadpage-visualeditor-node-pages-inspector-indexselector-no'
+               ),
+               'targets' => array( 'desktop', 'mobile' )
+       ),
 );
 
+$wgVisualEditorPluginModules[] = 'ext.proofreadpage.ve.node.pages';
+
 //Hooks
 $wgHooks['SetupAfterCache'][] = 
'ProofreadPage\ProofreadPageInit::initNamespaces';
 $wgHooks['ParserFirstCallInit'][] = 'ProofreadPage::onParserFirstCallInit';
diff --git a/i18n/en.json b/i18n/en.json
index 0551bc2..0ab3df1 100644
--- a/i18n/en.json
+++ b/i18n/en.json
@@ -84,5 +84,10 @@
        "apihelp-query+proofreadinfo-description": "Return information about 
configuration of ProofreadPage extension.",
        "apihelp-query+proofreadinfo-param-prop": "Which proofread properties 
to get:\n;namespaces:Information about \"{{ns:Page}}\" and \"{{ns:Index}}\" 
namespaces.\n;qualitylevels:List of proofread quality levels.",
        "apihelp-query+proofreadinfo-example-1": "Return default information",
-       "apihelp-query+proofreadinfo-example-3": "Return namespace information"
+       "apihelp-query+proofreadinfo-example-3": "Return namespace information",
+       "proofreadpage-visualeditor-node-pages-inspector-title": "Page 
selection",
+       "proofreadpage-visualeditor-node-pages-inspector-tooltip": "Pages",
+       "proofreadpage-visualeditor-node-pages-inspector-description": 
"Transclusion of $1",
+       "proofreadpage-visualeditor-node-pages-inspector-indexselector-yes": 
"Yes",
+       "proofreadpage-visualeditor-node-pages-inspector-indexselector-no": "No"
 }
diff --git a/i18n/qqq.json b/i18n/qqq.json
index b455a4c..d31bcd6 100644
--- a/i18n/qqq.json
+++ b/i18n/qqq.json
@@ -106,5 +106,10 @@
        "apihelp-query+proofreadinfo-description": 
"{{doc-apihelp-description|query+proofreadinfo}}",
        "apihelp-query+proofreadinfo-param-prop": "Use \"{{ns:Page}}\" and 
\"{{ns:Index}}\" with quotes instead of Page and Index 
namespaces.\n\n{{doc-apihelp-param|query+proofreadinfo|prop}}",
        "apihelp-query+proofreadinfo-example-1": 
"{{doc-apihelp-example|query+proofreadinfo}}",
-       "apihelp-query+proofreadinfo-example-3": 
"{{doc-apihelp-example|query+proofreadinfo}}"
+       "apihelp-query+proofreadinfo-example-3": 
"{{doc-apihelp-example|query+proofreadinfo}}",
+       "proofreadpage-visualeditor-node-pages-inspector-tooltip": "Title for 
the inspector to edit <nowiki><pages></nowiki> blocks.",
+       "proofreadpage-visualeditor-node-pages-inspector-title": "Title of the 
button to open the inspector to edit <nowiki><pages></nowiki> blocks.",
+       "proofreadpage-visualeditor-node-pages-inspector-description": 
"Description of the inspector to edit <nowiki><pages></nowiki> blocks.\n$1 is 
the title of the transcluded index (like 'My nice book.djvu').",
+       "proofreadpage-visualeditor-node-pages-inspector-indexselector-yes": 
"Label of the option that allows to include an header on top of the transluded 
pages.",
+       "proofreadpage-visualeditor-node-pages-inspector-indexselector-no": 
"Label of the option that disallows to include an header on top of the 
transluded pages."
 }
diff --git a/modules/ve/node/ve.ce.MWPagesNode.js 
b/modules/ve/node/ve.ce.MWPagesNode.js
new file mode 100644
index 0000000..0d1b448
--- /dev/null
+++ b/modules/ve/node/ve.ce.MWPagesNode.js
@@ -0,0 +1,37 @@
+/**
+ * ContentEditable MediaWiki pages node.
+ *
+ * @class
+ * @extends ve.ce.MWBlockExtensionNode
+ *
+ * @constructor
+ */
+ve.ce.MWPagesNode = function VeCeMWPagesNode() {
+       ve.ce.MWPagesNode.super.apply( this, arguments );
+};
+
+/* Inheritance */
+OO.inheritClass( ve.ce.MWPagesNode, ve.ce.MWBlockExtensionNode );
+
+/* Static Properties */
+ve.ce.MWPagesNode.static.name = 'mwPages';
+
+ve.ce.MWPagesNode.static.tagName = 'div';
+
+ve.ce.MWPagesNode.static.primaryCommandName = 'pages';
+
+ve.ce.MWPagesNode.static.iconWhenInvisible = 'article';
+
+ve.ce.MWPagesNode.static.rendersEmpty = true;
+
+/* Methods */
+/**
+ * @inheritdoc
+ */
+ve.ce.MWPagesNode.static.getDescription = function ( model ) {
+       var indexTitle = new mw.Title( model.getIndexName() );
+       return ve.msg( 
'proofreadpage-visualeditor-node-pages-inspector-description', 
indexTitle.getNameText() );
+};
+
+/* Registration */
+ve.ce.nodeFactory.register( ve.ce.MWPagesNode );
diff --git a/modules/ve/node/ve.dm.MWPagesNode.js 
b/modules/ve/node/ve.dm.MWPagesNode.js
new file mode 100644
index 0000000..f8542b0
--- /dev/null
+++ b/modules/ve/node/ve.dm.MWPagesNode.js
@@ -0,0 +1,32 @@
+/**
+ * DataModel MediaWiki pages node.
+ *
+ * @class
+ * @extends ve.dm.MWBlockExtensionNode
+ *
+ * @constructor
+ */
+ve.dm.MWPagesNode = function VeDmMWPagesNode() {
+       ve.dm.MWPagesNode.super.apply( this, arguments );
+};
+
+/* Inheritance */
+OO.inheritClass( ve.dm.MWPagesNode, ve.dm.MWBlockExtensionNode );
+
+/* Static Properties */
+ve.dm.MWPagesNode.static.name = 'mwPages';
+
+ve.dm.MWPagesNode.static.tagName = 'div';
+
+ve.dm.MWPagesNode.static.extensionName = 'pages';
+
+/* Methods */
+/**
+ * @return {string} Index name
+ */
+ve.dm.MWPagesNode.prototype.getIndexName = function () {
+       return this.getAttribute( 'mw' ).attrs.index || '';
+};
+
+/* Registration */
+ve.dm.modelRegistry.register( ve.dm.MWPagesNode );
diff --git a/modules/ve/node/ve.ui.MWPagesInspector.js 
b/modules/ve/node/ve.ui.MWPagesInspector.js
new file mode 100644
index 0000000..fc0d44e
--- /dev/null
+++ b/modules/ve/node/ve.ui.MWPagesInspector.js
@@ -0,0 +1,244 @@
+/**
+ * mw pages tag inspector.
+ *
+ * @class
+ * @extends ve.ui.MWExtensionInspector
+ *
+ * @constructor
+ */
+ve.ui.MWPagesInspector = function VeUiMWPagesInspector() {
+       ve.ui.MWPagesInspector.super.apply( this, arguments );
+};
+
+/* Inheritance */
+OO.inheritClass( ve.ui.MWPagesInspector, ve.ui.MWExtensionInspector );
+
+/* Static properties */
+ve.ui.MWPagesInspector.static.name = 'pages';
+
+ve.ui.MWPagesInspector.static.icon = 'article';
+
+ve.ui.MWPagesInspector.static.title = OO.ui.deferMsg( 
'proofreadpage-visualeditor-node-pages-inspector-title' );
+
+ve.ui.MWPagesInspector.static.modelClasses = [ ve.dm.MWPagesNode ];
+
+ve.ui.MWPagesInspector.static.allowedEmpty = true;
+
+ve.ui.MWPagesInspector.static.delayForIndexInput = 2000;
+
+/* Methods */
+/**
+ * @inheritdoc
+ */
+ve.ui.MWPagesInspector.prototype.initialize = function () {
+       this.constructor.super.prototype.initialize.apply( this, arguments );
+
+       this.attributeInputs = {};
+       this.$attributes = $( '<div>' );
+       this.form.$element.append( this.$attributes );
+};
+
+/**
+ * @inheritdoc
+ */
+ve.ui.MWPagesInspector.prototype.getSetupProcess = function ( data ) {
+       return this.constructor.super.prototype.getSetupProcess.call( this, 
data )
+               .next( function () {
+                       this.mwData = ( this.selectedNode !== null && 
this.selectedNode.getAttribute( 'mw' ) ) || {
+                               attrs: {}
+                       };
+
+                       this.setupForm();
+
+                       // Hide the tag content if possible
+                       if ( this.input.getValue().trim() === '' ) {
+                               this.input.toggle( false );
+                       }
+               }, this );
+};
+
+/**
+ * Creates the inspector form using the current data stored in this.mwData
+ */
+ve.ui.MWPagesInspector.prototype.setupForm = function () {
+       var key,
+               attributes = this.mwData.attrs,
+               inspector = this;
+
+       this.pushPending();
+       this.getFileInfo( attributes.index ).done( function ( imageInfo ) {
+               inspector.addAttributeToWidget( inspector.createIndexWidget(), 
'index' );
+
+               inspector.addAttributeToWidget( new OO.ui.DropdownInputWidget( {
+                       options: inspector.buildHeaderFieldSelectorOptions( 
attributes.header )
+               } ), 'header' );
+
+               if ( imageInfo.pagecount !== undefined ) {
+                       inspector.addAttributeToWidget( new 
OO.ui.NumberInputWidget( {
+                               isInteger: true,
+                               min: 1,
+                               max: imageInfo.pagecount
+                       } ), 'from' );
+                       inspector.addAttributeToWidget( new 
OO.ui.NumberInputWidget( {
+                               isInteger: true,
+                               min: 1,
+                               max: imageInfo.pagecount
+                       } ), 'to' );
+               } else {
+                       inspector.addAttributeToWidget( new 
mw.widgets.TitleInputWidget( {
+                               namespace: inspector.getIdForNamespace( 'page' )
+                       } ), 'from' );
+                       inspector.addAttributeToWidget( new 
mw.widgets.TitleInputWidget( {
+                               namespace: inspector.getIdForNamespace( 'page' )
+                       } ), 'to' );
+               }
+
+               inspector.addAttributeToWidget( new OO.ui.TextInputWidget(), 
'fromsection' );
+               inspector.addAttributeToWidget( new OO.ui.TextInputWidget(), 
'tosection' );
+
+               for ( key in attributes ) {
+                       if ( key in inspector.attributeInputs ) {
+                               inspector.attributeInputs[ key ].setValue( 
attributes[ key ] );
+                       } else {
+                               inspector.addAttributeToWidget( new 
OO.ui.TextInputWidget( {
+                                       value: attributes[ key ]
+                               } ), key );
+                       }
+               }
+
+               inspector.updateSize();
+               inspector.popPending();
+       } );
+};
+
+/**
+ * @return {mw.widgets.TitleInputWidget}
+ */
+ve.ui.MWPagesInspector.prototype.createIndexWidget = function () {
+       return new mw.widgets.TitleInputWidget( {
+               namespace: this.getIdForNamespace( 'index' ),
+               required: true
+       } ).connect( this, {
+               change: OO.ui.debounce( this.onIndexChange.bind( this ), 
this.constructor.static.delayForIndexInput )
+       } );
+};
+
+/**
+ * @param {string} namespaceName the name of the namespace like "Index" or 
"Page"
+ * @return {int} the namespace id like 252
+ */
+ve.ui.MWPagesInspector.prototype.getIdForNamespace = function ( namespaceName 
) {
+       var namespaceIds = mw.config.get( 'wgNamespaceIds' );
+
+       if ( namespaceName in namespaceIds ) {
+               return namespaceIds[ namespaceName ];
+       } else {
+               throw new Error( 'Unknown id for the ' + namespaceName + ': 
namespace' );
+       }
+};
+
+/**
+ * Builds the options for the header field dropdown
+ *
+ * @param {string|undefined} headerValue the current value of the header field
+ * @return {Object[]}
+ */
+ve.ui.MWPagesInspector.prototype.buildHeaderFieldSelectorOptions = function ( 
headerValue ) {
+       var headerInputOptions = [
+               { data: '', label: OO.ui.msg( 
'proofreadpage-visualeditor-node-pages-inspector-indexselector-no' ) },
+               { data: '1', label: OO.ui.msg( 
'proofreadpage-visualeditor-node-pages-inspector-indexselector-yes' ) }
+       ];
+       if ( [ '', '1' ].indexOf( headerValue ) === -1 ) {
+               headerInputOptions.push( { data: headerValue, label: 
headerValue } );
+       }
+       return headerInputOptions;
+};
+
+/**
+ * Get info about the file
+ *
+ * @param {string} fileName like "test.djvu"
+ * @return {jQuery.Promise} information about the file like {size: 222, 
pagecount: 1}. Always resolves
+ */
+ve.ui.MWPagesInspector.prototype.getFileInfo = function ( fileName ) {
+       if ( fileName !== '' ) {
+               return ( new mw.Api() ).get( {
+                       action: 'query',
+                       prop: 'imageinfo',
+                       titles: 'File:' + fileName,
+                       iiprop: 'size',
+                       indexpageids: 1
+               } ).then( function ( data ) {
+                       var file = data.query.pages[ data.query.pageids[ 0 ] ];
+                       return file.imageinfo && file.imageinfo[ 0 ] || {};
+               }, function () {
+                       return $.Deferred().resolve( {} );
+               } );
+       } else {
+               return $.Deferred().resolve( {} );
+       }
+};
+
+/**
+ * Adds to the inspector an input for a <pages> tag parameter
+ *
+ * @param {OO.ui.Widget} attributeInput
+ * @param {string} attributeKey the key of the attribute like "from"
+ */
+ve.ui.MWPagesInspector.prototype.addAttributeToWidget = function ( 
attributeInput, attributeKey ) {
+       var field = new OO.ui.FieldLayout(
+               attributeInput,
+               {
+                       align: 'left',
+                       label: attributeKey
+               }
+       );
+       this.$attributes.append( field.$element );
+       this.attributeInputs[ attributeKey ] = attributeInput;
+};
+
+/**
+ * Event callback when the index field changes
+ */
+ve.ui.MWPagesInspector.prototype.onIndexChange = function () {
+       if ( this.attributeInputs.index && 
this.attributeInputs.index.getValue() !== this.mwData.attrs.index ) {
+               this.updateMwData( this.mwData );
+               this.teardownForm();
+               this.setupForm();
+       }
+};
+
+/**
+ * Removes the inspector form
+ */
+ve.ui.MWPagesInspector.prototype.teardownForm = function () {
+       this.$attributes.empty();
+       this.attributeInputs = {};
+};
+
+/**
+ * @inheritdoc
+ */
+ve.ui.MWPagesInspector.prototype.getTeardownProcess = function ( data ) {
+       return this.constructor.super.prototype.getTeardownProcess.call( this, 
data )
+               .next( function () {
+                       this.teardownForm();
+               }, this );
+};
+
+/**
+ * @inheritdoc
+ */
+ve.ui.MWPagesInspector.prototype.updateMwData = function ( mwData ) {
+       var key;
+
+       this.constructor.super.prototype.updateMwData.call( this, mwData );
+
+       mwData.attrs = mwData.attrs || {};
+       for ( key in this.attributeInputs ) {
+               mwData.attrs[ key ] = this.attributeInputs[ key ].getValue();
+       }
+};
+
+/* Registration */
+ve.ui.windowFactory.register( ve.ui.MWPagesInspector );
diff --git a/modules/ve/node/ve.ui.MWPagesInspectorTool.js 
b/modules/ve/node/ve.ui.MWPagesInspectorTool.js
new file mode 100644
index 0000000..99c5936
--- /dev/null
+++ b/modules/ve/node/ve.ui.MWPagesInspectorTool.js
@@ -0,0 +1,34 @@
+/**
+ * MediaWiki UserInterface pages tool.
+ *
+ * @class
+ * @extends ve.ui.FragmentInspectorTool
+ *
+ * @constructor
+ */
+ve.ui.MWPagesInspectorTool = function VeUiMWPagesInspectorTool() {
+       ve.ui.MWPagesInspectorTool.super.apply( this, arguments );
+};
+
+/* Inheritance */
+OO.inheritClass( ve.ui.MWPagesInspectorTool, ve.ui.FragmentInspectorTool );
+
+/* Static properties */
+ve.ui.MWPagesInspectorTool.static.name = 'pages';
+
+ve.ui.MWPagesInspectorTool.static.group = 'object';
+
+ve.ui.MWPagesInspectorTool.static.icon = 'article';
+
+ve.ui.MWPagesInspectorTool.static.title = OO.ui.deferMsg( 
'proofreadpage-visualeditor-node-pages-inspector-tooltip' );
+
+ve.ui.MWPagesInspectorTool.static.modelClasses = [ ve.dm.MWPagesNode ];
+
+ve.ui.MWPagesInspectorTool.static.commandName = 'pages';
+
+/* Registration */
+ve.ui.toolFactory.register( ve.ui.MWPagesInspectorTool );
+
+ve.ui.commandRegistry.register(
+       new ve.ui.Command( 'pages', 'window', 'open', { args: [ 'pages' ] } )
+);

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

Gerrit-MessageType: merged
Gerrit-Change-Id: I876c902c7c8331617ddcdb23e3dc04c7f16224dd
Gerrit-PatchSet: 14
Gerrit-Project: mediawiki/extensions/ProofreadPage
Gerrit-Branch: master
Gerrit-Owner: Tpt <[email protected]>
Gerrit-Reviewer: Catrope <[email protected]>
Gerrit-Reviewer: Esanders <[email protected]>
Gerrit-Reviewer: GOIII <[email protected]>
Gerrit-Reviewer: Jforrester <[email protected]>
Gerrit-Reviewer: Siebrand <[email protected]>
Gerrit-Reviewer: Tpt <[email protected]>
Gerrit-Reviewer: jenkins-bot <>

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to