Create MediaWiki specific nodes to contain MW specific rules.

Heading and Preformatted nodes have rules that should only
exist under a document node in MediaWiki.

Two new node types have been created as has a new DropdownTool which
uses these. The MW init options have been changed to use the new
DropdownTool.

Bug: 45295

Change-Id: I3f47e1ae1f5c1415bde58a75385e4bf5f4b8fffc
This commit is contained in:
Ed Sanders 2013-02-28 17:17:02 -08:00 committed by Catrope
parent 404ddbf7cc
commit 0ce20b6e16
13 changed files with 259 additions and 8 deletions

View file

@ -242,8 +242,6 @@ $wgResourceModules += array(
've/dm/nodes/ve.dm.ListItemNode.js',
've/dm/nodes/ve.dm.ListNode.js',
've/dm/nodes/ve.dm.MetaNode.js',
've/dm/nodes/ve.dm.MWEntityNode.js',
've/dm/nodes/ve.dm.MWMetaNode.js',
've/dm/nodes/ve.dm.ParagraphNode.js',
've/dm/nodes/ve.dm.PreformattedNode.js',
've/dm/nodes/ve.dm.TableCellNode.js',
@ -252,6 +250,11 @@ $wgResourceModules += array(
've/dm/nodes/ve.dm.TableSectionNode.js',
've/dm/nodes/ve.dm.TextNode.js',
've/dm/nodes/ve.dm.MWEntityNode.js',
've/dm/nodes/ve.dm.MWHeadingNode.js',
've/dm/nodes/ve.dm.MWMetaNode.js',
've/dm/nodes/ve.dm.MWPreformattedNode.js',
've/dm/annotations/ve.dm.LinkAnnotation.js',
've/dm/annotations/ve.dm.MWExternalLinkAnnotation.js',
've/dm/annotations/ve.dm.MWInternalLinkAnnotation.js',
@ -281,7 +284,6 @@ $wgResourceModules += array(
've/ce/nodes/ve.ce.ImageNode.js',
've/ce/nodes/ve.ce.ListItemNode.js',
've/ce/nodes/ve.ce.ListNode.js',
've/ce/nodes/ve.ce.MWEntityNode.js',
've/ce/nodes/ve.ce.ParagraphNode.js',
've/ce/nodes/ve.ce.PreformattedNode.js',
've/ce/nodes/ve.ce.TableCellNode.js',
@ -289,6 +291,9 @@ $wgResourceModules += array(
've/ce/nodes/ve.ce.TableRowNode.js',
've/ce/nodes/ve.ce.TableSectionNode.js',
've/ce/nodes/ve.ce.TextNode.js',
've/ce/nodes/ve.ce.MWEntityNode.js',
've/ce/nodes/ve.ce.MWHeadingNode.js',
've/ce/nodes/ve.ce.MWPreformattedNode.js',
// ui
've/ui/ve.ui.js',
@ -337,6 +342,7 @@ $wgResourceModules += array(
've/ui/tools/buttons/ve.ui.UndoButtonTool.js',
've/ui/tools/dropdowns/ve.ui.FormatDropdownTool.js',
've/ui/tools/dropdowns/ve.ui.MWFormatDropdownTool.js',
've/ui/inspectors/ve.ui.LinkInspector.js',
've/ui/inspectors/ve.ui.MWLinkInspector.js',

View file

@ -0,0 +1,34 @@
/*!
* VisualEditor ContentEditable MWHeadingNode class.
*
* @copyright 2011-2013 VisualEditor Team and others; see AUTHORS.txt
* @license The MIT License (MIT); see LICENSE.txt
*/
/**
* ContentEditable MW heading node.
*
* @class
* @extends ve.ce.HeadingNode
* @constructor
* @param {ve.dm.MWHeadingNode} model Model to observe
*/
ve.ce.MWHeadingNode = function VeCeMWHeadingNode( model ) {
// Parent constructor
ve.ce.HeadingNode.call( this, model );
};
/* Inheritance */
ve.inheritClass( ve.ce.MWHeadingNode, ve.ce.HeadingNode );
/* Static Properties */
// TODO: Make this static
ve.ce.MWHeadingNode.domWrapperElementTypes = ve.ce.HeadingNode.domWrapperElementTypes;
ve.ce.MWHeadingNode.static.name = 'MWheading';
/* Registration */
ve.ce.nodeFactory.register( ve.ce.MWHeadingNode );

View file

@ -0,0 +1,31 @@
/*!
* VisualEditor ContentEditable MWPreformattedNode class.
*
* @copyright 2011-2013 VisualEditor Team and others; see AUTHORS.txt
* @license The MIT License (MIT); see LICENSE.txt
*/
/**
* ContentEditable MW preformatted node.
*
* @class
* @extends ve.ce.PreformattedNode
* @constructor
* @param {ve.dm.MWPreformattedNode} model Model to observe
*/
ve.ce.MWPreformattedNode = function VeCeMWPreformattedNode( model ) {
// Parent constructor
ve.ce.PreformattedNode.call( this, model );
};
/* Inheritance */
ve.inheritClass( ve.ce.MWPreformattedNode, ve.ce.PreformattedNode );
/* Static Properties */
ve.ce.MWPreformattedNode.static.name = 'MWpreformatted';
/* Registration */
ve.ce.nodeFactory.register( ve.ce.MWPreformattedNode );

View file

@ -0,0 +1,40 @@
/*!
* VisualEditor DataModel MWHeadingNode class.
*
* @copyright 2011-2013 VisualEditor Team and others; see AUTHORS.txt
* @license The MIT License (MIT); see LICENSE.txt
*/
/**
* DataModel MW heading node.
*
* @class
* @extends ve.dm.HeadingNode
* @constructor
* @param {ve.dm.LeafNode[]} [children] Child nodes to attach
* @param {Object} [element] Reference to element in linear model
*/
ve.dm.MWHeadingNode = function VeDmMWHeadingNode( children, element ) {
// Parent constructor
ve.dm.HeadingNode.call( this, children, element );
};
/* Inheritance */
ve.inheritClass( ve.dm.MWHeadingNode, ve.dm.HeadingNode );
/* Static Properties */
ve.dm.MWHeadingNode.static.name = 'MWheading';
ve.dm.MWHeadingNode.static.suggestedParentNodeTypes = [ 'document' ];
ve.dm.MWHeadingNode.static.toDataElement = function () {
var parentElement = ve.dm.HeadingNode.static.toDataElement.apply( this, arguments );
parentElement.type = 'MWheading';
return parentElement;
};
/* Registration */
ve.dm.modelRegistry.register( ve.dm.MWHeadingNode );

View file

@ -0,0 +1,38 @@
/*!
* VisualEditor DataModel MWPreformattedNode class.
*
* @copyright 2011-2013 VisualEditor Team and others; see AUTHORS.txt
* @license The MIT License (MIT); see LICENSE.txt
*/
/**
* DataModel MW preformatted node.
*
* @class
* @extends ve.dm.PreformattedNode
* @constructor
* @param {ve.dm.LeafNode[]} [children] Child nodes to attach
* @param {Object} [element] Reference to element in linear model
*/
ve.dm.MWPreformattedNode = function VeDmMWPreformattedNode( children, element ) {
// Parent constructor
ve.dm.PreformattedNode.call( this, children, element );
};
/* Inheritance */
ve.inheritClass( ve.dm.MWPreformattedNode, ve.dm.PreformattedNode );
/* Static Properties */
ve.dm.MWPreformattedNode.static.name = 'MWpreformatted';
ve.dm.MWPreformattedNode.static.suggestedParentNodeTypes = [ 'document' ];
ve.dm.MWPreformattedNode.static.toDataElement = function () {
return { 'type': 'MWpreformatted' };
};
/* Registration */
ve.dm.modelRegistry.register( ve.dm.MWPreformattedNode );

View file

@ -244,6 +244,20 @@ ve.dm.Node.static.childNodeTypes = null;
*/
ve.dm.Node.static.parentNodeTypes = null;
/**
* Array of suggested parent node types for this node type.
*
* These parent node types are allowed but the editor will avoid creating them.
*
* An empty array means this node type should not be the child of any node. null means this node type
* can be the child of any node type.
*
* @static
* @property {string[]|null} static.suggestedParentNodeTypes
* @inheritable
*/
ve.dm.Node.static.suggestedParentNodeTypes = null;
/**
* Default attributes to set for newly created linear model elements. These defaults will be used
* when creating a new element in ve.dm.NodeFactory#getDataElement when there is no DOM node or
@ -280,6 +294,16 @@ ve.dm.Node.prototype.getParentNodeTypes = function () {
return this.constructor.static.parentNodeTypes;
};
/**
* Get suggested parent node types.
*
* @method
* @returns {string[]|null} List of node types suggested as parents or null if any type is suggested
*/
ve.dm.Node.prototype.getSuggestedParentNodeTypes = function () {
return this.constructor.static.suggestedParentNodeTypes;
};
/**
* Check if the node can have children.
*

View file

@ -74,6 +74,21 @@ ve.dm.NodeFactory.prototype.getParentNodeTypes = function ( type ) {
throw new Error( 'Unknown node type: ' + type );
};
/**
* Get suggested parent node types for a node.
*
* @method
* @param {string} type Node type
* @returns {string[]|null} List of node types suggested as parents or null if any type is suggested
* @throws {Error} Unknown node type
*/
ve.dm.NodeFactory.prototype.getSuggestedParentNodeTypes = function ( type ) {
if ( type in this.registry ) {
return this.registry[type].static.suggestedParentNodeTypes;
}
throw new Error( 'Unknown node type: ' + type );
};
/**
* Check if a node can have children.
*

View file

@ -60,7 +60,7 @@ ve.init.mw.ViewPageTarget = function VeInitMwViewPageTarget() {
// copy-pasted from ve.Surface except using mwLink for the link tool
'tools': [
{ 'name': 'history', 'items' : ['undo', 'redo'] },
{ 'name': 'textStyle', 'items' : ['format'] },
{ 'name': 'textStyle', 'items' : ['mwFormat'] },
{ 'name': 'textStyle', 'items' : ['bold', 'italic', 'mwLink', 'clear'] },
{ 'name': 'list', 'items' : ['number', 'bullet', 'outdent', 'indent'] }
]

View file

@ -42,6 +42,11 @@ QUnit.test( 'getDataFromDom', 49, function ( assert ) {
var msg,
cases = ve.dm.example.domToDataCases;
// TODO: this is a hack to make normal heading/preformatted
// nodes the most recently registered, instead of the MW versions
ve.dm.modelRegistry.register( ve.dm.HeadingNode );
ve.dm.modelRegistry.register( ve.dm.PreformattedNode );
for ( msg in cases ) {
if ( cases[msg].html !== null ) {
ve.dm.example.preprocessAnnotations( cases[msg].data );

View file

@ -82,8 +82,6 @@
<script src="../../ve/dm/nodes/ve.dm.ListItemNode.js"></script>
<script src="../../ve/dm/nodes/ve.dm.ListNode.js"></script>
<script src="../../ve/dm/nodes/ve.dm.MetaNode.js"></script>
<script src="../../ve/dm/nodes/ve.dm.MWEntityNode.js"></script>
<script src="../../ve/dm/nodes/ve.dm.MWMetaNode.js"></script>
<script src="../../ve/dm/nodes/ve.dm.ParagraphNode.js"></script>
<script src="../../ve/dm/nodes/ve.dm.PreformattedNode.js"></script>
<script src="../../ve/dm/nodes/ve.dm.TableCellNode.js"></script>
@ -91,6 +89,10 @@
<script src="../../ve/dm/nodes/ve.dm.TableRowNode.js"></script>
<script src="../../ve/dm/nodes/ve.dm.TableSectionNode.js"></script>
<script src="../../ve/dm/nodes/ve.dm.TextNode.js"></script>
<script src="../../ve/dm/nodes/ve.dm.MWEntityNode.js"></script>
<script src="../../ve/dm/nodes/ve.dm.MWHeadingNode.js"></script>
<script src="../../ve/dm/nodes/ve.dm.MWMetaNode.js"></script>
<script src="../../ve/dm/nodes/ve.dm.MWPreformattedNode.js"></script>
<script src="../../ve/dm/annotations/ve.dm.LinkAnnotation.js"></script>
<script src="../../ve/dm/annotations/ve.dm.MWExternalLinkAnnotation.js"></script>
<script src="../../ve/dm/annotations/ve.dm.MWInternalLinkAnnotation.js"></script>
@ -117,7 +119,6 @@
<script src="../../ve/ce/nodes/ve.ce.ImageNode.js"></script>
<script src="../../ve/ce/nodes/ve.ce.ListItemNode.js"></script>
<script src="../../ve/ce/nodes/ve.ce.ListNode.js"></script>
<script src="../../ve/ce/nodes/ve.ce.MWEntityNode.js"></script>
<script src="../../ve/ce/nodes/ve.ce.ParagraphNode.js"></script>
<script src="../../ve/ce/nodes/ve.ce.PreformattedNode.js"></script>
<script src="../../ve/ce/nodes/ve.ce.TableCellNode.js"></script>
@ -125,6 +126,9 @@
<script src="../../ve/ce/nodes/ve.ce.TableRowNode.js"></script>
<script src="../../ve/ce/nodes/ve.ce.TableSectionNode.js"></script>
<script src="../../ve/ce/nodes/ve.ce.TextNode.js"></script>
<script src="../../ve/ce/nodes/ve.ce.MWEntityNode.js"></script>
<script src="../../ve/ce/nodes/ve.ce.MWHeadingNode.js"></script>
<script src="../../ve/ce/nodes/ve.ce.MWPreformattedNode.js"></script>
<script src="../../ve/ui/ve.ui.js"></script>
<script src="../../ve/ui/ve.ui.Context.js"></script>
<script src="../../ve/ui/ve.ui.Frame.js"></script>
@ -166,6 +170,7 @@
<script src="../../ve/ui/tools/buttons/ve.ui.RedoButtonTool.js"></script>
<script src="../../ve/ui/tools/buttons/ve.ui.UndoButtonTool.js"></script>
<script src="../../ve/ui/tools/dropdowns/ve.ui.FormatDropdownTool.js"></script>
<script src="../../ve/ui/tools/dropdowns/ve.ui.MWFormatDropdownTool.js"></script>
<script src="../../ve/ui/inspectors/ve.ui.LinkInspector.js"></script>
<script src="../../ve/ui/inspectors/ve.ui.MWLinkInspector.js"></script>

View file

@ -0,0 +1,41 @@
/*!
* VisualEditor UserInterface MWFormatDropdownTool class.
*
* @copyright 2011-2013 VisualEditor Team and others; see AUTHORS.txt
* @license The MIT License (MIT); see LICENSE.txt
*/
/**
* UserInterface format dropdown tool.
*
* @class
* @extends ve.ui.FormatDropdownTool
* @constructor
* @param {ve.ui.Toolbar} toolbar
*/
ve.ui.MWFormatDropdownTool = function VeUiMwFormatDropdownTool( toolbar, config ) {
// Parent constructor
ve.ui.FormatDropdownTool.call( this, toolbar, config );
};
/* Inheritance */
ve.inheritClass( ve.ui.MWFormatDropdownTool, ve.ui.FormatDropdownTool );
/* Static Properties */
ve.ui.MWFormatDropdownTool.static.name = 'mwFormat';
ve.ui.MWFormatDropdownTool.static.cssName = 'format';
ve.ui.MWFormatDropdownTool.static.items[1].data.type = 'MWheading';
ve.ui.MWFormatDropdownTool.static.items[2].data.type = 'MWheading';
ve.ui.MWFormatDropdownTool.static.items[3].data.type = 'MWheading';
ve.ui.MWFormatDropdownTool.static.items[4].data.type = 'MWheading';
ve.ui.MWFormatDropdownTool.static.items[5].data.type = 'MWheading';
ve.ui.MWFormatDropdownTool.static.items[6].data.type = 'MWheading';
ve.ui.MWFormatDropdownTool.static.items[7].data.type = 'MWpreformatted';
/* Registration */
ve.ui.toolFactory.register( 'mwFormat', ve.ui.MWFormatDropdownTool );

View file

@ -38,7 +38,7 @@ ve.ui.DropdownTool = function VeUiDropdownTool( toolbar, config ) {
// Initialization
this.$
.append( this.$icon, this.$label, this.menu.$ )
.addClass( 've-ui-dropdownTool ve-ui-dropdownTool-' + this.constructor.static.name )
.addClass( 've-ui-dropdownTool ve-ui-dropdownTool-' + ( this.constructor.static.cssName || this.constructor.static.name ) )
.attr( 'title', ve.msg( this.constructor.static.titleMessage ) );
this.$label.append( this.$labelText );
};

View file

@ -52,6 +52,18 @@ ve.inheritClass( ve.ui.Tool, ve.ui.Widget );
*/
ve.ui.Tool.static.name = '';
/**
* CSS class name, rendered as ve-ui-dropdownTool-cssName
*
* If this is left as null, static.name is used instead.
*
* @abstract
* @static
* @property
* @type {string}
*/
ve.ui.Tool.static.cssName = null;
/**
* Message key for tool title.
*