Esanders has uploaded a new change for review.

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

Change subject: Rewrite TOCWidget based on Linker::generateTOC
......................................................................

Rewrite TOCWidget based on Linker::generateTOC

Change-Id: I5eb75c5db5ca466fd6f16a57c693c2a4458cff7c
---
M extension.json
M modules/ve-mw/init/targets/ve.init.mw.DesktopArticleTarget.js
D modules/ve-mw/ui/styles/widgets/ve.ui.MWTocWidget.css
D modules/ve-mw/ui/widgets/ve.ui.MWTocItemWidget.js
M modules/ve-mw/ui/widgets/ve.ui.MWTocWidget.js
5 files changed, 69 insertions(+), 264 deletions(-)


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

diff --git a/extension.json b/extension.json
index 85eaa1b..cf6102e 100644
--- a/extension.json
+++ b/extension.json
@@ -1130,7 +1130,6 @@
                                
"modules/ve-mw/ui/datatransferhandlers/ve.ui.MWWikitextStringTransferHandler.js",
                                
"modules/ve-mw/ui/widgets/ve.ui.MWAceEditorWidget.js",
                                
"modules/ve-mw/ui/widgets/ve.ui.MWTargetWidget.js",
-                               
"modules/ve-mw/ui/widgets/ve.ui.MWTocItemWidget.js",
                                "modules/ve-mw/ui/widgets/ve.ui.MWTocWidget.js",
                                
"modules/ve-mw/ui/dialogs/ve.ui.MWExtensionDialog.js",
                                
"modules/ve-mw/ui/dialogs/ve.ui.MWExtensionPreviewDialog.js",
@@ -1155,7 +1154,6 @@
                                
"modules/ve-mw/ui/styles/elements/ve.ui.MWExpandableErrorElement.css",
                                
"modules/ve-mw/ui/styles/tools/ve.ui.MWPopupTool.css",
                                
"modules/ve-mw/ui/styles/widgets/ve.ui.MWAceEditorWidget.css",
-                               
"modules/ve-mw/ui/styles/widgets/ve.ui.MWTocWidget.css",
                                
