diff --git a/static/js/application.js b/static/js/application.js
new file mode 100644
index 0000000000..1591b6fc44
--- /dev/null
+++ b/static/js/application.js
@@ -0,0 +1,75 @@
+// Generated by CoffeeScript 1.3.2-pre
+(function() {
+
+ window.Calculator = (function() {
+
+ function Calculator() {}
+
+ Calculator.bind = function() {
+ var calculator;
+ calculator = new Calculator;
+ $('.calc').click(calculator.toggle);
+ $('form#calculator').submit(calculator.calculate).submit(function(e) {
+ return e.preventDefault();
+ });
+ return $('div.help-wrapper a').hover(calculator.helpToggle).click(function(e) {
+ return e.preventDefault();
+ });
+ };
+
+ Calculator.prototype.toggle = function() {
+ $('li.calc-main').toggleClass('open');
+ $('#calculator_wrapper #calculator_input').focus();
+ return $('.calc').toggleClass('closed');
+ };
+
+ Calculator.prototype.helpToggle = function() {
+ return $('.help').toggleClass('shown');
+ };
+
+ Calculator.prototype.calculate = function() {
+ return $.getJSON('/calculate', {
+ equation: $('#calculator_input').val()
+ }, function(data) {
+ return $('#calculator_output').val(data.result);
+ });
+ };
+
+ return Calculator;
+
+ })();
+
+ window.FeedbackForm = (function() {
+
+ function FeedbackForm() {}
+
+ FeedbackForm.bind = function() {
+ return $('#feedback_button').click(function() {
+ var data;
+ data = {
+ subject: $('#feedback_subject').val(),
+ message: $('#feedback_message').val(),
+ url: window.location.href
+ };
+ return $.post('/send_feedback', data, function() {
+ return $('#feedback_div').html('Feedback submitted. Thank you');
+ }, 'json');
+ });
+ };
+
+ return FeedbackForm;
+
+ })();
+
+ $(function() {
+ $.ajaxSetup({
+ headers: {
+ 'X-CSRFToken': $.cookie('csrftoken')
+ }
+ });
+ FeedbackForm.bind();
+ Calculator.bind();
+ return $("a[rel*=leanModal]").leanModal();
+ });
+
+}).call(this);
diff --git a/templates/coffee/files.json b/templates/coffee/files.json
new file mode 100644
index 0000000000..44494e8040
--- /dev/null
+++ b/templates/coffee/files.json
@@ -0,0 +1,8 @@
+{
+ "js_files": [
+ "/static/js/jquery-1.6.2.min.js"
+ ],
+ "static_files": [
+ "js/application.js"
+ ]
+}
diff --git a/templates/coffee/fixtures/calculator.html b/templates/coffee/fixtures/calculator.html
new file mode 100644
index 0000000000..61c6f5e153
--- /dev/null
+++ b/templates/coffee/fixtures/calculator.html
@@ -0,0 +1,18 @@
+
diff --git a/templates/coffee/fixtures/feedback_form.html b/templates/coffee/fixtures/feedback_form.html
new file mode 100644
index 0000000000..672663fe10
--- /dev/null
+++ b/templates/coffee/fixtures/feedback_form.html
@@ -0,0 +1,7 @@
+
+
+
diff --git a/templates/coffee/spec/calculator_spec.coffee b/templates/coffee/spec/calculator_spec.coffee
new file mode 100644
index 0000000000..5c3fde5e2d
--- /dev/null
+++ b/templates/coffee/spec/calculator_spec.coffee
@@ -0,0 +1,68 @@
+describe 'Calculator', ->
+ beforeEach ->
+ loadFixtures 'calculator.html'
+ @calculator = new Calculator
+
+ describe 'bind', ->
+ beforeEach ->
+ Calculator.bind()
+
+ it 'bind the calculator button', ->
+ expect($('.calc')).toHandleWith 'click', @calculator.toggle
+
+ it 'bind the help button', ->
+ # These events are bind by $.hover()
+ expect($('div.help-wrapper a')).toHandleWith 'mouseenter', @calculator.helpToggle
+ expect($('div.help-wrapper a')).toHandleWith 'mouseleave', @calculator.helpToggle
+
+ it 'prevent default behavior on help button', ->
+ $('div.help-wrapper a').click (e) ->
+ expect(e.isDefaultPrevented()).toBeTruthy()
+ $('div.help-wrapper a').click()
+
+ it 'bind the calculator submit', ->
+ expect($('form#calculator')).toHandleWith 'submit', @calculator.calculate
+
+ it 'prevent default behavior on form submit', ->
+ $('form#calculator').submit (e) ->
+ expect(e.isDefaultPrevented()).toBeTruthy()
+ e.preventDefault()
+ $('form#calculator').submit()
+
+ describe 'toggle', ->
+ it 'toggle the calculator and focus the input', ->
+ spyOn $.fn, 'focus'
+ @calculator.toggle()
+
+ expect($('li.calc-main')).toHaveClass('open')
+ expect($('#calculator_wrapper #calculator_input').focus).toHaveBeenCalled()
+
+ it 'toggle the close button on the calculator button', ->
+ @calculator.toggle()
+ expect($('.calc')).toHaveClass('closed')
+
+ @calculator.toggle()
+ expect($('.calc')).not.toHaveClass('closed')
+
+ describe 'helpToggle', ->
+ it 'toggle the help overlay', ->
+ @calculator.helpToggle()
+ expect($('.help')).toHaveClass('shown')
+
+ @calculator.helpToggle()
+ expect($('.help')).not.toHaveClass('shown')
+
+ describe 'calculate', ->
+ beforeEach ->
+ $('#calculator_input').val '1+2'
+ spyOn($, 'getJSON').andCallFake (url, data, callback) ->
+ callback({ result: 3 })
+ @calculator.calculate()
+
+ it 'send data to /calculate', ->
+ expect($.getJSON).toHaveBeenCalledWith '/calculate',
+ equation: '1+2'
+ , jasmine.any(Function)
+
+ it 'update the calculator output', ->
+ expect($('#calculator_output').val()).toEqual('3')
diff --git a/templates/coffee/spec/calculator_spec.js b/templates/coffee/spec/calculator_spec.js
new file mode 100644
index 0000000000..6e0f8a0dab
--- /dev/null
+++ b/templates/coffee/spec/calculator_spec.js
@@ -0,0 +1,80 @@
+// Generated by CoffeeScript 1.3.2-pre
+(function() {
+
+ describe('Calculator', function() {
+ beforeEach(function() {
+ loadFixtures('calculator.html');
+ return this.calculator = new Calculator;
+ });
+ describe('bind', function() {
+ beforeEach(function() {
+ return Calculator.bind();
+ });
+ it('bind the calculator button', function() {
+ return expect($('.calc')).toHandleWith('click', this.calculator.toggle);
+ });
+ it('bind the help button', function() {
+ expect($('div.help-wrapper a')).toHandleWith('mouseenter', this.calculator.helpToggle);
+ return expect($('div.help-wrapper a')).toHandleWith('mouseleave', this.calculator.helpToggle);
+ });
+ it('prevent default behavior on help button', function() {
+ $('div.help-wrapper a').click(function(e) {
+ return expect(e.isDefaultPrevented()).toBeTruthy();
+ });
+ return $('div.help-wrapper a').click();
+ });
+ it('bind the calculator submit', function() {
+ return expect($('form#calculator')).toHandleWith('submit', this.calculator.calculate);
+ });
+ return it('prevent default behavior on form submit', function() {
+ $('form#calculator').submit(function(e) {
+ expect(e.isDefaultPrevented()).toBeTruthy();
+ return e.preventDefault();
+ });
+ return $('form#calculator').submit();
+ });
+ });
+ describe('toggle', function() {
+ it('toggle the calculator and focus the input', function() {
+ spyOn($.fn, 'focus');
+ this.calculator.toggle();
+ expect($('li.calc-main')).toHaveClass('open');
+ return expect($('#calculator_wrapper #calculator_input').focus).toHaveBeenCalled();
+ });
+ return it('toggle the close button on the calculator button', function() {
+ this.calculator.toggle();
+ expect($('.calc')).toHaveClass('closed');
+ this.calculator.toggle();
+ return expect($('.calc')).not.toHaveClass('closed');
+ });
+ });
+ describe('helpToggle', function() {
+ return it('toggle the help overlay', function() {
+ this.calculator.helpToggle();
+ expect($('.help')).toHaveClass('shown');
+ this.calculator.helpToggle();
+ return expect($('.help')).not.toHaveClass('shown');
+ });
+ });
+ return describe('calculate', function() {
+ beforeEach(function() {
+ $('#calculator_input').val('1+2');
+ spyOn($, 'getJSON').andCallFake(function(url, data, callback) {
+ return callback({
+ result: 3
+ });
+ });
+ return this.calculator.calculate();
+ });
+ it('send data to /calculate', function() {
+ return expect($.getJSON).toHaveBeenCalledWith('/calculate', {
+ equation: '1+2'
+ }, jasmine.any(Function));
+ });
+ return it('update the calculator output', function() {
+ return expect($('#calculator_output').val()).toEqual('3');
+ });
+ });
+ });
+
+}).call(this);
diff --git a/templates/coffee/spec/feedback_form_spec.coffee b/templates/coffee/spec/feedback_form_spec.coffee
new file mode 100644
index 0000000000..191645b3d3
--- /dev/null
+++ b/templates/coffee/spec/feedback_form_spec.coffee
@@ -0,0 +1,28 @@
+describe 'FeedbackForm', ->
+ beforeEach ->
+ loadFixtures 'feedback_form.html'
+
+ describe 'bind', ->
+ beforeEach ->
+ FeedbackForm.bind()
+ spyOn($, 'post').andCallFake (url, data, callback, format) ->
+ callback()
+
+ it 'binds to the #feedback_button', ->
+ expect($('#feedback_button')).toHandle 'click'
+
+ it 'post data to /send_feedback on click', ->
+ $('#feedback_subject').val 'Awesome!'
+ $('#feedback_message').val 'This site is really good.'
+ $('#feedback_button').click()
+
+ expect($.post).toHaveBeenCalledWith '/send_feedback', {
+ subject: 'Awesome!'
+ message: 'This site is really good.'
+ url: window.location.href
+ }, jasmine.any(Function), 'json'
+
+ it 'replace the form with a thank you message', ->
+ $('#feedback_button').click()
+
+ expect($('#feedback_div').html()).toEqual 'Feedback submitted. Thank you'
diff --git a/templates/coffee/spec/feedback_form_spec.js b/templates/coffee/spec/feedback_form_spec.js
new file mode 100644
index 0000000000..2815cd73e5
--- /dev/null
+++ b/templates/coffee/spec/feedback_form_spec.js
@@ -0,0 +1,35 @@
+// Generated by CoffeeScript 1.3.2-pre
+(function() {
+
+ describe('FeedbackForm', function() {
+ beforeEach(function() {
+ return loadFixtures('feedback_form.html');
+ });
+ return describe('bind', function() {
+ beforeEach(function() {
+ FeedbackForm.bind();
+ return spyOn($, 'post').andCallFake(function(url, data, callback, format) {
+ return callback();
+ });
+ });
+ it('binds to the #feedback_button', function() {
+ return expect($('#feedback_button')).toHandle('click');
+ });
+ it('post data to /send_feedback on click', function() {
+ $('#feedback_subject').val('Awesome!');
+ $('#feedback_message').val('This site is really good.');
+ $('#feedback_button').click();
+ return expect($.post).toHaveBeenCalledWith('/send_feedback', {
+ subject: 'Awesome!',
+ message: 'This site is really good.',
+ url: window.location.href
+ }, jasmine.any(Function), 'json');
+ });
+ return it('replace the form with a thank you message', function() {
+ $('#feedback_button').click();
+ return expect($('#feedback_div').html()).toEqual('Feedback submitted. Thank you');
+ });
+ });
+ });
+
+}).call(this);
diff --git a/templates/coffee/spec/helper.coffee b/templates/coffee/spec/helper.coffee
new file mode 100644
index 0000000000..1f27e257c2
--- /dev/null
+++ b/templates/coffee/spec/helper.coffee
@@ -0,0 +1 @@
+jasmine.getFixtures().fixturesPath = "/_jasmine/fixtures/"
diff --git a/templates/coffee/spec/helper.js b/templates/coffee/spec/helper.js
new file mode 100644
index 0000000000..23b980d525
--- /dev/null
+++ b/templates/coffee/spec/helper.js
@@ -0,0 +1,6 @@
+// Generated by CoffeeScript 1.3.2-pre
+(function() {
+
+ jasmine.getFixtures().fixturesPath = "/_jasmine/fixtures/";
+
+}).call(this);
diff --git a/templates/coffee/src/calculator.coffee b/templates/coffee/src/calculator.coffee
new file mode 100644
index 0000000000..7d62f5a794
--- /dev/null
+++ b/templates/coffee/src/calculator.coffee
@@ -0,0 +1,20 @@
+class window.Calculator
+ @bind: ->
+ calculator = new Calculator
+ $('.calc').click calculator.toggle
+ $('form#calculator').submit(calculator.calculate).submit (e) ->
+ e.preventDefault()
+ $('div.help-wrapper a').hover(calculator.helpToggle).click (e) ->
+ e.preventDefault()
+
+ toggle: ->
+ $('li.calc-main').toggleClass 'open'
+ $('#calculator_wrapper #calculator_input').focus()
+ $('.calc').toggleClass 'closed'
+
+ helpToggle: ->
+ $('.help').toggleClass 'shown'
+
+ calculate: ->
+ $.getJSON '/calculate', { equation: $('#calculator_input').val() }, (data) ->
+ $('#calculator_output').val(data.result)
diff --git a/templates/coffee/src/feedback_form.coffee b/templates/coffee/src/feedback_form.coffee
new file mode 100644
index 0000000000..bbb5c09365
--- /dev/null
+++ b/templates/coffee/src/feedback_form.coffee
@@ -0,0 +1,10 @@
+class window.FeedbackForm
+ @bind: ->
+ $('#feedback_button').click ->
+ data =
+ subject: $('#feedback_subject').val()
+ message: $('#feedback_message').val()
+ url: window.location.href
+ $.post '/send_feedback', data, ->
+ $('#feedback_div').html 'Feedback submitted. Thank you'
+ ,'json'
diff --git a/templates/coffee/src/main.coffee b/templates/coffee/src/main.coffee
new file mode 100644
index 0000000000..dc8c4a0622
--- /dev/null
+++ b/templates/coffee/src/main.coffee
@@ -0,0 +1,7 @@
+$ ->
+ $.ajaxSetup
+ headers : { 'X-CSRFToken': $.cookie 'csrftoken' }
+
+ Calculator.bind()
+ FeedbackForm.bind()
+ $("a[rel*=leanModal]").leanModal()
diff --git a/templates/main.html b/templates/main.html
index 7078ed64d3..59a1ed7bd0 100644
--- a/templates/main.html
+++ b/templates/main.html
@@ -9,6 +9,7 @@
+