Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Nanne Baars 2015-08-09 07:46:43 +02:00
commit 87a621fa27
14 changed files with 180 additions and 79 deletions

View File

@ -98,29 +98,21 @@
<div class="col-md-8"> <div class="col-md-8">
<div class="col-md-12" align="left"> <div class="col-md-12" align="left">
<div class="panel" id="help-controls"> <div class="panel" id="help-controls">
<!-- <div id="help-buttons" class="panel-body"> -->
<!-- <button type="button" id="showSourceBtn" ng-show="showSource" class="btn btn-primary btn-xs" onclick="showLessonSource()">Java [Source]</button>
<button type="button" id="showSolutionBtn" class="btn btn-primary btn-xs" onclick="showLessonSolution()">Solution</button>
<button type="button" id="showPlanBtn" class="btn btn-primary btn-xs" onclick="showLessonPlan()">Lesson Plan</button>
<button type="button" id="showHintsBtn" ng-show="showHints" class="btn btn-primary btn-xs" onclick="viewHints()">Hints</button>
<button type="button" id="restartLessonBtn" class="btn btn-xs" onclick="restartLesson()">Restart Lesson</button> -->
<!-- </div> -->
</div> </div>
<div class="lesson-hint" id="lesson-hint-row"> <div class="lesson-hint" id="lesson-hint-container">
<h4>Hints</h4> <h4>Hints</h4>
<div class="panel" > <div class="panel" >
<div class="panel-body" id="lesson-hint"> <div class="panel-body" id="lesson-hint">
<span class="glyphicon-class glyphicon glyphicon-circle-arrow-left" id="showPrevHintBtn" onclick="viewPrevHint()"></span> <span class="glyphicon-class glyphicon glyphicon-circle-arrow-left" id="show-prev-hint"></span>
<span class="glyphicon-class glyphicon glyphicon-circle-arrow-right" id="showNextHintBtn" onclick="viewNextHint()"></span> <span class="glyphicon-class glyphicon glyphicon-circle-arrow-right" id="show-next-hint"></span>
<br/> <br/>
<span ng-show="showHints" bind-html-unsafe="curHint"></span> <span id="lesson-hint-content"></span>
<!--<span id="curHintContainer"></span>-->
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="col-md-12" align="left"> <div class="col-md-12" align="left">
<div id="lessonContentWrapper" class="panel"> <div id="lesson-content-wrapper" class="panel">
</div> </div>
</div> </div>
@ -133,8 +125,8 @@
<h3>Cookies / Parameters</h3> <h3>Cookies / Parameters</h3>
</div> </div>
<hr /> <hr />
<div id="cookiesAndParamsView"> <div id="cookies-and-params">
<div class="cookiesView"> <div class="cookies-view">
<h4>Cookies</h4> <h4>Cookies</h4>
<!-- <div class="cookieContainer" ng-repeat="cookie in cookies"> <!-- <div class="cookieContainer" ng-repeat="cookie in cookies">
<table class="cookieTable table-striped table-nonfluid" > <table class="cookieTable table-striped table-nonfluid" >
@ -160,30 +152,30 @@
</div><!--col-md-4 end--> </div><!--col-md-4 end-->
</div> </div>
<div id="lesson-helps-wrapper" class="panel"> <div id="lesson-helps-wrapper" class="panel">
<div class="row lesson-help" id="lesson-plan-row"> <div class="lesson-help" id="lesson-plan-row">
<div class="col-md-12"> <div class="col-md-12">
<h4>Lesson Plan</h4> <h4>Lesson Plan</h4>
<div class="panel" > <div class="panel" >
<div class="panel-body" id="lesson-plan"> <div class="panel-body" id="lesson-plan-content">
<!-- allowing jQuery to handle this one --> <!-- allowing jQuery to handle this one -->
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="row lesson-help" id="lesson-solution-row"> <div class="lesson-help" id="lesson-solution-row">
<div class="col-md-12"> <div class="col-md-12">
<h4>Lesson Solution</h4> <h4>Lesson Solution</h4>
<div class="panel"> <div class="panel">
<div class="panel-body" id="lesson-solution"> <div class="panel-body" id="lesson-solution-content">
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="row lesson-help" id="lesson-source-row"> <div class="lesson-help" id="lesson-source-row">
<div class="col-md-12"> <div class="col-md-12">
<h4>Lesson Source Code</h4> <h4>Lesson Source Code</h4>
<div class="panel"> <div class="panel">
<div class="panel-body" id="lesson-source"> <div class="panel-body" id="lesson-source-content">
</div> </div>
</div> </div>
</div> </div>