"modules/ve-mw/ui/styles/tools/ve.ui.MWEducationPopupTool.css"
                        ],
                        "skinStyles": {
diff --git a/modules/ve-mw/init/targets/ve.init.mw.DesktopArticleTarget.js 
b/modules/ve-mw/init/targets/ve.init.mw.DesktopArticleTarget.js
index c5b5ad8..c70004e 100644
--- a/modules/ve-mw/init/targets/ve.init.mw.DesktopArticleTarget.js
+++ b/modules/ve-mw/init/targets/ve.init.mw.DesktopArticleTarget.js
@@ -721,7 +721,7 @@
        // TODO: mwTocWidget should probably live in a ve.ui.MWSurface subclass
        if ( mw.config.get( 'wgVisualEditorConfig' ).enableTocWidget ) {
                surface.mwTocWidget = new ve.ui.MWTocWidget( this.getSurface() 
);
-               surface.$element.prepend( surface.mwTocWidget.$element );
+               surface.$element.before( surface.mwTocWidget.$element );
        }
 
        // Track how long it takes for the first transaction to happen
@@ -1023,9 +1023,6 @@
        // Update UI
        promises.push( this.teardownToolbar() );
        this.restoreDocumentTitle();
-       if ( this.getSurface().mwTocWidget ) {
-               this.getSurface().mwTocWidget.teardown();
-       }
 
        if ( this.saveDialog ) {
                if ( this.saveDialog.isOpened() ) {
diff --git a/modules/ve-mw/ui/styles/widgets/ve.ui.MWTocWidget.css 
b/modules/ve-mw/ui/styles/widgets/ve.ui.MWTocWidget.css
deleted file mode 100644
index 816438a..0000000
--- a/modules/ve-mw/ui/styles/widgets/ve.ui.MWTocWidget.css
+++ /dev/null
@@ -1,28 +0,0 @@
-/*!
- * VisualEditor MediaWiki UserInterface MWTocWidget styles.
- *
- * @copyright 2011-2016 VisualEditor Team and others; see AUTHORS.txt
- * @license The MIT License (MIT); see LICENSE.txt
- */
-
-.ve-ui-mwTocWidget {
-       /* Margin to mock the standard appearance of TOC */
-       margin: 1em 0 0 0;
-}
-.ve-ui-mwTocWidget .toctoggle {
-       margin: 0.25em;
-}
-.ve-ui-mwTocWidget .toctoggle:before {
-       content: ' [';
-}
-.ve-ui-mwTocWidget .toctoggle:after {
-       content: '] ';
-}
-
-.ve-ui-mwTocWidget .tocnumber:after {
-       content: ' ';
-}
-
-.ve-ui-mwTocWidget a {
-       cursor: pointer;
-}
diff --git a/modules/ve-mw/ui/widgets/ve.ui.MWTocItemWidget.js 
b/modules/ve-mw/ui/widgets/ve.ui.MWTocItemWidget.js
deleted file mode 100644
index 435d9d2..0000000
--- a/modules/ve-mw/ui/widgets/ve.ui.MWTocItemWidget.js
+++ /dev/null
@@ -1,89 +0,0 @@
-/*!
- * VisualEditor UserInterface MWTocItemWidget class.
- *
- * @copyright 2011-2016 VisualEditor Team and others; see AUTHORS.txt
- * @license The MIT License (MIT); see LICENSE.txt
- */
-
-/**
- * Creates an item an item for the MWTocWidget
- *
- * @class
- * @extends OO.ui.Widget
- * @mixins OO.ui.mixin.GroupElement
- *
- * @constructor
- * @param {Object} config TOC Item configuration
- * @cfg {ve.ce.Node} node ContentEditable node
- * @cfg {ve.ui.MWTocItemWidget} parent Parent toc item
- * @cfg {string} sectionPrefix TOC item section number
- * @cfg {number} tocLevel Depth level of the TOC item
- * @cfg {number} tocIndex Running count of TOC items
- *
- */
-ve.ui.MWTocItemWidget = function VeUiMWTocItemWidget( config ) {
-       // Parent constructor
-       OO.ui.Widget.call( this, config );
-
-       // Mixin Constructor
-       OO.ui.mixin.GroupElement.call( this, $.extend( {}, config, { $group: $( 
'<ul>' ) } ) );
-
-       config = config || {};
-
-       // Properties
-       this.node = config.node || null;
-       this.parent = config.parent;
-       this.sectionPrefix = config.sectionPrefix;
-       this.tocLevel = config.tocLevel;
-       this.tocIndex = config.tocIndex;
-
-       // Allows toc items to be optionally associated to a node.
-       // For the case of the zero level parent item.
-       if ( this.node ) {
-               this.$tocNumber = $( '<span>' ).addClass( 'tocnumber' )
-                       .text( this.sectionPrefix );
-               this.$tocText = $( '<span>' ).addClass( 'toctext' )
-                       .text( this.node.$element.text() );
-               this.$element
-                       .addClass( 'toclevel-' + this.tocLevel )
-                       .addClass( 'tocsection-' + this.tocIndex )
-                       .append( $( '<a>' ).append( this.$tocNumber, 
this.$tocText ) );
-
-               // Monitor node events
-               this.node.model.connect( this, { update: 'onUpdate' } );
-       }
-       this.$element.append( this.$group );
-};
-
-/* Inheritance */
-
-OO.inheritClass( ve.ui.MWTocItemWidget, OO.ui.Widget );
-
-OO.mixinClass( ve.ui.MWTocItemWidget, OO.ui.mixin.GroupElement );
-
-/* Static Properties */
-
-ve.ui.MWTocItemWidget.static.tagName = 'li';
-
-/* Methods */
-
-/**
- * Updates the text of the toc item
- *
- */
-ve.ui.MWTocItemWidget.prototype.onUpdate = function () {
-       var widget = this;
-       // Timeout needed to let the dom element actually update
-       setTimeout( function () {
-               widget.$tocText.text( widget.node.$element.text() );
-       } );
-};
-
-/**
- * Removes this toc item from its parent
- *
- */
-ve.ui.MWTocItemWidget.prototype.remove = function () {
-       this.node.model.disconnect( this );
-       this.parent.removeItems( [ this ] );
-};
diff --git a/modules/ve-mw/ui/widgets/ve.ui.MWTocWidget.js 
b/modules/ve-mw/ui/widgets/ve.ui.MWTocWidget.js
index 9b4d38e..67d20c8 100644
--- a/modules/ve-mw/ui/widgets/ve.ui.MWTocWidget.js
+++ b/modules/ve-mw/ui/widgets/ve.ui.MWTocWidget.js
@@ -16,7 +16,6 @@
  * @param {Object} [config] Configuration options
  */
 ve.ui.MWTocWidget = function VeUiMWTocWidget( surface, config ) {
-       var widget = this;
 
        // Parent constructor
        OO.ui.Widget.call( this, config );
@@ -26,42 +25,22 @@
        this.doc = surface.getModel().getDocument();
        this.metaList = surface.getModel().metaList;
        // Topic level 0 lives inside of a toc item
-       this.topics = new ve.ui.MWTocItemWidget();
-       // Place for a cloned previous toc to live while rebuilding.
-       this.$tempTopics = $( '<ul>' );
-       // Section keyed item map
-       this.items = {};
+       this.rootLength = 0;
        this.initialized = false;
        // Page settings cache
        this.mwTOCForce = false;
        this.mwTOCDisable = false;
 
-       // TODO: fix i18n
-       this.tocToggle = {
-               hideMsg: ve.msg( 'hidetoc' ),
-               showMsg: ve.msg( 'showtoc' ),
-               $link: $( '<a class="internal" id="togglelink"></a>' ).text( 
ve.msg( 'hidetoc' ) ),
-               open: true
-       };
+       this.$tocList = $( '<ul>' );
        this.$element.addClass( 'toc ve-ui-mwTocWidget' ).append(
-               $( '<div>' ).attr( 'id', 'toctitle' ).append(
-                       $( '<h2>' ).text( ve.msg( 'toc' ) ),
-                       $( '<span>' ).addClass( 'toctoggle' ).append( 
this.tocToggle.$link )
+               $( '<div>' ).addClass( 'toctitle' ).append(
+                       $( '<h2>' ).text( ve.msg( 'toc' ) )
                ),
-               this.topics.$group, this.$tempTopics
+               this.$tocList
        );
 
-       this.tocToggle.$link.on( 'click', function () {
-               if ( widget.tocToggle.open ) {
-                       widget.tocToggle.$link.text( widget.tocToggle.showMsg );
-                       widget.tocToggle.open = false;
-               } else {
-                       widget.tocToggle.$link.text( widget.tocToggle.hideMsg );
-                       widget.tocToggle.open = true;
-               }
-               // FIXME: We should really use CSS here
-               widget.topics.$group.add( widget.$tempTopics ).slideToggle();
-       } );
+       // Setup toggle link
+       mw.hook( 'wikipage.content' ).fire( this.$element );
 
        this.metaList.connect( this, {
                insert: 'onMetaListInsert',
@@ -90,7 +69,7 @@
                // hide
                this.mwTOCDisable = true;
        }
-       this.hideOrShow();
+       this.updateVisibility();
 };
 
 /**
@@ -104,7 +83,7 @@
        } else if ( metaItem instanceof ve.dm.MWTOCDisableMetaItem ) {
                this.mwTOCDisable = false;
        }
-       this.hideOrShow();
+       this.updateVisibility();
 };
 
 /**
@@ -124,17 +103,17 @@
                                this.mwTOCDisable = true;
                        }
                }
-               this.hideOrShow();
+               this.updateVisibility();
        }
 };
 
 /**
  * Hides or shows the TOC based on page and default settings
  */
-ve.ui.MWTocWidget.prototype.hideOrShow = function () {
+ve.ui.MWTocWidget.prototype.updateVisibility = function () {
        // In MediaWiki if __FORCETOC__ is anywhere TOC is always displayed
        // ... Even if there is a __NOTOC__ in the article
-       this.toggle( !this.mwTOCDisable && ( this.mwTOCForce || 
this.topics.items.length >= 3 ) );
+       this.toggle( !this.mwTOCDisable && ( this.mwTOCForce || this.rootLength 
>= 3 ) );
 };
 
 /**
@@ -144,130 +123,78 @@
 ve.ui.MWTocWidget.prototype.rebuild = ve.debounce( function () {
        var widget = this;
        // Only rebuild when initialized
-       if ( this.surface.mwTocWidget.initialized ) {
-               this.$tempTopics.append( this.topics.$group.children().clone() 
);
-               this.teardownItems();
+       if ( this.initialized ) {
                // Build after transactions
                setTimeout( function () {
                        widget.build();
-                       widget.$tempTopics.empty();
-               }, 0 );
+               } );
        }
-}, 0 );
-
-/**
- * Teardown all of the TOC items
- */
-ve.ui.MWTocWidget.prototype.teardownItems = function () {
-       var item;
-       for ( item in this.items ) {
-               this.items[ item ].remove();
-               delete this.items[ item ];
-       }
-       this.items = {};
-};
-
-/**
- * Teardown the widget and remove it from the dom
- */
-ve.ui.MWTocWidget.prototype.teardown = function () {
-       this.teardownItems();
-       this.$element.remove();
-};
+} );
 
 /**
  * Build TOC from mwHeading dm nodes
+ *
+ * Based on generateTOC in Linker.php
  */
 ve.ui.MWTocWidget.prototype.build = function () {
-       var i, l, node,
+       var i, l, level, levelDiff, tocNumber, modelNode, viewNode,
+               $list, $text, $item, $link,
                nodes = this.doc.getNodesByType( 'mwHeading', true ),
-               headingLevel = 0,
-               previousHeadingLevel = 0,
-               parentHeadingLevel = 0,
-               levelSkipped = false,
-               tocNumber = 0,
-               tocLevel = 0,
-               tocSection = 0,
-               sectionPrefix = [],
-               parentSectionArray,
-               key,
-               parent,
-               config,
-               ceNode;
+               lastLevel = 0,
+               stack = [];
 
-       this.topics.clearItems();
+       function getItemIndex( $list, n ) {
+               return $list.children( 'li' ).length + ( n === stack.length - 1 
? 1 : 0 );
+       }
+
+       function linkClickHandler( heading ) {
+               ve.init.target.goToHeading( heading );
+               return false;
+       }
+
+       function setHeadingText( $text, headingNode ) {
+               $text.text( headingNode.$element.text() );
+       }
+
+       this.$tocList.empty();
 
        for ( i = 0, l = nodes.length; i < l; i++ ) {
-               node = nodes[ i ];
-               headingLevel = node.getAttribute( 'level' );
-               // MW TOC Generation
-               // The first heading will always be be a zero level topic, even 
heading levels > 2
-               // If heading level is 1 then it is definitely a zero level 
topic
-               // If heading level is 2 then it is a zero level topic, unless 
a child of a 1 level
-               // If heading went up and skipped a number, the following 
headings of the skipped number are in the same level
-               if ( this.topics.items.length === 0 || headingLevel === 1 || ( 
headingLevel === 2 && parentHeadingLevel !== 1 ) ) {
-                       tocSection++;
-                       sectionPrefix = [ tocSection ];
-                       tocLevel = 0;
-                       // reset
-                       levelSkipped = false;
-                       parent = this.topics;
-                       parentHeadingLevel = headingLevel;
-               } else {
-                       // If previously skipped a level, place this heading in 
the same level as the previous higher one
-                       if ( headingLevel === previousHeadingLevel || 
headingLevel < previousHeadingLevel && levelSkipped ) {
-                               tocNumber++;
-                               sectionPrefix.pop();
-                               sectionPrefix.push( tocNumber );
-                               // Only remove the flag if the heading level 
has dropped but we skipped to a higher number previously
-                               if ( headingLevel < previousHeadingLevel ) {
-                                       levelSkipped = false;
-                               }
+               modelNode = nodes[ i ];
+               level = modelNode.getAttribute( 'level' );
+
+               if ( level > lastLevel ) {
+                       if ( stack.length ) {
+                               $list = $( '<ul>' );
+                               stack[ stack.length - 1 
].children().last().append( $list );
                        } else {
-                               tocNumber = 1;
-                               // Heading not the same as before
-                               if ( headingLevel > previousHeadingLevel ) {
-                                       // Did we skip a level? Flag in case we 
drop down a number
-                                       if ( headingLevel - 
previousHeadingLevel > 1 ) {
-                                               levelSkipped = true;
-                                       }
-                                       tocLevel++;
-                                       sectionPrefix.push( tocNumber );
-                               // Step to lower level unless we are at 1
-                               } else if ( headingLevel < previousHeadingLevel 
&& tocLevel !== 1 ) {
-                                       tocLevel--;
-                                       sectionPrefix.pop();
-                                       tocNumber = sectionPrefix[ 
sectionPrefix.length - 1 ] + 1;
-                                       sectionPrefix.pop();
-                                       sectionPrefix.push( tocNumber );
-                               }
+                               $list = this.$tocList;
+                       }
+                       stack.push( $list );
+               } else if ( level < lastLevel ) {
+                       levelDiff = lastLevel - level;
+                       while ( levelDiff > 0 ) {
+                               stack.pop();
+                               levelDiff--;
                        }
                }
-               // Determine parent
-               parentSectionArray = sectionPrefix.slice( 0 );
-               parentSectionArray.pop();
-               if ( parentSectionArray.length > 0 ) {
-                       key = parentSectionArray.join( '.' );
-                       parent = this.items[ key ];
-               } else {
-                       // Topic level is zero
-                       parent = this.topics;
-               }
-               ceNode = 
this.surface.getView().getDocument().getBranchNodeFromOffset( 
node.getRange().start );
-               config = {
-                       node: ceNode,
-                       tocIndex: i,
-                       parent: parent,
-                       tocLevel: tocLevel,
-                       tocSection: tocSection,
-                       sectionPrefix: sectionPrefix.join( '.' ),
-                       insertIndex: sectionPrefix[ sectionPrefix.length - 1 ]
-               };
-               // Add item
-               this.items[ sectionPrefix.join( '.' ) ] = new 
ve.ui.MWTocItemWidget( config );
-               config.parent.addItems( [ this.items[ sectionPrefix.join( '.' ) 
] ], config.insertIndex );
-               previousHeadingLevel = headingLevel;
+
+               tocNumber = stack.map( getItemIndex ).join( '.' );
+               viewNode = 
this.surface.getView().getDocument().getBranchNodeFromOffset( 
modelNode.getRange().start );
+               $item = $( '<li>' ).addClass( 'toclevel-' + stack.length 
).addClass( 'tocsection-' + ( i + 1 ) );
+               $link = $( '<a href="#">' ).append( '<span class="tocnumber">' 
+ tocNumber + '</span> ' );
+               $text = $( '<span>' ).addClass( 'toctext' );
+
+               setHeadingText( $text, viewNode );
+               modelNode.on( 'update', setHeadingText.bind( this, $text, 
viewNode ) );
+
+               stack[ stack.length - 1 ].append( $item.append( $link.append( 
$text ) ) );
+               $link.on( 'click', linkClickHandler.bind( this, viewNode ) );
+
+               lastLevel = level;
        }
+
+       this.rootLength = stack[ 0 ].children().length;
+
        this.initialized = true;
-       this.hideOrShow();
+       this.updateVisibility();
 };

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

Gerrit-MessageType: newchange
Gerrit-Change-Id: I5eb75c5db5ca466fd6f16a57c693c2a4458cff7c
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/VisualEditor
Gerrit-Branch: master
Gerrit-Owner: Esanders <esand...@wikimedia.org>

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to