JSFiddle - React, Tailwind, and code Playground

by hoffmanc

HTML

<script src="http://documentcloud.github.com/underscore/underscore.js"></script>
<script src="https://github.com/downloads/wycats/handlebars.js/handlebars-1.0.0.beta.6.js"></script>
<script src="http://documentcloud.github.com/backbone/backbone.js"></script>
<script src="https://raw.github.com/derickbailey/backbone.marionette/master/src/backbone.marionette.js"></script>
<html>
    <body>
        <h1>Survey Designer</h1>
        <p>This page is a testing ground for building a new 
            survey designer interface, using Backbone.js</p>
    
        <div id='questions'></div>
        <script type='text/template' id="question-template">
            <span class='handle'>↕</span>
            {{defaultText}}
            <ul data-id='{{id}}'></ul>
        </script>
    </body>
</html>

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

// customizations
// http://derickbailey.github.com/backbone.marionette/#backbone-marionette-renderer/caching-pre-compiled-templates
Backbone.Marionette.TemplateCache.loadTemplate = function(template, callback) {
    // pre-compile the template and store that in the cache.
    var compiledTemplate = Handlebars.compile($(template).html());
    callback.call(this, compiledTemplate);
};

Backbone.Marionette.Renderer.renderTemplate = function(template, data) {
    // because `template` is the pre-compiled template object,
    // we only need to execute the template with the data
    return template(data);
};

// MODELS
var QuestionTreeNode = Backbone.Model.extend({
    initialize: function() {
        var questions = this.get("questions");
        if (questions) {
            parent = this;
            this.questions = new QuestionTreeNodeCollection(
                _.map(questions, function(q) { return _.extend(q, {parent: parent}); })
            );
            this.unset("questions");
        }
    }
});

// COLLECTIONS
var QuestionTreeNodeCollection = Backbone.Collection.extend({
    model: QuestionTreeNode,
    all: function() {
        return this.reduce(function(memo, q) {
            if (_.isUndefined(q.questions)) {
                return memo.concat([q]);
            } else {
                return memo.concat([q]).concat(q.questions.models);
            }
        }, []);
    },
    findById: function(id) {
        return _.find(this.all(), function(q) {
            return q.get('id') == id;
        });        
    },
    reorderQuestion: function(question, parentId, pos) {
        var newParent = this.findById(parentId);
        
        // validation goes here??
         
        // remove association to last parent, or this.collection
        if (!_.isUndefined(question.get('parent'))) {
            question.get('parent').questions.remove(question);
            question.unset('parent');
        } else {
            this.remove(question);
        }
      ...