diff --git a/modules/ve2/dm/nodes/ve.dm.DefinitionListItemNode.js b/modules/ve2/dm/nodes/ve.dm.DefinitionListItemNode.js
index 5bcc9f5a24..b2705c5555 100644
--- a/modules/ve2/dm/nodes/ve.dm.DefinitionListItemNode.js
+++ b/modules/ve2/dm/nodes/ve.dm.DefinitionListItemNode.js
@@ -37,12 +37,18 @@ ve.dm.DefinitionListItemNode.rules = {
* @member
*/
ve.dm.DefinitionListItemNode.converters = {
- 'tags': 'dl',
+ 'tags': ['dt', 'dd'],
'toHtml': function( type, element ) {
- return ve.dm.createHtmlElement( 'dl' );
+ return element.attributes && ( {
+ 'term': ve.dm.createHtmlElement( 'dt' ),
+ 'definition': ve.dm.createHtmlElement( 'dd' )
+ } )[element.attributes['style']];
},
'toData': function( tag, element ) {
- return { 'type': 'definitionList' };
+ return ( {
+ 'dt': { 'type': 'definitionListItem', 'attributes': { 'style': 'term' } },
+ 'dd': { 'type': 'definitionListItem', 'attributes': { 'style': 'definition' } }
+ } )[tag];
}
};
diff --git a/modules/ve2/dm/nodes/ve.dm.DefinitionListNode.js b/modules/ve2/dm/nodes/ve.dm.DefinitionListNode.js
index 047c47aa91..bde0e2cea9 100644
--- a/modules/ve2/dm/nodes/ve.dm.DefinitionListNode.js
+++ b/modules/ve2/dm/nodes/ve.dm.DefinitionListNode.js
@@ -37,18 +37,12 @@ ve.dm.DefinitionListNode.rules = {
* @member
*/
ve.dm.DefinitionListNode.converters = {
- 'tags': ['dt', 'dd'],
+ 'tags': 'dl',
'toHtml': function( type, element ) {
- return element.attributes && ( {
- 'term': ve.dm.createHtmlElement( 'dt' ),
- 'definition': ve.dm.createHtmlElement( 'dd' )
- } )[element.attributes['style']];
+ return ve.dm.createHtmlElement( 'dl' );
},
'toData': function( tag, element ) {
- return ( {
- 'dt': { 'type': 'definitionList', 'attributes': { 'style': 'term' } },
- 'dd': { 'type': 'definitionList', 'attributes': { 'style': 'definition' } }
- } )[tag];
+ return { 'type': 'definitionList' };
}
};
diff --git a/modules/ve2/dm/ve.dm.Converter.js b/modules/ve2/dm/ve.dm.Converter.js
index 0440ed2d7f..f93d2789d8 100644
--- a/modules/ve2/dm/ve.dm.Converter.js
+++ b/modules/ve2/dm/ve.dm.Converter.js
@@ -78,6 +78,79 @@ ve.dm.Converter.prototype.onAnnotationRegister = function( type, constructor ) {
}
};
+/**
+ * ...
+ *
+ * @method
+ */
+ve.dm.Converter.prototype.getHtmlElementFromDataElement = function( dataElement ) {
+ var type = dataElement.type,
+ htmlElement = this.elements.toHtml[type]( type, dataElement ),
+ attributes = dataElement.attributes;
+ // Add 'html/*' attributes directly sans 'html/', others get packaged in the 'data-mw' attribute
+ if ( attributes ) {
+ var dataMw = {},
+ key,
+ value;
+ for ( key in dataElement.attributes ) {
+ value = dataElement.attributes[key];
+ if ( key.indexOf( 'html/' ) === 0 ) {
+ htmlElement.setAttribute( key.substr( 5 ), value );
+ } else if ( key.indexOf( 'mw/' ) === 0 ) {
+ dataMw[key] = value;
+ }
+ // Other attributes should have already been handled by the node's toHtml converter
+ }
+ for ( key in dataMw ) {
+ htmlElement.setAttribute( 'data-mw', JSON.stringify( dataMw ) );
+ break;
+ }
+ }
+ return htmlElement;
+};
+
+/**
+ * ...
+ *
+ * @method
+ */
+ve.dm.Converter.prototype.getHtmlContentFromDataContent = function( dataContent ) {
+ //
+};
+
+/**
+ * ...
+ *
+ * @method
+ */
+ve.dm.Converter.prototype.getDataElementFromHtmlElement = function( htmlElement ) {
+ var type = htmlElement.nodeName.toLowerCase(),
+ dataElement = this.elements.toData[type]( type, htmlElement );
+ // Add 'data-mw' attributes to the 'mw/' namespace, others get added under 'html/'
+ for ( var i = 0; i < htmlElement.attributes.length; i++ ) {
+ dataElement.attributes = {};
+ var attribute = htmlElement.attributes[i];
+ if ( attribute.name.toLowerCase() === 'data-mw' ) {
+ var dataMw = JSON.parse( attribute.value );
+ for ( var key in dataMw ) {
+ dataElement.attributes['mw/' + key] = dataMw[key];
+ }
+ } else {
+ dataElement.attributes['html/' + attribute.name] = attribute.value;
+ }
+ }
+ return dataElement;
+};
+
+/**
+ * ...
+ *
+ * @method
+ */
+ve.dm.Converter.prototype.getDataContentFromHtmlContent = function( htmlContent ) {
+ //
+};
+
/* Initialization */
ve.dm.converter = new ve.dm.Converter( ve.dm.nodeFactory, ve.dm.annotationFactory );
diff --git a/tests/ve2/dm/ve.dm.Converter.test.js b/tests/ve2/dm/ve.dm.Converter.test.js
new file mode 100644
index 0000000000..22070c1240
--- /dev/null
+++ b/tests/ve2/dm/ve.dm.Converter.test.js
@@ -0,0 +1,27 @@
+module( 've.dm.Converter' );
+
+/* Tests */
+
+test( 'getDataElementFromHtmlElement', function() {
+ for ( var msg in ve.dm.example.conversions ) {
+ var conversion = ve.dm.example.conversions[msg];
+ deepEqual(
+ ve.dm.converter.getDataElementFromHtmlElement( conversion.htmlElement ),
+ conversion.dataElement,
+ msg
+ );
+ }
+} );
+
+test( 'getHtmlElementFromDataElement', function() {
+ for ( var msg in ve.dm.example.conversions ) {
+ var conversion = ve.dm.example.conversions[msg];
+ deepEqual(
+ ve.example.getHtmlElementSummary(
+ ve.dm.converter.getHtmlElementFromDataElement( conversion.dataElement )
+ ),
+ ve.example.getHtmlElementSummary( conversion.htmlElement ),
+ msg
+ );
+ }
+} );
diff --git a/tests/ve2/dm/ve.dm.example.js b/tests/ve2/dm/ve.dm.example.js
index e83f2a14f5..7b3e2e9a4b 100644
--- a/tests/ve2/dm/ve.dm.example.js
+++ b/tests/ve2/dm/ve.dm.example.js
@@ -411,3 +411,86 @@ ve.dm.example.getOffsetMap = function( root ) {
lookup( root ) // 59 - document
];
};
+
+ve.dm.example.conversions = {
+ 'definitionListItem term': {
+ 'htmlElement': ve.dm.createHtmlElement( 'dt' ),
+ 'dataElement': { 'type': 'definitionListItem', 'attributes': { 'style': 'term' } }
+ },
+ 'definitionListItem definition': {
+ 'htmlElement': ve.dm.createHtmlElement( 'dd' ),
+ 'dataElement': { 'type': 'definitionListItem', 'attributes': { 'style': 'definition' } }
+ },
+ 'definitionList definition': {
+ 'htmlElement': ve.dm.createHtmlElement( 'dl' ),
+ 'dataElement': { 'type': 'definitionList' }
+ },
+ 'heading level 1': {
+ 'htmlElement': ve.dm.createHtmlElement( 'h1' ),
+ 'dataElement': { 'type': 'heading', 'attributes': { 'level': 1 } }
+ },
+ 'heading level 2': {
+ 'htmlElement': ve.dm.createHtmlElement( 'h2' ),
+ 'dataElement': { 'type': 'heading', 'attributes': { 'level': 2 } }
+ },
+ 'heading level 3': {
+ 'htmlElement': ve.dm.createHtmlElement( 'h3' ),
+ 'dataElement': { 'type': 'heading', 'attributes': { 'level': 3 } }
+ },
+ 'heading level 4': {
+ 'htmlElement': ve.dm.createHtmlElement( 'h4' ),
+ 'dataElement': { 'type': 'heading', 'attributes': { 'level': 4 } }
+ },
+ 'heading level 5': {
+ 'htmlElement': ve.dm.createHtmlElement( 'h5' ),
+ 'dataElement': { 'type': 'heading', 'attributes': { 'level': 5 } }
+ },
+ 'heading level 6': {
+ 'htmlElement': ve.dm.createHtmlElement( 'h6' ),
+ 'dataElement': { 'type': 'heading', 'attributes': { 'level': 6 } }
+ },
+ 'image': {
+ 'htmlElement': ve.dm.createHtmlElement( 'image' ),
+ 'dataElement': { 'type': 'image' }
+ },
+ 'listItem': {
+ 'htmlElement': ve.dm.createHtmlElement( 'li' ),
+ 'dataElement': { 'type': 'listItem' }
+ },
+ 'list bullet': {
+ 'htmlElement': ve.dm.createHtmlElement( 'ul' ),
+ 'dataElement': { 'type': 'list', 'attributes': { 'style': 'bullet' } }
+ },
+ 'list number': {
+ 'htmlElement': ve.dm.createHtmlElement( 'ol' ),
+ 'dataElement': { 'type': 'list', 'attributes': { 'style': 'number' } }
+ },
+ 'paragraph': {
+ 'htmlElement': ve.dm.createHtmlElement( 'p' ),
+ 'dataElement': { 'type': 'paragraph' }
+ },
+ 'preformatted': {
+ 'htmlElement': ve.dm.createHtmlElement( 'pre' ),
+ 'dataElement': { 'type': 'preformatted' }
+ },
+ 'tableCell': {
+ 'htmlElement': ve.dm.createHtmlElement( 'td' ),
+ 'dataElement': { 'type': 'tableCell' }
+ },
+ 'table': {
+ 'htmlElement': ve.dm.createHtmlElement( 'table' ),
+ 'dataElement': { 'type': 'table' }
+ },
+ 'tableRow': {
+ 'htmlElement': ve.dm.createHtmlElement( 'tr' ),
+ 'dataElement': { 'type': 'tableRow' }
+ },
+ 'paragraph with mw-data attribute': {
+ 'htmlElement': ve.dm.createHtmlElement( 'p', { 'data-mw': '{"test":1234}' } ),
+ 'dataElement': { 'type': 'paragraph', 'attributes': { 'mw/test': 1234 } }
+ },
+ 'paragraph with html attributes': {
+ 'htmlElement': ve.dm.createHtmlElement( 'p', { 'style': 'color:blue' } ),
+ 'dataElement': { 'type': 'paragraph', 'attributes': { 'html/style': 'color:blue' } }
+ }
+};
diff --git a/tests/ve2/index.html b/tests/ve2/index.html
index 8aa9cff53e..a5a757721c 100644
--- a/tests/ve2/index.html
+++ b/tests/ve2/index.html
@@ -104,6 +104,7 @@
+
diff --git a/tests/ve2/ve.example.js b/tests/ve2/ve.example.js
index 110fa46e0a..6d4c903537 100644
--- a/tests/ve2/ve.example.js
+++ b/tests/ve2/ve.example.js
@@ -282,6 +282,24 @@ ve.example.getNodeSelectionSummary = function( selection ) {
return summary;
};
+/**
+ * Builds a summary of an HTML element.
+ *
+ * Generated summaries contain...
+ *
+ * @method
+ * @param {HTMLElement} element Element to summarize
+ * @returns {Object} Summary of element
+ */
+ve.example.getHtmlElementSummary = function( element ) {
+ var $element = $( element );
+ return {
+ 'type': element.nodeName.toLowerCase(),
+ 'text': $element.text(),
+ 'html': $element.html()
+ };
+};
+
/**
* Looks up a value in a node tree.
*