+ final protected function _bind() {
+ if ( $this->bound )
+ return;
+
+ // Add the root node.
+ // Clear it first, just in case. Don't mess with The Root.
+ $this->remove_node( 'root' );
+ $this->add_node( array(
+ 'id' => 'root',
+ 'group' => false,
+ ) );
+
+ // Normalize nodes: define internal 'children' and 'type' properties.
+ foreach ( $this->_get_nodes() as $node ) {
+ $node->children = array();
+ $node->type = ( $node->group ) ? 'group' : 'item';
+ unset( $node->group );
+
+ // The Root wants your orphans. No lonely items allowed.
+ if ( ! $node->parent )
+ $node->parent = 'root';
+ }
+
+ foreach ( $this->_get_nodes() as $node ) {
+ if ( 'root' == $node->id )
+ continue;
+
+ // Fetch the parent node. If it isn't registered, ignore the node.
+ if ( ! $parent = $this->_get_node( $node->parent ) ) {
+ continue;
+ }
+
+ // Generate the group class (we distinguish between top level and other level groups).
+ $group_class = ( $node->parent == 'root' ) ? 'ab-top-menu' : 'ab-submenu';
+
+ if ( $node->type == 'group' ) {
+ if ( empty( $node->meta['class'] ) )
+ $node->meta['class'] = '';
+ $node->meta['class'] .= ' ' . $group_class;
+ }
+
+ // Items in items aren't allowed. Wrap nested items in 'default' groups.
+ if ( $parent->type == 'item' && $node->type == 'item' ) {
+ $default_id = $parent->id . '-default';
+ $default = $this->_get_node( $default_id );
+
+ // The default group is added here to allow groups that are
+ // added before standard menu items to render first.
+ if ( ! $default ) {
+ // Use _set_node because add_node can be overloaded.
+ // Make sure to specify default settings for all properties.
+ $this->_set_node( array(
+ 'id' => $default_id,
+ 'parent' => $parent->id,
+ 'type' => 'group',
+ 'children' => array(),
+ 'meta' => array(
+ 'class' => $group_class,
+ ),
+ 'title' => false,
+ 'href' => false,
+ ) );
+ $default = $this->_get_node( $default_id );
+ $parent->children[] = $default;
+ }
+ $parent = $default;
+
+ // Groups in groups aren't allowed. Add a special 'container' node.
+ // The container will invisibly wrap both groups.
+ } elseif ( $parent->type == 'group' && $node->type == 'group' ) {
+ $container_id = $parent->id . '-container';
+ $container = $this->_get_node( $container_id );
+
+ // We need to create a container for this group, life is sad.
+ if ( ! $container ) {
+ // Use _set_node because add_node can be overloaded.
+ // Make sure to specify default settings for all properties.
+ $this->_set_node( array(
+ 'id' => $container_id,
+ 'type' => 'container',
+ 'children' => array( $parent ),
+ 'parent' => false,
+ 'title' => false,
+ 'href' => false,
+ 'meta' => array(),
+ ) );
+
+ $container = $this->_get_node( $container_id );
+
+ // Link the container node if a grandparent node exists.
+ $grandparent = $this->_get_node( $parent->parent );
+
+ if ( $grandparent ) {
+ $container->parent = $grandparent->id;
+
+ $index = array_search( $parent, $grandparent->children, true );
+ if ( $index === false )
+ $grandparent->children[] = $container;
+ else
+ array_splice( $grandparent->children, $index, 1, array( $container ) );
+ }
+
+ $parent->parent = $container->id;
+ }
+
+ $parent = $container;
+ }
+
+ // Update the parent ID (it might have changed).
+ $node->parent = $parent->id;
+
+ // Add the node to the tree.
+ $parent->children[] = $node;
+ }
+
+ $root = $this->_get_node( 'root' );
+ $this->bound = true;
+ return $root;