Commit 83edb536 by Prem Sichanugrist

Merge pull request #22 from MITx/rewrite-javascript

Rewrite javascript on the main.html
parents 4ecb420f a39c2c19
......@@ -84,7 +84,7 @@ def render_accordion(request,course,chapter,section):
parameter. Returns (initialization_javascript, content)'''
if not course:
course = "6.002 Spring 2012"
toc=content_parser.toc_from_xml(content_parser.course_file(request.user), chapter, section)
active_chapter=1
for i in range(len(toc)):
......@@ -96,8 +96,7 @@ def render_accordion(request,course,chapter,section):
['format_url_params',content_parser.format_url_params],
['csrf',csrf(request)['csrf_token']]] + \
template_imports.items())
return {'init_js':render_to_string('accordion_init.js',context),
'content':render_to_string('accordion.html',context)}
return render_to_string('accordion.html',context)
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
def render_section(request, section):
......@@ -124,8 +123,8 @@ def render_section(request, section):
if 'init_js' not in module:
module['init_js']=''
context={'init':accordion['init_js']+module['init_js'],
'accordion':accordion['content'],
context={'init':module['init_js'],
'accordion':accordion,
'content':module['content'],
'csrf':csrf(request)['csrf_token']}
......@@ -179,8 +178,8 @@ def index(request, course="6.002 Spring 2012", chapter="Using the System", secti
if 'init_js' not in module:
module['init_js']=''
context={'init':accordion['init_js']+module['init_js'],
'accordion':accordion['content'],
context={'init':module['init_js'],
'accordion':accordion,
'content':module['content'],
'csrf':csrf(request)['csrf_token']}
......
......@@ -147,6 +147,7 @@ INSTALLED_APPS = (
'perfstats',
'util',
'masquerade',
'django_jasmine',
# Uncomment the next line to enable the admin:
# 'django.contrib.admin',
# Uncomment the next line to enable admin documentation:
......@@ -347,6 +348,7 @@ PROJECT_ROOT = os.path.dirname(__file__)
TEMPLATE_CONTEXT_PROCESSORS = (
'django.core.context_processors.request',
'django.core.context_processors.static',
'askbot.context.application_settings',
#'django.core.context_processors.i18n',
'askbot.user_messages.context_processors.user_messages',#must be before auth
......@@ -683,3 +685,5 @@ if MAKO_MODULE_DIR == None:
djcelery.setup_loader()
# Jasmine Settings
JASMINE_TEST_DIRECTORY = PROJECT_DIR+'/templates/coffee'
// 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.Courseware = (function() {
function Courseware() {}
Courseware.bind = function() {
return this.Navigation.bind();
};
Courseware.Navigation = (function() {
function Navigation() {}
Navigation.bind = function() {
var navigation;
if ($('#accordion').length) {
navigation = new Navigation;
$('#accordion').bind('accordionchange', navigation.log).accordion({
active: $('#accordion ul:has(li.active)').index('#accordion ul'),
header: 'h3',
autoHeight: false
});
return $('#open_close_accordion a').click(navigation.toggle);
}
};
Navigation.prototype.log = function(event, ui) {
return log_event('accordion', {
newheader: ui.newHeader.text(),
oldheader: ui.oldHeader.text()
});
};
Navigation.prototype.toggle = function() {
return $('.course-wrapper').toggleClass('closed');
};
return Navigation;
})();
return Courseware;
}).call(this);
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')
}
});
Calculator.bind();
Courseware.bind();
FeedbackForm.bind();
return $("a[rel*=leanModal]").leanModal();
});
}).call(this);
$("#accordion").accordion({
active: ${ active_chapter },
header: 'h3',
autoHeight: false,
});
$("#open_close_accordion a").click(function(){
if ($(".course-wrapper").hasClass("closed")){
$(".course-wrapper").removeClass("closed");
} else {
$(".course-wrapper").addClass("closed");
}
});
$('.ui-accordion').bind('accordionchange', function(event, ui) {
var event_data = {'newheader':ui.newHeader.text(),
'oldheader':ui.oldHeader.text()};
log_event('accordion', event_data);
});
CoffeeScript
============
This folder contains the CoffeeScript file that will be compiled to the static
directory. By default, we're compile and merge all the files ending `.coffee`
into `static/js/application.js`.
Install the Compiler
--------------------
CoffeeScript compiler are written in JavaScript. You'll need to install Node and
npm (Node Package Manager) to be able to install the CoffeeScript compiler.
### Mac OS X
Install Node via Homebrew, then use npm:
brew install node
curl http://npmjs.org/install.sh | sh
npm install -g git://github.com/jashkenas/coffee-script.git
(Note that we're using the edge version of CoffeeScript for now, as there was
some issue with directory watching in 1.3.1.)
Try to run `coffee` and make sure you get a coffee prompt.
### Debian/Ubuntu
Conveniently, you can install Node via `apt-get`, then use npm:
sudo apt-get install nodejs npm &&
sudo npm install -g git://github.com/jashkenas/coffee-script.git
Compiling
---------
Run this command in the `mitx` directory to easily make the compiler watch for
changes in your file, and join the result into `application.js`:
coffee -j static/js/application.js -cw templates/coffee/src
Please note that the compiler will not be able to detect the file that get added
after you've ran the command, so you'll need to restart the compiler if there's
a new CoffeeScript file.
Testing
=======
We're also using Jasmine to unit-testing the JavaScript files. All the specs are
written in CoffeeScript for the consistency. Because of the limitation of
`django-jasmine` plugin, we'll need to also running another compiler to compile
the test file.
Using this command to compile the test files:
coffee -cw templates/coffee/spec/*.coffee
Then start the server in debug mode, navigate to http://127.0.0.1:8000/_jasmine
to see the test result.
{
"js_files": [
"/static/js/jquery-1.6.2.min.js",
"/static/js/jquery-ui-1.8.16.custom.min.js",
"/static/js/jquery.leanModal.js"
],
"static_files": [
"js/application.js"
]
}
<div class="course-wrapper">
<header id="open_close_accordion">
<a href="#">close</a>
</header>
<div id="accordion"></div>
</div>
<ul>
<li class="calc-main">
<a href="#" class="calc">Calculator</a>
<div id="calculator_wrapper">
<form id="calculator">
<div class="input-wrapper">
<input type="text" id="calculator_input" />
<div class="help-wrapper">
<a href="#">Hints</a>
<dl class="help"></dl>
</div>
</div>
<input id="calculator_button" type="submit" value="="/>
<input type="text" id="calculator_output" readonly />
</form>
</div>
</li>
</ul>
<div id="feedback_div">
<form>
<label>Subject:</label> <input type="text" id="feedback_subject">
<label>Feedback: </label><textarea id="feedback_message"></textarea>
<input id="feedback_button" type="button" value="Submit">
</form>
</div>
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')
// 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);
describe 'Courseware', ->
describe 'bind', ->
it 'bind the navigation', ->
spyOn Courseware.Navigation, 'bind'
Courseware.bind()
expect(Courseware.Navigation.bind).toHaveBeenCalled()
describe 'Navigation', ->
beforeEach ->
loadFixtures 'accordion.html'
@navigation = new Courseware.Navigation
describe 'bind', ->
describe 'when the #accordion exists', ->
it 'activate the accordion with correct active section', ->
spyOn $.fn, 'accordion'
$('#accordion').append('<ul><li></li></ul><ul><li class="active"></li></ul>')
Courseware.Navigation.bind()
expect($('#accordion').accordion).toHaveBeenCalledWith
active: 1
header: 'h3'
autoHeight: false
it 'binds the accordionchange event', ->
Courseware.Navigation.bind()
expect($('#accordion')).toHandleWith 'accordionchange', @navigation.log
it 'bind the navigation toggle', ->
Courseware.Navigation.bind()
expect($('#open_close_accordion a')).toHandleWith 'click', @navigation.toggle
describe 'when the #accordion does not exists', ->
beforeEach ->
$('#accordion').remove()
it 'does not activate the accordion', ->
spyOn $.fn, 'accordion'
Courseware.Navigation.bind()
expect($('#accordion').accordion).wasNotCalled()
describe 'toggle', ->
it 'toggle closed class on the wrapper', ->
$('.course-wrapper').removeClass('closed')
@navigation.toggle()
expect($('.course-wrapper')).toHaveClass('closed')
@navigation.toggle()
expect($('.course-wrapper')).not.toHaveClass('closed')
describe 'log', ->
beforeEach ->
window.log_event = ->
spyOn window, 'log_event'
it 'submit event log', ->
@navigation.log {}, {
newHeader:
text: -> "new"
oldHeader:
text: -> "old"
}
expect(window.log_event).toHaveBeenCalledWith 'accordion',
newheader: 'new'
oldheader: 'old'
// Generated by CoffeeScript 1.3.2-pre
(function() {
describe('Courseware', function() {
describe('bind', function() {
return it('bind the navigation', function() {
spyOn(Courseware.Navigation, 'bind');
Courseware.bind();
return expect(Courseware.Navigation.bind).toHaveBeenCalled();
});
});
return describe('Navigation', function() {
beforeEach(function() {
loadFixtures('accordion.html');
return this.navigation = new Courseware.Navigation;
});
describe('bind', function() {
describe('when the #accordion exists', function() {
it('activate the accordion with correct active section', function() {
spyOn($.fn, 'accordion');
$('#accordion').append('<ul><li></li></ul><ul><li class="active"></li></ul>');
Courseware.Navigation.bind();
return expect($('#accordion').accordion).toHaveBeenCalledWith({
active: 1,
header: 'h3',
autoHeight: false
});
});
it('binds the accordionchange event', function() {
Courseware.Navigation.bind();
return expect($('#accordion')).toHandleWith('accordionchange', this.navigation.log);
});
return it('bind the navigation toggle', function() {
Courseware.Navigation.bind();
return expect($('#open_close_accordion a')).toHandleWith('click', this.navigation.toggle);
});
});
return describe('when the #accordion does not exists', function() {
beforeEach(function() {
return $('#accordion').remove();
});
return it('does not activate the accordion', function() {
spyOn($.fn, 'accordion');
Courseware.Navigation.bind();
return expect($('#accordion').accordion).wasNotCalled();
});
});
});
describe('toggle', function() {
return it('toggle closed class on the wrapper', function() {
$('.course-wrapper').removeClass('closed');
this.navigation.toggle();
expect($('.course-wrapper')).toHaveClass('closed');
this.navigation.toggle();
return expect($('.course-wrapper')).not.toHaveClass('closed');
});
});
return describe('log', function() {
beforeEach(function() {
window.log_event = function() {};
return spyOn(window, 'log_event');
});
return it('submit event log', function() {
this.navigation.log({}, {
newHeader: {
text: function() {
return "new";
}
},
oldHeader: {
text: function() {
return "old";
}
}
});
return expect(window.log_event).toHaveBeenCalledWith('accordion', {
newheader: 'new',
oldheader: 'old'
});
});
});
});
});
}).call(this);
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'
// 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);
jasmine.getFixtures().fixturesPath = "/_jasmine/fixtures/"
// Generated by CoffeeScript 1.3.2-pre
(function() {
jasmine.getFixtures().fixturesPath = "/_jasmine/fixtures/";
}).call(this);
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)
class window.Courseware
@bind: ->
@Navigation.bind()
class @Navigation
@bind: ->
if $('#accordion').length
navigation = new Navigation
$('#accordion').bind('accordionchange', navigation.log).accordion
active: $('#accordion ul:has(li.active)').index('#accordion ul')
header: 'h3'
autoHeight: false
$('#open_close_accordion a').click navigation.toggle
log: (event, ui) ->
log_event 'accordion',
newheader: ui.newHeader.text()
oldheader: ui.oldHeader.text()
toggle: ->
$('.course-wrapper').toggleClass('closed')
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'
$ ->
$.ajaxSetup
headers : { 'X-CSRFToken': $.cookie 'csrftoken' }
Calculator.bind()
Courseware.bind()
FeedbackForm.bind()
$("a[rel*=leanModal]").leanModal()
......@@ -9,6 +9,7 @@
<script type="text/javascript" src="${ settings.LIB_URL }jquery-1.6.2.min.js"></script>
<script type="text/javascript" src="${ settings.LIB_URL }jquery-ui-1.8.16.custom.min.js"></script>
<script type="text/javascript" src="${ settings.LIB_URL }swfobject/swfobject.js"></script>
<script type="text/javascript" src="/static/js/application.js"></script>
<!--[if lt IE 9]>
<script src="/static/js/html5shiv.js"></script>
......@@ -90,20 +91,18 @@
</nav>
</footer>
<div id="feedback_div" class="leanModal_box">
<h1>Feedback for MITx</h1>
<p>Found a bug? Got an idea for improving our system? Let us know.</p>
<form>
<ol>
<li><label>Subject:</label> <input type="text" id="feedback_subject"></li>
<li><label>Feedback: </label><textarea id="feedback_message"></textarea></li>
<li><input id="feedback_button" type="button" value="Submit"></li>
</ol>
</form>
</div>
<div id="feedback_div" class="leanModal_box">
<h1>Feedback for MITx</h1>
<p>Found a bug? Got an idea for improving our system? Let us know.</p>
<form>
<ol>
<li><label>Subject:</label> <input type="text" id="feedback_subject"></li>
<li><label>Feedback: </label><textarea id="feedback_message"></textarea></li>
<li><input id="feedback_button" type="button" value="Submit"></li>
</ol>
</form>
</div>
<script type="text/javascript" src="${ settings.LIB_URL }jquery.treeview.js"></script>
<script type="text/javascript" src="/static/js/jquery.leanModal.min.js"></script>
......@@ -112,52 +111,7 @@
<script type="text/javascript" src="/static/js/video_player.js"></script>
<script type="text/javascript" src="/static/js/schematic.js"></script>
<script type="text/javascript" src="/static/js/cktsim.js"></script>
<script>
// Feedback form
$(function() {
$("#feedback_button").click(function(){
postJSON("/send_feedback", {"subject":$("#feedback_subject").attr("value"),
"url":document.URL,
"message":$("#feedback_message").attr("value")},
function(data){
$("#feedback_subject").attr("value","");
$("#feedback_message").attr("value","");
$("#feedback_div").html("Feedback submitted. Thank you");
});
});
// Calculator
$(".calc").click(function(){
$("li.calc-main").toggleClass('open');
$("#calculator_wrapper #calculator_input").focus();
$(this).toggleClass("closed");
return false;
});
$("div.help-wrapper a").hover(function(){
$(".help").toggleClass("shown");
});
$("div.help-wrapper a").click(function(){
return false;
});
$("form#calculator").submit(function(e){
e.preventDefault();
$.getJSON("/calculate", {"equation":$("#calculator_input").attr("value")},
function(data){
$("#calculator_output").attr("value",data.result);
});
});
$("a[rel*=leanModal]").leanModal();
});
</script>
<%block name="js_extra"/>
<%block name="js_extra"/>
</body>
</html>
......@@ -77,6 +77,10 @@ if settings.ASKBOT_ENABLED:
# url(r'^robots.txt$', include('robots.urls')),
)
if settings.DEBUG:
## Jasmine
urlpatterns=urlpatterns + (url(r'^_jasmine/', include('django_jasmine.urls')),)
urlpatterns = patterns(*urlpatterns)
if settings.DEBUG:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment