Merge pull request #9 from misfir3/master

Initial cut-over of backbone port
This commit is contained in:
misfir3 2015-07-13 08:55:20 -04:00
commit 858380e95b
50 changed files with 12428 additions and 344 deletions

View File

@ -1,3 +1,23 @@
**********
* June 9 2015
* ##### WebGoat Container Migration from AngularJS to Backbone #####
* Why: I believe AngularJS is a little heavy (and acutally a little too secure, imagine that) for the purposes of WebGoat
* When: Now
* What: Porting current functionality in the 6.0.1 release into the webgoat-container build
* How: If you'd like to help. Fork this repository and contact me (jason.white@owasp.org) as to the current priorities/needs. Once you fork it,
* After forking and cloning this repo, you should also fork/clone the lessons repository (https://github.com/WebGoat/WebGoat-Lessons). More on that in a second
* in the core/container WebGoat (this) project
* $ mvn clean install
* Then either package and put the package in a local tomcat OR ...
* $ mvn tomcat:run
* Then you'll need to switch to the lessons directory and
* $ mvn clean packa
* Then copy some of the lesson jars into your $TOMCAT_HOME/webapps/{webgoat-container}/plugin_lessons
* Now you should have something to work against for the UI work
**********
##### Original WebGoat ReadMe follows #####
********** **********
********** WebGoat 6.0 ********** WebGoat 6.0
********** August 23, 2014 ********** August 23, 2014

View File

@ -551,7 +551,7 @@ public abstract class AbstractLesson extends Screen implements Comparable<Object
* return "*.do"-styled paths.</p> * return "*.do"-styled paths.</p>
*/ */
protected String getPath() { protected String getPath() {
return "attack"; return "#attack";
} }
/** /**
@ -563,13 +563,9 @@ public abstract class AbstractLesson extends Screen implements Comparable<Object
StringBuffer link = new StringBuffer(); StringBuffer link = new StringBuffer();
// mvc update: // mvc update:
link.append(getPath()).append("?"); link.append(getPath()).append("/");
link.append(WebSession.SCREEN);
link.append("=");
link.append(getScreenId()); link.append(getScreenId());
link.append("&"); link.append("/");
link.append(WebSession.MENU);
link.append("=");
link.append(getCategory().getRanking()); link.append(getCategory().getRanking());
return link.toString(); return link.toString();
} }

View File

@ -198,9 +198,9 @@ public abstract class Screen {
// hook all the links // hook all the links
public String getContent() { public String getContent() {
String makeAllAjax = "<script>goat.utils.makeFormsAjax();goat.utils.ajaxifyAttackHref();</script>"; //String makeAllAjax = "<script>goat.utils.makeFormsAjax();goat.utils.ajaxifyAttackHref();</script>";
// need to do this here as some of the lessons render forms after submission of an ajax form // need to do this here as some of the lessons render forms after submission of an ajax form
return (content == null) ? "" : content.toString() + makeAllAjax; return (content == null) ? "" : content.toString();// + makeAllAjax;
} }
/** /**

View File

@ -34,7 +34,7 @@
</div><!--toggle navigation end--> </div><!--toggle navigation end-->
<div class="lessonTitle" > <div class="lessonTitle" >
<h1 id="lessonTitle">Please login</h1>
</div><!--lesson title end--> </div><!--lesson title end-->
</header> </header>

View File

@ -27,12 +27,7 @@
<!-- end of CSS --> <!-- end of CSS -->
<!-- JS --> <!-- JS -->
<script src="js/jquery/jquery-1.10.2.min.js"></script>
<script src="js/angular/angular.min.js"></script>
<!-- angular modules -->
<script src="js/angular/angular-animate.min.js"></script>
<script src="js/angular/ui-bootstrap-tpls-0.11.0.min.js"></script>
<!-- Feature detection -->
<script src="js/modernizr-2.6.2.min.js"></script> <script src="js/modernizr-2.6.2.min.js"></script>
<!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries --> <!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!--[if lt IE 9]> <!--[if lt IE 9]>
@ -40,30 +35,14 @@
<script src="js/respond.min.js"></script> <script src="js/respond.min.js"></script>
<![endif]--> <![endif]-->
<!--Global JS--> <!-- Require.js used to load js asynchronously -->
<script src="js/libs/require.min.js" data-main="js/main.js"></script>
<script src="js/jquery_form/jquery.form.js"></script>
<script src="plugins/bootstrap/js/bootstrap.min.js"></script>
<script src="js/application.js"></script>
<script type="text/javascript">
var goat = angular.module("goatApp", ['ngAnimate', 'ui.bootstrap']);
</script>
<script type="text/javascript" src="js/goatConstants.js"></script>
<script type="text/javascript" src="js/goatUtil.js"></script>
<script type="text/javascript" src="js/goatData.js"></script>
<script type="text/javascript" src="js/goatLesson.js"></script>
<script type="text/javascript" src="js/goatControllers.js"></script>
<!-- end of JS -->
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" /> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
<title>WebGoat</title> <title>WebGoat</title>
</head> </head>
<body>
<body class="animated fadeIn" ng-app="goatApp"> <section id="container">
<section id="container" ng-controller="goatLesson">
<header id="header"> <header id="header">
<!--logo start--> <!--logo start-->
<div class="brand"> <div class="brand">
@ -75,12 +54,12 @@
<i class="fa fa-bars"></i> <i class="fa fa-bars"></i>
</button> </button>
</div><!--toggle navigation end--> </div><!--toggle navigation end-->
<div class="lessonTitle" > <div id="lesson-title-wrapper" >
<h1 id="lessonTitle"></h1>
</div><!--lesson title end--> </div><!--lesson title end-->
<div class="user-nav pull-right" style="margin-right: 75px;"> <div class="user-nav pull-right" style="margin-right: 75px;">
<div class="dropdown" style="display:inline"> <div class="dropdown" style="display:inline">
<button type="button" class="btn btn-default dropdown-toggle" id="dropdownMenu1" ng-disabled="disabled"> <button type="button" class="btn btn-default dropdown-toggle" id="dropdownMenu1" >
<i class="fa fa-user"></i> <span class="caret"></span> <i class="fa fa-user"></i> <span class="caret"></span>
</button> </button>
<ul class="dropdown-menu dropdown-menu-left" role="menu" aria-labelledby="dropdownMenu1"> <ul class="dropdown-menu dropdown-menu-left" role="menu" aria-labelledby="dropdownMenu1">
@ -94,7 +73,7 @@
</ul> </ul>
</div> </div>
<button type="button" class="btn btn-default right_nav_button" ng-click="showAbout()" data-toggle="tooltip" title="About WebGoat"> <button type="button" class="btn btn-default right_nav_button" data-toggle="tooltip" title="About WebGoat">
<i class="fa fa-info"></i> <i class="fa fa-info"></i>
</button> </button>
<a href="mailto:${contactEmail}?Subject=Webgoat%20feedback" target="_top"> <a href="mailto:${contactEmail}?Subject=Webgoat%20feedback" target="_top">
@ -107,47 +86,32 @@
</div> </div>
</header> </header>
<!--sidebar left start-->
<aside class="sidebar" > <aside class="sidebar" >
<div id="leftside-navigation" ng-controller="goatMenu" class="nano"> <div id="menu-container"></div>
<ul class="nano-content">
<li class="sub-menu" ng-repeat="item in menuTopics">
<a ng-click="accordionMenu(item.id)" href=""><i class="fa {{item.class}}"></i><span>{{item.name}}</span></a><!-- expanded = !expanded-->
<ul class="slideDown lessonsAndStages {{item.displayClass}}" id="{{item.id}}" isOpen=0>
<li ng-repeat="lesson in item.children" class="{{lesson.selectedClass}}">
<a ng-click="renderLesson(lesson.id, lesson.link, {showSource: lesson.showSource, showHints: lesson.showHints})" id="{{lesson.id}}" class="{{lesson.selectedClass}}" title="link to {{lesson.name}}" href="">{{lesson.name}}</a><span class="{{lesson.completeClass}}"></span>
<span ng-repeat="stage in lesson.children">
<a ng-click="renderLesson(stage.id, stage.link, {showSource: stage.showSource, showHints: stage.showHints})" class="selectedClass" id="{{stage.id}}" title="link to {{stage.name}}" href="">{{stage.name}}</a><span class="{{stage.completeClass}}"></span>
</span>
</li>
</ul>
</li>
</ul>
</div>
</aside> </aside>
<!--sidebar left end--> <!--sidebar left end-->
<!--main content start--> <!--main content start-->
<section class="main-content-wrapper"> <section class="main-content-wrapper">
<section id="main-content" > <!--ng-controller="goatLesson"--> <section id="main-content" > <!--ng-controller="goatLesson"-->
<div class="row"> <div class="row">
<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"> <div class="panel" id="help-controls">
<div class="panel-body"> <!-- <div id="help-buttons" class="panel-body"> -->
<button type="button" id="showSourceBtn" ng-show="showSource" class="btn btn-primary btn-xs" ng-click="showLessonSource()">Java [Source]</button> <!-- <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" ng-click="showLessonSolution()">Solution</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" ng-click="showLessonPlan()">Lesson Plan</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" ng-click="viewHints()">Hints</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" ng-click="restartLesson()">Restart Lesson</button> <button type="button" id="restartLessonBtn" class="btn btn-xs" onclick="restartLesson()">Restart Lesson</button> -->
</div> <!-- </div> -->
</div> </div>
<div class="lessonHelp" id="lesson_hint_row"> <div class="lessonHelp" id="lesson-hint-row">
<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" ng-click="viewPrevHint()"></span> <span class="glyphicon-class glyphicon glyphicon-circle-arrow-left" id="showPrevHintBtn" onclick="viewPrevHint()"></span>
<span class="glyphicon-class glyphicon glyphicon-circle-arrow-right" id="showNextHintBtn" ng-click="viewNextHint()"></span> <span class="glyphicon-class glyphicon glyphicon-circle-arrow-right" id="showNextHintBtn" onclick="viewNextHint()"></span>
<br/> <br/>
<span ng-show="showHints" bind-html-unsafe="curHint"></span> <span ng-show="showHints" bind-html-unsafe="curHint"></span>
<!--<span id="curHintContainer"></span>--> <!--<span id="curHintContainer"></span>-->
@ -155,12 +119,8 @@
</div> </div>
</div> </div>
</div> </div>
<div class="col-md-12" align="left">
<div class="col-md-12"> <div id="lessonContentWrapper" class="panel">
<div class="panel" >
<div class="panel-body" id="lesson_content">
<b>This should default to the "How to Work with Webgoat" lesson</b>
</div>
</div> </div>
</div> </div>
@ -176,10 +136,10 @@
<div id="cookiesAndParamsView"> <div id="cookiesAndParamsView">
<div class="cookiesView"> <div class="cookiesView">
<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" >
<thead> <thead>
<tr><th class="col-sm-1"></th><th class="col-sm-1"></th></tr> <!-- Field / Value --> <tr><th class="col-sm-1"></th><th class="col-sm-1"></th></tr>
</thead> </thead>
<tbody> <tbody>
<tr ng-repeat="(key, value) in cookie"> <tr ng-repeat="(key, value) in cookie">
@ -187,25 +147,11 @@
<td>{{value}}</td> <td>{{value}}</td>
</tr> </tr>
</tbody> </tbody>
<!--<li ng-repeat="(key, value) in cookie">{{key}} :: {{ value}} </td>-->
<!--</ul>-->
</table> </table>
</div> </div> -->
</div> </div>
<div id="paramsView"> <!--class="paramsView"--> <div id="paramsView"> <!--class="paramsView"-->
<h4>Params</h4> <h4>Params</h4>
<table class="paramsTable table-striped table-nonfluid" id="paramsTable">
<thead>
<tr><th>Param</th><th>Value</th></tr>
</thead>
<tbody>
<tr ng-repeat="param in parameters">
<td>{{param.name}}</td>
<td>{{param.value}}</td>
</tr>
</tbody>
</table>
</ul>
</div> </div>
</div> </div>
</div> </div>
@ -214,44 +160,18 @@
</div><!--col-md-4 end--> </div><!--col-md-4 end-->
</div> </div>
<div id="lessonHelpsWrapper"> <div id="lessonHelpsWrapper">
<!--div class="row lessonHelp" id="lesson_hint_row"> <div class="row lessonHelp" id="lesson-cookies-row">
<div class="col-md-12">
<h4>Hints</h4>
<div class="panel" >
<div class="panel-body" id="lesson_hint">
<span class="glyphicon-class glyphicon glyphicon-circle-arrow-left" id="showPrevHintBtn" ng-click="viewPrevHint()"></span>
<span class="glyphicon-class glyphicon glyphicon-circle-arrow-right" id="showNextHintBtn" ng-click="viewNextHint()"></span>
<br/>
{{curHint}}
</div>
</div>
</div>
</div-->
<div class="row lessonHelp" id="lesson_cookies_row">
<div class="col-md-12"> <div class="col-md-12">
<h4>Lesson Parameters and Cookies</h4> <h4>Lesson Parameters and Cookies</h4>
<div class="panel" >
<div class="panel-body" id="lesson_cookies">
</div>
</div>
</div> </div>
</div> </div>
<div class="row lessonHelp" id="lesson_hint_row">
<div class="col-md-12">
<h4>Lesson Hints</h4>
<div class="panel" >
<div class="panel-body" id="lesson_hint">
</div> <div class="row lessonHelp" id="lesson-plan-row">
</div>
</div>
</div>
<div class="row lessonHelp" 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">
<!-- allowing jQuery to handle this one --> <!-- allowing jQuery to handle this one -->
</div> </div>
</div> </div>
@ -271,7 +191,7 @@
<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">
<pre>{{source}}</pre> <!-- <pre>{{source}}</pre> -->
</div> </div>
</div> </div>
</div> </div>
@ -280,20 +200,8 @@
</section> </section>
</section> </section>
<!--main content end-->
</section> </section>
<!--main content end-->
</section>
<script>
$(document).ready(function() {
//TODO merge appliction.js code into other js files
app.init();
});
</script>
<!-- About WebGoat Modal --> <!-- About WebGoat Modal -->
<div class="modal fade" id="aboutModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true"> <div class="modal fade" id="aboutModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
@ -304,4 +212,6 @@
</div> </div>
</div> </div>
</body> </body>
</html> </html>

View File

@ -1,2 +1,2 @@
#lessonTitle {position:absolute;left:94px;top:75px;width:690px;height:22px;z-index:1;float: right;font-size: 20px;color: #FFFFFF;} #lesson-title {position:absolute;left:94px;top:75px;width:690px;height:22px;z-index:1;float: right;font-size: 20px;color: #FFFFFF;}
#hMenuBar {position:absolute;left:245px;top:108px;width:538px;height:22px;z-index:2;} #hMenuBar {position:absolute;left:245px;top:108px;width:538px;height:22px;z-index:2;}

View File

@ -132,7 +132,7 @@ img {
#header .user-nav button:hover i { #header .user-nav button:hover i {
color: #F6F6F6; color: #F6F6F6;
} }
#header .lessonTitle { #header #lesson-title-wrapper {
display: inline-block; display: inline-block;
margin:0 0 0 20px; margin:0 0 0 20px;
} }
@ -747,7 +747,7 @@ cookieContainer {
z-index: 100; z-index: 100;
} }
#leftside-navigation { #menu-container {
overflow-y:scroll; overflow-y:scroll;
overflow-x:hidden; overflow-x:hidden;
} }
@ -761,18 +761,18 @@ cookieContainer {
.sidebar-toggle { .sidebar-toggle {
margin-left: -240px; margin-left: -240px;
} }
#leftside-navigation ul, #menu-container ul,
#leftside-navigation ul ul { #menu-container ul ul {
margin: -2px 0 0; margin: -2px 0 0;
padding: 0; padding: 0;
} }
#leftside-navigation ul li { #menu-container ul li {
list-style-type: none; list-style-type: none;
border-bottom: 1px solid rgba(255, 255, 255, 0.05); border-bottom: 1px solid rgba(255, 255, 255, 0.05);
} }
#leftside-navigation ul li a { #menu-container ul li a {
color: #aeb2b7; color: #aeb2b7;
text-decoration: none; text-decoration: none;
display: block; display: block;
@ -785,38 +785,38 @@ cookieContainer {
-ms-transition: all 200ms ease-in; -ms-transition: all 200ms ease-in;
transition: all 200ms ease-in; transition: all 200ms ease-in;
} }
#leftside-navigation ul li a span { #menu-container ul li a span {
display: inline-block; display: inline-block;
} }
#leftside-navigation ul ul li { #menu-container ul ul li {
background: #333; background: #333;
margin-bottom: 0; margin-bottom: 0;
margin-left: 0; margin-left: 0;
margin-right: 0; margin-right: 0;
border-bottom: none; border-bottom: none;
} }
#leftside-navigation ul ul li a { #menu-container ul ul li a {
font-size: 11px; font-size: 11px;
padding-top: 5px; padding-top: 5px;
padding-bottom: 5px; padding-bottom: 5px;
color: #aeb2b7; color: #aeb2b7;
margin-left:8px; margin-left:8px;
} }
#leftside-navigation ul li a i { #menu-container ul li a i {
width: 20px; width: 20px;
} }
#leftside-navigation ul li a i.fa-angle-right, #menu-container ul li a i.fa-angle-right,
#leftside-navigation ul li a i.fa-angle-left { #menu-container ul li a i.fa-angle-left {
padding-top: 3px; padding-top: 3px;
} }
#leftside-navigation ul ul { #menu-container ul ul {
display: none; display: none;
} }
#leftside-navigation li.active ul { #menu-container li.active ul {
display: block; display: block;
} }
#leftside-navigation ul li a:hover, #menu-container ul li a:hover,
#leftside-navigation ul li.active > a { #menu-container ul li.active > a {
color: #e84c3d; color: #e84c3d;
} }
@ -827,15 +827,15 @@ cookieContainer {
padding-top: 15px; padding-top: 15px;
} }
#leftside-navigation ul li.selected { #menu-container ul li.selected {
background-color: #555; background-color: #555;
} }
#leftside-navigation ul li.selected a.selected { #menu-container ul li.selected a.selected {
color:white; color:white;
} }
#leftside-navigation ul ul.lessonsAndStages.keepOpen { #menu-container ul ul.lessonsAndStages.keepOpen {
display: block display: block
} }
@ -849,8 +849,16 @@ cookieContainer {
cursor: pointer; cursor: pointer;
} }
.info { .info {
color:#e84c3d; color:#e84c3d;
font-weight: bold; font-weight: bold;
} }
#help-controls {
padding-left: 4px;
padding-top: 4px
}
.help-button {
margin-right:4px;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,98 @@
define(['jquery',
'underscore',
'libs/backbone',
'goatApp/model/LessonContentData',
'goatApp/view/LessonContentView',
'goatApp/view/PlanView',
'goatApp/view/SourceView',
'goatApp/view/SolutionView',
'goatApp/view/LessonHintView',
'goatApp/view/HelpControlsView'
],
function($,
_,
Backbone,
LessonContentData,
LessonContentView,
PlanView,
SourceView,
SolutionView,
LessonHintView,
HelpControlsView
) {
'use strict'
var Controller = function(options) {
this.lessonContent = new LessonContentData();
this.lessonView = options.lessonView;
/*this.planView = new PlanView();
this.solutionView = new SolutionView();
this.sourceView = new SourceView();
*/
_.extend(Controller.prototype,Backbone.Events);
this.start = function() {
this.listenTo(this.lessonContent,'contentLoaded',this.onContentLoaded);
};
//load View, which can pull data
this.loadLesson = function(scr,menu) {
this.helpsLoaded = {};
this.lessonContent.loadData({
'screen': encodeURIComponent(scr),
'menu': encodeURIComponent(menu),
});
this.planView = {};
this.solutionView = {};
this.sourceView = {};
this.lessonHintView = {};
//
};
this.onContentLoaded = function() {
//this.lessonView = new LessonContentView({content:LessonContent.content});
this.lessonView.model = this.lessonContent;
this.lessonView.render();
//load cookies/parameters view
//load title view (initially hidden) << currently handled via menu click but need to be able to handle via routed request
//plan view (initially hidden)
this.planView = new PlanView();
this.listenTo(this.planView,'plan:loaded',this.areHelpsReady);
//solution view (initially hidden)
this.solutionView = new SolutionView();
this.listenTo(this.solutionView,'solution:loaded',this.areHelpsReady);
//source (initially hidden)
this.sourceView = new SourceView();
this.listenTo(this.sourceView,'source:loaded',this.areHelpsReady);
//load help controls view (contextul to what helps are available)
this.lessonHintView = new LessonHintView();
this.listenTo(this.lessonHintView,'hints:loaded',this.areHelpsReady);
};
this.areHelpsReady = function (curHelp) {
this.addCurHelpState(curHelp);
// check if all are ready
if (this.helpsLoaded['hints'] && this.helpsLoaded['plan'] && this.helpsLoaded['solution'] && this.helpsLoaded['source']) {
//
this.helpControlsView = new HelpControlsView({
hasPlan:(this.planView.model.get('content') !== null),
hasSolution:(this.solutionView.model.get('content') !== null),
hasSource:(this.sourceView.model.get('content') !== null),
hasHints:(this.lessonHintView.collection.length > 0),
});
this.helpControlsView.render();
}
};
this.addCurHelpState = function (curHelp) {
this.helpsLoaded[curHelp.helpElement] = curHelp.value;
};
};
return Controller;
});

