VersionOne API 101: An Agile User Story Editor in 101 Lines

(Comment-free version) Build a simple, open-source agile user story editor in exactly 101 lines of JavaScript using Backbone.js, Backbone Forms, jQuery, and the VersionOne REST API! You could easily adapt this to save your user story into any tool of your choice.

by JoshGough

HTML

<script src="http://documentcloud.github.com/underscore/underscore-min.js"></script>
<script src="http://documentcloud.github.com/backbone/backbone-min.js"></script>
<link rel="stylesheet" href="http://twitter.github.com/bootstrap/assets/css/bootstrap.css">
<script src="https://raw.github.com/powmedia/backbone-forms/master/distribution/backbone-forms.js"></script>
<script src="https://raw.github.com/powmedia/backbone-forms/master/distribution/editors/list.js"></script>
<script src="https://raw.github.com/powmedia/backbone-forms/master/distribution/adapters/backbone.bootstrap-modal.js"></script>
<script src="https://raw.github.com/powmedia/backbone-forms/master/distribution/templates/bootstrap.js"></script>
<html>
    <head>
        <title>Backbone-Fortified VersionOne Story Editor</title>
    </head>
    <body>
        <h1>Backbone-Fortified VersionOne Story Editor</h1>
        <hr/>
        <div id="editor">
            <form id="editorForm">
                <h4>Story Details</h4>
                <hr />
              	<div id="editorFields"></div>
            </form>
            <button id="save">Save Story</button> <span id="message"></span><span id="error"></span>
        </div>
        <h3>Enter a Story ID</h3>
        <input type="text" id="storyId" value="1154" /> (Hint: use 1154 if don't know another...)
        <br />
        <button id="storyGet">Load Story</button>
        <hr/>
        Visit the <a href="http://community.versionone.com/default.aspx">VersionOne Community</a> for more open source tools and APIs. Download code and <b>get involved</b> at <a href="http://www.github.com/VersionOne" target="_blank">VersionOne on GitHub</a>!
        <br/>
    </body>
</html>

CSS

body 
{
    padding: 5px;
    font-family: sans-serif;
}  

#editor
{
    padding: 10px;
    border: 1px solid darkblue;
    background: whitesmoke;
    display: none;
}

h4 { 
    color: #666666;
    font-style: italic;
}


label 
{
    color: darkblue;
}

textarea 
{
  height:100px;
}

#message 
{
  margin-top: 5px;
  color: darkgreen;
}

#storyIdLabel {
    font-weight: bold;
}

#message {
    display: none;
    font-weight: bold;
    color: darkgreen;
}

#error {
    display: none;
    font-weight: bold;
    color: red;
}

JavaScript

var StoryFormSchema = {
  Name: { validators: ['required'] },
  Description: 'TextArea',
  Benefits: 'TextArea',
  Estimate: 'Number',
  RequestedBy: {}
};

var storyForm = null;

var urlRoot = 'http://eval.versionone.net/platformtest/rest-1.v1/Data/Story/';
var headers = { Authorization: 'Basic ' + btoa('admin:admin'), Accept: 'haljson' };
var defaultFetchOptions = { dataType: 'json', headers: headers };
var defaultSaveOptions = { contentType: 'haljson', patch: true, headers: headers };

Backbone.emulateHTTP = true;

var StoryModel = Backbone.Model.extend({
  urlRoot: urlRoot,
  url: function () {
    if (this.hasChanged() && !this.isNew()) return this.urlRoot + this.id;
    return this.urlRoot + this.id + '?sel=' + _.keys(storyForm.schema).join(',');
  },
  fetch: function (options) {
    options || (options = {});
    _.defaults(options, defaultFetchOptions);
    return Backbone.Model.prototype.fetch.call(this, options);
  },
  save: function (attributes, options) {
    options || (options = {});
    _.defaults(options, defaultSaveOptions);
    return Backbone.Model.prototype.save.call(this, attributes, options);
  }
});

var storyModel = new StoryModel;

function createForm(model) {
  var finish, settings;
  finish = function () {
    storyForm = new Backbone.Form(settings);
    $('#editorFields').empty();
    $('#editorFields').append(storyForm.render().el);
    if (model) {
      return $('#editor').fadeIn();
    }
  };
  settings = {
    schema: StoryFormSchema
  };
  if (model) {
    return model.fetch().done(function (data) {
      settings.model = model;
      return finish();
    });
  } else {
    return finish();
  }
};

function storyLoad() {
  storyModel.id = $('#storyId').val();
  if (storyModel.id === '') {
    alert('Please enter a story id first');
    return;
  }
  return createForm(storyModel);
};

function storySave() {
  if (storyForm.validate() != null) return;
  storyForm.commit();
  return...