From 1f4024d1587fc21b01c59f303dd260d819dfec80 Mon Sep 17 00:00:00 2001 From: Jason White Date: Tue, 21 Apr 2015 18:40:14 -0400 Subject: [PATCH 01/19] Initial cutover to backbone/underscore/require --- .../js/goatApp/controller/LessonController.js | 40 + .../js/goatApp/controller/MenuController.js | 3 + src/main/webapp/js/goatApp/goatApp.js | 12 + .../webapp/js/goatApp/model/LessonData.js | 20 + src/main/webapp/js/goatApp/model/goatMenu.js | 32 + src/main/webapp/js/goatApp/model/menuData.js | 22 + .../js/goatApp/support/goatConstants.js | 39 + .../webapp/js/goatApp/support/goatUtils.js | 220 ++ src/main/webapp/js/goatApp/view/MenuView.js | 49 + src/main/webapp/js/goatApp/view/goatRouter.js | 34 + src/main/webapp/js/libs/backbone-min.js | 2 + src/main/webapp/js/libs/backbone.js | 1608 +++++++++++++ src/main/webapp/js/libs/jquery-1.10.2.min.js | 6 + src/main/webapp/js/libs/jquery.form.js | 1277 ++++++++++ src/main/webapp/js/libs/require.js | 2083 +++++++++++++++++ src/main/webapp/js/libs/require.min.js | 36 + src/main/webapp/js/libs/underscore-min.js | 6 + src/main/webapp/js/libs/underscore.js | 1416 +++++++++++ src/main/webapp/js/main.js | 37 + 19 files changed, 6942 insertions(+) create mode 100644 src/main/webapp/js/goatApp/controller/LessonController.js create mode 100644 src/main/webapp/js/goatApp/controller/MenuController.js create mode 100644 src/main/webapp/js/goatApp/goatApp.js create mode 100644 src/main/webapp/js/goatApp/model/LessonData.js create mode 100644 src/main/webapp/js/goatApp/model/goatMenu.js create mode 100644 src/main/webapp/js/goatApp/model/menuData.js create mode 100644 src/main/webapp/js/goatApp/support/goatConstants.js create mode 100644 src/main/webapp/js/goatApp/support/goatUtils.js create mode 100644 src/main/webapp/js/goatApp/view/MenuView.js create mode 100644 src/main/webapp/js/goatApp/view/goatRouter.js create mode 100644 src/main/webapp/js/libs/backbone-min.js create mode 100644 src/main/webapp/js/libs/backbone.js create mode 100644 src/main/webapp/js/libs/jquery-1.10.2.min.js create mode 100644 src/main/webapp/js/libs/jquery.form.js create mode 100644 src/main/webapp/js/libs/require.js create mode 100644 src/main/webapp/js/libs/require.min.js create mode 100644 src/main/webapp/js/libs/underscore-min.js create mode 100644 src/main/webapp/js/libs/underscore.js create mode 100644 src/main/webapp/js/main.js diff --git a/src/main/webapp/js/goatApp/controller/LessonController.js b/src/main/webapp/js/goatApp/controller/LessonController.js new file mode 100644 index 000000000..95319f609 --- /dev/null +++ b/src/main/webapp/js/goatApp/controller/LessonController.js @@ -0,0 +1,40 @@ +define(['jquery', + 'underscore', + 'libs/backbone', + 'goatApp/model/LessonData' + ], + function($,_,Backbone,LessonData) { + 'use strict' + //private vars + + var controller = function() { + this.loadLesson = function(scr,menu) { + var curLessonData = new LessonData({ + 'screen': encodeURIComponent(scr), + 'menu': encodeURIComponent(menu), + }); + } + }; + + return controller; + + //var curScreen,curMenu; + + //return { + // 'screen':curScreen + // loadLesson called from the router to load the given lesson + /*loadLesson: function (src,curMenu) { + var curLesson = new LessonData({ + 'screen': encodeURIComponent(scr), + 'menu': encodeURIComponent(curMenu), + }); + + //set listeners + + }, + restartLesson: function () { + + } + //getters & setters*/ + //}; +}); \ No newline at end of file diff --git a/src/main/webapp/js/goatApp/controller/MenuController.js b/src/main/webapp/js/goatApp/controller/MenuController.js new file mode 100644 index 000000000..e1cf6807f --- /dev/null +++ b/src/main/webapp/js/goatApp/controller/MenuController.js @@ -0,0 +1,3 @@ +define(['jquery','underscore','backbone','goatApp/model/goatMenu','goatApp/view/MenuView'], function($,_,Backbone,MenuData,MenuView) { + +}); \ No newline at end of file diff --git a/src/main/webapp/js/goatApp/goatApp.js b/src/main/webapp/js/goatApp/goatApp.js new file mode 100644 index 000000000..3086375c9 --- /dev/null +++ b/src/main/webapp/js/goatApp/goatApp.js @@ -0,0 +1,12 @@ +define(['jquery','underscore','backbone','goatApp/view/goatRouter','goatApp/controller/LessonController','goatApp/controller/MenuController'], + function($,_,Backbone,Router,LessonController,MenuController){ + 'use strict' + //var goatRouter = new Router(); + + return { + initApp: function() { + //TODO: add query/ability to load from where they left off + Router.init(); + } + } +}); \ No newline at end of file diff --git a/src/main/webapp/js/goatApp/model/LessonData.js b/src/main/webapp/js/goatApp/model/LessonData.js new file mode 100644 index 000000000..ca625d70c --- /dev/null +++ b/src/main/webapp/js/goatApp/model/LessonData.js @@ -0,0 +1,20 @@ +define(['jquery', 'underscore','backbone'], function($,_,Backbone){ + + return Backbone.Model.extend({ + urlRoot:null, + defaults: { + items:null, + selectedItem:null + }, + initialize: function (options) { + var self = this; + this.urlRoot = 'attack.jsp?Screen='+options.screen + '&menu=' + options.menu; + this.fetch().then(function(content){ + self.lessonContent = content + }); + }, + getHint: function (i) { + + } + }); +}); \ No newline at end of file diff --git a/src/main/webapp/js/goatApp/model/goatMenu.js b/src/main/webapp/js/goatApp/model/goatMenu.js new file mode 100644 index 000000000..d4a017962 --- /dev/null +++ b/src/main/webapp/js/goatApp/model/goatMenu.js @@ -0,0 +1,32 @@ +//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; + } + }); + +}); \ No newline at end of file diff --git a/src/main/webapp/js/goatApp/model/menuData.js b/src/main/webapp/js/goatApp/model/menuData.js new file mode 100644 index 000000000..94cb506a5 --- /dev/null +++ b/src/main/webapp/js/goatApp/model/menuData.js @@ -0,0 +1,22 @@ +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); + }); + } + }); \ No newline at end of file diff --git a/src/main/webapp/js/goatApp/support/goatConstants.js b/src/main/webapp/js/goatApp/support/goatConstants.js new file mode 100644 index 000000000..d7d7a44da --- /dev/null +++ b/src/main/webapp/js/goatApp/support/goatConstants.js @@ -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: '#menuContainer' + } + } +}; + + diff --git a/src/main/webapp/js/goatApp/support/goatUtils.js b/src/main/webapp/js/goatApp/support/goatUtils.js new file mode 100644 index 000000000..ce3426419 --- /dev/null +++ b/src/main/webapp/js/goatApp/support/goatUtils.js @@ -0,0 +1,220 @@ +goatUtils = { + //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); + }, + enhanceMenuData: function (menuItems,selectedItem) { + for (var i = 0; i < menuItems.length; i++) { + menuItems[i].id = goatUtils.makeId(menuItems[i].name);//TODO move the replace routine into util function + if (selectedItem) { + } + //TODO: maintain state marker on refresh + //menuItems[i].displayClass = ($scope.openMenu === menuItems[i].id) ? goatConstants.getClasses().keepOpenClass : ''; + //TODO: move to utility method + if (menuItems[i].children) { + for (var j = 0; j < menuItems[i].children.length; j++) { + menuItems[i].children[j].id = goatUtils.makeId(menuItems[i].children[j].name); + //handle selected Menu state + if (menuItems[i].children[j].id === selectedItem) { + menuItems[i].children[j].selectedClass = goatConstants.getClasses().selectedMenuClass; + menuItems[i].selectedClass = goatConstants.getClasses().selectedMenuClass; + } + //handle complete state + if (menuItems[i].children[j].complete) { + menuItems[i].children[j].completeClass = goatConstants.getClasses().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 = goatUtils.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 === selectedItem) { + menuItems[i].children[j].children[k].selectedClass = goatConstants.getClasses().selectedMenuClass; + menuItems[i].children[j].selectedClass = goatConstants.getClasses().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 = '' + } + } + } + } + } + } // done parsing menuItems + }, + // + 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("
"+goat.lesson.lessonInfo.source+"
"); + $('#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() { + // 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}); + } + ); + }); + } +}; + + +$(window).resize(function() { + //$('#leftside-navigation').css('height',$('div.panel-body').height()); + console.log($(window).height()); +}); diff --git a/src/main/webapp/js/goatApp/view/MenuView.js b/src/main/webapp/js/goatApp/view/MenuView.js new file mode 100644 index 000000000..091cd37cf --- /dev/null +++ b/src/main/webapp/js/goatApp/view/MenuView.js @@ -0,0 +1,49 @@ +define(['jquery','underscore','backbone','goatApp/model/goatMenu'], function($,_,Backbone,MenuData) { + + return Backbone.View.extend({ + el:'#menuContainer', + //TODO: set template + + render: function (model){ + //TODO: implement own HTML Encoder + this.$el.html(buildMenu(items)); + }, + buildMenu: function(items) { + + var menuData = new MenuData(); + + var i, j, k, $wholeMenu, $menuCat, itemClass, $lessonItem, lessons, stages, $stageItem; + var _renderMenu = function (items) { + $wholeMenu = $('