View File

@ -0,0 +1,25 @@
define(['jquery','underscore','backbone','goatApp/view/MenuView'],
function($,_,Backbone,MenuView) {
Controller = function(options){
_.extend(Controller.prototype,Backbone.Events);
options = options || {};
this.menuView = options.menuView;
this.titleView = options.titleView;
this.initMenu = function() {
this.listenTo(this.menuView,'lesson:click',this.renderTitle);
}
this.updateMenu = function() {
},
//TODO: move title rendering into lessonContent/View pipeline once data can support it
this.renderTitle = function(title) {
this.titleView.render(title);
}
};
return Controller;
});

View File

@ -0,0 +1,12 @@
define(['jquery','underscore','backbone','goatApp/view/GoatRouter'],
function($,_,Backbone,Router){
'use strict'
//var goatRouter = new Router();
return {
initApp: function() {
//TODO: add query/ability to load from where they left off
var goatRouter = new Router();
goatRouter.init();
}
};
});

View File

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

View File

@ -0,0 +1,40 @@
define(['jquery',
'underscore',
'backbone',
'goatApp/model/HTMLContentModel'],
function($,
_,
Backbone,
HTMLContentModel){
return HTMLContentModel.extend({
urlRoot:null,
defaults: {
items:null,
selectedItem:null
},
initialize: function (options) {
this.screenParam = null;
this.menuParam = null;
this.baseUrlRoot = 'attack?Screen=';//
},
loadData: function(options) {
this.urlRoot = this.baseUrlRoot +options.screen + '&menu=' + options.menu;
this.set('menuParam',options.menu);
this.set('screenParam',options.screen);
var self=this;
this.fetch().then(function(data) {
self.setContent(data);
});
},
setContent: function(content) {
this.set('content',content);
this.trigger('contentLoaded');
},
fetch: function (options) {
options = options || {};
return Backbone.Model.prototype.fetch.call(this, _.extend({ dataType: "html"}, options));
}
});
});

