mediawiki-extensions-Visual.../modules/ve/ce/ve.ce.ContentBranchNode.js
Timo Tijhof 3b6810eeec ce: Don't set backgroundColor transparent for every node in debug mode
This only affects debug mode, but things look broken when the
background of every single node is set to transparent (e.g. a
<pre> looks weird with a white instead of grey background).

It also leaves the DOM dirty full of inline styles.

Though setting a grey background isn't guaranteed to be visible
either, and all of these redraws and stuff really slow things
down (we should perhaps only start doing these after the initial
document is painted).. the least we can do is undo it and not
leave it there.

Change-Id: I9abfd46765914828ad8618748be5716a8c6b185c
2013-07-27 04:05:07 +00:00

190 lines
4.9 KiB
JavaScript

/*!
* VisualEditor ContentEditable ContentBranchNode class.
*
* @copyright 2011-2013 VisualEditor Team and others; see AUTHORS.txt
* @license The MIT License (MIT); see LICENSE.txt
*/
/**
* ContentEditable content branch node.
*
* Content branch nodes can only have content nodes as children.
*
* @abstract
* @extends ve.ce.BranchNode
* @constructor
* @param {ve.dm.BranchNode} model Model to observe
* @param {Object} [config] Config options
*/
ve.ce.ContentBranchNode = function VeCeContentBranchNode( model, config ) {
// Parent constructor
ve.ce.BranchNode.call( this, model, config );
// Properties
this.surfaceModelState = null;
// Events
this.connect( this, { 'childUpdate': 'onChildUpdate' } );
};
/* Inheritance */
ve.inheritClass( ve.ce.ContentBranchNode, ve.ce.BranchNode );
/* Methods */
/**
* Handle splice events.
*
* Rendering is only done once per transaction. If a paragraph has multiple nodes in it then it's
* possible to receive multiple `childUpdate` events for a single transaction such as annotating
* across them. State is tracked by storing and comparing the length of the surface model's complete
* history.
*
* This is used to automatically render contents.
* @see ve.ce.BranchNode#onSplice
*
* @method
*/
ve.ce.ContentBranchNode.prototype.onChildUpdate = function ( transaction ) {
var surfaceModel = this.getRoot().getSurface().getModel(),
surfaceModelState = surfaceModel.getDocument().getCompleteHistoryLength();
if ( transaction instanceof ve.dm.Transaction ) {
if ( surfaceModelState === this.surfaceModelState ) {
return;
}
this.surfaceModelState = surfaceModelState;
}
this.renderContents();
};
/**
* Handle splice events.
*
* This is used to automatically render contents.
* @see ve.ce.BranchNode#onSplice
*
* @method
*/
ve.ce.ContentBranchNode.prototype.onSplice = function () {
// Parent method
ve.ce.BranchNode.prototype.onSplice.apply( this, arguments );
// Rerender to make sure annotations are applied correctly
this.renderContents();
};
/**
* Get an HTML rendering of the contents.
*
* @method
* @returns {HTMLElement[]}
*/
ve.ce.ContentBranchNode.prototype.getRenderedContents = function () {
var i, ilen, j, jlen, item, itemAnnotations, ann,
store = this.model.doc.getStore(),
annotationStack = new ve.dm.AnnotationSet( store ),
annotatedHtml = [],
wrapper = document.createElement( 'div' ),
current = wrapper,
buffer = '';
function openAnnotation( annotation ) {
if ( buffer !== '' ) {
current.appendChild( document.createTextNode( buffer ) );
buffer = '';
}
// Create a new DOM node and descend into it
ann = ve.ce.annotationFactory.create( annotation.getType(), annotation ).$[0];
current.appendChild( ann );
current = ann;
}
function closeAnnotation() {
if ( buffer !== '' ) {
current.appendChild( document.createTextNode( buffer ) );
buffer = '';
}
// Traverse up
current = current.parentNode;
}
// Gather annotated HTML from the child nodes
for ( i = 0, ilen = this.children.length; i < ilen; i++ ) {
annotatedHtml = annotatedHtml.concat( this.children[i].getAnnotatedHtml() );
}
// Render HTML with annotations
for ( i = 0, ilen = annotatedHtml.length; i < ilen; i++ ) {
if ( ve.isArray( annotatedHtml[i] ) ) {
item = annotatedHtml[i][0];
itemAnnotations = new ve.dm.AnnotationSet( store, annotatedHtml[i][1] );
} else {
item = annotatedHtml[i];
itemAnnotations = new ve.dm.AnnotationSet( store );
}
ve.dm.Converter.openAndCloseAnnotations( annotationStack, itemAnnotations,
openAnnotation, closeAnnotation
);
// Handle the actual item
if ( typeof item === 'string' ) {
buffer += item;
} else {
if ( buffer !== '' ) {
current.appendChild( document.createTextNode( buffer ) );
buffer = '';
}
// DOM equivalent of $( current ).append( itemHtml );
for ( j = 0, jlen = item.length; j < jlen; j++ ) {
current.appendChild( item[j] );
}
}
}
if ( buffer !== '' ) {
current.appendChild( document.createTextNode( buffer ) );
buffer = '';
}
return Array.prototype.slice.apply( wrapper.childNodes );
};
/**
* Render contents.
*
* @method
*/
ve.ce.ContentBranchNode.prototype.renderContents = function () {
var i, len, node, rendered;
if ( this.root instanceof ve.ce.DocumentNode && !this.root.getSurface().isRenderingEnabled() ) {
return;
}
// Detach all child nodes from this.$
for ( i = 0, len = this.$.length; i < len; i++ ) {
node = this.$[i];
while ( node.firstChild ) {
node.removeChild( node.firstChild );
}
}
// Reattach child nodes with the right annotations
rendered = this.getRenderedContents();
for ( i = 0, len = rendered.length; i < len; i++ ) {
this.$[0].appendChild( rendered[i] );
}
// Add slugs
this.setupSlugs();
// Highlight the node in debug mode
if ( ve.debug ) {
this.$.css( 'backgroundColor', '#F6F6F6' );
setTimeout( ve.bind( function () {
this.$.css( 'backgroundColor', '' );
}, this ), 350 );
}
};