Robmoen has uploaded a new change for review.

  https://gerrit.wikimedia.org/r/108945


Change subject: WIP: TOC widget
......................................................................

WIP: TOC widget

Change-Id: I488cfbbdb060e50d81f51e0f757e67d0114b8936
---
M VisualEditor.php
M modules/ve-mw/ce/nodes/ve.ce.MWHeadingNode.js
A modules/ve-mw/ui/widgets/ve.ui.MWTocItemWidget.js
A modules/ve-mw/ui/widgets/ve.ui.MWTocSectionWidget.js
A modules/ve-mw/ui/widgets/ve.ui.MWTocWidget.js
5 files changed, 187 insertions(+), 0 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/VisualEditor 
refs/changes/45/108945/1

diff --git a/VisualEditor.php b/VisualEditor.php
index e57bf07..6c7daf1 100644
--- a/VisualEditor.php
+++ b/VisualEditor.php
@@ -584,6 +584,9 @@
                        
'modules/ve-mw/ui/widgets/ve.ui.MWReferenceSearchWidget.js',
                        
'modules/ve-mw/ui/widgets/ve.ui.MWReferenceResultWidget.js',
                        'modules/ve-mw/ui/widgets/ve.ui.MWTitleInputWidget.js',
+                       'modules/ve-mw/ui/widgets/ve.ui.MWTocItemWidget.js',
+                       'modules/ve-mw/ui/widgets/ve.ui.MWTocSectionWidget.js',
+                       'modules/ve-mw/ui/widgets/ve.ui.MWTocWidget.js',
 
                        'modules/ve-mw/ui/pages/ve.ui.MWSettingsPage.js',
                        'modules/ve-mw/ui/pages/ve.ui.MWCategoriesPage.js',
diff --git a/modules/ve-mw/ce/nodes/ve.ce.MWHeadingNode.js 
b/modules/ve-mw/ce/nodes/ve.ce.MWHeadingNode.js
index 053474d..fb56d21 100644
--- a/modules/ve-mw/ce/nodes/ve.ce.MWHeadingNode.js
+++ b/modules/ve-mw/ce/nodes/ve.ce.MWHeadingNode.js
@@ -27,6 +27,18 @@
 
 ve.ce.MWHeadingNode.static.name = 'mwHeading';
 
+/* Methods */
+
+ve.ce.MWHeadingNode.prototype.onSetup = function () {
+       ve.ce.HeadingNode.prototype.onSetup.call( this );
+       var surface = this.root.getSurface().getSurface();
+       // Lazy load TOC widget
+       if( !( 'toc' in surface ) ) {
+               surface.toc = new ve.ui.MWTocWidget( surface );
+       }
+       surface.toc.addItem( this );
+};
+
 /* Registration */
 
 ve.ce.nodeFactory.register( ve.ce.MWHeadingNode );