View File

@ -147,7 +147,7 @@ img {
background-color: #16a086; background-color: #16a086;
} }
.sidebar > div > ul > li > ul > li > span.lessonComplete { .sidebar > div > ul > li > ul > li > span.lesson-complete {
/*float: right; /*float: right;
margin-left: 1.5em;*/ margin-left: 1.5em;*/
margin-right: 5px; margin-right: 5px;
@ -192,6 +192,19 @@ img {
.main-content-toggle-right { .main-content-toggle-right {
margin-right: 240px; margin-right: 240px;
} }
/*==========================================================================
lesson content / wrapper
========================================================================= */
#lesson-content-wrapper {
padding:5px;
}
#lesson-content-wrapper table td, #lesson-content-wrapper table th {
padding:3px !important;
}
/* ========================================================================== /* ==========================================================================
Buttons Buttons
========================================================================== */ ========================================================================== */
@ -719,11 +732,11 @@ fieldset[disabled] .btn-warning.active {
margin-bottom:15px; margin-bottom:15px;
} }
cookieContainer { cookie-container {
margin-bottom:4px; margin-bottom:4px;
} }
.cookieTable tr td, .paramsTable tr td { .cookie-table tr td, .params-table tr td {
padding: 3px; padding: 3px;
max-width: 200px; max-width: 200px;
font-size: x-small; font-size: x-small;
@ -820,11 +833,12 @@ cookieContainer {
color: #e84c3d; color: #e84c3d;
} }
.sidebar ul span.lessonComplete { .sidebar ul span.lesson-complete {
float: right; float: right;
margin-left: -5px; margin-left: -5px;
/*margin-right: 5px;*/ /*margin-right: 5px;*/
padding-top: 15px; padding-top: 15px;
display:inline-block;
} }
#menu-container ul li.selected { #menu-container ul li.selected {
@ -840,12 +854,15 @@ cookieContainer {
} }
/* HINTS */ /* HINTS */
#lesson-hint-container {
display: none;
}
#hintsViewTop{ #hintsViewTop{
display: none; display: none;
background-color: #eee; background-color: #eee;
} }
#showPrevHintBtn, #showNextHintBtn { #show-prev-hint, #show-next-hint {
cursor: pointer; cursor: pointer;
} }

View File

