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

Change subject: Make the converter work with full HTML documents rather than 
fragments
......................................................................


Make the converter work with full HTML documents rather than fragments

The Parsoid output will also be expected to be a full HTML document. For
backwards compatibility, we allow for the Parsoid output to be a
document fragment as well. We don't send a full document back yet, also
for b/c -- we'll change this later once Parsoid has been updated in
production.

ve.dm.Converter.js:
* Make getDataFromDom() accept a document rather than a node
** Split off the recursion (which does use nodes) into its own function
** For now we just convert the <body>. In the future, we'll want to do
   things with the <head> as well
* Pass the document around so we can use it when creating elements
* Make getDomFromData() return a document rather than a <div>

ve.init.mw.Target.js:
* Store a document (this.doc) rather than a DOM node (this.dom)
* Pass around documents rather than DOM nodes
* Detect whether the Parsoid output is an HTML document or a fragment
  using a hacky regex
* When submitting to Parsoid, submit the innerHTML of the <body>

ve.init.mw.ViewPageTarget.js:
* s/dom/doc/
* Store body.innerHTML in this.originalHtml

ve.Surface.js:
* s/dom/doc/

demos/ve/index.php:
* Don't wrap HTML in <div>
* Pass HTML document rather than DOM node to ve.Surface

ve.dm.Converter.test.js:
* Construct a document from the test HTML, rather than a <div>

ve.dm.example.js:
* Wrap the HTML in the converter test cases in <body> tags to prevent
  misinterpretation (HTML fragments starting with comments, <meta>,
  <link> and whitespace are problematic)

Change-Id: I82fdad0a099febc5e658486cbf8becfcdbc85a2d
---
M demos/ve/index.php
M modules/ve/dm/ve.dm.Converter.js
M modules/ve/init/mw/targets/ve.init.mw.ViewPageTarget.js
M modules/ve/init/mw/ve.init.mw.Target.js
M modules/ve/test/dm/ve.dm.Converter.test.js
M modules/ve/test/dm/ve.dm.example.js
M modules/ve/ve.Surface.js
7 files changed, 140 insertions(+), 120 deletions(-)

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



diff --git a/demos/ve/index.php b/demos/ve/index.php
index 13f7910..3c59c2e 100644
--- a/demos/ve/index.php
+++ b/demos/ve/index.php
@@ -14,7 +14,7 @@
 if ( isset( $_GET['page'] ) && in_array( $path . '/' . $_GET['page'] . 
'.html', $pages ) ) {
        $page =  $path . '/' . $_GET['page'] . '.html';
 }
-$html = '<div>' . file_get_contents( $page ) . '</div>';
+$html = file_get_contents( $page );
 
 ?>
 <!DOCTYPE html>
@@ -215,7 +215,7 @@
                        $(document).ready( function () {
                                new ve.Surface(
                                        $( '.ve-demo-content' ),
-                                       $( <?php echo json_encode( $html ) ?> 
)[0]
+                                       ve.createDocumentFromHTML( <?php echo 
json_encode( $html ) ?> )
                                );
                                $( '.ve-ce-documentNode' ).focus();
                        } );
diff --git a/modules/ve/dm/ve.dm.Converter.js b/modules/ve/dm/ve.dm.Converter.js
index aa10696..543bee5 100644
--- a/modules/ve/dm/ve.dm.Converter.js
+++ b/modules/ve/dm/ve.dm.Converter.js
@@ -95,21 +95,22 @@
  *
  * @method
  * @param {Object} dataElement Linear model element
+ * @param {HTMLDocument} doc Document to create DOM elements in
  * @returns {HTMLElement|boolean} DOM element, or false if the element cannot 
be converted
  */
