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>
<script src="https://raw.github.com/PaulUithol/Backbone-relational/master/backbone-relational.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.RelationalModel.extend({
relations: [{
type: Backbone.HasMany,
key: 'questions',
relatedModel: 'QuestionTreeNode'
}]
});
// 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);
}
// remap to new parent or this.collection
if (!_.isUndefined(newParent)) {
question.set('parent', newParent);
newParent.questions.add(question, { at: pos });
...