text: /*!
   * VisualEditor ContentEditable MWReferencesListNode class.
   *
   * @copyright 2011-2017 Cite VisualEditor Team and others; see AUTHORS.txt
   * @license The MIT License (MIT); see LICENSE.txt
   */
  
  /**
   * ContentEditable MediaWiki references list node.
   *
   * @class
   * @extends ve.ce.LeafNode
   * @mixins ve.ce.FocusableNode
   *
   * @constructor
   * @param {ve.dm.MWReferencesListNode} model Model to observe
   * @param {Object} [config] Configuration options
   */
  ve.ce.MWReferencesListNode = function VeCeMWReferencesListNode() {
  	// Parent constructor
  	ve.ce.MWReferencesListNode.super.apply( this, arguments );
  
  	// Mixin constructors
  	ve.ce.FocusableNode.call( this );
  
  	// Properties
  	this.internalList = null;
  	this.listNode = null;
  
  	// DOM changes
  	this.$element.addClass( 've-ce-mwReferencesListNode' );
  	this.$reflist = $( '<ol>' ).addClass( 'mw-references references' );
  	this.$refmsg = $( '<p>' )
  		.addClass( 've-ce-mwReferencesListNode-muted' );
  
  	// Events
  	this.model.connect( this, { attributeChange: 'onAttributeChange' } );
  
  	this.updateDebounced = ve.debounce( this.update.bind( this ) );
  
  	// Initialization
  	this.updateDebounced();
  };
  
  /* Inheritance */
  
  OO.inheritClass( ve.ce.MWReferencesListNode, ve.ce.LeafNode );
  
  OO.mixinClass( ve.ce.MWReferencesListNode, ve.ce.FocusableNode );
  
  /* Static Properties */
  
  ve.ce.MWReferencesListNode.static.name = 'mwReferencesList';
  
  ve.ce.MWReferencesListNode.static.tagName = 'div';
  
  ve.ce.MWReferencesListNode.static.primaryCommandName = 'referencesList';
  
  /* Static Methods */
  
  /**
   * @inheritdoc
   */
  ve.ce.MWReferencesListNode.static.getDescription = function ( model ) {
  	return model.getAttribute( 'refGroup' );
  };
  
  /* Methods */
  
  /**
   * Handle setup events.
   *
   * @method
   */
  ve.ce.MWReferencesListNode.prototype.onSetup = function () {
  	this.internalList = this.model.getDocument().getInternalList();
  	this.listNode = this.internalList.getListNode();
  
  	this.internalList.connect( this, { update: 'onInternalListUpdate' } );
  	this.listNode.connect( this, { update: 'onListNodeUpdate' } );
  
  	// Parent method
  	ve.ce.MWReferencesListNode.super.prototype.onSetup.call( this );
  };
  
  /**
   * Handle teardown events.
   *
   * @method
   */
  ve.ce.MWReferencesListNode.prototype.onTeardown = function () {
  	this.internalList.disconnect( this, { update: 'onInternalListUpdate' } );
  	this.listNode.disconnect( this, { update: 'onListNodeUpdate' } );
  
  	this.internalList = null;
  	this.listNode = null;
  
  	// Parent method
  	ve.ce.MWReferencesListNode.super.prototype.onTeardown.call( this );
  };
  
  /**
   * Handle the updating of the InternalList object.
   *
   * This will occur after a document transaction.
   *
   * @method
   * @param {string[]} groupsChanged A list of groups which have changed in this transaction
   */
  ve.ce.MWReferencesListNode.prototype.onInternalListUpdate = function ( groupsChanged ) {
  	// Only update if this group has been changed
  	if ( groupsChanged.indexOf( this.model.getAttribute( 'listGroup' ) ) !== -1 ) {
  		this.updateDebounced();
  	}
  };
  
  /**
   * Rerender when the 'listGroup' attribute changes in the model.
   *
   * @param {string} key Attribute key
   * @param {string} from Old value
   * @param {string} to New value
   */
  ve.ce.MWReferencesListNode.prototype.onAttributeChange = function ( key ) {
  	switch ( key ) {
  		case 'listGroup':
  			this.updateDebounced();
  			break;
  		case 'isResponsive':
  			this.updateClasses();
  			break;
  	}
  };
  
  /**
   * Handle the updating of the InternalListNode.
   *
   * This will occur after changes to any InternalItemNode.
   *
   * @method
   */
  ve.ce.MWReferencesListNode.prototype.onListNodeUpdate = function () {
  	// When the list node updates we're not sure which list group the item
  	// belonged to so we always update
  	// TODO: Only re-render the reference which has been edited
  	this.updateDebounced();
  };
  
  /**
   * Update the references list.
   */
  ve.ce.MWReferencesListNode.prototype.update = function () {
  	var i, j, iLen, jLen, index, firstNode, key, keyedNodes, modelNode, viewNode,
  		$li, $refSpan, $link,
  		internalList = this.model.getDocument().internalList,
  		refGroup = this.model.getAttribute( 'refGroup' ),
  		listGroup = this.model.getAttribute( 'listGroup' ),
  		nodes = internalList.getNodeGroup( listGroup );
  
  	function updateGeneratedContent( viewNode, $li ) {
  		// HACK: PHP parser doesn't wrap single lines in a paragraph
  		if (
  			viewNode.$element.children().length === 1 &&
  			viewNode.$element.children( 'p' ).length === 1
  		) {
  			// unwrap inner
  			viewNode.$element.children().replaceWith(
  				viewNode.$element.children().contents()
  			);
  		}
  		$li.append(
  			$( '<span>' )
  				.addClass( 'reference-text' )
  				.append( viewNode.$element )
  		);
  
  		// Since this is running after content generation has finished, it's
  		// safe to destroy the view.
  		viewNode.destroy();
  	}
  
  	this.$reflist.detach().empty();
  	this.$refmsg.detach();
  
  	if ( refGroup !== '' ) {
  		this.$reflist.attr( 'data-mw-group', refGroup );
  	} else {
  		this.$reflist.removeAttr( 'data-mw-group' );
  	}
  
  	if ( !nodes || !nodes.indexOrder.length ) {
  		if ( refGroup !== '' ) {
  			this.$refmsg.text( ve.msg( 'cite-ve-referenceslist-isempty', refGroup ) );
  		} else {
  			this.$refmsg.text( ve.msg( 'cite-ve-referenceslist-isempty-default' ) );
  		}
  		this.$element.append( this.$refmsg );
  	} else {
  		for ( i = 0, iLen = nodes.indexOrder.length; i < iLen; i++ ) {
  			index = nodes.indexOrder[ i ];
  			firstNode = nodes.firstNodes[ index ];
  
  			key = internalList.keys[ index ];
  			keyedNodes = nodes.keyedNodes[ key ];
  			keyedNodes = keyedNodes.filter( function ( node ) {
  				// Exclude placeholder references
  				if ( node.getAttribute( 'placeholder' ) ) {
  					return false;
  				}
  				// Exclude references defined inside the references list node
  				while ( ( node = node.parent ) && node !== null ) {
  					if ( node instanceof ve.dm.MWReferencesListNode ) {
  						return false;
  					}
  				}
  				return true;
  			} );
  
  			if ( !keyedNodes.length ) {
  				continue;
  			}
  
  			$li = $( '<li>' );
  
  			if ( keyedNodes.length > 1 ) {
  				$refSpan = $( '<span rel="mw:referencedBy">' );
  				for ( j = 0, jLen = keyedNodes.length; j < jLen; j++ ) {
  					$link = $( '<a>' ).append(
  						$( '<span class="mw-linkback-text">' )
  							.text( ( j + 1 ) + ' ' )
  					);
  					if ( refGroup !== '' ) {
  						$link.attr( 'data-mw-group', refGroup );
  					}
  					$refSpan.append( $link );
  				}
  				$li.append( $refSpan );
  			} else {
  				$link = $( '<a rel="mw:referencedBy">' ).append(
  					$( '<span class="mw-linkback-text">' ).text( '↑ ' )
  				);
  				if ( refGroup !== '' ) {
  					$link.attr( 'data-mw-group', refGroup );
  				}
  				$li.append( $link );
  			}
  
  			// Generate reference HTML from first item in key
  			modelNode = internalList.getItemNode( firstNode.getAttribute( 'listIndex' ) );
  			if ( modelNode && modelNode.length ) {
  				viewNode = new ve.ce.InternalItemNode( modelNode );
  
  				ve.ce.GeneratedContentNode.static.awaitGeneratedContent( viewNode )
  					.then( updateGeneratedContent.bind( this, viewNode, $li ) );
  
  				// Because this update runs a number of times when using the
  				// basic dialog, disconnect the model here rather than waiting
  				// for when it's destroyed after the generated content is
  				// finished. Failing to do this causes teardown errors with
  				// basic citations.
  				modelNode.disconnect( viewNode );
  			} else {
  				$li.append(
  					$( '<span>' )
  						.addClass( 've-ce-mwReferencesListNode-muted' )
  						.text( ve.msg( 'cite-ve-referenceslist-missingref' ) )
  				);
  			}
  
  			this.$reflist.append( $li );
  		}
  		this.updateClasses();
  		this.$element.append( this.$reflist );
  	}
  };
  
  /**
   * Update ref list classes.
   *
   * Currently used to set responsive layout
   */
  ve.ce.MWReferencesListNode.prototype.updateClasses = function () {
  	var isResponsive = this.model.getAttribute( 'isResponsive' );
  
  	this.$element
  		.toggleClass( 'mw-references-wrap', isResponsive )
  		.toggleClass( 'mw-references-columns', isResponsive && this.$reflist.children().length > 10 );
  };
  
  /* Registration */
  
  ve.ce.nodeFactory.register( ve.ce.MWReferencesListNode );
  