-ve.dm.Converter.prototype.getDomElementFromDataElement = function ( 
dataElement ) {
+ve.dm.Converter.prototype.getDomElementFromDataElement = function ( 
dataElement, doc ) {
        var key, domElement, dataElementAttributes, wrapper,
                dataElementType = dataElement.type;
        if ( dataElementType === 'alienInline' || dataElementType === 
'alienBlock' ) {
                // Alien
                // Create nodes from source
-               wrapper = document.createElement( 'div' );
+               wrapper = doc.createElement( 'div' );
                wrapper.innerHTML = dataElement.attributes.html;
                if ( wrapper.childNodes.length > 1 ) {
                        // Wrap the HTML in a single element, this makes
                        // it much easier to deal with. It'll be unwrapped
                        // at the end of getDomFromData().
-                       domElement = document.createElement( 'div' );
+                       domElement = doc.createElement( 'div' );
                        domElement.setAttribute( 
'data-ve-multi-child-alien-wrapper', 'true' );
                        while ( wrapper.firstChild ) {
                                domElement.appendChild( wrapper.firstChild );
@@ -204,28 +205,36 @@
  * @param {Object} dataAnnotation Annotation object
  * @returns {HTMLElement} HTML DOM node
  */
-ve.dm.Converter.prototype.getDomElementFromDataAnnotation = function ( 
dataAnnotation ) {
+ve.dm.Converter.prototype.getDomElementFromDataAnnotation = function ( 
dataAnnotation, doc ) {
        var htmlData = dataAnnotation.toHTML(),
-               domElement = document.createElement( htmlData.tag );
+               domElement = doc.createElement( htmlData.tag );
        ve.setDomAttributes( domElement, htmlData.attributes );
        return domElement;
 };
 
 /**
- * Convert an HTML DOM tree to a linear model.
- *
- * Do not use the annotations, dataElement and path parameters, they're used 
for internal
- * recursion only.
+ * Convert an HTML document to a linear model.
+ * @param {HTMLDocument} doc HTML document to convert
+ * @returns {Array} Linear model data
+ */
+ve.dm.Converter.prototype.getDataFromDom = function ( doc ) {
+       // Possibly do things with doc and the head in the future
+       return this.getDataFromDomRecursion( doc.body );
+};
+
+/**
+ * Recursive implementation of getDataFromDom(). For internal use.
  *
  * @method
- * @param {HTMLElement} domElement Wrapper div containing the HTML to convert
+ * @param {HTMLElement} domElement HTML element to convert
  * @param {ve.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
  * @returns {Array} Linear model data
  */
-ve.dm.Converter.prototype.getDataFromDom = function ( domElement, annotations, 
dataElement, path, alreadyWrapped ) {
+ve.dm.Converter.prototype.getDataFromDomRecursion = function ( domElement, 
annotations,
+               dataElement, path, alreadyWrapped ) {
        function createAlien( domElement, context, isWrapper ) {
                // We generate alienBlock elements for block tags and 
alienInline elements for
                // inline tags; unless we're in a content location, in which 
case we have no choice
@@ -242,7 +251,7 @@
                if ( isWrapper ) {
                        html =  $( domElement ).html();
                } else {
-                       html = $( '<div>' ).append( $( domElement ).clone() 
).html();
+                       html = $( '<div>', doc ).append( $( domElement 
).clone() ).html();
                }
                alien = [
                        {
@@ -342,7 +351,7 @@
                                        // This is the second child in this 
group, so the
                                        // previous child is the first child in 
this group.
                                        // Wrap the previous child
-                                       aboutWrapper = document.createElement( 
'div' );
+                                       aboutWrapper = doc.createElement( 'div' 
);
                                        aboutWrapper.setAttribute( 
'data-ve-aboutgroup', aboutGroup );
                                        element.insertBefore( aboutWrapper, 
prevChild );
                                        aboutWrapper.appendChild( prevChild );
@@ -373,6 +382,7 @@
        path = path || ['document'];
        var i, j, childDomElement, annotation, childDataElement, text, 
childTypes, matches,
                wrappingParagraph, prevElement, alien, rdfaType, isLink, 
childAnnotations,
+               doc = domElement.ownerDocument,
                data = [],
                branchType = path[path.length - 1],
                branchHasContent = this.nodeFactory.canNodeContainContent( 
branchType ),
@@ -493,7 +503,7 @@
                                        childAnnotations = annotations.clone();
                                        childAnnotations.push( annotation );
                                        data = data.concat(
-                                               this.getDataFromDom(
+                                               this.getDataFromDomRecursion(
                                                        childDomElement, 
childAnnotations,
                                                        undefined, path, 
context.wrapping
                                                )
@@ -524,7 +534,7 @@
                                                if ( 
this.nodeFactory.canNodeHaveChildren( childDataElement.type ) ) {
                                                        // Append child element 
data
                                                        data = data.concat(
-                                                               
this.getDataFromDom(
+                                                               
this.getDataFromDomRecursion(
                                                                        
childDomElement,
                                                                        new 
ve.AnnotationSet(),
                                                                        
childDataElement,
@@ -744,13 +754,14 @@
  *
  * @method
  * @param {Array} data Linear model data
- * @returns {HTMLElement} Wrapper div containing the resulting HTML
+ * @returns {HTMLDocument} Document containing the resulting HTML
  */
 ve.dm.Converter.prototype.getDomFromData = function ( data ) {
        var text, i, j, k, annotations, annotation, annotationElement, 
dataElement, arr,
                childDomElement, pre, ours, theirs, parentDomElement, 
startClosingAt,
                isContentNode, changed, parentChanged,
-               container = document.createElement( 'div' ),
+               doc = ve.createDocumentFromHTML( '' ),
+               container = doc.body,
                domElement = container,
                annotationStack = new ve.AnnotationSet();
 
@@ -766,7 +777,7 @@
                        // i points to the first non-text thing, go back one so 
we don't skip this later
                        i--;
                        // Add text
-                       domElement.appendChild( document.createTextNode( text ) 
);
+                       domElement.appendChild( doc.createTextNode( text ) );
                } else if (
                        ve.isArray( data[i] ) ||
                        (
@@ -802,7 +813,7 @@
                                        for ( j = annotationStack.getLength() - 
1; j >= startClosingAt; j-- ) {
                                                // Add text if needed
                                                if ( text.length > 0 ) {
-                                                       domElement.appendChild( 
document.createTextNode( text ) );
+                                                       domElement.appendChild( 
doc.createTextNode( text ) );
                                                        text = '';
                                                }
                                                // Traverse up
@@ -819,11 +830,11 @@
                                        if ( !annotationStack.contains( 
annotation ) ) {
                                                // Add text if needed
                                                if ( text.length > 0 ) {
-                                                       domElement.appendChild( 
document.createTextNode( text ) );
+                                                       domElement.appendChild( 
doc.createTextNode( text ) );
                                                        text = '';
                                                }
                                                // Create new node and descend 
into it
-                                               annotationElement = 
this.getDomElementFromDataAnnotation( annotation );
+                                               annotationElement = 
this.getDomElementFromDataAnnotation( annotation, doc );
                                                domElement.appendChild( 
annotationElement );
                                                domElement = annotationElement;
                                                // Add to annotationStack
@@ -838,11 +849,11 @@
                                        // Annotated node
                                        // Add text if needed
                                        if ( text.length > 0 ) {
-                                               domElement.appendChild( 
document.createTextNode( text ) );
+                                               domElement.appendChild( 
doc.createTextNode( text ) );
                                                text = '';
                                        }
                                        // Insert the element
-                                       domElement.appendChild( 
this.getDomElementFromDataElement( data[i] ) );
+                                       domElement.appendChild( 
this.getDomElementFromDataElement( data[i], doc ) );
                                        // Increment i once more so we skip 
over the closing as well
                                        i++;
                                }
@@ -853,7 +864,7 @@
 
                        // Add any gathered text
                        if ( text.length > 0 ) {
-                               domElement.appendChild( 
document.createTextNode( text ) );
+                               domElement.appendChild( doc.createTextNode( 
text ) );
                                text = '';
                        }
                        // Close any remaining annotation nodes
@@ -890,7 +901,7 @@
                                                } else {
                                                        // Prepend a TextNode
                                                        domElement.insertBefore(
-                                                               
document.createTextNode( pre ),
+                                                               
doc.createTextNode( pre ),
                                                                
domElement.firstChild
                                                        );
                                                }
@@ -914,7 +925,7 @@
                                                } else {
                                                        // Append a TextNode
                                                        domElement.appendChild(
-                                                               
document.createTextNode( ours )
+                                                               
doc.createTextNode( ours )
                                                        );
                                                }
                                        }
@@ -977,7 +988,7 @@
                                domElement = parentDomElement;
                        } else {
                                // Create node from data
-                               childDomElement = 
this.getDomElementFromDataElement( dataElement );
+                               childDomElement = 
this.getDomElementFromDataElement( dataElement, doc );
                                // Add reference to internal data
                                if ( dataElement.internal ) {
                                        childDomElement.veInternal = 
dataElement.internal;
@@ -1023,7 +1034,7 @@
                                        if ( ours && ours === theirs ) {
                                                // Matches the duplicate, 
insert a TextNode
                                                parentDomElement.insertBefore(
-                                                       
document.createTextNode( ours ),
+                                                       doc.createTextNode( 
ours ),
                                                        domElement
                                                );
                                        }
@@ -1038,7 +1049,7 @@
                        container.lastChild.appendData( container.lastOuterPost 
);
                } else {
                        // Append a TextNode
-                       container.appendChild( document.createTextNode( 
container.lastOuterPost ) );
+                       container.appendChild( doc.createTextNode( 
container.lastOuterPost ) );
                }
                delete container.lastOuterPost;
        }
@@ -1063,7 +1074,7 @@
                        }
                }
        } );
-       return container;
+       return doc;
 };
 
 /* Initialization */
diff --git a/modules/ve/init/mw/targets/ve.init.mw.ViewPageTarget.js 
b/modules/ve/init/mw/targets/ve.init.mw.ViewPageTarget.js
index 1647125..70a2c83 100644
--- a/modules/ve/init/mw/targets/ve.init.mw.ViewPageTarget.js
+++ b/modules/ve/init/mw/targets/ve.init.mw.ViewPageTarget.js
@@ -293,12 +293,13 @@
  * Handle successful DOM load event.
  *
  * @method
- * @param {HTMLElement} dom Parsed DOM from server
+ * @param {HTMLDocument} doc Parsed DOM from server
  */
-ve.init.mw.ViewPageTarget.prototype.onLoad = function ( dom ) {
+ve.init.mw.ViewPageTarget.prototype.onLoad = function ( doc ) {
        if ( this.activating ) {
                this.edited = false;
-               this.setUpSurface( dom );
+               this.doc = doc;
+               this.setUpSurface( doc );
                this.setupToolbarEditNotices();
                this.setupToolbarButtons();
                this.setupSaveDialog();
@@ -677,15 +678,15 @@
  * Switch to editing mode.
  *
  * @method
- * @param {HTMLElement} dom HTML DOM to edit
+ * @param {HTMLDocument} doc HTML DOM to edit
  */
-ve.init.mw.ViewPageTarget.prototype.setUpSurface = function ( dom ) {
+ve.init.mw.ViewPageTarget.prototype.setUpSurface = function ( doc ) {
        var $contentText = $( '#mw-content-text' );
 
        // Store the HTML for reporting purposes
-       this.originalHtml = dom.innerHTML;
+       this.originalHtml = doc.body.innerHTML; // TODO store entire document 
in the future
        // Initialize surface
-       this.surface = new ve.Surface( $( '#content' ), dom, 
this.surfaceOptions );
+       this.surface = new ve.Surface( $( '#content' ), doc, 
this.surfaceOptions );
        this.surface.getContext().hide();
        this.$document = this.surface.$.find( '.ve-ce-documentNode' );
        this.surface.getModel().on( 'transact', 
this.proxiedOnSurfaceModelTransact );
diff --git a/modules/ve/init/mw/ve.init.mw.Target.js 
b/modules/ve/init/mw/ve.init.mw.Target.js
index 3975c2f..b61ca04 100644
--- a/modules/ve/init/mw/ve.init.mw.Target.js
+++ b/modules/ve/init/mw/ve.init.mw.Target.js
@@ -40,7 +40,7 @@
        this.submitting = false;
        this.baseTimeStamp = null;
        this.startTimeStamp = null;
-       this.dom = null;
+       this.doc = null;
        this.editNotices = null;
        this.isMobileDevice = (
                'ontouchstart' in window ||
@@ -78,7 +78,7 @@
  * Handle response to a successful load request.
  *
  * This method is called within the context of a target instance. If 
successful the DOM from the
- * server will be parsed, stored in {this.dom} and then 
{ve.init.mw.Target.onReady} will be called once
+ * server will be parsed, stored in {this.doc} and then 
{ve.init.mw.Target.onReady} will be called once
  * the modules are ready.
  *
  * @static
@@ -88,7 +88,7 @@
  * @emits loadError (null, message, null)
  */
 ve.init.mw.Target.onLoad = function ( response ) {
-       var key, tmp, el,
+       var key, tmp, el, html,
                data = response ? response.visualeditor : null;
 
        if ( !data && !response.error ) {
@@ -102,7 +102,14 @@
                        this, null, 'No HTML content in response from server', 
null
                );
        } else {
-               this.dom = $( '<div>' ).html( data.content )[0];
+               // HACK for backwards compatibility with older versions of 
Parsoid, detect whether
+               // data.content is a document fragment or a full HTML document
+               if ( data.content.match( /<(html|head|body)(>|\s)/ ) ) {
+                       html = data.content;
+               } else {
+                       html = '<!doctype html><html><head></head><body>' + 
data.content + '</body></html>';
+               }
+               this.doc = ve.createDocumentFromHTML( html );
 
                /* Don't show notices with no visible html (bug 43013). */
 
@@ -146,7 +153,7 @@
  * Handle both DOM and modules being loaded and ready.
  *
  * This method is called within the context of a target instance. After the 
load event is emitted
- * this.dom is cleared, allowing it to be garbage collected.
+ * this.doc is cleared, allowing it to be garbage collected.
  *
  * @static
  * @method
@@ -154,9 +161,9 @@
  */
 ve.init.mw.Target.onReady = function () {
        this.loading = false;
-       this.emit( 'load', this.dom );
+       this.emit( 'load', this.doc );
        // Release DOM data
-       this.dom = null;
+       this.doc = null;
 };
 
 /**
@@ -372,14 +379,14 @@
  *     target.save( dom, { 'summary': 'test', 'minor': true, 'watch': false } 
);
  *
  * @method
- * @param {HTMLElement} dom DOM to save
+ * @param {HTMLDocument} doc Document to save
  * @param {Object} options Saving options
  *  - {string} summary Edit summary
  *  - {boolean} minor Edit is a minor edit
  *  - {boolean} watch Watch the page
  * @returns {boolean} Saving has been started
 */
-ve.init.mw.Target.prototype.save = function ( dom, options ) {
+ve.init.mw.Target.prototype.save = function ( doc, options ) {
        // Prevent duplicate requests
        if ( this.saving ) {
                return false;
@@ -396,7 +403,7 @@
                        'oldid': this.oldid,
                        'basetimestamp': this.baseTimeStamp,
                        'starttimestamp': this.startTimeStamp,
-                       'html': $( dom ).html(),
+                       'html': doc.body.innerHTML, // TODO make this send the 
whole document in the future
                        'token': this.editToken,
                        'summary': options.summary,
                        'minor': Number( options.minor ),
@@ -416,9 +423,9 @@
  * Post DOM data to the Parsoid API to retreive wikitext diff.
  *
  * @method
- * @param {HTMLElement} dom DOM to compare against (via wikitext).
+ * @param {HTMLDocument} doc Document to compare against (via wikitext).
 */
-ve.init.mw.Target.prototype.showChanges = function ( dom ) {
+ve.init.mw.Target.prototype.showChanges = function ( doc ) {
        $.ajax( {
                'url': this.apiUrl,
                'data': {
@@ -426,7 +433,7 @@
                        'action': 'visualeditor',
                        'paction': 'diff',
                        'page': this.pageName,
-                       'html': $( dom ).html(),
+                       'html': doc.body.innerHTML, // TODO make this send the 
whole document in the future
                        // TODO: API required editToken, though not relevant 
for diff
                        'token': this.editToken
                },
@@ -497,11 +504,11 @@
  *     );
  *
  * @method
- * @param {HTMLElement} dom DOM to serialize
+ * @param {HTMLDocument} doc Document to serialize
  * @param {Function} callback Function to call when complete, accepts error 
and wikitext arguments
  * @returns {boolean} Serializing has beeen started
 */
-ve.init.mw.Target.prototype.serialize = function ( dom, callback ) {
+ve.init.mw.Target.prototype.serialize = function ( doc, callback ) {
        // Prevent duplicate requests
        if ( this.serializing ) {
                return false;
@@ -514,7 +521,7 @@
                'data': {
                        'action': 'visualeditor',
                        'paction': 'serialize',
-                       'html': $( dom ).html(),
+                       'html': doc.body.innerHTML, // TODO make this send the 
whole document in the future
                        'page': this.pageName,
                        'token': this.editToken,
                        'format': 'json'
@@ -548,9 +555,10 @@
                        'diff': this.diffHtml,
                        'originalHtml': this.originalHtml,
                        'originalData':
-                               ve.dm.converter.getDataFromDom( $( '<div>' 
).html( this.originalHtml )[0] ),
+                               // originalHTML only has the body's HTML for 
now, see TODO comment in ve.init.mw.ViewPageTarget.prototype.setUpSurface
+                               ve.dm.converter.getDataFromDom( 
ve.createDocumentFromHTML( '<body>' + this.originalHtml  + '</body>') ),
                        'editedData': editedData,
-                       'editedHtml': ve.dm.converter.getDomFromData( 
editedData ).innerHTML,
+                       'editedHtml': ve.dm.converter.getDomFromData( 
editedData ).body.innerHTML,
                        'wiki': mw.config.get( 'wgDBname' )
                };
        $.post(
diff --git a/modules/ve/test/dm/ve.dm.Converter.test.js 
b/modules/ve/test/dm/ve.dm.Converter.test.js
index 84b3529..aee865a 100644
--- a/modules/ve/test/dm/ve.dm.Converter.test.js
+++ b/modules/ve/test/dm/ve.dm.Converter.test.js
@@ -43,7 +43,7 @@
                if ( cases[msg].html !== null ) {
                        ve.dm.example.preprocessAnnotations( cases[msg].data );
                        assert.deepEqual(
-                               ve.dm.converter.getDataFromDom( $( '<div>' 
).html( cases[msg].html )[0] ),
+                               ve.dm.converter.getDataFromDom( 
ve.createDocumentFromHTML( cases[msg].html ) ),
                                cases[msg].data,
                                msg
                        );
@@ -59,7 +59,7 @@
                ve.dm.example.preprocessAnnotations( cases[msg].data );
                assert.equalDomElement(
                        ve.dm.converter.getDomFromData( cases[msg].data ),
-                       $( '<div>' ).html( cases[msg].normalizedHtml || 
cases[msg].html )[0],
+                       ve.createDocumentFromHTML( cases[msg].normalizedHtml || 
cases[msg].html ),
                        msg
                );
        }
diff --git a/modules/ve/test/dm/ve.dm.example.js 
b/modules/ve/test/dm/ve.dm.example.js
index d4025e5..6fe7d26 100644
--- a/modules/ve/test/dm/ve.dm.example.js
+++ b/modules/ve/test/dm/ve.dm.example.js
@@ -651,7 +651,7 @@
 
 ve.dm.example.domToDataCases = {
        'paragraph with plain text': {
-               'html': '<p>abc</p>',
+               'html': '<body><p>abc</p></body>',
                'data': [
                        { 'type': 'paragraph' },
                        'a',
@@ -661,7 +661,7 @@
                ]
        },
        'annotated text with bold, italic, underline formatting': {
-               'html': '<p><b>a</b><i>b</i><u>c</u></p>',
+               'html': '<body><p><b>a</b><i>b</i><u>c</u></p></body>',
                'data': [
                        { 'type': 'paragraph' },
                        ['a', [ ve.dm.example.bold ]],
@@ -671,7 +671,7 @@
                ]
        },
        'image': {
-               'html': '<img src="image.png">',
+               'html': '<body><img src="image.png"></body>',
                'data': [
                        { 'type': 'paragraph', 'internal': { 'generated': 
'wrapper' } },
                        { 'type': 'image', 'attributes' : { 'html/0/src' : 
'image.png' } },
@@ -680,7 +680,7 @@
                ]
        },
        'paragraph with alienInline inside': {
-               'html': '<p>a<tt class="foo">b</tt>c</p>',
+               'html': '<body><p>a<tt class="foo">b</tt>c</p></body>',
                'data': [
                        { 'type': 'paragraph' },
                        'a',
@@ -694,7 +694,7 @@
                ]
        },
        'paragraphs with an alienBlock between them': {
-               'html': '<p>abc</p><figure>abc</figure><p>def</p>',
+               'html': '<body><p>abc</p><figure>abc</figure><p>def</p></body>',
                'data': [
                        { 'type': 'paragraph' },
                        'a',
@@ -711,8 +711,8 @@
                ]
        },
        'annotated inline nodes': {
-               'html': '<p>a<b><tt class="foo">b</tt><i><span 
typeof="mw:Entity">c</span></i></b>' +
-                       '<i><br/>d</i>e</p>',
+               'html': '<body><p>a<b><tt class="foo">b</tt><i><span 
typeof="mw:Entity">c</span></i></b>' +
+                       '<i><br/>d</i>e</p></body>',
                'data': [
                        { 'type': 'paragraph' },
                        'a',
@@ -739,7 +739,7 @@
                ]
        },
        'wrapping of bare content': {
-               'html': 'abc',
+               'html': '<body>abc</body>',
                'data': [
                        { 'type': 'paragraph', 'internal': { 'generated': 
'wrapper' } },
                        'a',
@@ -749,7 +749,7 @@
                ]
        },
        'wrapping of bare content with inline node': {
-               'html': '1<br/>2',
+               'html': '<body>1<br/>2</body>',
                'data': [
                        { 'type': 'paragraph', 'internal': { 'generated': 
'wrapper' } },
                        '1',
@@ -760,7 +760,7 @@
                ]
        },
        'wrapping of bare content starting with inline node': {
-               'html': '<img src="foo.jpg">12',
+               'html': '<body><img src="foo.jpg">12</body>',
                'data': [
                        { 'type': 'paragraph', 'internal': { 'generated': 
'wrapper' } },
                        { 'type': 'image', 'attributes': { 'html/0/src': 
'foo.jpg' } },
@@ -771,7 +771,7 @@
                ]
        },
        'wrapping of bare content with inline alien': {
-               'html': '1<tt class="bar">baz</tt>2',
+               'html': '<body>1<tt class="bar">baz</tt>2</body>',
                'data': [
                        { 'type': 'paragraph', 'internal': { 'generated': 
'wrapper' } },
                        '1',
@@ -785,7 +785,7 @@
                ]
        },
        'wrapping of bare content with block alien': {
-               'html': '1<figure class="bar">baz</figure>2',
+               'html': '<body>1<figure class="bar">baz</figure>2</body>',
                'data': [
                        { 'type': 'paragraph', 'internal': { 'generated': 
'wrapper' } },
                        '1',
@@ -801,7 +801,7 @@
                ]
        },
        'wrapping of bare content with mw:unrecognized inline alien': {
-               'html': '1<span typeof="mw:Placeholder">baz</span>2',
+               'html': '<body>1<span 
typeof="mw:Placeholder">baz</span>2</body>',
                'data': [
                        { 'type': 'paragraph', 'internal': { 'generated': 
'wrapper' } },
                        '1',
@@ -815,7 +815,7 @@
                ]
        },
        'wrapping of bare content with mw:unrecognized block alien': {
-               'html': '1<div typeof="mw:Placeholder">baz</div>2',
+               'html': '<body>1<div typeof="mw:Placeholder">baz</div>2</body>',
                'data': [
                        { 'type': 'paragraph', 'internal': { 'generated': 
'wrapper' } },
                        '1',
@@ -831,7 +831,7 @@
                ]
        },
        'wrapping of bare content starting with mw:unrecognized inline alien': {
-               'html': '<span typeof="mw:Placeholder">Foo</span>Bar',
+               'html': '<body><span 
typeof="mw:Placeholder">Foo</span>Bar</body>',
                'data': [
                        { 'type': 'paragraph', 'internal': { 'generated': 
'wrapper' } },
                        {
@@ -846,7 +846,7 @@
                ]
        },
        'wrapping of bare content ending with mw:unrecognized inline alien': {
-               'html': 'Foo<span typeof="mw:Placeholder">Bar</span>',
+               'html': '<body>Foo<span 
typeof="mw:Placeholder">Bar</span></body>',
                'data': [
                        { 'type': 'paragraph', 'internal': { 'generated': 
'wrapper' } },
                        'F',
@@ -861,7 +861,7 @@
                ]
        },
        'wrapping of bare content with about group': {
-               'html': '1<tt about="#mwt1">foo</tt><tt 
about="#mwt1">bar</tt>2',
+               'html': '<body>1<tt about="#mwt1">foo</tt><tt 
about="#mwt1">bar</tt>2</body>',
                'data': [
                        { 'type': 'paragraph', 'internal': { 'generated': 
'wrapper' } },
                        '1',
@@ -877,7 +877,7 @@
                ]
        },
        'wrapping of bare content between structural nodes': {
-               'html': '<table></table>abc<table></table>',
+               'html': '<body><table></table>abc<table></table></body>',
                'data': [
                        { 'type': 'table' },
                        { 'type': '/table' },
@@ -891,7 +891,7 @@
                ]
        },
        'wrapping of bare content between paragraphs': {
-               'html': '<p>abc</p>def<p></p>',
+               'html': '<body><p>abc</p>def<p></p></body>',
                'data': [
                        { 'type': 'paragraph' },
                        'a',
@@ -908,7 +908,7 @@
                ]
        },
        'wrapping prevents empty list items': {
-               'html': '<ul><li></li></ul>',
+               'html': '<body><ul><li></li></ul></body>',
                'data': [
                        { 'type': 'list', 'attributes': { 'style': 'bullet' } },
                        { 'type': 'listItem' },
@@ -934,7 +934,7 @@
                        'o',
                        { 'type': '/paragraph' }
                ],
-               'normalizedHtml': '<p>Foo</p>'
+               'normalizedHtml': '<body><p>Foo</p></body>'
        },
        'empty list item with content added by the editor': {
                'html': null,
@@ -949,14 +949,14 @@
                        { 'type': '/listItem' },
                        { 'type': '/list' }
                ],
-               'normalizedHtml': '<ul><li><p>Foo</p></li></ul>'
+               'normalizedHtml': '<body><ul><li><p>Foo</p></li></ul></body>'
        },
        'example document': {
                'html': ve.dm.example.html,
                'data': ve.dm.example.data
        },
        'list item with space followed by link': {
-               'html': '<ul><li><p> <a rel="mw:WikiLink" href="Foo_bar" 
data-rt="{&quot;sHref&quot;:&quot;foo bar&quot;}">bar</a></p></li></ul>',
+               'html': '<body><ul><li><p> <a rel="mw:WikiLink" href="Foo_bar" 
data-rt="{&quot;sHref&quot;:&quot;foo bar&quot;}">bar</a></p></li></ul></body>',
                'data': [
                        { 'type': 'list', 'attributes': { 'style': 'bullet' } },
                        { 'type': 'listItem' },
@@ -1018,8 +1018,8 @@
                ]
        },
        'internal link with ./ and ../': {
-               'html': '<p><a rel="mw:WikiLink" 
href="./../../../Foo/Bar">Foo</a></p>',
-               'normalizedHtml': '<p><a rel="mw:WikiLink" 
href="./../../../Foo/Bar">Foo</a></p>',
+               'html': '<body><p><a rel="mw:WikiLink" 
href="./../../../Foo/Bar">Foo</a></p></body>',
+               'normalizedHtml': '<body><p><a rel="mw:WikiLink" 
href="./../../../Foo/Bar">Foo</a></p></body>',
                'data': [
                        { 'type': 'paragraph' },
                        [
@@ -1074,7 +1074,7 @@
                ]
        },
        'numbered external link': {
-               'html': '<p><a rel="mw:ExtLink/Numbered" 
href="http://www.mediawiki.org/";>[1]</a></p>',
+               'html': '<body><p><a rel="mw:ExtLink/Numbered" 
href="http://www.mediawiki.org/";>[1]</a></p></body>',
                'data': [
                        { 'type': 'paragraph' },
                        [
@@ -1123,7 +1123,7 @@
                ]
        },
        'URL link': {
-               'html': '<p><a rel="mw:ExtLink/URL" 
href="http://www.mediawiki.org/";>mw</a></p>',
+               'html': '<body><p><a rel="mw:ExtLink/URL" 
href="http://www.mediawiki.org/";>mw</a></p></body>',
                'data': [
                        { 'type': 'paragraph' },
                        [
@@ -1158,7 +1158,7 @@
                ]
        },
        'whitespace preservation in headings': {
-               'html': '<h2>Foo</h2><h2> Bar</h2><h2>Baz </h2><h2>  Quux   
</h2>',
+               'html': '<body><h2>Foo</h2><h2> Bar</h2><h2>Baz </h2><h2>  Quux 
  </h2></body>',
                'data': [
                        { 'type': 'heading', 'attributes': { 'level': 2 } },
                        'F',
@@ -1196,7 +1196,7 @@
                ]
        },
        'whitespace preservation in list items': {
-               'html': '<ul><li>Foo</li><li> Bar</li><li>Baz </li><li>  Quux   
</li></ul>',
+               'html': '<body><ul><li>Foo</li><li> Bar</li><li>Baz </li><li>  
Quux   </li></ul></body>',
                'data': [
                        { 'type': 'list', 'attributes': { 'style': 'bullet' } },
                        { 'type': 'listItem' },
@@ -1235,7 +1235,7 @@
                ]
        },
        'whitespace preservation with annotations': {
-               'html': '<p> <i>  Foo   </i>    </p>',
+               'html': '<body><p> <i>  Foo   </i>    </p></body>',
                'data': [
                        {
                                'type': 'paragraph',
@@ -1253,7 +1253,7 @@
                ]
        },
        'outer whitespace preservation in a list with bare text and a wrapper 
paragraph': {
-               'html': '\n<ul>\n\n<li>\n\n\nBa 
re\n\n\n\n</li>\n\n\n\n\n<li>\t<p>\t\tP\t\t\t</p>\t\t\t\t</li>\t\n</ul>\t\n\t\n',
+               'html': '<body>\n<ul>\n\n<li>\n\n\nBa 
re\n\n\n\n</li>\n\n\n\n\n<li>\t<p>\t\tP\t\t\t</p>\t\t\t\t</li>\t\n</ul>\t\n\t\n</body>',
                'data': [
                        { 'type': 'list', 'attributes': { 'style': 'bullet' }, 
'internal': { 'whitespace': [ '\n', '\n\n', '\t\n', '\t\n\t\n' ] } },
                        { 'type': 'listItem', 'internal': { 'whitespace': [ 
'\n\n', '\n\n\n', '\n\n\n\n', '\n\n\n\n\n' ] } },
@@ -1274,7 +1274,7 @@
                ]
        },
        'outer whitespace preservation in a list with bare text and a sublist': 
{
-               'html': '<ul>\n<li>\n\nBa re\n\n\n<ul>\n\n\n\n<li> <p>  P   
</p>    </li>\t</ul>\t\t</li>\t\t\t</ul>',
+               'html': '<body><ul>\n<li>\n\nBa re\n\n\n<ul>\n\n\n\n<li> <p>  P 
  </p>    </li>\t</ul>\t\t</li>\t\t\t</ul></body>',
                'data': [
                        { 'type': 'list', 'attributes': { 'style': 'bullet' }, 
'internal': { 'whitespace': [ undefined, '\n', '\t\t\t' ] } },
                        { 'type': 'listItem', 'internal': { 'whitespace': [ 
'\n', '\n\n', '\t\t', '\t\t\t' ] } },
@@ -1297,7 +1297,7 @@
                ]
        },
        'whitespace preservation leaves non-edge content whitespace alone': {
-               'html': '<p> A  B   <b>    
C\t</b>\t\tD\t\t\t</p>\nE\n\nF\n\n\n<b>\n\n\n\nG </b>  H   ',
+               'html': '<body><p> A  B   <b>    
C\t</b>\t\tD\t\t\t</p>\nE\n\nF\n\n\n<b>\n\n\n\nG </b>  H   </body>',
                'data': [
                        { 'type': 'paragraph', 'internal': { 'whitespace': [ 
undefined, ' ', '\t\t\t', '\n' ] } },
                        'A',
@@ -1338,7 +1338,7 @@
                ]
        },
        'whitespace preservation with non-edge content whitespace with nested 
annotations': {
-               'html': '<p> A  B   <b>    
C\t<i>\t\tD\t\t\t</i>\t\t\t\tE\n</b>\n\nF\n\n\n</p>',
+               'html': '<body><p> A  B   <b>    
C\t<i>\t\tD\t\t\t</i>\t\t\t\tE\n</b>\n\nF\n\n\n</p></body>',
                'data': [
                        { 'type': 'paragraph', 'internal': { 'whitespace': [ 
undefined, ' ', '\n\n\n' ] } },
                        'A',
@@ -1373,7 +1373,7 @@
                ]
        },
        'whitespace preservation with tightly nested annotations': {
-               'html': '<p> A  B   <b><i>\t\tC\t\t\t</i></b>\n\nD\n\n\n</p>',
+               'html': '<body><p> A  B   
<b><i>\t\tC\t\t\t</i></b>\n\nD\n\n\n</p></body>',
                'data': [
                        { 'type': 'paragraph', 'internal': { 'whitespace': [ 
undefined, ' ', '\n\n\n' ] } },
                        'A',
@@ -1396,7 +1396,7 @@
                ]
        },
        'whitespace preservation with nested annotations with whitespace on the 
left side': {
-               'html': '<p> A  B   
<b>\n\t<i>\t\tC\t\t\t</i></b>\n\nD\n\n\n</p>',
+               'html': '<body><p> A  B   
<b>\n\t<i>\t\tC\t\t\t</i></b>\n\nD\n\n\n</p></body>',
                'data': [
                        { 'type': 'paragraph', 'internal': { 'whitespace': [ 
undefined, ' ', '\n\n\n' ] } },
                        'A',
@@ -1421,7 +1421,7 @@
                ]
        },
        'whitespace preservation with nested annotations with whitespace on the 
right side': {
-               'html': '<p> A  B   
<b><i>\t\tC\t\t\t</i>\n\t</b>\n\nD\n\n\n</p>',
+               'html': '<body><p> A  B   
<b><i>\t\tC\t\t\t</i>\n\t</b>\n\nD\n\n\n</p></body>',
                'data': [
                        { 'type': 'paragraph', 'internal': { 'whitespace': [ 
undefined, ' ', '\n\n\n' ] } },
                        'A',
@@ -1446,7 +1446,7 @@
                ]
        },
        'whitespace preservation with aliens': {
-               'html': ' <p typeof="mw:Placeholder">  <br>   </p>    
<p>\tFoo\t\t<tt>\t\t\tBar\t\t\t\t</tt>\nBaz\n\n<span 
typeof="mw:Placeholder">\n\n\nQuux\n\n\n\n</span> \tWhee 
\n</p>\t\n<figure>\n\tYay \t </figure> \n ',
+               'html': '<body> <p typeof="mw:Placeholder">  <br>   </p>    
<p>\tFoo\t\t<tt>\t\t\tBar\t\t\t\t</tt>\nBaz\n\n<span 
typeof="mw:Placeholder">\n\n\nQuux\n\n\n\n</span> \tWhee 
\n</p>\t\n<figure>\n\tYay \t </figure> \n </body>',
                'data': [
                        {
                                'type': 'alienBlock',
@@ -1499,7 +1499,7 @@
                ]
        },
        'whitespace preservation not triggered inside <pre>': {
-               'html': '\n<pre>\n\n\nFoo\n\n\nBar\n\n\n\n</pre>\n\n\n\n\n',
+               'html': 
'<body>\n<pre>\n\n\nFoo\n\n\nBar\n\n\n\n</pre>\n\n\n\n\n</body>',
                'data': [
                        { 'type': 'preformatted', 'internal': { 'whitespace': 
['\n', undefined, undefined, '\n\n\n\n\n' ] } },
                        '\n',
@@ -1522,7 +1522,7 @@
                // pre newline hack
                // TODO we should test this using a better, more 
.innerHTML-based mechanism for
                // comparing DOM trees
-               'normalizedHtml': 
'\n<pre>\n\n\n\nFoo\n\n\nBar\n\n\n\n</pre>\n\n\n\n\n'
+               'normalizedHtml': 
'<body>\n<pre>\n\n\n\nFoo\n\n\nBar\n\n\n\n</pre>\n\n\n\n\n</body>'
        },
        'mismatching whitespace data is ignored': {
                'html': null,
@@ -1538,10 +1538,10 @@
                        { 'type': '/listItem' },
                        { 'type': '/list' }
                ],
-               'normalizedHtml': ' <ul><li><p>\tA\n</p>  <p>B</p></li></ul>    
'
+               'normalizedHtml': '<body> <ul><li><p>\tA\n</p>  
<p>B</p></li></ul>    </body>'
        },
        'order of nested annotations is preserved': {
-               'html': '<p><b><a rel="mw:WikiLink" 
href="Foo"><i>Foo</i></a></b></p>',
+               'html': '<body><p><b><a rel="mw:WikiLink" 
href="Foo"><i>Foo</i></a></b></p></body>',
                'data': [
                        { 'type': 'paragraph' },
                        [
@@ -1608,7 +1608,7 @@
                ]
        },
        'nested annotations are closed and reopened in the correct order': {
-               'html': '<p><a rel="mw:WikiLink" 
href="Foo">F<b>o<i>o</i></b><i>b</i></a><i>a<b>r</b>b<u>a</u>z</i></p>',
+               'html': '<body><p><a rel="mw:WikiLink" 
href="Foo">F<b>o<i>o</i></b><i>b</i></a><i>a<b>r</b>b<u>a</u>z</i></p></body>',
                'data': [
                        { 'type': 'paragraph' },
                        [
@@ -1723,12 +1723,12 @@
                ]
        },
        'document with meta elements': {
-               'html': '<!-- No content conversion --><meta 
property="mw:PageProp/nocc" /><p>Foo' +
+               'html': '<body><!-- No content conversion --><meta 
property="mw:PageProp/nocc" /><p>Foo' +
                        '<link rel="mw:WikiLink/Category" href="./Category:Bar" 
/>Bar' +
                        '<meta property="mw:foo" content="bar" />Ba<!-- inline 
-->z</p>' +
                        '<meta property="mw:bar" content="baz" /><!--barbaz-->' 
+
                        '<link rel="mw:WikiLink/Category" 
href="./Category:Foo#Bar baz%23quux" />' +
-                       '<meta typeof="mw:Placeholder" data-parsoid="foobar" 
/>',
+                       '<meta typeof="mw:Placeholder" data-parsoid="foobar" 
/></body>',
                'data': ve.dm.example.withMeta
        },
        'change markers': {
@@ -1762,19 +1762,19 @@
                        { 'type': '/listItem' },
                        { 'type': '/list' }
                ],
-               'normalizedHtml': '<p 
data-ve-changed="{&quot;content&quot;:1}">' +
+               'normalizedHtml': '<body><p 
data-ve-changed="{&quot;content&quot;:1}">' +
                                'Foo<img 
data-ve-changed="{&quot;attributes&quot;:2}" />' +
                                '</p><p 
data-ve-changed="{&quot;created&quot;:1}">Bar</p>' +
-                               '<ul><li 
data-ve-changed="{&quot;content&quot;:1}">Baz</li></ul>'
+                               '<ul><li 
data-ve-changed="{&quot;content&quot;:1}">Baz</li></ul></body>'
        },
        'about grouping': {
-               'html': '<div typeof="mw:Placeholder" about="#mwt1">Foo</div>' +
+               'html': '<body><div typeof="mw:Placeholder" 
about="#mwt1">Foo</div>' +
                        '<figure typeof="mw:Placeholder" 
about="#mwt1">Bar</figure>' +
                        '<figure typeof="mw:Placeholder" 
about="#mwt2">Baz</figure>' +
                        '<span typeof="mw:Placeholder" 
about="#mwt2">Quux</span>' +
                        '<p>Whee</p><span typeof="mw:Placeholder" 
about="#mwt2">Yay</span>' +
                        '<div typeof="mw:Placeholder" about="#mwt2">Blah</div>' 
+
-                       '<span typeof="mw:Placeholder" 
about="#mwt3">Meh</span>',
+                       '<span typeof="mw:Placeholder" 
about="#mwt3">Meh</span></body>',
                'data': [
                        {
                                'type': 'alienBlock',
@@ -1818,8 +1818,8 @@
                ]
        },
        'whitespace preservation with an about group': {
-               'html': ' <div typeof="mw:Placeholder" 
about="#mwt1">\tFoo\t\t</div>\t\t\t' +
-                       '<div typeof="mw:Placeholder" about="#mwt1">  Bar   
</div>    ',
+               'html': '<body> <div typeof="mw:Placeholder" 
about="#mwt1">\tFoo\t\t</div>\t\t\t' +
+                       '<div typeof="mw:Placeholder" about="#mwt1">  Bar   
</div>    </body>',
                'data': [
                        {
                                'type': 'alienBlock',
@@ -1835,7 +1835,7 @@
                ]
        },
        'mw:Entity': {
-               'html': '<p>a<span typeof="mw:Entity">¢</span>b<span 
typeof="mw:Entity">¥</span><span typeof="mw:Entity">™</span></p>',
+               'html': '<body><p>a<span typeof="mw:Entity">¢</span>b<span 
typeof="mw:Entity">¥</span><span typeof="mw:Entity">™</span></p></body>',
                'data': [
                        { 'type': 'paragraph' },
                        'a',
@@ -1850,7 +1850,7 @@
                ]
        },
        'wrapping with mw:Entity': {
-               'html': 'a<span typeof="mw:Entity">¢</span>b<span 
typeof="mw:Entity">¥</span><span typeof="mw:Entity">™</span>',
+               'html': '<body>a<span typeof="mw:Entity">¢</span>b<span 
typeof="mw:Entity">¥</span><span typeof="mw:Entity">™</span></body>',
                'data': [
                        { 'type': 'paragraph', 'internal': { 'generated': 
'wrapper' } },
                        'a',
@@ -1865,7 +1865,7 @@
                ]
        },
        'whitespace preservation with mw:Entity': {
-               'html': '<p> a  <span typeof="mw:Entity"> </span>   b    <span 
typeof="mw:Entity">¥</span>\t<span typeof="mw:Entity">™</span></p>',
+               'html': '<body><p> a  <span typeof="mw:Entity"> </span>   b    
<span typeof="mw:Entity">¥</span>\t<span 
typeof="mw:Entity">™</span></p></body>',
                'data': [
                        { 'type': 'paragraph', 'internal': { 'whitespace': [ 
undefined, ' ' ] } },
                        'a',
@@ -1890,7 +1890,7 @@
                ]
        },
        'block node inside annotation node is alienated': {
-               'html': '<span>\n<p>Bar</p></span>',
+               'html': '<body><span>\n<p>Bar</p></span></body>',
                'data': [
                        { 'type': 'paragraph', 'internal': { 'generated': 
'wrapper' } },
                        [ '\n', [ ve.dm.example.span ] ],
@@ -1906,7 +1906,7 @@
                ]
        },
        'block node inside annotation node surrounded by tables': {
-               'html': 
'<table></table><span>\n<p>Bar</p></span><table></table>',
+               'html': 
'<body><table></table><span>\n<p>Bar</p></span><table></table></body>',
                'data': [
                        { 'type': 'table' },
                        { 'type': '/table' },
@@ -1926,7 +1926,7 @@
                ]
        },
        'block node inside annotation node is alienated and continues 
wrapping': {
-               'html': 'Foo<span>\n<p>Bar</p></span>Baz',
+               'html': '<body>Foo<span>\n<p>Bar</p></span>Baz</body>',
                'data': [
                        { 'type': 'paragraph', 'internal': { 'generated': 
'wrapper' } },
                        'F',
@@ -1948,7 +1948,7 @@
                ]
        },
        'whitespace before meta node in wrapping mode': {
-               'html': '<table><tbody><tr><td>Foo\n<meta property="mw:foo" 
content="bar" /></td></tr></tbody></table>',
+               'html': '<body><table><tbody><tr><td>Foo\n<meta 
property="mw:foo" content="bar" /></td></tr></tbody></table></body>',
                'data': [
                        { 'type': 'table' },
                        { 'type': 'tableSection', 'attributes': { 'style': 
'body' } },
diff --git a/modules/ve/ve.Surface.js b/modules/ve/ve.Surface.js
index da94ce7..90685a4 100644
--- a/modules/ve/ve.Surface.js
+++ b/modules/ve/ve.Surface.js
@@ -19,13 +19,13 @@
  * @class
  * @constructor
  * @param {string} parent Selector of element to attach to
- * @param {HTMLElement} dom HTML element of document to edit
+ * @param {HTMLDocument} doc HTML document to edit
  * @param {Object} options Configuration options
  */
-ve.Surface = function VeSurface( parent, dom, options ) {
+ve.Surface = function VeSurface( parent, doc, options ) {
        // Properties
        this.$ = $( '<div>' );
-       this.documentModel = new ve.dm.Document( 
ve.dm.converter.getDataFromDom( dom ) );
+       this.documentModel = new ve.dm.Document( 
ve.dm.converter.getDataFromDom( doc ) );
        this.options = ve.extendObject( true, ve.Surface.defaultOptions, 
options );
        this.model = new ve.dm.Surface( this.documentModel );
        this.view = new ve.ce.Surface( this.$, this.model, this );

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

Gerrit-MessageType: merged
Gerrit-Change-Id: I82fdad0a099febc5e658486cbf8becfcdbc85a2d
Gerrit-PatchSet: 3
Gerrit-Project: mediawiki/extensions/VisualEditor
Gerrit-Branch: master
Gerrit-Owner: Catrope <[email protected]>
Gerrit-Reviewer: Trevor Parscal <[email protected]>
Gerrit-Reviewer: jenkins-bot

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

Reply via email to