Index: wp-includes/class-wp-admin-bar.php
===================================================================
--- wp-includes/class-wp-admin-bar.php	(revision 19491)
+++ wp-includes/class-wp-admin-bar.php	(working copy)
@@ -1,22 +1,27 @@
 <?php
 class WP_Admin_Bar {
 	private $nodes = array();
-	private $root = array();
-
-	public $proto = 'http://';
 	public $user;
 
-	function initialize() {
-		/* Set the protocol used throughout this code */
-		if ( is_ssl() )
-			$this->proto = 'https://';
+	public function __get( $name ) {
+		switch ( $name ) {
+			case 'proto' :
+				return is_ssl() ? 'https://' : 'http://';
+				break;
+			case 'menu' :
+				$this->_bind();
+				return $this->get_nodes();
+				break;
+		}
+	}
 
+	public function initialize() {
 		$this->user = new stdClass;
-		$this->root = (object) array(
+
+		$this->add_node( array(
 			'id'       => 'root',
 			'group'    => false,
-			'children' => array(),
-		);
+		) );
 
 		if ( is_user_logged_in() ) {
 			/* Populate settings we need for the menu based on the current user. */
@@ -69,13 +74,16 @@
 	 * - parent     - string    - The ID of the parent node. Optional.
 	 * - href       - string    - The link for the item. Optional.
 	 * - group      - boolean   - If the node is a group. Optional. Default false.
-	 * - meta       - array     - Meta data including the following keys: html, class, onclick, target, title.
+	 * - meta       - array     - Meta data including the following keys: html, class, onclick, target, title, tabindex.
 	 */
 	public function add_node( $args ) {
 		// Shim for old method signature: add_node( $parent_id, $menu_obj, $args )
 		if ( func_num_args() >= 3 && is_string( func_get_arg(0) ) )
 			$args = array_merge( array( 'parent' => func_get_arg(0) ), func_get_arg(2) );
 
+		if ( is_object( $args ) )
+			$args = get_object_vars( $args );
+
 		// Ensure we have a valid title.
 		if ( empty( $args['id'] ) ) {
 			if ( empty( $args['title'] ) )
@@ -96,52 +104,90 @@
 		);
 
 		// If the node already exists, keep any data that isn't provided.
-		if ( isset( $this->nodes[ $args['id'] ] ) )
-			$defaults = (array) $this->nodes[ $args['id'] ];
+		if ( $this->get_node( $args['id'] ) )
+			$defaults = get_object_vars( $this->get_node( $args['id'] ) );
 
+		// Do the same for 'meta' items.
+		if ( ! empty( $defaults['meta'] ) && empty( $args['meta'] ) )
+			$args['meta'] = wp_parse_args( $args['meta'], $defaults['meta'] );
+
 		$args = wp_parse_args( $args, $defaults );
-		$args['children'] = array();
 
+		$this->_set_node( $args );
+	}
+
+	final protected function _set_node( $args ) {
 		$this->nodes[ $args['id'] ] = (object) $args;
 	}
 
 	/**
+	 * Gets a node.
+	 *
+	 * @return object Node.
+	 */
+	final public function get_node( $id ) {
+		if ( isset( $this->nodes[ $id ] ) )
+			return $this->nodes[ $id ];
+	}
+
+	final protected function get_nodes() {
+		return $this->nodes;
+	}
+
+	/**
 	 * Add a group to a menu node.
 	 *
+	 * @since 3.3.0
+	 *
 	 * @param array $args - The arguments for each node.
 	 * - id         - string    - The ID of the item.
 	 * - parent     - string    - The ID of the parent node. Optional. Default root.
 	 * - meta       - array     - Meta data including the following keys: class, onclick, target, title.
 	 */
-	public function add_group( $args ) {
+	final public function add_group( $args ) {
 		$args['group'] = true;
 
 		$this->add_node( $args );
 	}
 
+	/**
+	 * Remove a node.
+	 *
+	 * @return object The removed node.
+	 */
 	public function remove_node( $id ) {
+		$node = $this->get_node( $id );
+		$this->_unset_node( $id );
+		return $node;
+	}
+
+	final protected function _unset_node( $id ) {
 		unset( $this->nodes[ $id ] );
 	}
 
 	public function render() {
-		global $is_IE, $is_iphone;
+		$this->_bind();
+		$this->_render();
+	}
 
-		// Link nodes to parents.
-		foreach ( $this->nodes as $node ) {
+	final protected function _bind() {
+		static $bound = false;
+		if ( $bound )
+			return;
+		$bound = true;
 
+		foreach ( $this->get_nodes() as $node ) {
+			if ( 'root' == $node->id )
+				continue;
+
 			// Handle root menu items
 			if ( empty( $node->parent ) ) {
-				$parent = $this->root;
-
-			// If the parent node isn't registered, ignore the node.
-			} elseif ( ! isset( $this->nodes[ $node->parent ] ) ) {
+				$parent = $this->get_node( 'root' );
+			} elseif ( ! $parent = $this->get_node( $node->parent ) ) {
+				// If the parent node isn't registered, ignore the node.
 				continue;
-
-			} else {
-				$parent = $this->nodes[ $node->parent ];
 			}
 
-
 			// Ensure that our tree is of the form "item -> group -> item -> group -> ..."
 			if ( ! $parent->group && ! $node->group ) { // Both are items.
 				// The default group is added here to allow groups that are
@@ -160,10 +206,17 @@
 			// Update the parent ID (it might have changed).
 			$node->parent = $parent->id;
 
+			if ( ! isset( $parent->children ) )
+				$parent->children = array();
+
 			// Add the node to the tree.
 			$parent->children[] = $node;
 		}
+	}
 
+	protected function _render() {
+		global $is_IE, $is_iphone;
+
 		// Add browser classes.
 		// We have to do this here since admin bar shows on the front end.
 		$class = 'nojq nojs';
@@ -181,8 +234,8 @@
 		?>
 		<div id="wpadminbar" class="<?php echo $class; ?>" role="navigation">
 			<div class="quicklinks" role="menubar">
-				<?php foreach ( $this->root->children as $group ) {
-					$this->render_group( $group, 'ab-top-menu' );
+				<?php foreach ( $this->get_node( 'root' )->children as $group ) {
+					$this->_render_group( $group, 'ab-top-menu' );
 				} ?>
 			</div>
 		</div>
@@ -190,7 +243,7 @@
 		<?php
 	}
 
-	private function render_group( $node, $class = '' ) {
+	protected function _render_group( $node, $class = '' ) {
 		if ( ! $node->group )
 			return;
 
@@ -211,37 +264,37 @@
 
 		$is_single_group = count( $groups ) === 1;
 
-
 		// If we don't have any subgroups, render the group.
-		if ( $is_single_group && ! empty( $node->children ) ):
+		if ( $is_single_group && ! empty( $node->children ) ) :
 
 			if ( ! empty( $node->meta['class'] ) )
 				$class .= ' ' . $node->meta['class'];
 
 			?><ul id="<?php echo esc_attr( "wp-admin-bar-{$node->id}" ); ?>" class="<?php echo esc_attr( $class ); ?>" role="menu"><?php
 				foreach ( $node->children as $item ) {
-					$this->render_item( $item );
+					$this->_render_item( $item );
 				}
 			?></ul><?php
 
 		// Wrap the subgroups in a div and render each individual subgroup.
-		elseif ( ! $is_single_group ):
+		elseif ( ! $is_single_group ) :
 			?><div id="<?php echo esc_attr( "wp-admin-bar-{$node->id}-container" ); ?>" class="ab-group-container" role="menu"><?php
 				foreach ( $groups as $group ) {
-					$this->render_group( $group, $class );
+					$this->_render_group( $group, $class );
 				}
 			?></div><?php
 		endif;
 	}
 
-	private function render_item( $node ) {
+	protected function _render_item( $node ) {
 		if ( $node->group )
 			return;
 
-		$is_parent = (bool) $node->children;
-		$has_link  = (bool) $node->href;
-		$tabindex = isset($node->meta['tabindex']) ? (int) $node->meta['tabindex'] : 10;
+		$is_parent = ! empty( $node->children );
+		$has_link  = ! empty( $node->href );
 
+		$tabindex = isset( $node->meta['tabindex'] ) ? (int) $node->meta['tabindex'] : 10;
+
 		$menuclass = '';
 		$aria_attributes = 'tabindex="' . $tabindex . '" role="menuitem"';
 
@@ -269,7 +322,7 @@
 				endif;
 				?>><?php
 			else:
-				?><div class="ab-item ab-empty-item" <?php echo $aria_attributes; ?>><?php
+				?><div class="ab-item ab-empty-item" <?php echo $aria_attributes; ?>"><?php
 			endif;
 
 			echo $node->title;
@@ -283,7 +336,7 @@
 			if ( $is_parent ) :
 				?><div class="ab-sub-wrapper"><?php
 					foreach ( $node->children as $group ) {
-						$this->render_group( $group, 'ab-submenu' );
+						$this->_render_group( $group, 'ab-submenu' );
 					}
 				?></div><?php
 			endif;
@@ -295,11 +348,12 @@
 		</li><?php
 	}
 
-	function recursive_render( $node ) {
-		$this->render_item( $node );
+	public function recursive_render( $id, $node ) {
+		_deprecated_function( __METHOD__, '3.3', 'WP_Admin_Bar::_render_item()' );
+		$this->_render_item( $node );
 	}
 
-	function add_menus() {
+	public function add_menus() {
 		// User related, aligned right.
 		add_action( 'admin_bar_menu', 'wp_admin_bar_my_account_menu', 10 );
 
