Composite View: Menu

A menu built w/ Marionette

HTML

<script src="http://documentcloud.github.com/underscore/underscore-min.js"></script>
<script src="http://documentcloud.github.com/backbone/backbone-min.js"></script>
<script src="https://raw.github.com/derickbailey/backbone.marionette/master/lib/backbone.marionette.js"></script>
<script src="http://craigsworks.com/projects/qtip2/packages/nightly/jquery.qtip.js"></script>
<link rel="stylesheet" href="http://craigsworks.com/projects/qtip2/packages/nightly/jquery.qtip.css">
<div id="menu">
</div>

<div id="output">
</div>

<div id="option">
    <button id="additem">Add Dynamic Menu</button>
</div>

CSS

body {
    margin: 100px;
}

#output {
    clear: both;
    padding-top: 90px;
}

ul.nested {
    display: none;
}

.toolbar li {
    font-family: arial, sans-serif;
    float: left;
    -webkit-user-select: none;
    cursor: pointer;
    outline: 0;
    border: 1px solid rgba(0, 0, 0, 0.1);
    display: inline-block;
    -webkit-transition: border-color .218s;
    text-decoration: none !important; 
    text-shadow: 0 1px 0 #FFFFFF;
    color: #666;
    background-image: -webkit-linear-gradient(top,#f5f5f5,#f1f1f1);
    background-image: -moz-linear-gradient(top,#f5f5f5,#f1f1f1);
    background-image: -ms-linear-gradient(top,#f5f5f5,#f1f1f1);
    background-image: -o-linear-gradient(top,#f5f5f5,#f1f1f1);
    background-image: linear-gradient(top,#f5f5f5,#f1f1f1);
    padding: 0 15px;
    
}

.toolbar li:hover {
    background-image: -webkit-linear-gradient(top,#f8f8f8,#f1f1f1);
    background-image: -moz-linear-gradient(top,#f8f8f8,#f1f1f1);
    background-image: -ms-linear-gradient(top,#f8f8f8,#f1f1f1);
    background-image: -o-linear-gradient(top,#f8f8f8,#f1f1f1);
    background-image: linear-gradient(top,#f8f8f8,#f1f1f1);
    border: 1px solid #939393; 
}

.toolbar li:last-child {
    -moz-border-radius-topright: 4px;
    -webkit-border-top-right-radius: 4px;
    border-top-right-radius: 4px;
    -moz-border-radius-bottomright: 4px;
    -webkit-border-bottom-right-radius: 4px;
    border-bottom-right-radius: 4px;
}

.toolbar li:first-child {
    -moz-border-radius-topleft: 4px;
    -webkit-border-top-left-radius: 4px;
    border-top-left-radius: 4px;
    -moz-border-radius-bottomleft: 4px;
    -webkit-border-bottom-left-radius: 4px;
    border-bottom-left-radius: 4px;
}

.ui-tooltip-nav {
    min-width: 150px;
    max-width: 240px;
}

.ui-tooltip-menu {
    -webkit-box-shadow: 0 2px 4px rgba(0,0,0,0.2);
    -moz-box-shadow: 0 2px 4px rgba(0,0,0,0.2);
    box-shadow: 0 2px 4px rgba(0,0,0,0.2);
    -webkit-transition: opacity .218s;
    -moz-transition: opacity...

JavaScript

// Created by Anton Vishnyak


// ----------------------------------------------------------------
// Definition of a menu item
//
// Sub-menus can be defined using a "submenu" attribute
// ----------------------------------------------------------------
var MenuItem = Backbone.Model.extend({
    defaults: {
        // true, false or null/undefined;
        disabled: false,

        // true, false or null/undefined;
        checked: false,

        // 'std', 'separator', 'group-header' or null/undefined;        
        type: "std",

        icon: null,

        text: "",

        // This will be the event that get triggered
        action: false
    },

    initialize: function() {
        var nodes = this.get("submenu");

        if (nodes) {
            this.nodes = new MenuItemCollection(nodes);

            this.unset("submenu");
        }
    }
});



// ----------------------------------------------------------------
// A collection of menu items
// ----------------------------------------------------------------
var MenuItemCollection = Backbone.Collection.extend({
    model: MenuItem
});




// ----------------------------------------------------------------
// Menu view renders individual menu items recursively
// ----------------------------------------------------------------
var MenuViewController = Backbone.Marionette.CompositeView.extend({
    tagName: "li",

    isFirstLevel: false,

    itemView: MenuViewController,

    initialize: function() {
        // grab the child collection from the parent model
        // so that we can render the collection as children
        // of this parent node
        this.collection = this.model.nodes;

        // Bubble up a view refresh to the top
        this.on("item:added", function(view) {
            this.bindTo(view, 'refresh', this.renderTree, this);
        });
    },

    initialEvents: function() {
        // Bind to any changes in the collection and redraw the entire menu chain
        if...