diff --git a/src/main/webapp/WEB-INF/pages/main_new.jsp b/src/main/webapp/WEB-INF/pages/main_new.jsp index 2aaf174a7..ce8ad6982 100644 --- a/src/main/webapp/WEB-INF/pages/main_new.jsp +++ b/src/main/webapp/WEB-INF/pages/main_new.jsp @@ -87,7 +87,7 @@ diff --git a/src/main/webapp/css/main.css b/src/main/webapp/css/main.css index 87bbecd7d..9c45a472e 100644 --- a/src/main/webapp/css/main.css +++ b/src/main/webapp/css/main.css @@ -747,7 +747,7 @@ cookieContainer { z-index: 100; } -#leftside-navigation { +#menu-container { overflow-y:scroll; overflow-x:hidden; } @@ -761,18 +761,18 @@ cookieContainer { .sidebar-toggle { margin-left: -240px; } -#leftside-navigation ul, -#leftside-navigation ul ul { +#menu-container ul, +#menu-container ul ul { margin: -2px 0 0; padding: 0; } -#leftside-navigation ul li { +#menu-container ul li { list-style-type: none; border-bottom: 1px solid rgba(255, 255, 255, 0.05); } -#leftside-navigation ul li a { +#menu-container ul li a { color: #aeb2b7; text-decoration: none; display: block; @@ -785,38 +785,38 @@ cookieContainer { -ms-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; } -#leftside-navigation ul ul li { +#menu-container ul ul li { background: #333; margin-bottom: 0; margin-left: 0; margin-right: 0; border-bottom: none; } -#leftside-navigation ul ul li a { +#menu-container ul ul li a { font-size: 11px; padding-top: 5px; padding-bottom: 5px; color: #aeb2b7; margin-left:8px; } -#leftside-navigation ul li a i { +#menu-container ul li a i { width: 20px; } -#leftside-navigation ul li a i.fa-angle-right, -#leftside-navigation ul li a i.fa-angle-left { +#menu-container ul li a i.fa-angle-right, +#menu-container ul li a i.fa-angle-left { padding-top: 3px; } -#leftside-navigation ul ul { +#menu-container ul ul { display: none; } -#leftside-navigation li.active ul { +#menu-container li.active ul { display: block; } -#leftside-navigation ul li a:hover, -#leftside-navigation ul li.active > a { +#menu-container ul li a:hover, +#menu-container ul li.active > a { color: #e84c3d; } @@ -827,15 +827,15 @@ cookieContainer { padding-top: 15px; } -#leftside-navigation ul li.selected { +#menu-container ul li.selected { background-color: #555; } -#leftside-navigation ul li.selected a.selected { +#menu-container ul li.selected a.selected { color:white; } -#leftside-navigation ul ul.lessonsAndStages.keepOpen { +#menu-container ul ul.lessonsAndStages.keepOpen { display: block } diff --git a/src/main/webapp/js/goatApp/model/MenuCollection.js b/src/main/webapp/js/goatApp/model/MenuCollection.js index 0552f31ae..2c10179af 100644 --- a/src/main/webapp/js/goatApp/model/MenuCollection.js +++ b/src/main/webapp/js/goatApp/model/MenuCollection.js @@ -1,7 +1,7 @@ define(['jquery', 'underscore', 'backbone', - 'goatApp/model/MenuItemModel'], + 'goatApp/model/MenuModel'], function($,_,Backbone,MenuModel) { return Backbone.Collection.extend({ @@ -18,4 +18,4 @@ define(['jquery', this.trigger('menuData:loaded'); } }); -}); \ No newline at end of file +}) \ No newline at end of file diff --git a/src/main/webapp/js/goatApp/model/MenuModel.js b/src/main/webapp/js/goatApp/model/MenuModel.js index 849c16290..f141d1108 100644 --- a/src/main/webapp/js/goatApp/model/MenuModel.js +++ b/src/main/webapp/js/goatApp/model/MenuModel.js @@ -23,29 +23,3 @@ define(['jquery', // } // } - - //urlRoot:'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; - // } - // }); diff --git a/src/main/webapp/js/goatApp/support/GoatUtils.js b/src/main/webapp/js/goatApp/support/GoatUtils.js index a05b85f14..0812fe92a 100644 --- a/src/main/webapp/js/goatApp/support/GoatUtils.js +++ b/src/main/webapp/js/goatApp/support/GoatUtils.js @@ -22,7 +22,7 @@ define(['jquery', // debugFormSubmission: false, // pre-submit callback showRequest: function(formData, jqForm, options) { - if (goat.utils.debugFormSubmission) { + 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); @@ -49,17 +49,17 @@ define(['jquery', // 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) { + 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 - goat.utils.showLessonCookiesAndParams(); + 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 - 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 + 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(); }, @@ -67,8 +67,8 @@ define(['jquery', // 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 + 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 @@ -106,29 +106,29 @@ define(['jquery', $('.lessonHelp').hide(); //$('#lesson_source').html("
"+goat.lesson.lessonInfo.source+""); $('#lesson_source_row').show(); - goat.utils.scrollToHelp(); + GoatUtils.scrollToHelp(); }, showLessonSolution: function() { $('.lessonHelp').hide(); $('#lesson_solution').html(goat.lesson.lessonInfo.solution); $('#lesson_solution_row').show(); - goat.utils.scrollToHelp(); + GoatUtils.scrollToHelp(); }, showLessonPlan: function(plan) { $('.lessonHelp').hide(); $("#lesson_plan").html(goat.lesson.lessonInfo.plan); $('#lesson_plan_row').show(); - goat.utils.scrollToHelp(); + GoatUtils.scrollToHelp(); }, scrollToHelp: function() { $('#leftside-navigation').height($('#main-content').height() + 15) var target = $('#lessonHelpsWrapper'); - goat.utils.scrollEasy(target); + GoatUtils.scrollEasy(target); }, scrollToTop: function() { $('.lessonHelp').hide(); var target = $('#container'); - goat.utils.scrollEasy(target); + GoatUtils.scrollEasy(target); }, scrollEasy: function(target) { $('html,body').animate({ diff --git a/src/main/webapp/js/goatApp/support/goatConstants.js b/src/main/webapp/js/goatApp/support/goatConstants.js index d7d7a44da..425d16236 100644 --- a/src/main/webapp/js/goatApp/support/goatConstants.js +++ b/src/main/webapp/js/goatApp/support/goatConstants.js @@ -31,7 +31,7 @@ var goatConstants = { }, getDOMContainers:function() { return { - lessonMenu: '#menuContainer' + lessonMenu: '#menu-container' } } }; diff --git a/src/main/webapp/js/goatApp/view/MenuView.js b/src/main/webapp/js/goatApp/view/MenuView.js index a8508fe0c..38b732266 100644 --- a/src/main/webapp/js/goatApp/view/MenuView.js +++ b/src/main/webapp/js/goatApp/view/MenuView.js @@ -6,13 +6,13 @@ define(['jquery', 'goatApp/support/GoatUtils'], function( $, - _, + _,t Backbone, MenuCollection, MenuItemView, GoatUtils) { return Backbone.View.extend({ - el:'#menuContainer', + el:'#menu-container', //TODO: set template initialize: function() { this.collection = new MenuCollection(); diff --git a/src/main/webapp/js/goatConstants.js b/src/main/webapp/js/goatConstants.js new file mode 100644 index 000000000..793344f46 --- /dev/null +++ b/src/main/webapp/js/goatConstants.js @@ -0,0 +1,35 @@ +//goatConstants + +var goatConstants = { + CATEGORYCLASS:'fa-angle-right pull-right', + lessonCompleteClass:'glyphicon glyphicon-check lessonComplete', + selectedMenuClass:'selected', + keepOpenClass:'keepOpen', + menuPrefix : [ + { + name:'LESSONS', + type:'STATIC', + complete:false, + link:'', + children:null, + class:'fa-bars static' + }], + //services + 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', + + // literal messages + notFound: 'Could not find', + noHints: 'There are no hints defined.', + noSourcePulled: 'No source was retrieved for this lesson' + +}; + + diff --git a/src/main/webapp/js/goatControllers.js b/src/main/webapp/js/goatControllers.js new file mode 100644 index 000000000..2d3c31d19 --- /dev/null +++ b/src/main/webapp/js/goatControllers.js @@ -0,0 +1,265 @@ +//main goat application file +//TODO: reorg + +/* ### GOAT CONTROLLERS ### */ + +/* menu controller */ +var goatMenu = function($scope, $http, $modal, $log, $templateCache) { + $scope.cookies = []; + $scope.params = []; + $scope.renderMenu = function() { + goat.data.loadMenu($http).then(//$http({method: 'GET', url: goatConstants.lessonService}) + function(menuData) { + var menuItems = goat.utils.addMenuClasses(goatConstants.menuPrefix.concat(menuData.data)); + //top-tier 'categories' + for (var i = 0; i < menuItems.length; i++) { + menuItems[i].id = goat.utils.makeId(menuItems[i].name);//TODO move the replace routine into util function + menuItems[i].displayClass = ($scope.openMenu === menuItems[i].id) ? goatConstants.keepOpenClass : ''; + if (menuItems[i].children) { + for (var j = 0; j < menuItems[i].children.length; j++) { + menuItems[i].children[j].id = goat.utils.makeId(menuItems[i].children[j].name); + //handle selected Menu state + if (menuItems[i].children[j].id === $scope.curMenuItemSelected) { + menuItems[i].children[j].selectedClass = goatConstants.selectedMenuClass; + menuItems[i].selectedClass = goatConstants.selectedMenuClass; + } + //handle complete state + if (menuItems[i].children[j].complete) { + menuItems[i].children[j].completeClass = goatConstants.lessonCompleteClass; + } else { + menuItems[i].children[j].completeClass = ''; + } + if (menuItems[i].children[j].children) { + for (var k = 0; k < menuItems[i].children[j].children.length; k++) { + //TODO make utility function for name >> id + menuItems[i].children[j].children[k].id = goat.utils.makeId(menuItems[i].children[j].children[k].name); + //menuItems[i].children[j].children[k].id = menuItems[i].children[j].children[k].name.replace(/\s|\(|\)/g,''); + //handle selected Menu state + if (menuItems[i].children[j].children[k].id === $scope.curMenuItemSelected) { + menuItems[i].children[j].children[k].selectedClass = goatConstants.selectedMenuClass; + menuItems[i].children[j].selectedClass = goatConstants.selectedMenuClass; + } + //handle complete state + if (menuItems[i].children[j].children[k].complete) { + menuItems[i].children[j].children[k].completeClass = goatConstants.lessonCompleteClass; + } else { + menuItems[i].children[j].children[k].completeClass = '' + } + } + } + } + } + } + $scope.menuTopics = menuItems; + // + if ($scope.openMenu) { + $('ul' + $scope.openMenu).show(); + } + + }, + function(error) { + // TODO - handle this some way other than an alert + console.error("Error rendering menu: " + error); + } + ); + }; + + $scope.renderLesson = function(id, url, showControls) {//TODO convert to single object parameter + $scope.hintIndex = 0; + var curScope = $scope; + $('.lessonHelp').hide(); + // clean up menus, mark selected + $scope.curMenuItemSelected = id; + goat.utils.highlightCurrentLessonMenu(id); + curScope.parameters = goat.utils.scrapeParams(url); + // lesson content + goat.data.loadLessonContent($http, url).then( + function(reply) { + goat.data.loadLessonTitle($http).then( + function(reply) { + $("#lessonTitle").text(reply.data); + } + ); + //TODO encode html or get angular js portion working + $("#lesson_content").html(reply.data); + //hook forms and links (safe to call twice) + // links are hooked with each lesson now (see Java class Screen.getContent()) + goat.utils.makeFormsAjax();// inject form? + goat.utils.ajaxifyAttackHref(); + $('#leftside-navigation').height($('#main-content').height() + 15)//TODO: get ride of fixed value (15)here + //notifies goatLesson Controller of the less change + $scope.$emit('lessonUpdate', {params: curScope.parameters, 'showControls': showControls}); + } + ) + $scope.renderMenu(); + }; + $scope.accordionMenu = function(id) { + if ($('ul#' + id).attr('isOpen') == 0) { + $scope.expandMe = true; + } else { + $('ul#' + id).slideUp(300).attr('isOpen', 0); + return; + } + $scope.openMenu = id; + $('.lessonsAndStages').not('ul#' + id).slideUp(300).attr('isOpen', 0); + if ($scope.expandMe) { + $('ul#' + id).slideDown(300).attr('isOpen', 1); + } + } + $scope.renderMenu(); + // runs on first loadcan be augmented later to ' + // resume' for a given user ... currently kluged to start at fixed lesson + var url = 'attack?Screen=32&menu=5'; + angular.element($('#leftside-navigation')).scope().renderLesson(null, url); +} + +/* lesson controller */ +var goatLesson = function($scope, $http, $log) { + + $('#hintsView').hide(); + // adjust menu to lessonContent size if necssary + //cookies + + $scope.$on('lessonUpdate', function(params) { + $scope.parameters = arguments[1].params; + $scope.showHints = (arguments[1].showControls && arguments[1].showControls.showHints); + $scope.showSource = (arguments[1].showControls && arguments[1].showControls.showSource); + curScope = $scope; //TODO .. update below, this curScope is probably not needed + goat.data.loadCookies($http).then( + function(resp) { + curScope.cookies = resp.data; + } + ); + //hints + curScope.hintIndex = 0; + if ($scope.showHints) { + goat.data.loadHints($http).then( + function(resp) { + curScope.hints = resp.data; + if (curScope.hints.length > 0 && curScope.hints[0].hint.indexOf(goatConstants.noHints) === -1) { + goat.utils.displayButton('showHintsBtn', true); + } else { + goat.utils.displayButton('showHintsBtn', false); + } + } + ); + } else { + $scope.hints = null; + goat.utils.displayButton('showHintsBtn', false); + } + //source + if ($scope.showSource) { + goat.data.loadSource($http).then( + function(resp) { + curScope.source = resp.data; + } + ); + } else { + $scope.source = goatConstants.noSourcePulled; + } + + //plan + goat.data.loadPlan($http).then( + function(resp) { + curScope.plan = resp.data; + } + ); + //solution + goat.data.loadSolution($http).then( + function(resp) { + curScope.solution = resp.data; + } + ); + }); + + //goat.utils.scrollToTop(); + + + $scope.showLessonSource = function() { + $('.lessonHelp').hide(); + $('#lesson_source_row').show(); + goat.utils.scrollToHelp(); + } + + $scope.showLessonPlan = function() { + $('.lessonHelp').hide(); + $("#lesson_plan").html($scope.plan); + $('#lesson_plan_row').show(); + goat.utils.scrollToHelp(); + } + + $scope.showLessonSolution = function() { + $('.lessonHelp').hide(); + $("#lesson_solution").html($scope.solution); + $('#lesson_solution_row').show(); + goat.utils.scrollToHelp(); + } + + $scope.manageHintButtons = function() { + if ($scope.hintIndex === $scope.hints.length - 1) { + $('#showNextHintBtn').css('visibility', 'hidden'); + } else if ($scope.hintIndex < $scope.hints.length - 1) { + $('#showNextHintBtn').css('visibility', 'visible'); + } + // + if ($scope.hintIndex === 0) { + $('#showPrevHintBtn').css('visibility', 'hidden'); + } else if ($scope.hintIndex > 0) { + $('#showPrevHintBtn').css('visibility', 'visible'); + } + }; + + $scope.viewHints = function() { + if (!$scope.hints) { + return; + } + + $('.lessonHelp').hide(); + $('#lesson_hint_row').show(); + //goat.utils.scrollToHelp(); + //TODO + $scope.curHint = $scope.hints[$scope.hintIndex].hint; + //$scope.curHint = $sce.trustAsHtml($scope.hints[$scope.hintIndex].hint); + //TODO get html binding workin in the UI ... in the meantime ... + //$scope.renderCurHint(); + $scope.manageHintButtons(); + }; + + $scope.viewNextHint = function() { + $scope.hintIndex++; + $scope.curHint = $scope.hints[$scope.hintIndex].hint; + $scope.renderCurHint(); + $scope.manageHintButtons(); + }; + + $scope.viewPrevHint = function() { + $scope.hintIndex--; + $scope.curHint = $scope.hints[$scope.hintIndex].hint; + $scope.renderCurHint(); + $scope.manageHintButtons(); + }; + + $scope.renderCurHint = function() { + $('#curHintContainer').html($scope.curHint); + } + + $scope.hideHints = function() { + + }; + + $scope.restartLesson = function() { + goat.data.loadRestart($http).then( + function(resp) { + angular.element($('#leftside-navigation')).scope().renderLesson(null, resp.data, {showSource: $scope.showSource, showHints: $scope.showHints}); + } + ) + } + + $scope.showAbout = function() { + $('#aboutModal').modal({ + //remote: 'about.mvc' + }); + }; +} + + diff --git a/src/main/webapp/js/goatData.js b/src/main/webapp/js/goatData.js new file mode 100644 index 000000000..b01f0f873 --- /dev/null +++ b/src/main/webapp/js/goatData.js @@ -0,0 +1,44 @@ +/* ### GOAT DATA/PROMISES ### */ + +goat.data = { + /**** jQuery loads ... ****/ + loadLessonContent: function ($http,_url) { + //TODO: switch to $http (angular) later + return $http({method:'GET', url: _url}); + //return $.get(_url, {}, null, "html"); + }, + loadCookies: function($http) { + return $http({method: 'GET', url: goatConstants.cookieService}); + //return $.get(goatConstants.cookieService, {}); + }, + loadHints: function ($http) { + return $http({method: 'GET', url: goatConstants.hintService}); + //return $.get(goatConstants.hintService, {}); + }, + loadSource: function($http) { + return $http({method: 'GET', url: goatConstants.sourceService}); + //return $.get(goatConstants.sourceService, {}); + }, + loadSolution: function ($http) { + return $http({method: 'GET', url: goatConstants.solutionService}); + //return $.get(goatConstants.solutionService, {}); + }, + loadPlan: function ($http) { + return $http({method: 'GET', url: goatConstants.lessonPlanService}); + //return $.get(goatConstants.lessonPlanService, {}); + }, + loadParams: function($http) { + return $http({method: 'GET', url: goatConstants.paramsService}); + //return $.get(goatConstants.paramsService,{}); + }, + loadMenu: function($http) { + return $http({method: 'GET', url: goatConstants.lessonService}); + }, + loadLessonTitle: function ($http) { + return $http({method: 'GET', url: goatConstants.lessonTitleService}); + }, + loadRestart: function ($http) { + return $http({method: 'GET', url:goatConstants.restartLessonService}) + } + +}; diff --git a/src/main/webapp/js/goatLesson.js b/src/main/webapp/js/goatLesson.js new file mode 100644 index 000000000..ad6939c4c --- /dev/null +++ b/src/main/webapp/js/goatLesson.js @@ -0,0 +1,110 @@ +// goat.lesson name space +goat.lesson = { + CurLesson: function(_lessonUrl) { + return { + hints:[], + hintIndex:0, + solution:null, + plan:null, + cookiesAndParams:[], + params:[], + source:null, + lessonUrl:(_lessonUrl || null), + clearInfo: function() { + this.hints = null; + this.solution = null; + this.plan = null; + this.cookies = null; + this.source = null; + this.params = null; + }, + loadInfo: function() { + this.getHints(); + this.getPlan(); + this.getSolution(); + this.getCookies(); + this.getSource(); + this.getParams(); + }, + getHints:function() { + var scope = this; + goat.data.loadHints().then( + function(resp) { + scope.hints = resp.data; + if (scope.hints.length > 0 && scope.hints[0].hint.indexOf(goatConstants.noHints) === -1) { + goat.utils.displayButton('showHintsBtn',true); + } else { + goat.utils.displayButton('showHintsBtn',false); + } + return scope; + }, + function(err){ + goat.utils.displayButton('showHintsBtn',false); + //TODO handle errors + } + ); + }, + getSolution:function() { + var scope = this; + goat.data.loadSolution().then( + function(resp) { + scope.solution = resp.data; + goat.utils.displayButton('showSolutionBtn',true); + $('#showSolutionBtn').unbind().click(goat.utils.showLessonSolution); + return scope; + }, + function(err){ + scope.solution = null; + goat.utils.displayButton('showSolutionBtn',false); + //TODO handle errors + } + ); + }, + getPlan: function() { + var scope = this; + goat.data.loadPlan().then( + function(resp) { + scope.plan = resp.data; + goat.utils.displayButton('showPlanBtn',true); + $('#showPlanBtn').unbind().click(goat.utils.showLessonPlan); + return scope; + }, + function(err){ + goat.utils.displayButton('showPlanBtn',false); + //TODO handle errors + } + ); + }, + getSource: function() { + var scope = this; + goat.data.loadSource().then( + function(resp) { + scope.source = resp.data; + goat.utils.displayButton('showSourceBtn',true); + $('#showSourceBtn').unbind().click(goat.utils.showLessonSource); + return scope; + }, + function(err){ + goat.utils.displayButton('showSourceBtn',false); + //TODO handle errors + } + ); + }, + getCookies: function() { + var scope = this; + goat.data.loadCookies().then( + function(resp) { + scope.cookies = resp.data; + return scope; + }, + function(err){ + //TODO handle errors + } + ); + }, + getParams: function() { + this.params = goat.utils.scrapeParams(this.lessonUrl); + } + } + } +}; \ No newline at end of file diff --git a/src/main/webapp/js/goatApp/support/goatUtil.js b/src/main/webapp/js/goatUtil.js similarity index 100% rename from src/main/webapp/js/goatApp/support/goatUtil.js rename to src/main/webapp/js/goatUtil.js