View File

@ -0,0 +1,24 @@
define(['jquery',
'underscore',
'backbone',
'goatApp/model/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();
});
},
onDataLoaded:function() {
this.trigger('hints:loaded');//copied over as boiler-plate ... use this event trigger?
},
checkNullModel:function() {
//
}
});
});

View File

@ -0,0 +1,10 @@
define(['jquery',
'underscore',
'backbone'],
function($,
_,
Backbone,
HTMLContentModel) {
return Backbone.Model.extend({
});
});

View File

@ -0,0 +1,18 @@
define(['jquery',
'underscore',
'backbone',
'goatApp/model/HTMLContentModel'],
function($,
_,
Backbone,
HTMLContentModel) {
return HTMLContentModel.extend({
url:'service/lessonplan.mvc',
checkNullModel: function() {
if (this.get('content').indexOf('Plan is not available for this lesson.') > -1) {
this.set('content',null);
}
}
});
});

View File

@ -0,0 +1,18 @@
define(['jquery',
'underscore',
'backbone',
'goatApp/model/HTMLContentModel'],
function($,
_,
Backbone,
HTMLContentModel) {
return HTMLContentModel.extend({
url:'service/solution.mvc',
checkNullModel: function() {
if (this.get('content').indexOf('Solution is not available. Contact') === 0) {
this.set('content',null);
}
}
});
});

View File

@ -0,0 +1,19 @@
define(['jquery',
'underscore',
'backbone',
'goatApp/model/HTMLContentModel'],
function($,
_,
Backbone,
HTMLContentModel) {
return HTMLContentModel.extend({
url:'service/source.mvc',
checkNullModel: function () {
//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) {
this.set('content',null);
}
}
});
});

View File

@ -0,0 +1,21 @@
define(['jquery',
'underscore',
'backbone',
'goatApp/model/MenuModel'],
function($,_,Backbone,MenuModel) {
return Backbone.Collection.extend({
model: MenuModel,
url:'service/lessonmenu.mvc',
initialize: function () {
var self = this;
this.fetch().then(function (data) {
this.models = data;
self.onDataLoaded();
});
},
onDataLoaded:function() {
this.trigger('menuData:loaded');
}
});
});

View File

@ -0,0 +1,57 @@
//var goatApp = goatApp || {};
define(['jquery','underscore','backbone'], function($,_,Backbone) {
var menuData = Backbone.Model.extend({
urlRoot:'/webgoat/service/lessonmenu.mvc',
defaults: {
items:null,
selectedItem:null
},
initialize: function () {
var self = this;
this.fetch().then(function(menuItems){
menuItems = goatUtils.enhanceMenuData(menuItems,this.selectedItem);
this.setDataItems(menuItems);
});
},
update: function() {
var self = this;
this.fetch().then(function(menuItems) {
menuItems = goatUtils.enhanceMenuData(menuItems,this.selectedItem);
self.setDataItems(menuItems);
});
},
setDataItems: function (data) {
this.items = data;
}
});
});
/*
var menuData = Backbone.Model.extend({
urlRoot:'/webgoat/service/lessonmenu.mvc',
defaults: {
items:null,
selectedItem:null
},
initialize: function () {
var self = this;
this.fetch().then(function(menuItems){
menuItems = goatUtils.enhanceMenuData(menuItems,this.selectedItem);
self.items = menuItems;
});
},
update: function() {
var self = this;
this.fetch().then(function(data) {
self.items = data;
self.render(0);
});
}
});
*/

View File

@ -0,0 +1,10 @@
define(['jquery',
'underscore',
'backbone'],
function($,_,Backbone) {
return Backbone.Model.extend({
});
});

View File

