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. *