diff --git a/modules/ve-mw/ui/widgets/ve.ui.MWTocItemWidget.js 
b/modules/ve-mw/ui/widgets/ve.ui.MWTocItemWidget.js
new file mode 100644
index 0000000..7431d87
--- /dev/null
+++ b/modules/ve-mw/ui/widgets/ve.ui.MWTocItemWidget.js
@@ -0,0 +1,41 @@
+ve.ui.MWTocItemWidget = function VeCeMWTocItemWidget ( node, config ) {
+       OO.ui.Element.call( this, config );
+       this.node = node;
+       this.config = $.extend( { 'node': node }, config );
+       // Setup
+       this.$element.append(
+               this.$( '<span>' ).addClass( 'tocnumber' ),
+               this.$( '<span>' ).addClass( 'toctext' )
+       );
+       this.update();
+       // Events
+       this.node.model.connect( this, { 'update': 'onUpdate' } );
+       this.node.connect( this, { 'update': 'onUpdate', 'teardown': 
'onTeardown' } );
+};
+
+/* Inheritance */
+
+OO.inheritClass( ve.ui.MWTocItemWidget, OO.ui.Element );
+
+/* Static Properties */
+ve.ui.MWTocItemWidget.static.tagName = 'li';
+
+ve.ui.MWTocItemWidget.prototype.onUpdate = function () {
+       // Timeout needed to let the dom element actually update
+       setTimeout( ve.bind( function () {
+               this.update();
+       }, this ), 0 );
+};
+
+ve.ui.MWTocItemWidget.prototype.update = function () {
+       this.$element
+               .addClass( 'toclevel-' + this.config.tocLevel )
+               .addClass( 'tocsection-' + this.config.tocIndex )
+               .addClass( 'tocnumber-' + this.config.tocNumber )
+               .find( '.tocnumber' ).text( this.config.sectionPrefix ).end()
+               .find( '.toctext' ).text( this.node.$element.text() );
+};
+
+ve.ui.MWTocItemWidget.prototype.onTeardown = function () {
+       this.$element.detach();
+};
diff --git a/modules/ve-mw/ui/widgets/ve.ui.MWTocSectionWidget.js 
b/modules/ve-mw/ui/widgets/ve.ui.MWTocSectionWidget.js
new file mode 100644
index 0000000..1a0b78c
--- /dev/null
+++ b/modules/ve-mw/ui/widgets/ve.ui.MWTocSectionWidget.js
@@ -0,0 +1,28 @@
+//ve.ui.MWTocSectionWidget.js
+ve.ui.MWTocSectionWidget = function VeUiMWTocSectionWidget ( toc, config ) {
+       // Parent constructor
+       OO.ui.Widget.call( this, config );
+
+       // Mixin constructors
+       OO.ui.GroupElement.call( this, this.$( '<ul>' ), config );
+
+       // Properties
+       this.surface = toc.surface;
+       this.config = config;
+
+       config.$parent.append( this.$group );
+};
+
+/* Inheritance */
+
+
+OO.inheritClass( ve.ui.MWTocSectionWidget, OO.ui.Widget );
+
+OO.mixinClass( ve.ui.MWTocSectionWidget, OO.ui.GroupElement );
+
+ve.ui.MWTocSectionWidget.prototype.addItem = function ( node, config ) {
+       OO.ui.GroupElement.prototype.addItems.call( this,
+               [ new ve.ui.MWTocItemWidget( node, config ) ],
+               config.tocIndex
+       );
+};
\ No newline at end of file
diff --git a/modules/ve-mw/ui/widgets/ve.ui.MWTocWidget.js 
b/modules/ve-mw/ui/widgets/ve.ui.MWTocWidget.js
new file mode 100644
index 0000000..0d2691d
--- /dev/null
+++ b/modules/ve-mw/ui/widgets/ve.ui.MWTocWidget.js
@@ -0,0 +1,103 @@
+ve.ui.MWTocWidget = function VeUiMWTocWidget ( surface, config ) {
+       // Parent constructor
+       OO.ui.Widget.call( this, config );
+
+       // Mixin constructors
+       OO.ui.GroupElement.call( this, this.$( '<ul>' ), config );
+
+       this.surface = surface;
+       this.sections = {};
+
+       surface.$element.append( this.$group.addClass( 'toc' ) );
+};
+
+/* Inheritance */
+
+OO.inheritClass( ve.ui.MWTocWidget, OO.ui.Widget );
+
+OO.mixinClass( ve.ui.MWTocWidget, OO.ui.GroupElement );
+
+/**
+ * Adds a Toc item.
+ *
+ * @method
+ * @param {ve.ce.MWHeadingNode} node mwHeading node
+ */
+ve.ui.MWTocWidget.prototype.addItem = function ( node ) {
+       var config = $.extend( { '$': this.$ }, this.getTocItemConfig( node ) ),
+               parentSectionArray, key;
+       if( config.sectionPrefix in this.sections ) {
+               // Update this guy
+               this.sections[config.sectionPrefix].$element.detach();
+       }
+       parentSectionArray = config.sectionPrefix.split( '.' );
+       parentSectionArray.pop();
+       if( parentSectionArray.length > 1 ) {
+               key = parentSectionArray.join( '.' );
+               config.$parent = 
this.sections[key].items[this.sections[key].items.length-1].$element;
+       } else {
+               config.$parent = this.$group;
+       }
+       this.sections[config.sectionPrefix] = new ve.ui.MWTocSectionWidget( 
this, config );
+       this.sections[config.sectionPrefix].addItem( node, config );
+};
+
+ve.ui.MWTocWidget.prototype.update = function () {
+       var section;
+       for( section in this.sections ) {
+               section.update( this.getTocItemConfig( section.node ) );
+       }
+};
+
+/**
+ * Returns item configuration for a heading node
+ *
+ * @method
+ * @param {ve.ce.MWHeadingNode} node mwHeading node
+ */
+ve.ui.MWTocWidget.prototype.getTocItemConfig = function ( node ) {
+       var doc = this.surface.getModel().getDocument(),
+               nodes = doc.selectNodes( new ve.Range( 0, 
doc.getDocumentNode().getLength(), 'covered' ) ),
+               i = 0, headingLevel = 0, lastHeadingLevel = 0, tocNumber = 0, 
tocLevel = 0, tocSection = 0, tocIndex = 0,
+               sectionPrefix = [];
+       for ( i; i < nodes.length - 1; i++ ) {
+               if ( nodes[i].node.parent.getType() === 'mwHeading' ) {
+                       tocIndex++;
+                       headingLevel = nodes[i].node.parent.getAttribute( 
'level' );
+                       if ( headingLevel === 1 || headingLevel === 2 && 
lastHeadingLevel !== 1 ) {
+                               tocSection++;
+                               sectionPrefix = [ tocSection ];
+                               tocLevel = 0;
+                       } else {
+                               if( headingLevel === lastHeadingLevel ) {
+                                       tocNumber++;
+                                       sectionPrefix.pop();
+                                       sectionPrefix.push( tocNumber );
+                               } else {
+                                       tocNumber = 1;
+                                       // Heading not the same as before
+                                       if ( headingLevel > lastHeadingLevel ) {
+                                               tocLevel++;
+                                               sectionPrefix.push( tocNumber );
+                                       } else if ( headingLevel < 
lastHeadingLevel ) {
+                                               tocLevel--;
+                                               sectionPrefix.pop();
+                                               tocNumber = 
sectionPrefix[sectionPrefix.length-1] + 1;
+                                               sectionPrefix.pop();
+                                               sectionPrefix.push( tocNumber );
+                                       }
+                               }
+                       }
+                       if ( node.getModel() === nodes[i].node.parent ) {
+                               return {
+                                       'tocIndex': tocIndex--,
+                                       'tocLevel': tocLevel,
+                                       'tocNumber': tocNumber,
+                                       'tocSection': tocSection,
+                                       'sectionPrefix': sectionPrefix.join( 
'.' )
+                               };
+                       }
+                       lastHeadingLevel = headingLevel;
+               }
+       }
+};

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

Gerrit-MessageType: newchange
Gerrit-Change-Id: I488cfbbdb060e50d81f51e0f757e67d0114b8936
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/VisualEditor
Gerrit-Branch: master
Gerrit-Owner: Robmoen <[email protected]>

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

Reply via email to