jenkins-bot has submitted this change and it was merged.
Change subject: Resolve rendered URLs according to the provided <base>
......................................................................
Resolve rendered URLs according to the provided <base>
This is done by using the computed property value rather than the
literal attribute value when rendering href and src attributes.
Helpfully, this provides perfect URL resolution natively in the browser,
which means the document's <base> is respected and all that good stuff.
For GeneratedContentNodes, we also need to find all DOM elements inside
the rendered DOM that have href or src attributes and resolve those.
This is done in the new getRenderedDomElements() function, which the
existing cleanup steps (remove <link>/<meta>/<style>, clone for
correct document) were moved into.
In order to make sure that the computed values are always computed
correctly, we need to make sure that in cases where HTML strings
in data-mw are parsed, they're parsed in the context of the correct
document so the correct <base> is applied.
We still need to solve this problem for models that actually store and
edit an href or src as an attribute. I'll post more about that on
bug 48915.
Bug: 48915
Change-Id: Iaccb9e3fc05cd151a0f5e632c8d3bd3568735309
---
M modules/ve-mw/ce/annotations/ve.ce.MWInternalLinkAnnotation.js
M modules/ve-mw/dm/nodes/ve.dm.MWReferenceListNode.js
M modules/ve-mw/test/dm/ve.dm.mwExample.js
M modules/ve/ce/nodes/ve.ce.GeneratedContentNode.js
M modules/ve/ce/ve.ce.View.js
M modules/ve/dm/ve.dm.Converter.js
M modules/ve/dm/ve.dm.InternalList.js
M modules/ve/test/dm/ve.dm.InternalList.test.js
M modules/ve/test/dm/ve.dm.example.js
M modules/ve/test/ve.test.utils.js
10 files changed, 356 insertions(+), 107 deletions(-)
Approvals:
Esanders: Looks good to me, approved
jenkins-bot: Verified
diff --git a/modules/ve-mw/ce/annotations/ve.ce.MWInternalLinkAnnotation.js
b/modules/ve-mw/ce/annotations/ve.ce.MWInternalLinkAnnotation.js
index 5d5869d..6cb6e77 100644
--- a/modules/ve-mw/ce/annotations/ve.ce.MWInternalLinkAnnotation.js
+++ b/modules/ve-mw/ce/annotations/ve.ce.MWInternalLinkAnnotation.js
@@ -22,9 +22,14 @@
// DOM changes
this.$.addClass( 've-ce-mwInternalLinkAnnotation' );
this.$.attr( 'title', model.getAttribute( 'title' ) );
- // Get href from DM rendering
- dmRendering = model.getDomElements()[0];
- this.$.attr( 'href', dmRendering.getAttribute( 'href' ) );
+ // HACK get href from DM rendering
+ // HACK HACK except if we already have a computed href
+ // FIXME get rid of this hack, see bug 51487
+ if ( !this.$.attr( 'href' ) ) {
+ dmRendering = model.getDomElements()[0];
+ this.$.attr( 'href', dmRendering.getAttribute( 'href' ) );
+ }
+ // else let the default attribute rendering happen
};
/* Inheritance */
diff --git a/modules/ve-mw/dm/nodes/ve.dm.MWReferenceListNode.js
b/modules/ve-mw/dm/nodes/ve.dm.MWReferenceListNode.js
index 66bf96c..a36b556 100644
--- a/modules/ve-mw/dm/nodes/ve.dm.MWReferenceListNode.js
+++ b/modules/ve-mw/dm/nodes/ve.dm.MWReferenceListNode.js
@@ -53,7 +53,7 @@
}
};
if ( mwData.body && mwData.body.html ) {
- $contents = $( '<div>' ).append( mwData.body.html );
+ $contents = $( '<div>', domElements[0].ownerDocument ).append(
mwData.body.html );
contentsData = converter.getDataFromDomRecursionClean(
$contents[0] );
return [ referenceListData ].
concat( contentsData ).
diff --git a/modules/ve-mw/test/dm/ve.dm.mwExample.js
b/modules/ve-mw/test/dm/ve.dm.mwExample.js
index bfe6c2b..a65c5a1 100644
--- a/modules/ve-mw/test/dm/ve.dm.mwExample.js
+++ b/modules/ve-mw/test/dm/ve.dm.mwExample.js
@@ -258,7 +258,17 @@
'origSortkey': '',
'origRel': 'mw:PageProp/Category'
},
- 'htmlAttributes': [ { 'values': { 'rel':
'mw:PageProp/Category', 'href': './Category:Bar' } } ]
+ 'htmlAttributes': [
+ {
+ 'values': {
+ 'rel': 'mw:PageProp/Category',
+ 'href': './Category:Bar'
+ },
+ 'computed': {
+ 'href':
'http://example.com/Category:Bar'
+ }
+ }
+ ]
},
{ 'type': '/mwCategory' },
'B',
@@ -306,10 +316,18 @@
'origSortkey': 'Bar baz%23quux',
'origRel': 'mw:PageProp/Category'
},
- 'htmlAttributes': [ { 'values': {
- 'rel': 'mw:PageProp/Category',
- 'href': './Category:Foo_foo#Bar baz%23quux'
- } } ]
+
+ 'htmlAttributes': [
+ {
+ 'values': {
+ 'rel': 'mw:PageProp/Category',
+ 'href': './Category:Foo_foo#Bar
baz%23quux'
+ },
+ 'computed': {
+ 'href':
'http://example.com/Category:Foo_foo#Bar baz%23quux'
+ }
+ }
+ ]
},
{ 'type': '/mwCategory' },
{
@@ -367,7 +385,17 @@
'sortkey': '',
'origSortkey': ''
},
- 'htmlAttributes': [ { 'values': { 'rel':
'mw:WikiLink/Category', 'href': './Category:Bar' } } ]
+ 'htmlAttributes': [
+ {
+ 'values': {
+ 'rel': 'mw:WikiLink/Category',
+ 'href': './Category:Bar'
+ },
+ 'computed': {
+ 'href':
'http://example.com/Category:Bar'
+ }
+ }
+ ]
}
],
undefined,
@@ -956,6 +984,7 @@
'</p>' +
ve.dm.mwExample.MWReference.referenceList +
'</body>',
+ 'head': '<base href="http://example.com" />',
'data': [
{ 'type': 'paragraph' },
'F', 'o', 'o',
@@ -986,6 +1015,9 @@
{
'values': {
'href':
'#cite_note-bar-1'
+ },
+ 'computed': {
+ 'href':
'http://example.com/#cite_note-bar-1'
}
}
]
@@ -1165,9 +1197,16 @@
'refGroup': ''
},
'htmlAttributes': [ {
- 'children': [ { 'values': {
- 'href': '#cite_note-foo-3'
- } } ],
+ 'children': [
+ {
+ 'values': {
+ 'href':
'#cite_note-foo-3'
+ },
+ 'computed': {
+ 'href':
'http://example.com/#cite_note-foo-3'
+ }
+ }
+ ],
'values': {
'about': '#mwt8',
'class': 'reference',
@@ -1193,10 +1232,17 @@
'normalizedTitle': 'Bar',
'hrefPrefix': './'
},
- 'htmlAttributes': [ { 'values': {
- 'href': './Bar',
- 'rel': 'mw:WikiLink'
- } } ]
+ 'htmlAttributes': [
+ {
+ 'values': {
+ 'href': './Bar',
+ 'rel':
'mw:WikiLink'
+ },
+ 'computed': {
+ 'href':
'http://example.com/Bar'
+ }
+ }
+ ]
} ]
],
[
@@ -1209,10 +1255,17 @@
'normalizedTitle': 'Bar',
'hrefPrefix': './'
},
- 'htmlAttributes': [ { 'values': {
- 'href': './Bar',
- 'rel': 'mw:WikiLink'
- } } ]
+ 'htmlAttributes': [
+ {
+ 'values': {
+ 'href': './Bar',
+ 'rel':
'mw:WikiLink'
+ },
+ 'computed': {
+ 'href':
'http://example.com/Bar'
+ }
+ }
+ ]
} ]
],
[
@@ -1225,10 +1278,17 @@
'normalizedTitle': 'Bar',
'hrefPrefix': './'
},
- 'htmlAttributes': [ { 'values': {
- 'href': './Bar',
- 'rel': 'mw:WikiLink'
- } } ]
+ 'htmlAttributes': [
+ {
+ 'values': {
+ 'href': './Bar',
+ 'rel':
'mw:WikiLink'
+ },
+ 'computed': {
+ 'href':
'http://example.com/Bar'
+ }
+ }
+ ]
} ]
],
{ 'type': '/paragraph' },
@@ -1257,6 +1317,7 @@
'{"html":"Foo<!-- bar
-->"},"attrs":{}}" ' +
'id="cite_ref-1-0" rel="dc:references"
typeof="mw:Extension/ref" data-parsoid="{}">' +
'<a href="#cite_note-bar-1"
data-parsoid="{}">[1]</a></span></p>',
+ 'head': '<base href="http://example.com" />',
'data': [
{ 'type': 'paragraph' },
{
@@ -1288,7 +1349,15 @@
'rel': 'dc:references',
'typeof':
'mw:Extension/ref'
},
- 'children': [ { 'values': {
'data-parsoid': '{}', 'href': '#cite_note-bar-1' } } ]
+ 'children': [
+ {
+ 'values': {
+
'data-parsoid': '{}',
+ 'href':
'#cite_note-bar-1'
+ },
+ 'computed': {
+ 'href':
'http://example.com/#cite_note-bar-1'
+ } } ]
}
]
},
@@ -1317,6 +1386,7 @@
},
'internal link with ./ and ../': {
'html': '<body><p><a rel="mw:WikiLink"
href="./../../../Foo/Bar">Foo</a></p></body>',
+ 'head': '<base
href="http://example.com/one/two/three/four/five" />',
'data': [
{ 'type': 'paragraph' },
[
@@ -1329,10 +1399,17 @@
'normalizedTitle': 'Foo/Bar',
'hrefPrefix': './../../../'
},
- 'htmlAttributes': [ { 'values': {
- 'href': './../../../Foo/Bar',
- 'rel': 'mw:WikiLink'
- } } ]
+ 'htmlAttributes': [
+ {
+ 'values': {
+ 'href':
'./../../../Foo/Bar',
+ 'rel':
'mw:WikiLink'
+ },
+ 'computed': {
+ 'href':
'http://example.com/one/Foo/Bar'
+ }
+ }
+ ]
} ]
],
[
@@ -1345,10 +1422,17 @@
'normalizedTitle': 'Foo/Bar',
'hrefPrefix': './../../../'
},
- 'htmlAttributes': [ { 'values': {
- 'href': './../../../Foo/Bar',
- 'rel': 'mw:WikiLink'
- } } ]
+ 'htmlAttributes': [
+ {
+ 'values': {
+ 'href':
'./../../../Foo/Bar',
+ 'rel':
'mw:WikiLink'
+ },
+ 'computed': {
+ 'href':
'http://example.com/one/Foo/Bar'
+ }
+ }
+ ]
} ]
],
[
@@ -1361,10 +1445,17 @@
'normalizedTitle': 'Foo/Bar',
'hrefPrefix': './../../../'
},
- 'htmlAttributes': [ { 'values': {
- 'href': './../../../Foo/Bar',
- 'rel': 'mw:WikiLink'
- } } ]
+ 'htmlAttributes': [
+ {
+ 'values': {
+ 'href':
'./../../../Foo/Bar',
+ 'rel':
'mw:WikiLink'
+ },
+ 'computed': {
+ 'href':
'http://example.com/one/Foo/Bar'
+ }
+ }
+ ]
} ]
],
{ 'type': '/paragraph' },
@@ -1386,6 +1477,9 @@
'values': {
'href':
'http://www.example.com',
'rel': 'mw:ExtLink'
+ },
+ 'computed': {
+ 'href':
'http://www.example.com/'
}
} ]
},
@@ -1408,10 +1502,17 @@
'href':
'http://www.mediawiki.org/',
'rel': 'mw:ExtLink'
},
- 'htmlAttributes': [ { 'values': {
- 'rel': 'mw:ExtLink',
- 'href':
'http://www.mediawiki.org/'
- } } ]
+ 'htmlAttributes': [
+ {
+ 'values': {
+ 'href':
'http://www.mediawiki.org/',
+ 'rel':
'mw:ExtLink'
+ },
+ 'computed': {
+ 'href':
'http://www.mediawiki.org/'
+ }
+ }
+ ]
} ]
],
[
@@ -1422,10 +1523,17 @@
'href':
'http://www.mediawiki.org/',
'rel': 'mw:ExtLink'
},
- 'htmlAttributes': [ { 'values': {
- 'rel': 'mw:ExtLink',
- 'href':
'http://www.mediawiki.org/'
- } } ]
+ 'htmlAttributes': [
+ {
+ 'values': {
+ 'href':
'http://www.mediawiki.org/',
+ 'rel':
'mw:ExtLink'
+ },
+ 'computed': {
+ 'href':
'http://www.mediawiki.org/'
+ }
+ }
+ ]
} ]
],
{ 'type': '/paragraph' },
@@ -1455,10 +1563,17 @@
'href':
'http://de.wikipedia.org/wiki/Foo',
'origRel': 'mw:WikiLink/Language'
},
- 'htmlAttributes': [ { 'values': {
- 'href':
'http://de.wikipedia.org/wiki/Foo',
- 'rel': 'mw:WikiLink/Language'
- } } ],
+ 'htmlAttributes': [
+ {
+ 'values': {
+ 'href':
'http://de.wikipedia.org/wiki/Foo',
+ 'rel':
'mw:WikiLink/Language'
+ },
+ 'computed': {
+ 'href':
'http://de.wikipedia.org/wiki/Foo'
+ }
+ }
+ ],
'internal': { 'whitespace': [ '\n', undefined,
undefined, '\n' ] }
},
{ 'type': '/mwLanguage' },
@@ -1468,10 +1583,17 @@
'href':
'http://fr.wikipedia.org/wiki/Foo',
'origRel': 'mw:PageProp/Language'
},
- 'htmlAttributes': [ { 'values': {
- 'href':
'http://fr.wikipedia.org/wiki/Foo',
- 'rel': 'mw:PageProp/Language'
- } } ],
+ 'htmlAttributes': [
+ {
+ 'values': {
+ 'href':
'http://fr.wikipedia.org/wiki/Foo',
+ 'rel':
'mw:PageProp/Language'
+ },
+ 'computed': {
+ 'href':
'http://fr.wikipedia.org/wiki/Foo'
+ }
+ }
+ ],
'internal': { 'whitespace': [ '\n' ] }
},
{ 'type': '/mwLanguage' },
@@ -1486,6 +1608,7 @@
'<meta property="mw:bar" content="baz" /><!--barbaz-->'
+
'<link rel="mw:PageProp/Category"
href="./Category:Foo_foo#Bar baz%23quux" />' +
'<meta typeof="mw:Placeholder" data-parsoid="foobar"
/></body>',
+ 'head': '<base href="http://example.com" />',
'data': ve.dm.mwExample.withMeta
},
'RDFa types spread across two attributes, about grouping is forced': {
@@ -1638,6 +1761,7 @@
'</figcaption>' +
'</figure>' +
'</body>',
+ 'head': '<base href="http://example.com" />',
'data': [
{
'type': 'mwBlockImage',
@@ -1683,7 +1807,18 @@
'normalizedTitle': 'Bar',
'hrefPrefix': './'
},
- 'htmlAttributes': [ { 'values': {
'href': './Bar', 'rel': 'mw:WikiLink', 'data-parsoid': '{}' } } ]
+ 'htmlAttributes': [
+ {
+ 'values': {
+ 'href': './Bar',
+ 'rel':
'mw:WikiLink',
+ 'data-parsoid':
'{}'
+ },
+ 'computed': {
+ 'href':
'http://example.com/Bar'
+ }
+ }
+ ]
} ]
],
[
@@ -1696,7 +1831,18 @@
'normalizedTitle': 'Bar',
'hrefPrefix': './'
},
- 'htmlAttributes': [ { 'values': {
'href': './Bar', 'rel': 'mw:WikiLink', 'data-parsoid': '{}' } } ]
+ 'htmlAttributes': [
+ {
+ 'values': {
+ 'href': './Bar',
+ 'rel':
'mw:WikiLink',
+ 'data-parsoid':
'{}'
+ },
+ 'computed': {
+ 'href':
'http://example.com/Bar'
+ }
+ }
+ ]
} ]
],
[
@@ -1709,7 +1855,18 @@
'normalizedTitle': 'Bar',
'hrefPrefix': './'
},
- 'htmlAttributes': [ { 'values': {
'href': './Bar', 'rel': 'mw:WikiLink', 'data-parsoid': '{}' } } ]
+ 'htmlAttributes': [
+ {
+ 'values': {
+ 'href': './Bar',
+ 'rel':
'mw:WikiLink',
+ 'data-parsoid':
'{}'
+ },
+ 'computed': {
+ 'href':
'http://example.com/Bar'
+ }
+ }
+ ]
} ]
],
' ', 'b', 'a', 'z',
diff --git a/modules/ve/ce/nodes/ve.ce.GeneratedContentNode.js
b/modules/ve/ce/nodes/ve.ce.GeneratedContentNode.js
index c0ee2d1..c01684b 100644
--- a/modules/ve/ce/nodes/ve.ce.GeneratedContentNode.js
+++ b/modules/ve/ce/nodes/ve.ce.GeneratedContentNode.js
@@ -85,6 +85,50 @@
};
/**
+ * Make an array of DOM elements suitable for rendering.
+ *
+ * Subclasses can override this to provide their own cleanup steps. This
function takes an
+ * array of DOM elements cloned within the source document and returns an
array of DOM elements
+ * cloned into the target document. If it's important that the DOM elements
still be associated
+ * with the original document, you should modify domElements before calling
the parent
+ * implementation, otherwise you should call the parent implementation first
and modify its
+ * return value.
+ *
+ * @param {HTMLElement[]} domElements Clones of the DOM elements from the store
+ * @returns {HTMLElement[]} Clones of the DOM elements in the right document,
with modifications
+ */
+ve.ce.GeneratedContentNode.prototype.getRenderedDomElements = function (
domElements ) {
+ var i, len, attr, $rendering,
+ doc = this.getElementDocument();
+
+ /**
+ * Callback for jQuery.fn.each that resolves the value of attr to the
computed
+ * property value. Called in the context of an HTMLElement.
+ * @private
+ */
+ function resolveAttribute() {
+ this.setAttribute( attr, this[attr] );
+ }
+
+ // Copy domElements so we can modify the elements
+ // Filter out link, meta and style tags for bug 50043
+ $rendering = $( domElements ).not( 'link, meta, style' );
+ // Also remove link, meta and style tags nested inside other tags
+ $rendering.find( 'link, meta, style' ).remove();
+
+ // Render the computed values of some attributes
+ for ( i = 0, len = ve.dm.Converter.computedAttributes.length; i < len;
i++ ) {
+ attr = ve.dm.Converter.computedAttributes[i];
+ $rendering.find( '[' + attr + ']' )
+ .add( $rendering.filter( '[' + attr + ']' ) )
+ .each( resolveAttribute );
+ }
+
+ // Clone the elements into the target document
+ return ve.copyDomElements( $rendering.toArray(), doc );
+};
+
+/**
* Rerender the contents of this node.
*
* @param {Object|string|Array} generatedContents Generated contents, in the
default case an HTMLElement array
@@ -92,15 +136,10 @@
* @fires teardown
*/
ve.ce.GeneratedContentNode.prototype.render = function ( generatedContents ) {
- var $rendering, doc = this.getElementDocument();
if ( this.live ) {
this.emit( 'teardown' );
}
- // Filter out link, meta and style tags for bug 50043
- $rendering = $( ve.copyDomElements( generatedContents, doc ) ).not(
'link, meta, style' );
- // Also remove link, meta and style tags nested inside other tags
- $rendering.find( 'link, meta, style' ).remove();
- this.$.empty().append( $rendering );
+ this.$.empty().append( this.getRenderedDomElements( ve.copyDomElements(
generatedContents ) ) );
if ( this.live ) {
this.emit( 'setup' );
this.afterRender( generatedContents );
diff --git a/modules/ve/ce/ve.ce.View.js b/modules/ve/ce/ve.ce.View.js
index 5c8e5d1..22112b2 100644
--- a/modules/ve/ce/ve.ce.View.js
+++ b/modules/ve/ce/ve.ce.View.js
@@ -158,6 +158,7 @@
ve.dm.Converter.renderHtmlAttributeList(
attributeList || this.model.getHtmlAttributes(),
this.$,
- this.constructor.static.renderHtmlAttributes
+ this.constructor.static.renderHtmlAttributes,
+ true // computed attributes
);
};
diff --git a/modules/ve/dm/ve.dm.Converter.js b/modules/ve/dm/ve.dm.Converter.js
index 736502c..c031de6 100644
--- a/modules/ve/dm/ve.dm.Converter.js
+++ b/modules/ve/dm/ve.dm.Converter.js
@@ -29,6 +29,14 @@
this.contextStack = null;
};
+/* Static Properties */
+
+/**
+ * List of HTML attribute names that {#buildHtmlAttributeList} should store
computed values for.
+ * @type {string[]}
+ */
+ve.dm.Converter.computedAttributes = [ 'href', 'src' ];
+
/* Static Methods */
/**
@@ -124,8 +132,9 @@
* Build an HTML attribute list for attribute preservation.
*
* The attribute list is an array of objects, one for each DOM element. Each
object contains a
- * map with attribute keys and values in .values, an (ordered) array of
attribute keys in .keys,
- * and an array of attribute lists for the child nodes in .children .
+ * map with attribute keys and values in .values, a map with a subset of the
attribute keys and
+ * their computed values in .computed (see {#computedAttributes}), and an
array of attribute lists
+ * for the child nodes in .children .
*
* @static
* @param {HTMLElement[]} domElements Array of DOM elements to build attribute
list for
@@ -135,14 +144,22 @@
* @returns {Object[]|undefined} Attribute list, or undefined if empty
*/
ve.dm.Converter.buildHtmlAttributeList = function ( domElements, spec, deep,
attributeList ) {
- var i, ilen, j, jlen, domAttributes, childList, empty = true;
+ var i, ilen, j, jlen, domAttributes, childList, attrName,
+ empty = true;
attributeList = attributeList || [];
for ( i = 0, ilen = domElements.length; i < ilen; i++ ) {
domAttributes = domElements[i].attributes || [];
attributeList[i] = { 'values': {} };
for ( j = 0, jlen = domAttributes.length; j < jlen; j++ ) {
- if ( ve.dm.Model.matchesAttributeSpec(
domAttributes[j].name, spec ) ) {
- attributeList[i].values[domAttributes[j].name]
= domAttributes[j].value;
+ attrName = domAttributes[j].name;
+ if ( ve.dm.Model.matchesAttributeSpec( attrName, spec )
) {
+ attributeList[i].values[attrName] =
domAttributes[j].value;
+ if ( ve.indexOf( attrName,
this.computedAttributes ) !== -1 ) {
+ if ( !attributeList[i].computed ) {
+ attributeList[i].computed = {};
+ }
+ attributeList[i].computed[attrName] =
domElements[i][attrName];
+ }
empty = false;
}
}
@@ -174,10 +191,11 @@
* @param {Object[]} attributeList Attribute list, see buildHtmlAttributeList()
* @param {HTMLElement[]} domElements Array of DOM elements to render onto
* @param {boolean|string|RegExp|Array|Object} [spec=true] Attribute
specification, see ve.dm.Model
+ * @param {boolean} [computed=false] If true, use the computed values of
attributes where available
* @param {boolean} [overwrite=false] If true, overwrite attributes that are
already set
*/
-ve.dm.Converter.renderHtmlAttributeList = function ( attributeList,
domElements, spec, overwrite ) {
- var i, ilen, key, values;
+ve.dm.Converter.renderHtmlAttributeList = function ( attributeList,
domElements, spec, computed, overwrite ) {
+ var i, ilen, key, values, value;
if ( spec === undefined ) {
spec = true;
}
@@ -191,16 +209,17 @@
values = attributeList[i].values;
for ( key in values ) {
if ( ve.dm.Model.matchesAttributeSpec( key, spec ) ) {
- if ( values[key] === undefined ) {
+ value = computed && attributeList[i].computed
&& attributeList[i].computed[key] || values[key];
+ if ( value === undefined ) {
domElements[i].removeAttribute( key );
} else if ( overwrite ||
!domElements[i].hasAttribute( key ) ) {
- domElements[i].setAttribute( key,
values[key] );
+ domElements[i].setAttribute( key, value
);
}
}
}
if ( attributeList[i].children ) {
ve.dm.Converter.renderHtmlAttributeList(
- attributeList[i].children,
domElements[i].children, spec
+ attributeList[i].children,
domElements[i].children, spec, computed, overwrite
);
}
}
@@ -382,7 +401,7 @@
store,
this.getDataFromDomRecursion( doc.body )
);
- refData = this.internalList.convertToData( this );
+ refData = this.internalList.convertToData( this, doc );
linearData.batchSplice( linearData.getLength(), 0, refData );
// Clear the state
diff --git a/modules/ve/dm/ve.dm.InternalList.js
b/modules/ve/dm/ve.dm.InternalList.js
index 20ba482..398c0a1 100644
--- a/modules/ve/dm/ve.dm.InternalList.js
+++ b/modules/ve/dm/ve.dm.InternalList.js
@@ -208,16 +208,17 @@
*
* @method
* @param {ve.dm.Converter} converter Converter object
+ * @param {HTMLDocument} doc Document to create nodes in
* @returns {Array} Linear model data
*/
-ve.dm.InternalList.prototype.convertToData = function ( converter ) {
+ve.dm.InternalList.prototype.convertToData = function ( converter, doc ) {
var i, length, itemData,
itemHtmlQueue = this.getItemHtmlQueue(), list = [];
list.push( { 'type': 'internalList' } );
for ( i = 0, length = itemHtmlQueue.length; i < length; i++ ) {
if ( itemHtmlQueue[i] !== '' ) {
- itemData = converter.getDataFromDomRecursion( $(
'<div>' ).html( itemHtmlQueue[i] )[0] );
+ itemData = converter.getDataFromDomRecursion( $(
'<div>', doc ).html( itemHtmlQueue[i] )[0] );
list = list.concat(
[{ 'type': 'internalItem' }],
itemData,
diff --git a/modules/ve/test/dm/ve.dm.InternalList.test.js
b/modules/ve/test/dm/ve.dm.InternalList.test.js
index b5e80c7..d49d533 100644
--- a/modules/ve/test/dm/ve.dm.InternalList.test.js
+++ b/modules/ve/test/dm/ve.dm.InternalList.test.js
@@ -72,7 +72,7 @@
internalList.queueItemHtml( 'reference', 'foo', 'Bar' );
internalList.queueItemHtml( 'reference', 'bar', 'Baz' );
- assert.deepEqual( internalList.convertToData( ve.dm.converter ),
expectedData, 'Data matches' );
+ assert.deepEqual( internalList.convertToData( ve.dm.converter, doc ),
expectedData, 'Data matches' );
assert.deepEqual( internalList.getItemHtmlQueue(), [], 'Items html is
emptied after conversion' );
} );
diff --git a/modules/ve/test/dm/ve.dm.example.js
b/modules/ve/test/dm/ve.dm.example.js
index a45b6fb..4013439 100644
--- a/modules/ve/test/dm/ve.dm.example.js
+++ b/modules/ve/test/dm/ve.dm.example.js
@@ -163,7 +163,13 @@
ve.dm.example.testDir = window.VE_TESTDIR || '.';
+ve.dm.example.fullUrl = function ( href ) {
+ return $( '<a>' ).attr( 'href', href )[0].href;
+};
+
ve.dm.example.imgSrc = ve.dm.example.testDir + '/example.png';
+
+ve.dm.example.fullImgSrc = ve.dm.example.fullUrl( ve.dm.example.imgSrc );
ve.dm.example.image = {
html: '<img src="' + ve.dm.example.imgSrc + '" alt="Example"
width="100" height="50">',
@@ -175,12 +181,17 @@
'width': 100,
'height': 50
},
- 'htmlAttributes': [ { 'values': {
- 'src': ve.dm.example.imgSrc,
- 'alt': 'Example',
- 'width': '100',
- 'height': '50'
- } } ]
+ 'htmlAttributes': [
+ {
+ 'values': {
+ 'src': ve.dm.example.imgSrc,
+ 'alt': 'Example',
+ 'width': '100',
+ 'height': '50'
+ },
+ 'computed': { 'src': ve.dm.example.fullImgSrc }
+ }
+ ]
}
};
@@ -1570,6 +1581,7 @@
},
'list item with space followed by link': {
'html': '<body><ul><li><p> <a
href="Foobar">bar</a></p></li></ul></body>',
+ 'head': '<base href="http://example.com/Foo" />',
'data': [
{ 'type': 'list', 'attributes': { 'style': 'bullet' } },
{ 'type': 'listItem' },
@@ -1581,9 +1593,10 @@
'attributes': {
'href': 'Foobar'
},
- 'htmlAttributes': [ { 'values': {
- 'href': 'Foobar'
- } } ]
+ 'htmlAttributes': [ {
+ 'values': { 'href': 'Foobar' },
+ 'computed': { 'href':
'http://example.com/Foobar' }
+ } ]
} ]
],
[
@@ -1593,9 +1606,10 @@
'attributes': {
'href': 'Foobar'
},
- 'htmlAttributes': [ { 'values': {
- 'href': 'Foobar'
- } } ]
+ 'htmlAttributes': [ {
+ 'values': { 'href': 'Foobar' },
+ 'computed': { 'href':
'http://example.com/Foobar' }
+ } ]
} ]
],
[
@@ -1605,9 +1619,10 @@
'attributes': {
'href': 'Foobar'
},
- 'htmlAttributes': [ { 'values': {
- 'href': 'Foobar'
- } } ]
+ 'htmlAttributes': [ {
+ 'values': { 'href': 'Foobar' },
+ 'computed': { 'href':
'http://example.com/Foobar' }
+ } ]
} ]
],
{ 'type': '/paragraph' },
@@ -2374,6 +2389,7 @@
},
'nested annotations are closed and reopened in the correct order': {
'html': '<body><p><a
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>',
+ 'head': '<base href="http://example.com/Bar/Baz" />',
'data': [
{ 'type': 'paragraph' },
[
@@ -2384,9 +2400,10 @@
'attributes': {
'href': 'Foo'
},
- 'htmlAttributes': [ { 'values':
{
- 'href': 'Foo'
- } } ]
+ 'htmlAttributes': [ {
+ 'values': { 'href':
'Foo' },
+ 'computed': { 'href':
'http://example.com/Bar/Foo' }
+ } ]
}
]
],
@@ -2398,9 +2415,10 @@
'attributes': {
'href': 'Foo'
},
- 'htmlAttributes': [ { 'values':
{
- 'href': 'Foo'
- } } ]
+ 'htmlAttributes': [ {
+ 'values': { 'href':
'Foo' },
+ 'computed': { 'href':
'http://example.com/Bar/Foo' }
+ } ]
},
ve.dm.example.bold
]
@@ -2413,9 +2431,10 @@
'attributes': {
'href': 'Foo'
},
- 'htmlAttributes': [ { 'values':
{
- 'href': 'Foo'
- } } ]
+ 'htmlAttributes': [ {
+ 'values': { 'href':
'Foo' },
+ 'computed': { 'href':
'http://example.com/Bar/Foo' }
+ } ]
},
ve.dm.example.bold,
ve.dm.example.italic
@@ -2429,9 +2448,10 @@
'attributes': {
'href': 'Foo'
},
- 'htmlAttributes': [ { 'values':
{
- 'href': 'Foo'
- } } ]
+ 'htmlAttributes': [ {
+ 'values': { 'href':
'Foo' },
+ 'computed': { 'href':
'http://example.com/Bar/Foo' }
+ } ]
},
ve.dm.example.italic
]
diff --git a/modules/ve/test/ve.test.utils.js b/modules/ve/test/ve.test.utils.js
index b7adce3..b4b6305 100644
--- a/modules/ve/test/ve.test.utils.js
+++ b/modules/ve/test/ve.test.utils.js
@@ -48,7 +48,7 @@
};
ve.test.utils.runGetDataFromDomTests = function( assert, cases ) {
- var msg, doc, store, internalList, i, length, hash, data, n = 0;
+ var msg, doc, store, internalList, i, length, hash, data, html, n = 0;
// TODO: this is a hack to make normal heading/preformatted
// nodes the most recently registered, instead of the MW versions
@@ -70,8 +70,15 @@
doc = new ve.dm.Document( [] );
store = doc.getStore();
internalList = doc.getInternalList();
+
+ html = '';
+ if ( cases[msg].head ) {
+ html = '<head>' + cases[msg].head + '</head>';
+ }
+ html += cases[msg].html;
+
data = ve.dm.converter.getDataFromDom(
- ve.createDocumentFromHtml( cases[msg].html ),
store, internalList
+ ve.createDocumentFromHtml( html ), store,
internalList
).getData();
ve.dm.example.preprocessAnnotations( cases[msg].data,
store );
assert.deepEqualWithDomElements( data, cases[msg].data,
msg );
--
To view, visit https://gerrit.wikimedia.org/r/86065
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: Iaccb9e3fc05cd151a0f5e632c8d3bd3568735309
Gerrit-PatchSet: 15
Gerrit-Project: mediawiki/extensions/VisualEditor
Gerrit-Branch: master
Gerrit-Owner: Catrope <[email protected]>
Gerrit-Reviewer: Catrope <[email protected]>
Gerrit-Reviewer: Esanders <[email protected]>
Gerrit-Reviewer: Krinkle <[email protected]>
Gerrit-Reviewer: jenkins-bot
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits