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="{"sHref":"foo bar"}">bar</a></p></li></ul>',
+ 'html': '<body><ul><li><p> <a rel="mw:WikiLink" href="Foo_bar"
data-rt="{"sHref":"foo bar"}">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="{"content":1}">' +
+ 'normalizedHtml': '<body><p
data-ve-changed="{"content":1}">' +
'Foo<img
data-ve-changed="{"attributes":2}" />' +
'</p><p
data-ve-changed="{"created":1}">Bar</p>' +
- '<ul><li
data-ve-changed="{"content":1}">Baz</li></ul>'
+ '<ul><li
data-ve-changed="{"content":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