@ -0,0 +1,178 @@
define(['jquery',
'underscore',
'backbone',
'libs/jquery.form'
],
function($,
_,
Backbone,
JQueryForm) {
var goatUtils = {
makeId: function(lessonName) {
//var id =
return lessonName.replace(/\s|\(|\)|\!|\:|\;|\@|\#|\$|\%|\^|\&|\*/g, '');
},
addMenuClasses: function(arr) {
for (var i = 0; i < arr.length; i++) {
var menuItem = arr[i];
//console.log(menuItem);
if (menuItem.type && menuItem.type === 'CATEGORY') {
menuItem.class = 'fa-angle-right pull-right';
}
}
return arr;
},
// debugFormSubmission: false,
// pre-submit callback
showRequest: function(formData, jqForm, options) {
if (GoatUtils.debugFormSubmission) {
// formData is an array; here we use $.param to convert it to a string to display it
// but the form plugin does this for you automatically when it submits the data
var queryString = $.param(formData);
// jqForm is a jQuery object encapsulating the form element. To access the
// DOM element for the form do this:
// var formElement = jqForm[0];
alert('About to submit: \n\n' + queryString);
}
// here we could return false to prevent the form from being submitted;
// returning anything other than false will allow the form submit to continue
return true;
},
// post-submit callback
showResponse: function(responseText, statusText, xhr, $form) {
// for normal html responses, the first argument to the success callback
// is the XMLHttpRequest object's responseText property
// if the ajaxForm method was passed an Options Object with the dataType
// property set to 'xml' then the first argument to the success callback
// is the XMLHttpRequest object's responseXML property
// if the ajaxForm method was passed an Options Object with the dataType
// property set to 'json' then the first argument to the success callback
// is the json data object returned by the server
if (GoatUtils.debugFormSubmission) {
alert('status: ' + statusText + '\n\nresponseText: \n' + responseText +
'\n\nThe output div should have already been updated with the responseText.');
}
// update lesson cookies and params
// make any embedded forms ajaxy
GoatUtils.showLessonCookiesAndParams();
// forms and links are now hooked with each standard lesson render (see Java class Screen.getContent())
// but these are safe to call twice
GoatUtils.makeFormsAjax();
GoatUtils.ajaxifyAttackHref(); //TODO find some way to hook scope for current menu. Likely needs larger refactor which is already started/stashed
//refresh menu
angular.element($('#leftside-navigation')).scope().renderMenu();
},
makeFormsAjax: function() {
// make all forms ajax forms
var options = {
target: '#lesson_content', // target element(s) to be updated with server response
beforeSubmit: GoatUtils.showRequest, // pre-submit callback, comment out after debugging
success: GoatUtils.showResponse // post-submit callback, comment out after debugging
// other available options:
//url: url // override for form's 'action' attribute
//type: type // 'get' or 'post', override for form's 'method' attribute
//dataType: null // 'xml', 'script', or 'json' (expected server response type)
//clearForm: true // clear all form fields after successful submit
//resetForm: true // reset the form after successful submit
// $.ajax options can be used here too, for example:
//timeout: 3000
};
//console.log("Hooking any lesson forms to make them ajax");
$("form").ajaxForm(options);
},
displayButton: function(id, show) {
if ($('#' + id)) {
if (show) {
$('#' + id).show();
} else {
$('#' + id).hide();
}
}
},
showLessonCookiesAndParams: function() {
$.get(goatConstants.cookieService, {}, function(reply) {
$("#lesson_cookies").html(reply);
}, "html");
},
showLessonHints: function() {
$('.lessonHelp').hide();
$('#lesson_hint').html();
$('#lesson_hint_row').show();
},
showLessonSource: function(source) {
$('.lessonHelp').hide();
//$('#lesson_source').html("<pre>"+goat.lesson.lessonInfo.source+"</pre>");
$('#lesson_source_row').show();
GoatUtils.scrollToHelp();
},
showLessonSolution: function() {
$('.lessonHelp').hide();
$('#lesson_solution').html(goat.lesson.lessonInfo.solution);
$('#lesson_solution_row').show();
GoatUtils.scrollToHelp();
},
showLessonPlan: function(plan) {
$('.lessonHelp').hide();
$("#lesson_plan").html(goat.lesson.lessonInfo.plan);
$('#lesson_plan_row').show();
GoatUtils.scrollToHelp();
},
scrollToHelp: function() {
$('#leftside-navigation').height($('#main-content').height() + 15)
var target = $('#lessonHelpsWrapper');
GoatUtils.scrollEasy(target);
},
scrollToTop: function() {
$('.lessonHelp').hide();
var target = $('#container');
GoatUtils.scrollEasy(target);
},
scrollEasy: function(target) {
$('html,body').animate({
scrollTop: target.offset().top
}, 1000);
},
scrapeParams: function(url) {
if (!url) {
return;
}
var params = url.split('?')[1].split('&');
var paramsArr = [];
for (var i = 0; i < params.length; i++) {
var paramObj = {};
paramObj.name = params[i].split('=')[0];
paramObj.value = params[i].split('=')[1];
paramsArr.push(paramObj);
}
return paramsArr;
},
highlightCurrentLessonMenu: function(id) {
//TODO: move selectors in first two lines into goatConstants
$('ul li.selected').removeClass(goatConstants.selectedMenuClass)
$('ul li.selected a.selected').removeClass(goatConstants.selectedMenuClass)
$('#' + id).addClass(goatConstants.selectedMenuClass);
$('#' + id).parent().addClass(goatConstants.selectedMenuClass);
},
ajaxifyAttackHref: function() { // rewrite any links with hrefs point to relative attack URLs
$.each($('a[href^="attack?"]'),
function(i,el) {
var url = $(el).attr('href');
$(el).unbind('click').attr('href','#').attr('link',url);
//TODO pull currentMenuId
$(el).click(function() {
event.preventDefault();
var _url = $(el).attr('link');
$.get(_url, {success:showResponse});
});
});
}
};
return goatUtils;
});

View File

@ -0,0 +1,39 @@
//goatConstants
var goatConstants = {
getClasses: function() {
return {
categoryClass:'fa-angle-right pull-right',
lessonCompleteClass:'glyphicon glyphicon-check lessonComplete',
selectedMenuClass:'selected',
keepOpenClass:'keepOpen'
};
},
getServices: function() {
return {
lessonService: 'service/lessonmenu.mvc',
cookieService: 'service/cookie.mvc', //cookies_widget.mvc
hintService: 'service/hint.mvc',
sourceService: 'service/source.mvc',
solutionService: 'service/solution.mvc',
lessonPlanService: 'service/lessonplan.mvc',
menuService: 'service/lessonmenu.mvc',
lessonTitleService: 'service/lessontitle.mvc',
restartLessonService: 'service/restartlesson.mvc'
}
},
getMessages: function() {
return {
notFound: 'Could not find',
noHints: 'There are no hints defined.',
noSourcePulled: 'No source was retrieved for this lesson'
}
},
getDOMContainers:function() {
return {
lessonMenu: '#menu-container'
}
}
};

View File

@ -0,0 +1,50 @@
define(['jquery',
'underscore',
'backbone',
'goatApp/controller/LessonController',
'goatApp/controller/MenuController',
'goatApp/view/LessonContentView',
'goatApp/view/MenuView',
'goatApp/view/TitleView'
], function ($,_,Backbone,LessonController,MenuController,LessonContentView,MenuView,TitleView) {
var lessonView = new LessonContentView();
var menuView = new MenuView();
var titleView = new TitleView();
var GoatAppRouter = Backbone.Router.extend({
routes: {
//#....
'welcome':'welcomeRoute',
'attack/:scr/:menu':'attackRoute' //
},
lessonController: new LessonController({
lessonView:lessonView
}),
menuController: new MenuController({
menuView:menuView,
titleView:titleView
}),
init:function() {
goatRouter = new GoatAppRouter();
this.lessonController.start();
this.menuController.initMenu();
goatRouter.on('route:attackRoute', function(scr,menu) {
console.log('attack route');
this.lessonController.loadLesson(scr,menu);
this.menuController.updateMenu(scr,menu);
//update menu
});
goatRouter.on('route:welcomeRoute', function() {
alert('welcome route');
});
Backbone.history.start();
}
});
return GoatAppRouter;
});

View File

@ -0,0 +1,41 @@
define(['jquery',
'underscore',
'backbone'],
function($,_,Backbone) {
return Backbone.View.extend({
el:'#help-controls', //Check this
helpButtons: {
//TODO: move this into a template
showSource:$('<button>',{id:'showSourceBtn','class':'btn btn-primary btn-xs help-button',type:'button',text:'Java [Source]'}),
showSolution:$('<button>',{id:'showSolutionBtn','class':'btn btn-primary btn-xs help-button',type:'button',text:'Solution'}),
showPlan:$('<button>',{id:'showPlanBtn','class':'btn btn-primary btn-xs help-button',type:'button',text:'Lesson Plan]'}),
showHints:$('<button>',{id:'showHintsBtn','class':'btn btn-primary btn-xs help-button',type:'button',text:'Hints'}),
restartLesson:$('<button>',{id:'restartLessonBtn','class':'btn btn-xs help-button',type:'button',text:'Restart Lesson'})
},
initialize: function (options) {
if (!options) {
return;
}
this.hasPlan = options.hasPlan;
this.hasSolution = options.hasSolution;
this.hasSource = options.hasSource;
this.hasHints = options.hasHints;
},
render:function(title) {
if (this.hasSource) {
this.$el.append(this.helpButtons.showSource);
}
if (this.hasSolution) {
this.$el.append(this.helpButtons.showSolution);
}
if (this.hasPlan) {
this.$el.append(this.helpButtons.showPlan);
}
if (this.hasHints) {
this.$el.append(this.helpButtons.showHints);
}
//
this.$el.append(this.helpButtons.restartLesson);
}
});
});

View File

@ -0,0 +1,23 @@
define(['jquery',
'underscore',
'backbone',
'goatApp/model/LessonSourceModel'],
function($,
_,
Backbone,
LessonSourceModel) {
return Backbone.View.extend({
el:'#lessonHelpWrapper .lessonHelp.lessonPlan', //Check this
initialize: function() {
this.model = new LessonSourceModel();
this.listenTo(this.model,'loaded',this.onModelLoaded);
this.model.loadData();
},
render:function(title) {
},
onModelLoaded: function() {
this.trigger(this.loadedMessage,this.helpElement);
}
});
});

View File

@ -0,0 +1,39 @@
//LessonContentView
define(['jquery',
'underscore',
'backbone',
'libs/jquery.form',
'goatApp/model/LessonContentData'],
function($,_,Backbone,JQueryForm,LessonData) {
return Backbone.View.extend({
el:'#lessonContentWrapper', //TODO << get this fixed up in DOM
initialize: function(options) {
options = options || {};
},
render: function() {
this.$el.html(this.model.get('content'));
this.makeFormsAjax();
},
//TODO: reimplement this in custom fashion maybe?
makeFormsAjax: function () {
var options = {
//target: '#lesson_content', // target element(s) to be updated with server response
//beforeSubmit: GoatUtils.showRequest, // pre-submit callback, comment out after debugging
//success: GoatUtils.showResponse // post-submit callback, comment out after debugging
success:this.reLoadView.bind(this),
url:'attack?Screen=' + this.model.get('screenParam') + '&menu=' + this.model.get('menuParam'),
type:'GET'
// $.ajax options can be used here too, for example:
//timeout: 3000
};
//hook forms //TODO: clarify form selectors later
$("form").ajaxForm(options);
},
reLoadView: function(content) {
this.model.setContent(content);
this.render();
}
});
});

View File

@ -0,0 +1,22 @@
define(['jquery',
'underscore',
'backbone',
'goatApp/model/LessonHintCollection'],
function($,
_,
Backbone,
LessonHintCollection) {
return Backbone.View.extend({
el:'#lessonHelpWrapper .lessonHelp.lessonHint',
initialize: function() {
this.collection = new LessonHintCollection();
this.listenTo(this.collection,'hints:loaded',this.onModelLoaded);
},
render:function(title) {
},
onModelLoaded: function() {
this.trigger('hints:loaded',{'helpElement':'hints','value':true})
}
});
});

View File

@ -0,0 +1,30 @@
define(['jquery',
'underscore',
'backbone',
'goatApp/support/GoatUtils',
'goatApp/view/MenuItemView'],
function(
$,
_,
Backbone,
GoatUtils,
MenuItemView) {
return Backbone.View.extend({
initialize: function(options) {
options = options || {};
this.items = options.items;
},
render: function() {
var viewItems = [];
for (var i=0;i<this.items.length;i++) {
var listItem = $('<li>',{text:this.items[i].name});
//viewItems
viewItems.push(listItem);
}
return viewItems;
}
});
});

View File

@ -0,0 +1,14 @@
define(['jquery',
'underscore',
'backbone'], function($,_,Backbone) {
return Backbone.View.extend({
initialize: function(options) {
options = options || {};
}
});
});

View File

@ -0,0 +1,96 @@
define(['jquery',
'underscore',
'backbone',
'goatApp/model/MenuCollection',
'goatApp/view/MenuItemView',
'goatApp/support/GoatUtils'],
function(
$,
_,
Backbone,
MenuCollection,
MenuItemView,
GoatUtils) {
return Backbone.View.extend({
el:'#menu-container',
//TODO: set template
initialize: function() {
this.collection = new MenuCollection();
this.listenTo(this.collection,'menuData:loaded',this.render);
this.listenTo(this,'menu:click',this.accordionMenu);
},
// rendering top level menu
render: function (model){
//for now, just brute force
//TODO: refactor into sub-views/components
var items, catItems, stages;
items = this.collection.models; // top level items
var menuMarkup = '';
var menuUl = $('<ul>',{class:'nano-content'});
for(var i=0;i<items.length;i++) { //CATEGORY LEVEL
var catId, category, catLink, catArrow, catLinkText, lessonName, stageName;
catId = GoatUtils.makeId(items[i].get('name'));
category = $('<li>',{class:'sub-menu ng-scope'});
catLink = $('<a>',{'category':catId});
catArrow = $('<i>',{class:'fa fa-angle-right pull-right'});
catLinkText = $('<span>',{text:items[i].get('name')});
catLink.append(catArrow);
catLink.append(catLinkText);
//TODO: refactor this along with sub-views/components
var self = this;
catLink.click(_.bind(this.expandCategory,this,catId));
category.append(catLink);
// lesson level (first children level)
//var lessons = new MenuItemView({items:items[i].get('children')}).render();
var lessons=items[i].get('children');
if (lessons) {
var categoryLessonList = $('<ul>',{class:'slideDown lessonsAndStages',id:catId}); //keepOpen
for (var j=0; j < lessons.length;j++) {
var lessonItem = $('<li>');
lessonName = lessons[j].name;
var lessonLink = $('<a>',{href:lessons[j].link,text:lessonName,id:lessonName});
lessonLink.click(_.bind(this.triggerTitleRender,this,lessonName));
lessonItem.append(lessonLink);
//check for lab/stages
categoryLessonList.append(lessonItem);
var stages = lessons[j].children;
for (k=0; k < stages.length; k++) {
var stageName = stages[k].name;
var stageSpan = $('<span>');
var stageLink = $('<a>',{href:stages[k].link,text:stageName,id:GoatUtils.makeId(stageName)});
stageSpan.append(stageLink);
categoryLessonList.append(stageSpan);
}
}
category.append(categoryLessonList);
}
menuUl.append(category);
}
this.$el.append(menuUl);
//if we need to keep a menu open
if (this.openMenu) {
this.accordionMenu(this.openMenu);
}
},
triggerTitleRender: function (title) {
this.trigger('lesson:click',title);
},
expandCategory: function (id) {
if (id) {
this.accordionMenu(id);
}
},
accordionMenu: function(id) {
if (this.openMenu !== id) {
this.$el.find('#' + id).slideDown(300);
this.openMenu = id;
} else { //it's open
this.$el.find('#' + id).slideUp(300).attr('isOpen', 0);
this.openMenu = null;
return;
}
}
});
});

View File

@ -0,0 +1,23 @@
define(['jquery',
'underscore',
'backbone',
'goatApp/model/LessonPlanModel'],
function($,
_,
Backbone,
LessonPlanModel) {
return Backbone.View.extend({
el:'#lessonHelpWrapper .lessonHelp.lessonPlan', //Check this
initialize: function() {
this.model = new LessonPlanModel();
this.listenTo(this.model,'loaded',this.onModelLoaded);
this.model.loadData();
},
render:function(title) {
},
onModelLoaded: function() {
this.trigger('plan:loaded',{'helpElement':'plan','value':true});
}
});
});

View File

@ -0,0 +1,22 @@
define(['jquery',
'underscore',
'backbone',
'goatApp/model/LessonSolutionModel'],
//TODO: create a base 'HelpView class'
function($,_,Backbone,LessonSolutionModel) {
return Backbone.View.extend({
el:'#lessonHelpWrapper .lessonHelp.lessonSolution', //Check this
initialize: function() {
this.model = new LessonSolutionModel();
this.listenTo(this.model,'loaded',this.onModelLoaded);
this.model.loadData();
//TODO: handle error cases
},
render:function(title) {
},
onModelLoaded: function() {
this.trigger('solution:loaded',{'helpElement':'solution','value':true});
}
});
});

View File

@ -0,0 +1,21 @@
define(['jquery',
'underscore',
'backbone',
'goatApp/model/LessonSourceModel',
'goatApp/view/HelpView'],
function($,
_,
Backbone,
LessonSourceModel,
HelpView) {
return HelpView.extend({
helpElement:{'helpElement':'source','value':true},
loadedMessage:'source:loaded',
el:'#lessonHelpWrapper .lessonHelp.lessonPlan', //Check this
initialize: function() {
this.model = new LessonSourceModel();
this.listenTo(this.model,'loaded',this.onModelLoaded);
this.model.loadData();
}
});
});

View File

@ -0,0 +1,13 @@
define(['jquery',
'underscore',
'backbone'],
function($,_,Backbone) {
return Backbone.View.extend({
el:'#header #lesson-title-wrapper',
render:function(title) {
var lessonTitleEl = $('<h1>',{id:'lesson-title',text:title});
this.$el.html(lessonTitleEl);
//this.$el.append(lessonTitleEl);
}
});
});

View File

@ -0,0 +1,44 @@
define(['jquery',
'underscore',
'backbone',
'goatApp/controller/LessonController',
'goatApp/controller/MenuController',
'goatApp/view/LessonContentView',
'goatApp/view/MenuView'
], function ($,_,Backbone,LessonController,MenuController,LessonContentView,MenuView) {
var lessonView = new LessonContentView();
var menuView = new MenuView();
var GoatAppRouter = Backbone.Router.extend({
routes: {
//#....
'welcome':'welcomeRoute',
'attack/:scr/:menu':'attackRoute' //
},
lessoonController: lessoonController({
lessonView:lessonView
}),
menuView: new MenuController({
menuView:menuView
})
});
var init = function() {
goatRouter = new GoatAppRouter();
goatRouter.on('route:attackRoute', function(scr,menu) {
this.lessonController.loadLesson(scr,menu);
//update menu
});
goatRouter.on('route:welcomeRoute', function() {
alert('welcome route');
});
// init the history/router
Backbone.history.start();
}
return {
init:init
};
});

View File

@ -1,189 +0,0 @@
goat.utils = {
//TODO add recursion to handle arr[i].children objects
// ... in case lower-level's need classes as well ... don't right now
addMenuClasses: function(arr) {
for (var i = 0; i < arr.length; i++) {
var menuItem = arr[i];
//console.log(menuItem);
if (menuItem.type && menuItem.type === 'CATEGORY') {
menuItem.class = 'fa-angle-right pull-right';
}
}
return arr;
},
debugFormSubmission: false,
// pre-submit callback
showRequest: function(formData, jqForm, options) {
if (goat.utils.debugFormSubmission) {
// formData is an array; here we use $.param to convert it to a string to display it
// but the form plugin does this for you automatically when it submits the data
var queryString = $.param(formData);
// jqForm is a jQuery object encapsulating the form element. To access the
// DOM element for the form do this:
// var formElement = jqForm[0];
alert('About to submit: \n\n' + queryString);
}
// here we could return false to prevent the form from being submitted;
// returning anything other than false will allow the form submit to continue
return true;
},
// post-submit callback
showResponse: function(responseText, statusText, xhr, $form) {
// for normal html responses, the first argument to the success callback
// is the XMLHttpRequest object's responseText property
// if the ajaxForm method was passed an Options Object with the dataType
// property set to 'xml' then the first argument to the success callback
// is the XMLHttpRequest object's responseXML property
// if the ajaxForm method was passed an Options Object with the dataType
// property set to 'json' then the first argument to the success callback
// is the json data object returned by the server
if (goat.utils.debugFormSubmission) {
alert('status: ' + statusText + '\n\nresponseText: \n' + responseText +
'\n\nThe output div should have already been updated with the responseText.');
}
// update lesson cookies and params
// make any embedded forms ajaxy
goat.utils.showLessonCookiesAndParams();
// forms and links are now hooked with each standard lesson render (see Java class Screen.getContent())
// but these are safe to call twice
goat.utils.makeFormsAjax();
goat.utils.ajaxifyAttackHref(); //TODO find some way to hook scope for current menu. Likely needs larger refactor which is already started/stashed
//refresh menu
angular.element($('#leftside-navigation')).scope().renderMenu();
},
makeFormsAjax: function() {
// make all forms ajax forms
var options = {
target: '#lesson_content', // target element(s) to be updated with server response
beforeSubmit: goat.utils.showRequest, // pre-submit callback, comment out after debugging
success: goat.utils.showResponse // post-submit callback, comment out after debugging
// other available options:
//url: url // override for form's 'action' attribute
//type: type // 'get' or 'post', override for form's 'method' attribute
//dataType: null // 'xml', 'script', or 'json' (expected server response type)
//clearForm: true // clear all form fields after successful submit
//resetForm: true // reset the form after successful submit
// $.ajax options can be used here too, for example:
//timeout: 3000
};
//console.log("Hooking any lesson forms to make them ajax");
$("form").ajaxForm(options);
},
displayButton: function(id, show) {
if ($('#' + id)) {
if (show) {
$('#' + id).show();
} else {
$('#' + id).hide();
}
}
},
showLessonCookiesAndParams: function() {
$.get(goatConstants.cookieService, {}, function(reply) {
$("#lesson_cookies").html(reply);
}, "html");
},
showLessonHints: function() {
$('.lessonHelp').hide();
$('#lesson_hint').html();
$('#lesson_hint_row').show();
},
showLessonSource: function(source) {
$('.lessonHelp').hide();
//$('#lesson_source').html("<pre>"+goat.lesson.lessonInfo.source+"</pre>");
$('#lesson_source_row').show();
goat.utils.scrollToHelp();
},
showLessonSolution: function() {
$('.lessonHelp').hide();
$('#lesson_solution').html(goat.lesson.lessonInfo.solution);
$('#lesson_solution_row').show();
goat.utils.scrollToHelp();
},
showLessonPlan: function(plan) {
$('.lessonHelp').hide();
$("#lesson_plan").html(goat.lesson.lessonInfo.plan);
$('#lesson_plan_row').show();
goat.utils.scrollToHelp();
},
scrollToHelp: function() {
$('#leftside-navigation').height($('#main-content').height() + 15)
var target = $('#lessonHelpsWrapper');
goat.utils.scrollEasy(target);
},
scrollToTop: function() {
$('.lessonHelp').hide();
var target = $('#container');
goat.utils.scrollEasy(target);
},
scrollEasy: function(target) {
$('html,body').animate({
scrollTop: target.offset().top
}, 1000);
},
scrapeParams: function(url) {
if (!url) {
return;
}
var params = url.split('?')[1].split('&');
var paramsArr = [];
for (var i = 0; i < params.length; i++) {
var paramObj = {};
paramObj.name = params[i].split('=')[0];
paramObj.value = params[i].split('=')[1];
paramsArr.push(paramObj);
}
return paramsArr;
},
highlightCurrentLessonMenu: function(id) {
//TODO: move selectors in first two lines into goatConstants
$('ul li.selected').removeClass(goatConstants.selectedMenuClass)
$('ul li.selected a.selected').removeClass(goatConstants.selectedMenuClass)
$('#' + id).addClass(goatConstants.selectedMenuClass);
$('#' + id).parent().addClass(goatConstants.selectedMenuClass);
},
makeId: function(lessonName) {
return lessonName.replace(/\s|\(|\)|\!|\:|\;|\@|\#|\$|\%|\^|\&|\*/g, '');//TODO move the replace routine into util function
},
ajaxifyAttackHref: function() {
/* Jason I commented this implementation out
* I think we should show the attack link on the lessons that need it by modifying the lesson
* itself or we could add a new button up top for "show lesson link"
$.each($('a[href^="attack?"]'),
function(i,el) {
var url = $(el).attr('href');
$(el).attr('href','#');
$(el).attr('link',url);
//TODO pull currentMenuId
$(el).click(
function() {
var _url = $(el).attr('link');
$.get(_url, {success:showResponse});
}
)
}
);
*/
// alternate implementation
// unbind any bound events so we are safe to be called twice
$('#lesson_content a').unbind('click');
$('#lesson_content a').bind('click', function(event) {
event.preventDefault();
$.get(this.href, {}, function(response) {
$('#lesson_content').html(response);
});
});
}
};
$(window).resize(function() {
//$('#leftside-navigation').css('height',$('div.panel-body').height());
console.log($(window).height());
});

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

36
src/main/webapp/js/libs/require.min.js vendored Normal file
View File

@ -0,0 +1,36 @@
/*
RequireJS 2.1.16 Copyright (c) 2010-2015, The Dojo Foundation All Rights Reserved.
Available via the MIT or new BSD license.
see: http://github.com/jrburke/requirejs for details
*/
var requirejs,require,define;
(function(ba){function G(b){return"[object Function]"===K.call(b)}function H(b){return"[object Array]"===K.call(b)}function v(b,c){if(b){var d;for(d=0;d<b.length&&(!b[d]||!c(b[d],d,b));d+=1);}}function T(b,c){if(b){var d;for(d=b.length-1;-1<d&&(!b[d]||!c(b[d],d,b));d-=1);}}function t(b,c){return fa.call(b,c)}function m(b,c){return t(b,c)&&b[c]}function B(b,c){for(var d in b)if(t(b,d)&&c(b[d],d))break}function U(b,c,d,e){c&&B(c,function(c,g){if(d||!t(b,g))e&&"object"===typeof c&&c&&!H(c)&&!G(c)&&!(c instanceof
RegExp)?(b[g]||(b[g]={}),U(b[g],c,d,e)):b[g]=c});return b}function u(b,c){return function(){return c.apply(b,arguments)}}function ca(b){throw b;}function da(b){if(!b)return b;var c=ba;v(b.split("."),function(b){c=c[b]});return c}function C(b,c,d,e){c=Error(c+"\nhttp://requirejs.org/docs/errors.html#"+b);c.requireType=b;c.requireModules=e;d&&(c.originalError=d);return c}function ga(b){function c(a,k,b){var f,l,c,d,e,g,i,p,k=k&&k.split("/"),h=j.map,n=h&&h["*"];if(a){a=a.split("/");l=a.length-1;j.nodeIdCompat&&
Q.test(a[l])&&(a[l]=a[l].replace(Q,""));"."===a[0].charAt(0)&&k&&(l=k.slice(0,k.length-1),a=l.concat(a));l=a;for(c=0;c<l.length;c++)if(d=l[c],"."===d)l.splice(c,1),c-=1;else if(".."===d&&!(0===c||1==c&&".."===l[2]||".."===l[c-1])&&0<c)l.splice(c-1,2),c-=2;a=a.join("/")}if(b&&h&&(k||n)){l=a.split("/");c=l.length;a:for(;0<c;c-=1){e=l.slice(0,c).join("/");if(k)for(d=k.length;0<d;d-=1)if(b=m(h,k.slice(0,d).join("/")))if(b=m(b,e)){f=b;g=c;break a}!i&&(n&&m(n,e))&&(i=m(n,e),p=c)}!f&&i&&(f=i,g=p);f&&(l.splice(0,
g,f),a=l.join("/"))}return(f=m(j.pkgs,a))?f:a}function d(a){z&&v(document.getElementsByTagName("script"),function(k){if(k.getAttribute("data-requiremodule")===a&&k.getAttribute("data-requirecontext")===i.contextName)return k.parentNode.removeChild(k),!0})}function e(a){var k=m(j.paths,a);if(k&&H(k)&&1<k.length)return k.shift(),i.require.undef(a),i.makeRequire(null,{skipMap:!0})([a]),!0}function n(a){var k,c=a?a.indexOf("!"):-1;-1<c&&(k=a.substring(0,c),a=a.substring(c+1,a.length));return[k,a]}function p(a,
k,b,f){var l,d,e=null,g=k?k.name:null,j=a,p=!0,h="";a||(p=!1,a="_@r"+(K+=1));a=n(a);e=a[0];a=a[1];e&&(e=c(e,g,f),d=m(r,e));a&&(e?h=d&&d.normalize?d.normalize(a,function(a){return c(a,g,f)}):-1===a.indexOf("!")?c(a,g,f):a:(h=c(a,g,f),a=n(h),e=a[0],h=a[1],b=!0,l=i.nameToUrl(h)));b=e&&!d&&!b?"_unnormalized"+(O+=1):"";return{prefix:e,name:h,parentMap:k,unnormalized:!!b,url:l,originalName:j,isDefine:p,id:(e?e+"!"+h:h)+b}}function s(a){var k=a.id,b=m(h,k);b||(b=h[k]=new i.Module(a));return b}function q(a,
k,b){var f=a.id,c=m(h,f);if(t(r,f)&&(!c||c.defineEmitComplete))"defined"===k&&b(r[f]);else if(c=s(a),c.error&&"error"===k)b(c.error);else c.on(k,b)}function w(a,b){var c=a.requireModules,f=!1;if(b)b(a);else if(v(c,function(b){if(b=m(h,b))b.error=a,b.events.error&&(f=!0,b.emit("error",a))}),!f)g.onError(a)}function x(){R.length&&(ha.apply(A,[A.length,0].concat(R)),R=[])}function y(a){delete h[a];delete V[a]}function F(a,b,c){var f=a.map.id;a.error?a.emit("error",a.error):(b[f]=!0,v(a.depMaps,function(f,
d){var e=f.id,g=m(h,e);g&&(!a.depMatched[d]&&!c[e])&&(m(b,e)?(a.defineDep(d,r[e]),a.check()):F(g,b,c))}),c[f]=!0)}function D(){var a,b,c=(a=1E3*j.waitSeconds)&&i.startTime+a<(new Date).getTime(),f=[],l=[],g=!1,h=!0;if(!W){W=!0;B(V,function(a){var i=a.map,j=i.id;if(a.enabled&&(i.isDefine||l.push(a),!a.error))if(!a.inited&&c)e(j)?g=b=!0:(f.push(j),d(j));else if(!a.inited&&(a.fetched&&i.isDefine)&&(g=!0,!i.prefix))return h=!1});if(c&&f.length)return a=C("timeout","Load timeout for modules: "+f,null,
f),a.contextName=i.contextName,w(a);h&&v(l,function(a){F(a,{},{})});if((!c||b)&&g)if((z||ea)&&!X)X=setTimeout(function(){X=0;D()},50);W=!1}}function E(a){t(r,a[0])||s(p(a[0],null,!0)).init(a[1],a[2])}function I(a){var a=a.currentTarget||a.srcElement,b=i.onScriptLoad;a.detachEvent&&!Y?a.detachEvent("onreadystatechange",b):a.removeEventListener("load",b,!1);b=i.onScriptError;(!a.detachEvent||Y)&&a.removeEventListener("error",b,!1);return{node:a,id:a&&a.getAttribute("data-requiremodule")}}function J(){var a;
for(x();A.length;){a=A.shift();if(null===a[0])return w(C("mismatch","Mismatched anonymous define() module: "+a[a.length-1]));E(a)}}var W,Z,i,L,X,j={waitSeconds:7,baseUrl:"./",paths:{},bundles:{},pkgs:{},shim:{},config:{}},h={},V={},$={},A=[],r={},S={},aa={},K=1,O=1;L={require:function(a){return a.require?a.require:a.require=i.makeRequire(a.map)},exports:function(a){a.usingExports=!0;if(a.map.isDefine)return a.exports?r[a.map.id]=a.exports:a.exports=r[a.map.id]={}},module:function(a){return a.module?
a.module:a.module={id:a.map.id,uri:a.map.url,config:function(){return m(j.config,a.map.id)||{}},exports:a.exports||(a.exports={})}}};Z=function(a){this.events=m($,a.id)||{};this.map=a;this.shim=m(j.shim,a.id);this.depExports=[];this.depMaps=[];this.depMatched=[];this.pluginMaps={};this.depCount=0};Z.prototype={init:function(a,b,c,f){f=f||{};if(!this.inited){this.factory=b;if(c)this.on("error",c);else this.events.error&&(c=u(this,function(a){this.emit("error",a)}));this.depMaps=a&&a.slice(0);this.errback=
c;this.inited=!0;this.ignore=f.ignore;f.enabled||this.enabled?this.enable():this.check()}},defineDep:function(a,b){this.depMatched[a]||(this.depMatched[a]=!0,this.depCount-=1,this.depExports[a]=b)},fetch:function(){if(!this.fetched){this.fetched=!0;i.startTime=(new Date).getTime();var a=this.map;if(this.shim)i.makeRequire(this.map,{enableBuildCallback:!0})(this.shim.deps||[],u(this,function(){return a.prefix?this.callPlugin():this.load()}));else return a.prefix?this.callPlugin():this.load()}},load:function(){var a=
this.map.url;S[a]||(S[a]=!0,i.load(this.map.id,a))},check:function(){if(this.enabled&&!this.enabling){var a,b,c=this.map.id;b=this.depExports;var f=this.exports,l=this.factory;if(this.inited)if(this.error)this.emit("error",this.error);else{if(!this.defining){this.defining=!0;if(1>this.depCount&&!this.defined){if(G(l)){if(this.events.error&&this.map.isDefine||g.onError!==ca)try{f=i.execCb(c,l,b,f)}catch(d){a=d}else f=i.execCb(c,l,b,f);this.map.isDefine&&void 0===f&&((b=this.module)?f=b.exports:this.usingExports&&
(f=this.exports));if(a)return a.requireMap=this.map,a.requireModules=this.map.isDefine?[this.map.id]:null,a.requireType=this.map.isDefine?"define":"require",w(this.error=a)}else f=l;this.exports=f;if(this.map.isDefine&&!this.ignore&&(r[c]=f,g.onResourceLoad))g.onResourceLoad(i,this.map,this.depMaps);y(c);this.defined=!0}this.defining=!1;this.defined&&!this.defineEmitted&&(this.defineEmitted=!0,this.emit("defined",this.exports),this.defineEmitComplete=!0)}}else this.fetch()}},callPlugin:function(){var a=
this.map,b=a.id,d=p(a.prefix);this.depMaps.push(d);q(d,"defined",u(this,function(f){var l,d;d=m(aa,this.map.id);var e=this.map.name,P=this.map.parentMap?this.map.parentMap.name:null,n=i.makeRequire(a.parentMap,{enableBuildCallback:!0});if(this.map.unnormalized){if(f.normalize&&(e=f.normalize(e,function(a){return c(a,P,!0)})||""),f=p(a.prefix+"!"+e,this.map.parentMap),q(f,"defined",u(this,function(a){this.init([],function(){return a},null,{enabled:!0,ignore:!0})})),d=m(h,f.id)){this.depMaps.push(f);
if(this.events.error)d.on("error",u(this,function(a){this.emit("error",a)}));d.enable()}}else d?(this.map.url=i.nameToUrl(d),this.load()):(l=u(this,function(a){this.init([],function(){return a},null,{enabled:!0})}),l.error=u(this,function(a){this.inited=!0;this.error=a;a.requireModules=[b];B(h,function(a){0===a.map.id.indexOf(b+"_unnormalized")&&y(a.map.id)});w(a)}),l.fromText=u(this,function(f,c){var d=a.name,e=p(d),P=M;c&&(f=c);P&&(M=!1);s(e);t(j.config,b)&&(j.config[d]=j.config[b]);try{g.exec(f)}catch(h){return w(C("fromtexteval",
"fromText eval for "+b+" failed: "+h,h,[b]))}P&&(M=!0);this.depMaps.push(e);i.completeLoad(d);n([d],l)}),f.load(a.name,n,l,j))}));i.enable(d,this);this.pluginMaps[d.id]=d},enable:function(){V[this.map.id]=this;this.enabling=this.enabled=!0;v(this.depMaps,u(this,function(a,b){var c,f;if("string"===typeof a){a=p(a,this.map.isDefine?this.map:this.map.parentMap,!1,!this.skipMap);this.depMaps[b]=a;if(c=m(L,a.id)){this.depExports[b]=c(this);return}this.depCount+=1;q(a,"defined",u(this,function(a){this.defineDep(b,
a);this.check()}));this.errback?q(a,"error",u(this,this.errback)):this.events.error&&q(a,"error",u(this,function(a){this.emit("error",a)}))}c=a.id;f=h[c];!t(L,c)&&(f&&!f.enabled)&&i.enable(a,this)}));B(this.pluginMaps,u(this,function(a){var b=m(h,a.id);b&&!b.enabled&&i.enable(a,this)}));this.enabling=!1;this.check()},on:function(a,b){var c=this.events[a];c||(c=this.events[a]=[]);c.push(b)},emit:function(a,b){v(this.events[a],function(a){a(b)});"error"===a&&delete this.events[a]}};i={config:j,contextName:b,
registry:h,defined:r,urlFetched:S,defQueue:A,Module:Z,makeModuleMap:p,nextTick:g.nextTick,onError:w,configure:function(a){a.baseUrl&&"/"!==a.baseUrl.charAt(a.baseUrl.length-1)&&(a.baseUrl+="/");var b=j.shim,c={paths:!0,bundles:!0,config:!0,map:!0};B(a,function(a,b){c[b]?(j[b]||(j[b]={}),U(j[b],a,!0,!0)):j[b]=a});a.bundles&&B(a.bundles,function(a,b){v(a,function(a){a!==b&&(aa[a]=b)})});a.shim&&(B(a.shim,function(a,c){H(a)&&(a={deps:a});if((a.exports||a.init)&&!a.exportsFn)a.exportsFn=i.makeShimExports(a);
b[c]=a}),j.shim=b);a.packages&&v(a.packages,function(a){var b,a="string"===typeof a?{name:a}:a;b=a.name;a.location&&(j.paths[b]=a.location);j.pkgs[b]=a.name+"/"+(a.main||"main").replace(ia,"").replace(Q,"")});B(h,function(a,b){!a.inited&&!a.map.unnormalized&&(a.map=p(b))});if(a.deps||a.callback)i.require(a.deps||[],a.callback)},makeShimExports:function(a){return function(){var b;a.init&&(b=a.init.apply(ba,arguments));return b||a.exports&&da(a.exports)}},makeRequire:function(a,e){function j(c,d,m){var n,
q;e.enableBuildCallback&&(d&&G(d))&&(d.__requireJsBuild=!0);if("string"===typeof c){if(G(d))return w(C("requireargs","Invalid require call"),m);if(a&&t(L,c))return L[c](h[a.id]);if(g.get)return g.get(i,c,a,j);n=p(c,a,!1,!0);n=n.id;return!t(r,n)?w(C("notloaded",'Module name "'+n+'" has not been loaded yet for context: '+b+(a?"":". Use require([])"))):r[n]}J();i.nextTick(function(){J();q=s(p(null,a));q.skipMap=e.skipMap;q.init(c,d,m,{enabled:!0});D()});return j}e=e||{};U(j,{isBrowser:z,toUrl:function(b){var d,
e=b.lastIndexOf("."),k=b.split("/")[0];if(-1!==e&&(!("."===k||".."===k)||1<e))d=b.substring(e,b.length),b=b.substring(0,e);return i.nameToUrl(c(b,a&&a.id,!0),d,!0)},defined:function(b){return t(r,p(b,a,!1,!0).id)},specified:function(b){b=p(b,a,!1,!0).id;return t(r,b)||t(h,b)}});a||(j.undef=function(b){x();var c=p(b,a,!0),e=m(h,b);d(b);delete r[b];delete S[c.url];delete $[b];T(A,function(a,c){a[0]===b&&A.splice(c,1)});e&&(e.events.defined&&($[b]=e.events),y(b))});return j},enable:function(a){m(h,a.id)&&
s(a).enable()},completeLoad:function(a){var b,c,d=m(j.shim,a)||{},g=d.exports;for(x();A.length;){c=A.shift();if(null===c[0]){c[0]=a;if(b)break;b=!0}else c[0]===a&&(b=!0);E(c)}c=m(h,a);if(!b&&!t(r,a)&&c&&!c.inited){if(j.enforceDefine&&(!g||!da(g)))return e(a)?void 0:w(C("nodefine","No define call for "+a,null,[a]));E([a,d.deps||[],d.exportsFn])}D()},nameToUrl:function(a,b,c){var d,e,h;(d=m(j.pkgs,a))&&(a=d);if(d=m(aa,a))return i.nameToUrl(d,b,c);if(g.jsExtRegExp.test(a))d=a+(b||"");else{d=j.paths;
a=a.split("/");for(e=a.length;0<e;e-=1)if(h=a.slice(0,e).join("/"),h=m(d,h)){H(h)&&(h=h[0]);a.splice(0,e,h);break}d=a.join("/");d+=b||(/^data\:|\?/.test(d)||c?"":".js");d=("/"===d.charAt(0)||d.match(/^[\w\+\.\-]+:/)?"":j.baseUrl)+d}return j.urlArgs?d+((-1===d.indexOf("?")?"?":"&")+j.urlArgs):d},load:function(a,b){g.load(i,a,b)},execCb:function(a,b,c,d){return b.apply(d,c)},onScriptLoad:function(a){if("load"===a.type||ja.test((a.currentTarget||a.srcElement).readyState))N=null,a=I(a),i.completeLoad(a.id)},
onScriptError:function(a){var b=I(a);if(!e(b.id))return w(C("scripterror","Script error for: "+b.id,a,[b.id]))}};i.require=i.makeRequire();return i}var g,x,y,D,I,E,N,J,s,O,ka=/(\/\*([\s\S]*?)\*\/|([^:]|^)\/\/(.*)$)/mg,la=/[^.]\s*require\s*\(\s*["']([^'"\s]+)["']\s*\)/g,Q=/\.js$/,ia=/^\.\//;x=Object.prototype;var K=x.toString,fa=x.hasOwnProperty,ha=Array.prototype.splice,z=!!("undefined"!==typeof window&&"undefined"!==typeof navigator&&window.document),ea=!z&&"undefined"!==typeof importScripts,ja=
z&&"PLAYSTATION 3"===navigator.platform?/^complete$/:/^(complete|loaded)$/,Y="undefined"!==typeof opera&&"[object Opera]"===opera.toString(),F={},q={},R=[],M=!1;if("undefined"===typeof define){if("undefined"!==typeof requirejs){if(G(requirejs))return;q=requirejs;requirejs=void 0}"undefined"!==typeof require&&!G(require)&&(q=require,require=void 0);g=requirejs=function(b,c,d,e){var n,p="_";!H(b)&&"string"!==typeof b&&(n=b,H(c)?(b=c,c=d,d=e):b=[]);n&&n.context&&(p=n.context);(e=m(F,p))||(e=F[p]=g.s.newContext(p));
n&&e.configure(n);return e.require(b,c,d)};g.config=function(b){return g(b)};g.nextTick="undefined"!==typeof setTimeout?function(b){setTimeout(b,4)}:function(b){b()};require||(require=g);g.version="2.1.16";g.jsExtRegExp=/^\/|:|\?|\.js$/;g.isBrowser=z;x=g.s={contexts:F,newContext:ga};g({});v(["toUrl","undef","defined","specified"],function(b){g[b]=function(){var c=F._;return c.require[b].apply(c,arguments)}});if(z&&(y=x.head=document.getElementsByTagName("head")[0],D=document.getElementsByTagName("base")[0]))y=
x.head=D.parentNode;g.onError=ca;g.createNode=function(b){var c=b.xhtml?document.createElementNS("http://www.w3.org/1999/xhtml","html:script"):document.createElement("script");c.type=b.scriptType||"text/javascript";c.charset="utf-8";c.async=!0;return c};g.load=function(b,c,d){var e=b&&b.config||{};if(z)return e=g.createNode(e,c,d),e.setAttribute("data-requirecontext",b.contextName),e.setAttribute("data-requiremodule",c),e.attachEvent&&!(e.attachEvent.toString&&0>e.attachEvent.toString().indexOf("[native code"))&&
!Y?(M=!0,e.attachEvent("onreadystatechange",b.onScriptLoad)):(e.addEventListener("load",b.onScriptLoad,!1),e.addEventListener("error",b.onScriptError,!1)),e.src=d,J=e,D?y.insertBefore(e,D):y.appendChild(e),J=null,e;if(ea)try{importScripts(d),b.completeLoad(c)}catch(m){b.onError(C("importscripts","importScripts failed for "+c+" at "+d,m,[c]))}};z&&!q.skipDataMain&&T(document.getElementsByTagName("script"),function(b){y||(y=b.parentNode);if(I=b.getAttribute("data-main"))return s=I,q.baseUrl||(E=s.split("/"),
s=E.pop(),O=E.length?E.join("/")+"/":"./",q.baseUrl=O),s=s.replace(Q,""),g.jsExtRegExp.test(s)&&(s=I),q.deps=q.deps?q.deps.concat(s):[s],!0});define=function(b,c,d){var e,g;"string"!==typeof b&&(d=c,c=b,b=null);H(c)||(d=c,c=null);!c&&G(d)&&(c=[],d.length&&(d.toString().replace(ka,"").replace(la,function(b,d){c.push(d)}),c=(1===d.length?["require"]:["require","exports","module"]).concat(c)));if(M){if(!(e=J))N&&"interactive"===N.readyState||T(document.getElementsByTagName("script"),function(b){if("interactive"===
b.readyState)return N=b}),e=N;e&&(b||(b=e.getAttribute("data-requiremodule")),g=F[e.getAttribute("data-requirecontext")])}(g?g.defQueue:R).push([b,c,d])};define.amd={jQuery:!0};g.exec=function(b){return eval(b)};g(q)}})(this);

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,34 @@
//main.js
/*
/js
js/main.js << main file for require.js
--/libs/(jquery,backbone,etc.) << base libs
--/goatApp/ << base dir for goat application, js-wise
--/goatApp/model
--/goatApp/view
--/goatApp/support
--/goatApp/controller
*/
require.config({
baseUrl: "js/",
paths: {
jquery: 'libs/jquery-1.10.2.min',
underscore: 'libs/underscore-min',
backbone: 'libs/backbone-min'
}
,
shim: {
underscore: {
exports: "_"
},
backbone: {
deps: ['underscore', 'jquery'],
exports: 'Backbone'
}
}
});
require(['jquery','underscore','backbone','goatApp/goatApp'], function($,_,Backbone,Goat){
Goat.initApp();
});