JSFiddle - React, Tailwind, and code Playground

by hoffmanc

HTML

<script src="http://documentcloud.github.com/underscore/underscore.js"></script>
<script src="http://documentcloud.github.com/backbone/backbone.js"></script>
<script src="http://cloud.github.com/downloads/wycats/handlebars.js/handlebars-1.0.0.beta.6.js"></script>
<h1>Survey Designer</h1>
    <p>This is a prototype version of the Survey Designer.</p>
    <div id='items'></div>

<script id="surveyItems-template" type="text/x-handlebars-template">
    {{#each Items}}
        <li data-id='{{ID}}'>                    
            <span class='handle'>↕</span>
            <span>{{Name}}</span>       
            {{#if Items}}
                <ul data-id='{{ID}}'>
                    {{#each Items}}
                        <li data-id='{{ID}}'>
                            <div style='float: right;'>{{QADomain}}</div>
                            <span class='handle'>↕</span>
                            <span>{{Name}}</span>
                        </li>
                    {{/each}}
                </ul>
            {{/if}}
        </li>
    {{/each}}
</script>

CSS

ul {
  list-style: none;
  margin: 10px;    
}

span.handle{
  cursor: move;
  margin-right: 10px;
}

h1 {
    font-size: 18pt;
}

.ui-state-hover {
    background-color: #990;
    height: 1em;
}

JavaScript

SurveyDesigner = {};

var SurveyTree = SurveyDesigner.SurveyTree = Backbone.Model.extend({
    idAttribute: "SurveyID",
    initialize: function () {
        this.Items = this.get("Items");
    },
    // find each item by it's ID and move it!
    move: function (itemID, newParentID, pos) {
        // rebuild and return dictionary for each move
        // (this seems to be better than trying to maintain a client-only attribute)
        var itemDictionary = this.getItemDictionary();
        var item = itemDictionary[itemID];
        var oldParent = itemDictionary[item.ParentID];
        var newParent = itemDictionary[newParentID];

        // check to see if either the newParent is root (ID=0), or
        // the item being moved matches the new parent's QADomain
        if (newParent.ID !== 0 && newParent.QADomain !== item.QADomain) {
            throw new Error("item does not match the QADomain of the container.");
        }

        // check to see if the old parent will be made empty when this
        // item is moved out of it
        if (oldParent.ID !== 0 && oldParent.Items.length === 1) {
            // remove the oldParent from the root
            var root = itemDictionary[0];
            root.Items.splice(root.Items.indexOf(oldParent), 1);
        }

        // check if we are nesting matrix headers
        if (newParent.ID !== 0 && item.Type.indexOf('Header') !== -1) {
            throw new Error("cannot nest containers.");
        }

        // remove the item at this position (can't use handy Collection helpers, 
        // since this is just an Array)
        oldParent.Items.splice(oldParent.Items.indexOf(item), 1);

        // add new item (again, no Collection niceties)
        newParent.Items.splice(pos, 0, item);

        // set the ParentID of the item to the new parent's ID
        item.ParentID = newParent.ID;
    },
    // create a key-value dictionary for keeping references to 
    // relocation candidates
    getItemDictionary: function ()...