@ -50,12 +50,14 @@ define(['jquery',
this.solutionView = {}; this.solutionView = {};
this.sourceView = {}; this.sourceView = {};
this.lessonHintView = {}; this.lessonHintView = {};
this.screen = scr;
this.menu = menu;
// //
}; };
this.onContentLoaded = function() { this.onContentLoaded = function() {
//this.lessonView = new LessonContentView({content:LessonContent.content}); this.helpControlsView = null;
this.lessonView.model = this.lessonContent; this.lessonView.model = this.lessonContent;
this.lessonView.render(); this.lessonView.render();
@ -64,22 +66,24 @@ define(['jquery',
//load title view (initially hidden) << //TODO: currently handled via menu click but need to be able to handle via routed request //load title view (initially hidden) << //TODO: currently handled via menu click but need to be able to handle via routed request
//plan view (initially hidden) //plan view (initially hidden)
this.planView = new PlanView(); this.planView = new PlanView();
this.listenTo(this.planView,'plan:loaded',this.areHelpsReady); this.listenToOnce(this.planView,'plan:loaded',this.areHelpsReady);
//solution view (initially hidden) //solution view (initially hidden)
this.solutionView = new SolutionView(); this.solutionView = new SolutionView();
this.listenTo(this.solutionView,'solution:loaded',this.areHelpsReady); this.listenToOnce(this.solutionView,'solution:loaded',this.areHelpsReady);
//source (initially hidden) //source (initially hidden)
this.sourceView = new SourceView(); this.sourceView = new SourceView();
this.listenTo(this.sourceView,'source:loaded',this.areHelpsReady); this.listenToOnce(this.sourceView,'source:loaded',this.areHelpsReady);
//load help controls view (contextul to what helps are available) //load help controls view (contextul to what helps are available)
this.lessonHintView = new LessonHintView(); this.lessonHintView = new LessonHintView();
this.listenTo(this.lessonHintView,'hints:loaded',this.areHelpsReady); this.listenToOnce(this.lessonHintView,'hints:loaded',this.areHelpsReady);
//
this.hideShowHelps(null);
}; };
this.areHelpsReady = function (curHelp) { this.areHelpsReady = function (curHelp) {
this.addCurHelpState(curHelp); this.addCurHelpState(curHelp);
// check if all are ready // check if all are ready
if (this.helpsLoaded['hints'] && this.helpsLoaded['plan'] && this.helpsLoaded['solution'] && this.helpsLoaded['source']) { if (this.helpsLoaded['hints'] && this.helpsLoaded['plan'] && this.helpsLoaded['solution'] && this.helpsLoaded['source'] && !this.helpControlsView) {
// //
this.helpControlsView = new HelpControlsView({ this.helpControlsView = new HelpControlsView({
hasPlan:(this.planView.model.get('content') !== null), hasPlan:(this.planView.model.get('content') !== null),
@ -91,7 +95,7 @@ define(['jquery',
// //
this.listenTo(this.helpControlsView,'plan:show',this.hideShowHelps); this.listenTo(this.helpControlsView,'plan:show',this.hideShowHelps);
this.listenTo(this.helpControlsView,'solution:show',this.hideShowHelps); this.listenTo(this.helpControlsView,'solution:show',this.hideShowHelps);
this.listenTo(this.helpControlsView,'hints:show',this.showHints) this.listenTo(this.helpControlsView,'hints:show',this.onShowHints)
this.listenTo(this.helpControlsView,'source:show',this.hideShowHelps); this.listenTo(this.helpControlsView,'source:show',this.hideShowHelps);
this.listenTo(this.helpControlsView,'lesson:restart',this.restartLesson); this.listenTo(this.helpControlsView,'lesson:restart',this.restartLesson);
} }
@ -103,27 +107,38 @@ define(['jquery',
this.hideShowHelps = function(showHelp) { this.hideShowHelps = function(showHelp) {
var showId = '#lesson-' + showHelp + '-row'; var showId = '#lesson-' + showHelp + '-row';
var contentId = '#lesson-' + showHelp + '-content';
$('.lesson-help').not(showId).hide(); $('.lesson-help').not(showId).hide();
if (!showId) {
return;
}
switch(showHelp) { switch(showHelp) {
case 'plan': case 'plan':
$(showId).html(this.planView.model.get('content')).show(); $(contentId).html(this.planView.model.get('content'));
break; break;
case 'solution': case 'solution':
$(showId).html(this.solutionView.model.get('content')).show(); $(showId).html(this.solutionView.model.get('content'));
break; break;
case 'source': case 'source':
$(showId).html(this.sourceView.model.get('content')).show(); $(contentId).html('<pre>' + this.sourceView.model.get('content') + '</pre>');
break; break;
} }
$(showId).show();
GoatUtils.scrollToHelp() GoatUtils.scrollToHelp()
};; };
this.showHints = function() { this.onShowHints = function() {
console.log('show Hints'); this.lessonHintView.render();
}; };
this.restartLesson = function() { this.restartLesson = function() {
console.log('restart lesson'); self=this;
$.ajax({
url:'service/restartlesson.mvc',
method:'GET'
}).then(function() {
self.loadLesson(self.screen,self.menu);
});
}; };
}; };

View File

@ -10,7 +10,7 @@ define(['jquery','underscore','backbone','goatApp/view/MenuView'],
this.listenTo(this.menuView,'lesson:click',this.renderTitle); this.listenTo(this.menuView,'lesson:click',this.renderTitle);
} }
this.updateMenu = function() { this.updateMenu = function(curLesson) {
}, },

View File

@ -2,26 +2,24 @@ define(['jquery',
'underscore', 'underscore',
'backbone'], 'backbone'],
function($,_,Backbone) { function($,_,Backbone) {
//TODO: make a base class to extend for items with 'traditional data' (e.g. LessonContentData, this ... others?)
return Backbone.Model.extend({ return Backbone.Model.extend({
//url:'service/lessonplan.mvc', //url:'service/lessonplan.mvc',
fetch: function (options) { fetch: function (options) {
options = options || {}; options = options || {};
return Backbone.Model.prototype.fetch.call(this, _.extend({ dataType: "html"}, options)); return Backbone.Model.prototype.fetch.call(this, _.extend({ dataType: "html"}, options));
}, },
loadData: function() { loadData: function() {
var self=this; var self=this;
this.fetch().then(function(data) { this.fetch().then(function(data) {
self.setContent(data); self.setContent(data);
self.onModelLoaded();
}); });
}, },
setContent: function(content) { setContent: function(content) {
this.set('content',content); this.set('content',content);
this.trigger('loaded');
},
onModelLoaded: function() {
this.checkNullModel(); this.checkNullModel();
this.trigger('loaded');
} }
}); });
}); });

View File

@ -2,23 +2,31 @@ define(['jquery',
'underscore', 'underscore',
'backbone', 'backbone',
'goatApp/model/LessonHintModel'], 'goatApp/model/LessonHintModel'],
function($,_,Backbone,LessonHintModel) {
function($,
_,
Backbone,
LessonHintModel) {
return Backbone.Collection.extend({
model: LessonHintModel,
url:'service/hint.mvc',
initialize: function () {
var self = this;
this.fetch().then(function (data) {
this.models = data;
self.onDataLoaded();
});
},
return Backbone.Collection.extend({ onDataLoaded:function() {
model: LessonHintModel, this.trigger('loaded');
url:'service/hint.mvc', },
initialize: function () {
var self = this; checkNullModel:function() {
this.fetch().then(function (data) { if (this.models[0].indexOf('There are no hints defined.') > -1) {
this.models = data; this.reset([]);
self.onDataLoaded(); //return this.models;
}); }
}, }
onDataLoaded:function() { });
this.trigger('hints:loaded');//copied over as boiler-plate ... use this event trigger?
},
checkNullModel:function() {
//
}
});
}); });

View File

@ -9,10 +9,9 @@ define(['jquery',
return HTMLContentModel.extend({ return HTMLContentModel.extend({
url:'service/lessonplan.mvc', url:'service/lessonplan.mvc',
checkNullModel: function() { checkNullModel: function() {
if (this.get('content').indexOf('Plan is not available for this lesson.') > -1) { if (this.get('content').indexOf('Could not find lesson plan for') > -1) {
this.set('content',null); this.set('content',null);
} }
} }
}); });
}); });

View File

@ -9,7 +9,7 @@ define(['jquery',
return HTMLContentModel.extend({ return HTMLContentModel.extend({
url:'service/solution.mvc', url:'service/solution.mvc',
checkNullModel: function() { checkNullModel: function() {
if (this.get('content').indexOf('Solution is not available. Contact') === 0) { if (this.get('content').indexOf('Could not find the solution file or solution file does not exist') === 0) {
this.set('content',null); this.set('content',null);
} }
} }

View File

@ -10,7 +10,7 @@ define(['jquery',
url:'service/source.mvc', url:'service/source.mvc',
checkNullModel: function () { checkNullModel: function () {
//TODO: move this function into HTMLContentModel and make the string a property of this 'child' model //TODO: move this function into HTMLContentModel and make the string a property of this 'child' model
if (this.get('content').indexOf("No source listing found") > -1) { if (this.get('content').indexOf("Could not find the source file or") > -1) {
this.set('content',null); this.set('content',null);
} }
} }

View File

@ -18,9 +18,11 @@ define(['jquery',
'welcome':'welcomeRoute', 'welcome':'welcomeRoute',
'attack/:scr/:menu':'attackRoute' // 'attack/:scr/:menu':'attackRoute' //
}, },
lessonController: new LessonController({ lessonController: new LessonController({
lessonView:lessonView lessonView:lessonView
}), }),
menuController: new MenuController({ menuController: new MenuController({
menuView:menuView, menuView:menuView,
titleView:titleView titleView:titleView
@ -32,7 +34,6 @@ define(['jquery',
this.menuController.initMenu(); this.menuController.initMenu();
goatRouter.on('route:attackRoute', function(scr,menu) { goatRouter.on('route:attackRoute', function(scr,menu) {
console.log('attack route');
this.lessonController.loadLesson(scr,menu); this.lessonController.loadLesson(scr,menu);
this.menuController.updateMenu(scr,menu); this.menuController.updateMenu(scr,menu);
//update menu //update menu
@ -42,7 +43,14 @@ define(['jquery',
}); });
Backbone.history.start(); Backbone.history.start();
this.listenTo('menu:reload',this.reloadMenu)
},
reloadMenu: function (curLesson) {
this.menuController.updateMenu(curLesson);
} }
}); });
return GoatAppRouter; return GoatAppRouter;

View File

@ -22,6 +22,8 @@ function($,_,Backbone) {
this.hasHints = options.hasHints; this.hasHints = options.hasHints;
}, },
render:function(title) { render:function(title) {
this.$el.html();
if (this.hasSource) { if (this.hasSource) {
this.helpButtons.showSource.unbind().on('click',_.bind(this.showSource,this)); this.helpButtons.showSource.unbind().on('click',_.bind(this.showSource,this));
this.$el.append(this.helpButtons.showSource); this.$el.append(this.helpButtons.showSource);
@ -38,7 +40,8 @@ function($,_,Backbone) {
this.helpButtons.showHints.unbind().on('click',_.bind(this.showHints,this)); this.helpButtons.showHints.unbind().on('click',_.bind(this.showHints,this));
this.$el.append(this.helpButtons.showHints); this.$el.append(this.helpButtons.showHints);
} }
//
this.helpButtons.restartLesson.unbind().on('click',_.bind(this.restartLesson,this));
this.$el.append(this.helpButtons.restartLesson); this.$el.append(this.helpButtons.restartLesson);
}, },
@ -56,6 +59,9 @@ function($,_,Backbone) {
showHints: function() { showHints: function() {
this.trigger('hints:show','hints'); this.trigger('hints:show','hints');
},
restartLesson: function() {
this.trigger('lesson:restart');
} }
}); });
}); });

View File

@ -6,7 +6,7 @@ define(['jquery',
'goatApp/model/LessonContentData'], 'goatApp/model/LessonContentData'],
function($,_,Backbone,JQueryForm,LessonData) { function($,_,Backbone,JQueryForm,LessonData) {
return Backbone.View.extend({ return Backbone.View.extend({
el:'#lessonContentWrapper', //TODO << get this fixed up in DOM el:'#lesson-content-wrapper', //TODO << get this fixed up in DOM
initialize: function(options) { initialize: function(options) {
options = options || {}; options = options || {};
}, },

View File

@ -7,16 +7,65 @@ function($,
Backbone, Backbone,
LessonHintCollection) { LessonHintCollection) {
return Backbone.View.extend({ return Backbone.View.extend({
el:'#lessonHelpWrapper .lessonHelp.lessonHint', el:'#lesson-hint-container',
initialize: function() { events: {
this.collection = new LessonHintCollection(); "click #show-next-hint": "showNextHint",
this.listenTo(this.collection,'hints:loaded',this.onModelLoaded); "click #show-prev-hint": "showPrevHint"
}, },
render:function(title) { initialize: function() {
this.curHint=0;
this.collection = new LessonHintCollection();
this.listenTo(this.collection,'loaded',this.onModelLoaded);
}, },
render:function() {
if (this.$el.is(':visible')) {
this.$el.hide(350);
} else {
this.$el.show(350);
}
if (this.collection.length > 0) {
this.hideShowPrevNextButtons();
}
this.displayHint(this.curHint);
},
onModelLoaded: function() { onModelLoaded: function() {
this.trigger('hints:loaded',{'helpElement':'hints','value':true}) this.trigger('hints:loaded',{'helpElement':'hints','value':true})
},
showNextHint: function() {
this.curHint = (this.curHint < this.collection.length -1) ? this.curHint+1 : this.curHint;
this.hideShowPrevNextButtons();
this.displayHint(this.curHint);
},
showPrevHint: function() {
this.curHint = (this.curHint > 0) ? this.curHint-1 : this.curHint;
this.hideShowPrevNextButtons();
this.displayHint(this.curHint);
},
displayHint: function(curHint) {
this.$el.find('#lesson-hint-content').html(this.collection.models[curHint].get('hint'));
},
hideShowPrevNextButtons: function() {
if (this.curHint === this.collection.length -1) {
this.$el.find('#show-next-hint').css('visibility','hidden');
} else {
this.$el.find('#show-next-hint').css('visibility','visible');
}
if (this.curHint === 0) {
this.$el.find('#show-prev-hint').css('visibility','hidden');
} else {
this.$el.find('#show-prev-hint').css('visibility','visible');
}
} }
}); });
}); });

View File

@ -49,18 +49,24 @@ define(['jquery',
for (var j=0; j < lessons.length;j++) { for (var j=0; j < lessons.length;j++) {
var lessonItem = $('<li>'); var lessonItem = $('<li>');
lessonName = lessons[j].name; lessonName = lessons[j].name;
var lessonLink = $('<a>',{href:lessons[j].link,text:lessonName,id:lessonName}); var lessonLink = $('<a>',{href:lessons[j].link,text:lessonName,id:lessonName});
lessonLink.click(_.bind(this.triggerTitleRender,this,lessonName)); lessonLink.click(_.bind(this.titleRender,this,lessonName));
lessonItem.append(lessonLink); lessonItem.append(lessonLink);
//check for lab/stages //check for lab/stages
categoryLessonList.append(lessonItem); categoryLessonList.append(lessonItem);
if (lessons[j].complete) {
lessonItem.append($('<span>',{class:'glyphicon glyphicon-check lesson-complete'}));
}
var stages = lessons[j].children; var stages = lessons[j].children;
for (k=0; k < stages.length; k++) { for (k=0; k < stages.length; k++) {
var stageName = stages[k].name; var stageName = stages[k].name;
var stageSpan = $('<span>');
var stageLink = $('<a>',{href:stages[k].link,text:stageName,id:GoatUtils.makeId(stageName)}); var stageLink = $('<a>',{href:stages[k].link,text:stageName,id:GoatUtils.makeId(stageName)});
stageSpan.append(stageLink); stageSpan.append(stageLink);
categoryLessonList.append(stageSpan); categoryLessonList.append(stageSpan);
if (stages[j].complete) {
stageSpan.append($('<span>',{class:'glyphicon glyphicon-check lesson-complete'}));
}
} }
} }
category.append(categoryLessonList); category.append(categoryLessonList);
@ -74,14 +80,17 @@ define(['jquery',
this.accordionMenu(this.openMenu); this.accordionMenu(this.openMenu);
} }
}, },
triggerTitleRender: function (title) {
titleRender: function (title) {
this.trigger('lesson:click',title); this.trigger('lesson:click',title);
}, },
expandCategory: function (id) { expandCategory: function (id) {
if (id) { if (id) {
this.accordionMenu(id); this.accordionMenu(id);
} }
}, },
accordionMenu: function(id) { accordionMenu: function(id) {
if (this.openMenu !== id) { if (this.openMenu !== id) {
this.$el.find('#' + id).slideDown(300); this.$el.find('#' + id).slideDown(300);