Catrope has uploaded a new change for review.

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


Change subject: Make getDataFromDomRecursion() use a context stack to pass 
context info
......................................................................

Make getDataFromDomRecursion() use a context stack to pass context info

This will allow toDataElement() functions to just call this function
with a DOM element, rather than having to have all the recursion context
data to pass in.

Also expose this information using getters.

Change-Id: I89574c42385267e08704f018c0892d63014376a6
---
M modules/ve/dm/ve.dm.Converter.js
1 file changed, 144 insertions(+), 63 deletions(-)


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

diff --git a/modules/ve/dm/ve.dm.Converter.js b/modules/ve/dm/ve.dm.Converter.js
index 8b23684..b8d28e5 100644
--- a/modules/ve/dm/ve.dm.Converter.js
+++ b/modules/ve/dm/ve.dm.Converter.js
@@ -22,6 +22,9 @@
        this.nodeFactory = nodeFactory;
        this.annotationFactory = annotationFactory;
        this.metaItemFactory = metaItemFactory;
+       this.doc = null;
+       this.store = null;
+       this.contextStack = null;
 };
 
 /* Static Methods */
@@ -95,6 +98,78 @@
 };
 
 /* Methods */
+
+/**
+ * Check whether this converter instance is currently inside a 
getDataFromDom() conversion.
+ * @returns {Boolean} Whether we're converting
+ */
+ve.dm.Converter.prototype.isConverting = function () {
+       return this.contextStack !== null;
+};
+
+/**
+ * Get the IndexValueStore used for the current conversion.
+ * @returns {ve.dm.IndexValueStore|null} Current store, or null if not 
converting
+ */
+ve.dm.Converter.prototype.getStore = function () {
+       return this.store;
+};
+
+/**
+ * Get the HTML document currently being converted
+ * @returns {HTMLDocument|null} HTML document being converted, or null if not 
converting
+ */
+ve.dm.Converter.prototype.getHtmlDocument = function () {
+       return this.doc;
+};
+
+/**
+ * Get the current conversion context. This is the recursion state of 
getDataFromDomRecursion().
+ * @returns {Object|null} Context object, or null if not converting
+ */
+ve.dm.Converter.prototype.getCurrentContext = function ()  {
+       return this.contextStack === null ? null : 
this.contextStack[this.contextStack.length - 1];
+};
+
+/**
+ * Get the annotations currently being applied by the converter. Note that 
this is specific to
+ * the current recursion level.
+ * @returns {ve.dm.AnnotationSet|null} Annotation set, or null if not 
converting
+ */
+ve.dm.Converter.prototype.getActiveAnnotations = function () {
+       var context = this.getCurrentContext();
+       return context ? context.annotations : null;
+};
+
+/**
+ * Whether the converter is currently expecting content. Note that this is 
specific to the current
+ * recursion level.
+ * @returns {boolean|null} Boolean indicating whether content is expected, or 
null if not converting
+ */
+ve.dm.Converter.prototype.isExpectingContent = function () {
+       var context = this.getCurrentContext();
+       return context ? context.expectingContent : null;
+};
+
+/**
+ * Whether the conversion is currently inside a wrapper paragraph generated by 
the converter.
+ * Note that this is specific to the current recursion level.
+ * @returns {boolean|null} Boolean indicating whether we're wrapping, or null 
if not converting
+ */
+ve.dm.Converter.prototype.isInWrapper = function () {
+       var context = this.getCurrentContext();
+       return context ? context.inWrapper : null;
+};
+
+/**
+ * Whether the active wrapper can be closed. Note that this is specific to the 
current recursion
+ * level. If there is no active wrapper, this returns false.
+ * @returns {boolean|null} Boolean indicating whether the wrapper can be 
closed, or null if not converting
+ */
+ve.dm.Converter.prototype.canCloseWrapper = function () {
+       var context = this.getCurrentContext();
+       return context ? context.canCloseWrapper : null;
+};
 
 /**
  * Get the DOM element for a given linear model element.
@@ -191,27 +266,34 @@
  * @returns {ve.dm.ElementLinearData} Linear model data
  */
 ve.dm.Converter.prototype.getDataFromDom = function ( store, doc ) {
+       var result;
+       // Set up the converter state
+       this.doc = doc;
+       this.store = store;
+       this.contextStack = [];
        // Possibly do things with doc and the head in the future
-       return new ve.dm.ElementLinearData(
+       result = new ve.dm.ElementLinearData(
                store,
-               this.getDataFromDomRecursion( store, doc.body )
+               this.getDataFromDomRecursion( doc.body )
        );
+       // Clear the state
+       this.doc = null;
+       this.store = null;
+       this.contextStack = null;
+       return result;
 };
 
 /**
- * Recursive implementation of getDataFromDom(). For internal use.
+ * Recursive implementation of getDataFromDom(). For internal use, and for use 
in
+ * ve.dm.Model.static.toDataElement() implementations.
  *
  * @method
- * @param {ve.dm.IndexValueStore} store Index-value store
  * @param {HTMLElement} domElement HTML element to convert
- * @param {ve.dm.AnnotationSet} [annotations] Annotations to apply to the 
generated data
- * @param {Object} [dataElement] Data element to wrap the returned data in
- * @param {Array} [path] Array of linear model element types
- * @param {boolean} [alreadyWrapped] Whether the caller has already started 
wrapping bare content in a paragraph
+ * @param {Object} [wrapperElement] Data element to wrap the returned data in
+ * @param {ve.dm.AnnotationSet} [annotationSet] Override the set of 
annotations to use
  * @returns {Array} Linear model data
  */
-ve.dm.Converter.prototype.getDataFromDomRecursion = function ( store, 
domElement, annotations,
-               dataElement, path, alreadyWrapped ) {
+ve.dm.Converter.prototype.getDataFromDomRecursion = function ( domElement, 
wrapperElement, annotationSet ) {
        function addWhitespace( element, index, whitespace ) {
                if ( !element.internal ) {
                        element.internal = {};
@@ -260,7 +342,7 @@
                wrappingParagraph = undefined;
                context.inWrapper = false;
                context.canCloseWrapper = false;
-               context.expectingContent = originallyExpectingContent;
+               context.expectingContent = context.originallyExpectingContent;
        }
 
        function getAboutGroup( el ) {
@@ -287,27 +369,32 @@
                return aboutGroup;
        }
 
-       // Fallback to defaults
-       annotations = annotations || new ve.dm.AnnotationSet( store );
-       path = path || ['document'];
        var i, childDomElement, childDomElements, childDataElement, text, 
childTypes, matches,
                wrappingParagraph, prevElement, childAnnotations, modelName, 
modelClass,
                annotation, annotationData, childIsContent, aboutGroup,
                data = [],
-               branchType = path[path.length - 1],
-               branchHasContent = this.nodeFactory.canNodeContainContent( 
branchType ),
-               originallyExpectingContent = branchHasContent || 
!annotations.isEmpty(),
                nextWhitespace = '',
                wrappedWhitespace = '',
                wrappedWhitespaceIndex,
-               context = {
-                       'expectingContent': originallyExpectingContent,
-                       'inWrapper': alreadyWrapped,
-                       'canCloseWrapper': false
-               };
+               context = {},
+               prevContext = this.contextStack.length ? 
this.contextStack[this.contextStack.length - 1] : null;
+
+       context.annotations = annotationSet || (
+               prevContext ? prevContext.annotations.clone() : new 
ve.dm.AnnotationSet( this.store )
+       );
+       context.branchType = wrapperElement ? wrapperElement.type : (
+               prevContext ? prevContext.branchType : 'document'
+       );
+       context.branchHasContent = this.nodeFactory.canNodeContainContent( 
context.branchType );
+       context.originallyExpectingContent = context.branchHasContent || 
!context.annotations.isEmpty();
+       context.expectingContent = context.originallyExpectingContent;
+       context.inWrapper = prevContext ? prevContext.inWrapper : false;
+       context.canCloseWrapper = false;
+       this.contextStack.push( context );
+
        // Open element
-       if ( dataElement ) {
-               data.push( dataElement );
+       if ( wrapperElement ) {
+               data.push( wrapperElement );
        }
        // Add contents
        for ( i = 0; i < domElement.childNodes.length; i++ ) {
@@ -327,13 +414,10 @@
                                                prevElement = wrappingParagraph;
                                        }
                                        // Append child element data
-                                       childAnnotations = annotations.clone();
+                                       childAnnotations = 
context.annotations.clone();
                                        childAnnotations.push( annotation );
                                        data = data.concat(
-                                               this.getDataFromDomRecursion(
-                                                       store, childDomElement, 
childAnnotations,
-                                                       undefined, path, 
context.inWrapper
-                                               )
+                                               this.getDataFromDomRecursion( 
childDomElement, undefined, childAnnotations )
                                        );
                                } else {
                                        // Node or meta item
@@ -372,8 +456,8 @@
                                        }
 
                                        // Annotate child
-                                       if ( childIsContent && 
!annotations.isEmpty() ) {
-                                               childDataElement.annotations = 
annotations.getIndexes().slice();
+                                       if ( childIsContent && 
!context.annotations.isEmpty() ) {
+                                               childDataElement.annotations = 
context.annotations.getIndexes().slice();
                                        }
 
                                        // Output child and its children, if any
@@ -384,13 +468,8 @@
                                                // Recursion
                                                // Opening and closing elements 
are added by the recursion too
                                                data = data.concat(
-                                                       
this.getDataFromDomRecursion(
-                                                               store,
-                                                               childDomElement,
-                                                               new 
ve.dm.AnnotationSet( store ),
-                                                               
childDataElement,
-                                                               path.concat( 
childDataElement.type ),
-                                                               
context.inWrapper
+                                                       
this.getDataFromDomRecursion( childDomElement, childDataElement,
+                                                               new 
ve.dm.AnnotationSet( this.store )
                                                        )
                                                );
                                        } else {
@@ -411,7 +490,7 @@
                                        // Empty text node?!?
                                        break;
                                }
-                               if ( !originallyExpectingContent ) {
+                               if ( !context.originallyExpectingContent ) {
                                        // Strip and store outer whitespace
                                        if ( text.match( /^\s+$/ ) ) {
                                                // This text node is whitespace 
only
@@ -422,15 +501,15 @@
                                                        wrappedWhitespace = 
text;
                                                        wrappedWhitespaceIndex 
= data.length;
                                                        data = data.concat(
-                                                               
ve.dm.Converter.getDataContentFromText( wrappedWhitespace, annotations )
+                                                               
ve.dm.Converter.getDataContentFromText( wrappedWhitespace, context.annotations )
                                                        );
                                                } else {
                                                        // We're not in 
wrapping mode, store this whitespace
                                                        if ( !prevElement ) {
-                                                               if ( 
dataElement ) {
+                                                               if ( 
wrapperElement ) {
                                                                        // 
First child, store as inner
                                                                        // 
whitespace in the parent
-                                                                       
addWhitespace( dataElement, 1, text );
+                                                                       
addWhitespace( wrapperElement, 1, text );
                                                                }
                                                                // Else, WTF?!? 
This is not supposed to
                                                                // happen, but 
it's not worth
@@ -457,10 +536,10 @@
                                                        // started wrapping
                                                        if ( matches[1] !== '' 
) {
                                                                if ( 
!prevElement ) {
-                                                                       if ( 
dataElement ) {
+                                                                       if ( 
wrapperElement ) {
                                                                                
// First child, store as inner
                                                                                
// whitespace in the parent
-                                                                               
addWhitespace( dataElement, 1, matches[1] );
+                                                                               
addWhitespace( wrapperElement, 1, matches[1] );
                                                                        }
                                                                        // 
Else, WTF?!? This is not supposed to
                                                                        // 
happen, but it's not worth
@@ -474,12 +553,12 @@
                                                        // We were already 
wrapping in a paragraph,
                                                        // so the leading 
whitespace must be output
                                                        data = data.concat(
-                                                               
ve.dm.Converter.getDataContentFromText( matches[1], annotations )
+                                                               
ve.dm.Converter.getDataContentFromText( matches[1], context.annotations )
                                                        );
                                                }
                                                // Output the text sans 
whitespace
                                                data = data.concat(
-                                                       
ve.dm.Converter.getDataContentFromText( matches[2], annotations )
+                                                       
ve.dm.Converter.getDataContentFromText( matches[2], context.annotations )
                                                );
 
                                                // Don't store this in 
wrappingParagraph.internal.whitespace[3]
@@ -492,7 +571,7 @@
                                                wrappedWhitespace = matches[3];
                                                wrappedWhitespaceIndex = 
data.length;
                                                data = data.concat(
-                                                       
ve.dm.Converter.getDataContentFromText( wrappedWhitespace, annotations )
+                                                       
ve.dm.Converter.getDataContentFromText( wrappedWhitespace, context.annotations )
                                                );
                                                prevElement = wrappingParagraph;
                                                break;
@@ -503,26 +582,26 @@
                                // (but only in non-annotation nodes)
                                // and store it so it can be restored later.
                                if (
-                                       annotations.isEmpty() && i === 0 && 
dataElement &&
-                                       
!this.nodeFactory.doesNodeHaveSignificantWhitespace( dataElement.type )
+                                       context.annotations.isEmpty() && i === 
0 && wrapperElement &&
+                                       
!this.nodeFactory.doesNodeHaveSignificantWhitespace( wrapperElement.type )
                                ) {
                                        // Strip leading whitespace from the 
first child
                                        matches = text.match( /^\s+/ );
                                        if ( matches && matches[0] !== '' ) {
-                                               addWhitespace( dataElement, 1, 
matches[0] );
+                                               addWhitespace( wrapperElement, 
1, matches[0] );
                                                text = text.substring( 
matches[0].length );
                                        }
                                }
                                if (
-                                       annotations.isEmpty() &&
+                                       context.annotations.isEmpty() &&
                                        i === domElement.childNodes.length - 1 
&&
-                                       dataElement &&
-                                       
!this.nodeFactory.doesNodeHaveSignificantWhitespace( dataElement.type )
+                                       wrapperElement &&
+                                       
!this.nodeFactory.doesNodeHaveSignificantWhitespace( wrapperElement.type )
                                ) {
                                        // Strip trailing whitespace from the 
last child
                                        matches = text.match( /\s+$/ );
                                        if ( matches && matches[0] !== '' ) {
-                                               addWhitespace( dataElement, 2, 
matches[0] );
+                                               addWhitespace( wrapperElement, 
2, matches[0] );
                                                text = text.substring( 0,
                                                        text.length - 
matches[0].length );
                                        }
@@ -530,7 +609,7 @@
 
                                // Annotate the text and output it
                                data = data.concat(
-                                       ve.dm.Converter.getDataContentFromText( 
text, annotations )
+                                       ve.dm.Converter.getDataContentFromText( 
text, context.annotations )
                                );
                                break;
                        case Node.COMMENT_NODE:
@@ -558,10 +637,10 @@
 
        // If we're closing a node that doesn't have any children, but could 
contain a paragraph,
        // add a paragraph. This prevents things like empty list items
-       childTypes = this.nodeFactory.getChildNodeTypes( branchType );
-       if ( branchType !== 'paragraph' && dataElement && data[data.length - 1] 
=== dataElement &&
-               !context.inWrapper && !this.nodeFactory.canNodeContainContent( 
branchType ) &&
-               !this.nodeFactory.isNodeContent( branchType ) &&
+       childTypes = this.nodeFactory.getChildNodeTypes( context.branchType );
+       if ( context.branchType !== 'paragraph' && wrapperElement && 
data[data.length - 1] === wrapperElement &&
+               !context.inWrapper && !this.nodeFactory.canNodeContainContent( 
context.branchType ) &&
+               !this.nodeFactory.isNodeContent( context.branchType ) &&
                ( childTypes === null || ve.indexOf( 'paragraph', childTypes ) 
!== -1 )
        ) {
                data.push( { 'type': 'paragraph', 'internal': { 'generated': 
'empty' } } );
@@ -569,21 +648,23 @@
        }
 
        // Close element
-       if ( dataElement ) {
-               data.push( { 'type': '/' + dataElement.type } );
+       if ( wrapperElement ) {
+               data.push( { 'type': '/' + wrapperElement.type } );
                // Add the whitespace after the last child to the parent as 
innerPost
                if ( nextWhitespace !== '' ) {
-                       addWhitespace( dataElement, 2, nextWhitespace );
+                       addWhitespace( wrapperElement, 2, nextWhitespace );
                        nextWhitespace = '';
                }
        }
        // Don't return an empty document
-       if ( branchType === 'document' && data.length === 0 ) {
+       if ( context.branchType === 'document' && data.length === 0 ) {
                return [
                        { 'type': 'paragraph', 'internal': { 'generated': 
'empty' } },
                        { 'type': '/paragraph' }
                ];
        }
+
+       this.contextStack.pop();
        return data;
 };
 

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

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

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

Reply via email to