jenkins-bot has submitted this change and it was merged.
Change subject: Add ve.ce.View as a common base class for ce.Node and
ce.Annotation
......................................................................
Add ve.ce.View as a common base class for ce.Node and ce.Annotation
Just like ve.dm.Model is a common base class for dm.Node, dm.Annotation
and dm.MetaItem.
ce.View abstracts the this.model and this.$ behavior, including liveness,
whitelisted HTML attribute rendering and adding a back reference in
.data(). The back reference has been renamed from .data( 'node' ) to
the more generic .data( 'view' ).
At this point this means ce.Annotation is just a shell around ce.View
(except where it defaults to a span rather than a div), but that could
change in the future.
Change-Id: I0eef5b80718e0b0fcd3f8bba096b452f0bb680d0
---
M .docs/categories.json
M VisualEditor.php
M demos/ve/index.php
M modules/ve/ce/ve.ce.Annotation.js
M modules/ve/ce/ve.ce.BranchNode.js
M modules/ve/ce/ve.ce.Document.js
M modules/ve/ce/ve.ce.Node.js
M modules/ve/ce/ve.ce.SurfaceObserver.js
A modules/ve/ce/ve.ce.View.js
M modules/ve/ce/ve.ce.js
M modules/ve/dm/ve.dm.MetaItem.js
M modules/ve/init/mw/targets/ve.init.mw.ViewPageTarget.js
M modules/ve/test/index.php
13 files changed, 143 insertions(+), 174 deletions(-)
Approvals:
Esanders: Looks good to me, approved
jenkins-bot: Verified
diff --git a/.docs/categories.json b/.docs/categories.json
index 33b7ea7..b37471d 100644
--- a/.docs/categories.json
+++ b/.docs/categories.json
@@ -15,6 +15,7 @@
"name": "General",
"classes": [
"ve.ce",
+ "ve.ce.View",
"ve.ce.AnnotationFactory",
"ve.ce.NodeFactory",
"ve.ce.Surface",
diff --git a/VisualEditor.php b/VisualEditor.php
index 752e7ee..5b3e5f0 100644
--- a/VisualEditor.php
+++ b/VisualEditor.php
@@ -290,6 +290,7 @@
've/ce/ve.ce.AnnotationFactory.js',
've/ce/ve.ce.NodeFactory.js',
've/ce/ve.ce.Document.js',
+ 've/ce/ve.ce.View.js',
've/ce/ve.ce.Annotation.js',
've/ce/ve.ce.Node.js',
've/ce/ve.ce.BranchNode.js',
diff --git a/demos/ve/index.php b/demos/ve/index.php
index d48b9e0..eb342d3 100644
--- a/demos/ve/index.php
+++ b/demos/ve/index.php
@@ -172,6 +172,7 @@
<script
src="../../modules/ve/ce/ve.ce.AnnotationFactory.js"></script>
<script src="../../modules/ve/ce/ve.ce.NodeFactory.js"></script>
<script src="../../modules/ve/ce/ve.ce.Document.js"></script>
+ <script src="../../modules/ve/ce/ve.ce.View.js"></script>
<script src="../../modules/ve/ce/ve.ce.Annotation.js"></script>
<script src="../../modules/ve/ce/ve.ce.Node.js"></script>
<script src="../../modules/ve/ce/ve.ce.BranchNode.js"></script>
@@ -424,8 +425,8 @@
validateButton.on( 'click', function () {
var failed = false;
$('.ve-ce-branchNode').each( function ( index,
element ) {
- var $element = $( element ),
- view = $element.data( 'node' );
+ var $element = $( element ),
+ view = $element.data( 'view' );
if ( view.canContainContent() ) {
var nodeRange =
view.model.getRange();
var textModel =
ve.instances[0].view.model.getDocument().getText( nodeRange );
diff --git a/modules/ve/ce/ve.ce.Annotation.js
b/modules/ve/ce/ve.ce.Annotation.js
index 66cc053..e172f19 100644
--- a/modules/ve/ce/ve.ce.Annotation.js
+++ b/modules/ve/ce/ve.ce.Annotation.js
@@ -13,93 +13,18 @@
*
* Subclasses of ve.dm.Annotation should have a corresponding subclass here
that controls rendering.
*
- * @class
+ * @abstract
+ * @extends ve.ce.View
+ *
* @constructor
* @param {ve.dm.Annotation} model Model to observe
* @param {jQuery} [$element] Element to use as a container
*/
ve.ce.Annotation = function VeCeAnnotation( model, $element ) {
- // Properties
- this.model = model;
- this.$ = $element || $( '<span>' );
- this.parent = null;
- this.live = false;
-
- // Initialization
- this.$.data( 'annotation', this );
- ve.setDomAttributes(
- this.$[0],
- this.model.getAttributes( 'html/0/' ),
- this.constructor.static.domAttributeWhitelist
- );
+ // Parent constructor
+ ve.ce.View.call( this, model, $element || $( '<span>' ) );
};
-/* Events */
+/* Inheritance */
-/**
- * @event live
- */
-
-/* Static Members */
-
-// TODO create a single base class for ce.Node and ce.Annotation
-
-ve.ce.Annotation.static = {};
-
-/**
- * Allowed attributes for DOM elements.
- *
- * This list includes attributes that are generally safe to include in HTML
loaded from a
- * foreign source and displaying it inside the browser. It doesn't include any
event attributes,
- * for instance, which would allow arbitrary JavaScript execution. This alone
is not enough to
- * make HTML safe to display, but it helps.
- *
- * TODO: Rather than use a single global list, set these on a per-annotation
basis to something that makes
- * sense for that annotation in particular.
- *
- * @static
- * @property static.domAttributeWhitelist
- * @inheritable
- */
-ve.ce.Annotation.static.domAttributeWhitelist = [
- 'abbr', 'about', 'align', 'alt', 'axis', 'bgcolor', 'border',
'cellpadding', 'cellspacing',
- 'char', 'charoff', 'cite', 'class', 'clear', 'color', 'colspan',
'datatype', 'datetime',
- 'dir', 'face', 'frame', 'headers', 'height', 'href', 'id', 'itemid',
'itemprop', 'itemref',
- 'itemscope', 'itemtype', 'lang', 'noshade', 'nowrap', 'property',
'rbspan', 'rel',
- 'resource', 'rev', 'rowspan', 'rules', 'scope', 'size', 'span', 'src',
'start', 'style',
- 'summary', 'title', 'type', 'typeof', 'valign', 'value', 'width'
-];
-
-/* Methods */
-
-/**
- * Get the model this CE annotation observes.
- *
- * @method
- * @returns {ve.ce.Annotation} Model
- */
-ve.ce.Annotation.prototype.getModel = function () {
- return this.model;
-};
-
-/**
- * Check if the annotation is attached to the live DOM.
- *
- * @method
- * @returns {boolean} Annotation is attached to the live DOM
- */
-ve.ce.Annotation.prototype.isLive = function () {
- return this.live;
-};
-
-/**
- * Set live state.
- *
- * @method
- * @param {boolean} live The annotation has been attached to the live DOM (use
false on detach)
- * @emits live
- */
-ve.ce.Annotation.prototype.setLive = function ( live ) {
- this.live = live;
- this.emit( 'live' );
-};
+ve.inheritClass( ve.ce.Annotation, ve.ce.View );
diff --git a/modules/ve/ce/ve.ce.BranchNode.js
b/modules/ve/ce/ve.ce.BranchNode.js
index a4f02f5..203771c 100644
--- a/modules/ve/ce/ve.ce.BranchNode.js
+++ b/modules/ve/ce/ve.ce.BranchNode.js
@@ -129,7 +129,7 @@
*
* This method uses {getDomWrapperType} to determine the proper element type
to use.
*
- * WARNING: The contents, .data( 'node' ) and any classes the wrapper already
has will be moved to
+ * WARNING: The contents, .data( 'view' ) and any classes the wrapper already
has will be moved to
* the new wrapper, but other attributes and any other information added using
$.data() will be
* lost upon updating the wrapper. To retain information added to the wrapper,
subscribe to the
* 'rewrap' event and copy information from the {$old} wrapper the {$new}
wrapper.
@@ -146,8 +146,8 @@
$element = $( document.createElement( type ) );
// Copy classes
$element.attr( 'class', this.$.attr( 'class' ) );
- // Copy .data( 'node' )
- $element.data( 'node', this.$.data( 'node' ) );
+ // Copy .data( 'view' )
+ $element.data( 'view', this.$.data( 'view' ) );
// Move contents
$element.append( this.$.contents() );
// Emit an event that can be handled to copy other things over
if needed
diff --git a/modules/ve/ce/ve.ce.Document.js b/modules/ve/ce/ve.ce.Document.js
index b6610b6..78ea471 100644
--- a/modules/ve/ce/ve.ce.Document.js
+++ b/modules/ve/ce/ve.ce.Document.js
@@ -216,7 +216,7 @@
};
}
} else if ( $item.is( '.ve-ce-branchNode,
.ve-ce-leafNode' ) ) {
- length = $item.data( 'node'
).model.getOuterLength();
+ length = $item.data( 'view'
).model.getOuterLength();
if ( offset >= startOffset && offset <
startOffset + length ) {
stack.push( [$item.contents(), 0] );
current[1]++;
diff --git a/modules/ve/ce/ve.ce.Node.js b/modules/ve/ce/ve.ce.Node.js
index 55fd1fb..f099adb 100644
--- a/modules/ve/ce/ve.ce.Node.js
+++ b/modules/ve/ce/ve.ce.Node.js
@@ -9,7 +9,8 @@
* Generic ContentEditable node.
*
* @abstract
- * @extends ve.Node
+ * @extends ve.ce.View
+ * @mixins ve.Node
*
* @constructor
* @param {ve.dm.Node} model Model to observe
@@ -17,35 +18,22 @@
*/
ve.ce.Node = function VeCeNode( model, $element ) {
// Parent constructor
+ ve.ce.View.call( this, model, $element );
+ // Mixin constructor
ve.Node.call( this );
// Properties
- this.model = model;
- this.$ = $element || $( '<div>' );
this.parent = null;
- this.live = false;
// Events
this.model.on( 'attributeChange', ve.bind( this.onAttributeChange, this
) );
-
- // Initialization
- this.$.data( 'node', this );
- ve.setDomAttributes(
- this.$[0],
- this.model.getAttributes( 'html/0/' ),
- this.constructor.static.domAttributeWhitelist
- );
};
/* Inheritance */
-ve.inheritClass( ve.ce.Node, ve.Node );
+ve.inheritClass( ve.ce.Node, ve.ce.View );
-/* Events */
-
-/**
- * @event live
- */
+ve.mixinClass( ve.ce.Node, ve.Node );
/* Static Members */
@@ -60,30 +48,6 @@
* @inheritable
*/
ve.ce.Node.static.canBeSplit = false;
-
-/**
- * Allowed attributes for DOM elements.
- *
- * This list includes attributes that are generally safe to include in HTML
loaded from a
- * foreign source and displaying it inside the browser. It doesn't include any
event attributes,
- * for instance, which would allow arbitrary JavaScript execution. This alone
is not enough to
- * make HTML safe to display, but it helps.
- *
- * TODO: Rather than use a single global list, set these on a per-node basis
to something that makes
- * sense for that node in particular.
- *
- * @static
- * @property static.domAttributeWhitelist
- * @inheritable
- */
-ve.ce.Node.static.domAttributeWhitelist = [
- 'abbr', 'about', 'align', 'alt', 'axis', 'bgcolor', 'border',
'cellpadding', 'cellspacing',
- 'char', 'charoff', 'cite', 'class', 'clear', 'color', 'colspan',
'datatype', 'datetime',
- 'dir', 'face', 'frame', 'headers', 'height', 'href', 'id', 'itemid',
'itemprop', 'itemref',
- 'itemscope', 'itemtype', 'lang', 'noshade', 'nowrap', 'property',
'rbspan', 'rel',
- 'resource', 'rev', 'rowspan', 'rules', 'scope', 'size', 'span', 'src',
'start', 'style',
- 'summary', 'title', 'type', 'typeof', 'valign', 'value', 'width'
-];
/**
* Template for shield elements.
@@ -270,16 +234,6 @@
};
/**
- * Get the model the node observes.
- *
- * @method
- * @returns {ve.dm.Node} Model the node observes
- */
-ve.ce.Node.prototype.getModel = function () {
- return this.model;
-};
-
-/**
* Get the closest splittable node upstream.
*
* @method
@@ -298,26 +252,4 @@
} );
return splitableNode;
-};
-
-/**
- * Check if the node is attached to the live DOM.
- *
- * @method
- * @returns {boolean} Node is attached to the live DOM
- */
-ve.ce.Node.prototype.isLive = function () {
- return this.live;
-};
-
-/**
- * Set live state.
- *
- * @method
- * @param {boolean} live The node has been attached to the live DOM (use false
on detach)
- * @emits live
- */
-ve.ce.Node.prototype.setLive = function ( live ) {
- this.live = live;
- this.emit( 'live' );
};
diff --git a/modules/ve/ce/ve.ce.SurfaceObserver.js
b/modules/ve/ce/ve.ce.SurfaceObserver.js
index 79d9d31..53c4b61 100644
--- a/modules/ve/ce/ve.ce.SurfaceObserver.js
+++ b/modules/ve/ce/ve.ce.SurfaceObserver.js
@@ -157,7 +157,7 @@
this.rangyRange = rangyRange;
$branch = $( rangyRange.anchorNode ).closest(
'.ve-ce-branchNode' );
if ( $branch.length ) {
- node = $branch.data( 'node' );
+ node = $branch.data( 'view' );
if ( node.canHaveChildrenNotContent() ) {
node = null;
} else {
diff --git a/modules/ve/ce/ve.ce.View.js b/modules/ve/ce/ve.ce.View.js
new file mode 100644
index 0000000..cb9ae7e
--- /dev/null
+++ b/modules/ve/ce/ve.ce.View.js
@@ -0,0 +1,106 @@
+/*!
+ * VisualEditor ContentEditable View class.
+ *
+ * @copyright 2011-2013 VisualEditor Team and others; see AUTHORS.txt
+ * @license The MIT License (MIT); see LICENSE.txt
+ */
+
+/**
+ * Generic base class for CE views.
+ *
+ * @abstract
+ * @mixins ve.EventEmitter
+ *
+ * @constructor
+ * @param {ve.dm.Model} model Model to observe
+ * @param {jQuery} [$element] Element to use as a container
+ */
+ve.ce.View = function VeCeView( model, $element ) {
+ // Parent constructor
+ ve.EventEmitter.call( this );
+
+ // Properties
+ this.model = model;
+ this.$ = $element || $( '<div>' );
+ this.live = false;
+
+ // Initialization
+ this.$.data( 'view', this );
+ ve.setDomAttributes(
+ this.$[0],
+ this.model.getAttributes( 'html/0/' ),
+ this.constructor.static.domAttributeWhitelist
+ );
+};
+
+/* Inheritance */
+
+ve.mixinClass( ve.ce.View, ve.EventEmitter );
+
+/* Events */
+
+/**
+ * @event live
+ */
+
+/* Static Members */
+
+ve.ce.View.static = {};
+
+/**
+ * Allowed attributes for DOM elements.
+ *
+ * This list includes attributes that are generally safe to include in HTML
loaded from a
+ * foreign source and displaying it inside the browser. It doesn't include any
event attributes,
+ * for instance, which would allow arbitrary JavaScript execution. This alone
is not enough to
+ * make HTML safe to display, but it helps.
+ *
+ * TODO: Rather than use a single global list, set these on a per-view basis
to something that makes
+ * sense for that view in particular.
+ *
+ * @static
+ * @property static.domAttributeWhitelist
+ * @inheritable
+ */
+ve.ce.View.static.domAttributeWhitelist = [
+ 'abbr', 'about', 'align', 'alt', 'axis', 'bgcolor', 'border',
'cellpadding', 'cellspacing',
+ 'char', 'charoff', 'cite', 'class', 'clear', 'color', 'colspan',
'datatype', 'datetime',
+ 'dir', 'face', 'frame', 'headers', 'height', 'href', 'id', 'itemid',
'itemprop', 'itemref',
+ 'itemscope', 'itemtype', 'lang', 'noshade', 'nowrap', 'property',
'rbspan', 'rel',
+ 'resource', 'rev', 'rowspan', 'rules', 'scope', 'size', 'span', 'src',
'start', 'style',
+ 'summary', 'title', 'type', 'typeof', 'valign', 'value', 'width'
+];
+
+/* Methods */
+
+/**
+ * Get the model the view observes.
+ *
+ * @method
+ * @returns {ve.dm.Node} Model the view observes
+ */
+ve.ce.View.prototype.getModel = function () {
+ return this.model;
+};
+
+/**
+ * Check if the view is attached to the live DOM.
+ *
+ * @method
+ * @returns {boolean} View is attached to the live DOM
+ */
+ve.ce.View.prototype.isLive = function () {
+ return this.live;
+};
+
+/**
+ * Set live state.
+ *
+ * @method
+ * @param {boolean} live The view has been attached to the live DOM (use false
on detach)
+ * @emits live
+ */
+ve.ce.View.prototype.setLive = function ( live ) {
+ this.live = live;
+ this.emit( 'live' );
+};
diff --git a/modules/ve/ce/ve.ce.js b/modules/ve/ce/ve.ce.js
index fb6b29a..1c7511f 100644
--- a/modules/ve/ce/ve.ce.js
+++ b/modules/ve/ce/ve.ce.js
@@ -55,7 +55,7 @@
} else if ( $element.hasClass( 've-ce-leafNode' ) ) {
// For leaf nodes, don't return the content,
but return
// the right amount of characters so the
offsets match up
- numChars = $element.data( 'node'
).getOuterLength();
+ numChars = $element.data( 'view'
).getOuterLength();
return new Array( numChars + 1 ).join( '\u2603'
);
} else {
// Traverse its children
@@ -133,7 +133,7 @@
$node = $( domNode ).closest(
'.ve-ce-branchNode, .ve-ce-leafNode'
);
- nodeModel = $node.data( 'node' ).getModel();
+ nodeModel = $node.data( 'view' ).getModel();
// IE sometimes puts the cursor in a text node inside ce="false". BAD!
if ( $node[0].contentEditable === 'false' ) {
@@ -171,7 +171,7 @@
} else if ( $item.hasClass( 've-ce-leafNode' ) ) {
offset += 2;
} else if ( $item.hasClass( 've-ce-branchNode' ) ) {
- offset += $item.data( 'node' ).getOuterLength();
+ offset += $item.data( 'view' ).getOuterLength();
} else {
stack.push( [$item.contents(), 0 ] );
current[1]++;
@@ -200,25 +200,25 @@
if ( $domNode.hasClass( 've-ce-slug' ) ) {
if ( $domNode.prev().length ) {
- nodeModel = $domNode.prev().data( 'node' ).getModel();
+ nodeModel = $domNode.prev().data( 'view' ).getModel();
return nodeModel.getOffset() +
nodeModel.getOuterLength();
}
if ( $domNode.next().length ) {
- nodeModel = $domNode.next().data( 'node' ).getModel();
+ nodeModel = $domNode.next().data( 'view' ).getModel();
return nodeModel.getOffset();
}
}
// IE sometimes puts the cursor in a text node inside ce="false". BAD!
if ( domNode.contentEditable === 'false' ) {
- nodeModel = $domNode.data( 'node' ).getModel();
+ nodeModel = $domNode.data( 'view' ).getModel();
return nodeModel.getOffset() + nodeModel.getOuterLength();
}
if ( domOffset === 0 ) {
- node = $domNode.data( 'node' );
+ node = $domNode.data( 'view' );
if ( node ) {
- nodeModel = $domNode.data( 'node' ).getModel();
+ nodeModel = $domNode.data( 'view' ).getModel();
if ( addOuterLength === true ) {
return nodeModel.getOffset() +
nodeModel.getOuterLength();
} else {
@@ -249,10 +249,10 @@
ve.ce.getOffsetOfSlug = function ( $node ) {
var model;
if ( $node.index() === 0 ) {
- model = $node.parent().data( 'node' ).getModel();
+ model = $node.parent().data( 'view' ).getModel();
return model.getOffset() + ( model.isWrapped() ? 1 : 0 );
} else if ( $node.prev().length ) {
- model = $node.prev().data( 'node' ).getModel();
+ model = $node.prev().data( 'view' ).getModel();
return model.getOffset() + model.getOuterLength();
} else {
throw new Error( 'Incorrect slug location' );
diff --git a/modules/ve/dm/ve.dm.MetaItem.js b/modules/ve/dm/ve.dm.MetaItem.js
index e4e2595..738c312 100644
--- a/modules/ve/dm/ve.dm.MetaItem.js
+++ b/modules/ve/dm/ve.dm.MetaItem.js
@@ -11,6 +11,8 @@
* @class
* @abstract
* @extends ve.dm.Model
+ * @mixins ve.EventEmitter
+ *
* @constructor
* @param {Object} element Reference to element in meta-linmod
*/
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 99a0df9..21dc153 100644
--- a/modules/ve/init/mw/targets/ve.init.mw.ViewPageTarget.js
+++ b/modules/ve/init/mw/targets/ve.init.mw.ViewPageTarget.js
@@ -1541,7 +1541,7 @@
surfaceView = this.surface.getView(),
surfaceModel = surfaceView.getModel();
this.$document.find( 'h1, h2, h3, h4, h5, h6' ).eq(
this.section - 1 ).each( function () {
- var headingNode = $(this).data( 'node' );
+ var headingNode = $(this).data( 'view' );
if ( headingNode ) {
offset =
surfaceModel.getDocument().data.getNearestContentOffset(
headingNode.getModel().getOffset()
diff --git a/modules/ve/test/index.php b/modules/ve/test/index.php
index cbdf7a5..1ec8fe6 100644
--- a/modules/ve/test/index.php
+++ b/modules/ve/test/index.php
@@ -116,6 +116,7 @@
<script src="../../ve/ce/ve.ce.AnnotationFactory.js"></script>
<script src="../../ve/ce/ve.ce.NodeFactory.js"></script>
<script src="../../ve/ce/ve.ce.Document.js"></script>
+ <script src="../../ve/ce/ve.ce.View.js"></script>
<script src="../../ve/ce/ve.ce.Annotation.js"></script>
<script src="../../ve/ce/ve.ce.Node.js"></script>
<script src="../../ve/ce/ve.ce.BranchNode.js"></script>
--
To view, visit https://gerrit.wikimedia.org/r/57242
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: I0eef5b80718e0b0fcd3f8bba096b452f0bb680d0
Gerrit-PatchSet: 8
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: jenkins-bot
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits