Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Nanne Baars 2015-09-22 20:40:02 +02:00
commit 6f8befd9a4
14 changed files with 605 additions and 130 deletions

View File

@ -0,0 +1,68 @@
package org.owasp.webgoat.lessons.model;
import org.owasp.webgoat.lessons.AbstractLesson;
import org.owasp.webgoat.lessons.Category;
import org.owasp.webgoat.session.WebSession;
/**
* Created by jason on 9/18/15.
*/
public class LessonInfoModel {
private String lessonTitle;
private int numberHints;
private boolean hasSource;
private boolean hasSolution;
private boolean hasPlan;
private String source;
private String solution;
private String plan;
public LessonInfoModel(WebSession webSession) {
AbstractLesson lesson = webSession.getCurrentLesson();
//TODO make these first class citizens of the lesson itself; and stop passing the session all over
this.hasSource = !lesson.getSource(webSession).contains("Could not find the source file or source file does not exist");
this.hasPlan = !lesson.getSource(webSession).contains("Could not find lesson plan");
this.hasSolution = !lesson.getSolution(webSession).contains("Could not find the solution file or solution file does not exist");
this.lessonTitle = lesson.getTitle();
this.numberHints = lesson.getHintCount(webSession);
if (lesson.getCategory().equals(Category.CHALLENGE) || this.numberHints < 1 || lesson.getHint(webSession,0).equals("Hint: There are no hints defined.")) {
this.numberHints = 0;
}
}
// GETTERS
public String getLessonTitle() {
return lessonTitle;
}
public int getNumberHints() {
return numberHints;
}
public boolean isHasSource() {
return hasSource;
}
public boolean isHasSolution() {
return hasSolution;
}
public boolean isHasPlan() {
return hasPlan;
}
public String getSource() {
return source;
}
public String getSolution() {
return solution;
}
public String getPlan() {
return plan;
}
}

View File

@ -46,8 +46,8 @@ public class LessonMenuItem {
private List<LessonMenuItem> children = new ArrayList<LessonMenuItem>(); private List<LessonMenuItem> children = new ArrayList<LessonMenuItem>();
private boolean complete; private boolean complete;
private String link; private String link;
private boolean showSource = true; // private boolean showSource = true;
private boolean showHints = true; // private boolean showHints = true;
/** /**
* <p>Getter for the field <code>name</code>.</p> * <p>Getter for the field <code>name</code>.</p>
@ -157,40 +157,6 @@ public class LessonMenuItem {
this.link = link; this.link = link;
} }
/**
* <p>isShowSource.</p>
*
* @return the showSource
*/
public boolean isShowSource() {
return showSource;
}
/**
* <p>Setter for the field <code>showSource</code>.</p>
*
* @param showSource the showSource to set
*/
public void setShowSource(boolean showSource) {
this.showSource = showSource;
}
/**
* <p>isShowHints.</p>
*
* @return the showHints
*/
public boolean isShowHints() {
return showHints;
}
/**
* <p>Setter for the field <code>showHints</code>.</p>
*
* @param showHints the showHints to set
*/
public void setShowHints(boolean showHints) {
this.showHints = showHints;
}
} }

View File

@ -9,6 +9,7 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import javax.servlet.http.HttpSession; import javax.servlet.http.HttpSession;
import org.owasp.webgoat.lessons.AbstractLesson; import org.owasp.webgoat.lessons.AbstractLesson;
import org.owasp.webgoat.lessons.Category;
import org.owasp.webgoat.lessons.model.Hint; import org.owasp.webgoat.lessons.model.Hint;
import org.owasp.webgoat.session.WebSession; import org.owasp.webgoat.session.WebSession;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
@ -40,11 +41,12 @@ public class HintService extends BaseService {
if (l == null) { if (l == null) {
return listHints; return listHints;
} }
List<String> hints; List<String> hints = (l.getCategory().equals(Category.CHALLENGE)) ? null : l.getHintsPublic(ws);
hints = l.getHintsPublic(ws);
if (hints == null) { if (hints == null) {
return listHints; return listHints;
} }
int idx = 0; int idx = 0;
for (String h : hints) { for (String h : hints) {
Hint hint = new Hint(); Hint hint = new Hint();

View File

@ -0,0 +1,42 @@
package org.owasp.webgoat.service;
import org.owasp.webgoat.lessons.AbstractLesson;
import org.owasp.webgoat.lessons.model.LessonInfoModel;
import org.owasp.webgoat.lessons.model.LessonMenuItem;
import org.owasp.webgoat.session.WebSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import javax.servlet.http.HttpSession;
@Controller
public class LessonInfoService extends BaseService {
private static final Logger logger = LoggerFactory.getLogger(LessonMenuService.class);
@RequestMapping(value = "/lessoninfo.mvc", produces = "application/json")
public @ResponseBody
LessonInfoModel getLessonInfo(HttpSession session) {
WebSession webSession = getWebSession(session);
return new LessonInfoModel(webSession);
}
@ExceptionHandler(Exception.class)
@ResponseBody
@ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
public String handleException(Exception ex) {
return "An error occurred retrieving the LessonInfoModel:" + ex.getMessage();
}
protected LessonInfoModel getLessonInfoModel(WebSession webSession) {
return new LessonInfoModel(webSession);
}
}

View File

@ -88,20 +88,6 @@ public class LessonMenuService extends BaseService {
if (lesson.isCompleted(ws)) { if (lesson.isCompleted(ws)) {
lessonItem.setComplete(true); lessonItem.setComplete(true);
} }
/* @TODO - do this in a more efficient way
if (lesson.isAuthorized(ws, role, WebSession.SHOWHINTS)) {
lessonItem.setShowHints(true);
}
if (lesson.isAuthorized(ws, role, WebSession.SHOWSOURCE)) {
lessonItem.setShowSource(true);
}
*/
// special handling for challenge role
if (Category.CHALLENGE.equals(lesson.getCategory())) {
lessonItem.setShowHints(lesson.isAuthorized(ws, AbstractLesson.CHALLENGE_ROLE, WebSession.SHOWHINTS));
lessonItem.setShowSource(lesson.isAuthorized(ws, AbstractLesson.CHALLENGE_ROLE, WebSession.SHOWHINTS));
}
categoryItem.addChild(lessonItem); categoryItem.addChild(lessonItem);
// Does the lesson have stages // Does the lesson have stages

View File

@ -9,6 +9,11 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"> <html xmlns="http://www.w3.org/1999/xhtml">
<head> <head>
<meta http-equiv="Expires" CONTENT="0">
<meta http-equiv="Pragma" CONTENT="no-cache">
<meta http-equiv="Cache-Control" CONTENT="no-cache">
<meta http-equiv="Cache-Control" CONTENT="no-store">
<!--[if lt IE 7]> <html class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]--> <!--[if lt IE 7]> <html class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]-->
<!--[if IE 7]> <html class="no-js lt-ie9 lt-ie8"> <![endif]--> <!--[if IE 7]> <html class="no-js lt-ie9 lt-ie8"> <![endif]-->
<!--[if IE 8]> <html class="no-js lt-ie9"> <![endif]--> <!--[if IE 8]> <html class="no-js lt-ie9"> <![endif]-->

View File

@ -13,7 +13,9 @@ define(['jquery',
'goatApp/model/ParamModel', 'goatApp/model/ParamModel',
'goatApp/support/GoatUtils', 'goatApp/support/GoatUtils',
'goatApp/view/UserAndInfoView', 'goatApp/view/UserAndInfoView',
'goatApp/view/MenuButtonView' 'goatApp/view/MenuButtonView',
'goatApp/model/LessonInfoModel',
'goatApp/view/TitleView'
], ],
function($, function($,
_, _,
@ -30,7 +32,9 @@ define(['jquery',
ParamModel, ParamModel,
GoatUtils, GoatUtils,
UserAndInfoView, UserAndInfoView,
MenuButtonView MenuButtonView,
LessonInfoModel,
TitleView
) { ) {
'use strict' 'use strict'
@ -42,13 +46,13 @@ define(['jquery',
_.extend(Controller.prototype,Backbone.Events); _.extend(Controller.prototype,Backbone.Events);
this.start = function() { this.start = function() {
this.listenTo(this.lessonContent,'contentLoaded',this.onContentLoaded); this.listenTo(this.lessonContent,'content:loaded',this.onContentLoaded);
//'static' elements of page/app
this.userAndInfoView = new UserAndInfoView(); this.userAndInfoView = new UserAndInfoView();
this.menuButtonView = new MenuButtonView(); this.menuButtonView = new MenuButtonView();
}; };
//load View, which can pull data
this.loadLesson = function(scr,menu,stage) { this.loadLesson = function(scr,menu,stage) {
this.titleView = new TitleView();
this.helpsLoaded = {}; this.helpsLoaded = {};
this.lessonContent.loadData({ this.lessonContent.loadData({
'screen': scr, 'screen': scr,
@ -59,64 +63,54 @@ define(['jquery',
this.solutionView = {}; this.solutionView = {};
this.sourceView = {}; this.sourceView = {};
this.lessonHintView = {}; this.lessonHintView = {};
this.screen = scr; //needed anymore? this.screen = scr;
this.menu = menu; this.menu = menu;
//
}; };
this.onContentLoaded = function() { this.onInfoLoaded = function() {
this.helpControlsView = null; this.helpControlsView = new HelpControlsView({
this.lessonView.model = this.lessonContent; hasPlan:this.lessonInfoModel.get('hasPlan'),
this.lessonView.render(); hasSolution:this.lessonInfoModel.get('hasSolution'),
//load title view (initially hidden) << //TODO: currently handled via menu click but need to be able to handle via routed request hasSource:this.lessonInfoModel.get('hasSource'),
//plan view (initially hidden) hasHints:(this.lessonInfoModel.get('numberHints') > 0),
this.planView = new PlanView();
this.listenToOnce(this.planView,'plan:loaded',this.areHelpsReady);
//solution view (initially hidden)
this.solutionView = new SolutionView();
this.listenToOnce(this.solutionView,'solution:loaded',this.areHelpsReady);
//source (initially hidden)
this.sourceView = new SourceView();
this.listenToOnce(this.sourceView,'source:loaded',this.areHelpsReady);
//load help controls view (contextul to what helps are available)
this.lessonHintView = new HintView();
this.listenToOnce(this.lessonHintView,'hints:loaded',this.areHelpsReady);
//
this.cookieView = new CookieView();
// parameter model & view
//TODO: instantiate model with values (not sure why was not working before)
var paramModel = new ParamModel({
}); });
paramModel.set('screenParam',this.lessonContent.get('screenParam'));
paramModel.set('menuParam',this.lessonContent.get('menuParam'));
paramModel.set('stageParam',this.lessonContent.get('stageParam'));
this.paramView = new ParamView({model:paramModel});
$('.lesson-help').hide(); this.listenTo(this.helpControlsView,'plan:show',this.hideShowHelps);
this.trigger('menu:reload'); this.listenTo(this.helpControlsView,'solution:show',this.hideShowHelps);
this.listenTo(this.helpControlsView,'hints:show',this.onShowHints)
this.listenTo(this.helpControlsView,'source:show',this.hideShowHelps);
this.listenTo(this.helpControlsView,'lesson:restart',this.restartLesson);
this.helpControlsView.render();
this.titleView.render(this.lessonInfoModel.get('lessonTitle'));
}; };
this.areHelpsReady = function (curHelp) { this.onContentLoaded = function(loadHelps) {
//TODO: significantly refactor (remove) this once LessonInfoService can be used to support lazy loading this.lessonInfoModel = new LessonInfoModel();
this.addCurHelpState(curHelp); this.listenTo(this.lessonInfoModel,'info:loaded',this.onInfoLoaded); //TODO onInfoLoaded function to handle title view and helpview
// check if all are ready
if (this.helpsLoaded['hints'] && this.helpsLoaded['plan'] && this.helpsLoaded['solution'] && this.helpsLoaded['source'] && !this.helpControlsView) { if (loadHelps) {
this.helpControlsView = null;
this.helpControlsView = new HelpControlsView({ this.lessonView.model = this.lessonContent;
hasPlan:(this.planView.model.get('content') !== null), this.lessonView.render();
hasSolution:(this.solutionView.model.get('content') !== null), //load title view (initially hidden) << //TODO: currently handled via menu click but need to be able to handle via routed request
hasSource:(this.sourceView.model.get('content') !== null), this.planView = new PlanView();
hasHints:(this.lessonHintView.collection.length > 0), this.solutionView = new SolutionView();
}); this.sourceView = new SourceView();
this.helpControlsView.render(); this.lessonHintView = new HintView();
this.cookieView = new CookieView();
this.listenTo(this.helpControlsView,'plan:show',this.hideShowHelps); // parameter model & view
this.listenTo(this.helpControlsView,'solution:show',this.hideShowHelps); //TODO: instantiate model with values (not sure why was not working before)
this.listenTo(this.helpControlsView,'hints:show',this.onShowHints) var paramModel = new ParamModel({});
this.listenTo(this.helpControlsView,'source:show',this.hideShowHelps); paramModel.set('screenParam',this.lessonContent.get('screenParam'));
this.listenTo(this.helpControlsView,'lesson:restart',this.restartLesson); paramModel.set('menuParam',this.lessonContent.get('menuParam'));
} paramModel.set('stageParam',this.lessonContent.get('stageParam'));
this.paramView = new ParamView({model:paramModel});
$('.lesson-help').hide();
}
this.trigger('menu:reload');
}; };
this.addCurHelpState = function (curHelp) { this.addCurHelpState = function (curHelp) {

View File

@ -11,22 +11,15 @@ define(['jquery',
_.extend(Controller.prototype,Backbone.Events); _.extend(Controller.prototype,Backbone.Events);
options = options || {}; options = options || {};
this.menuView = options.menuView; this.menuView = options.menuView;
this.titleView = options.titleView;
this.initMenu = function() { this.initMenu = function() {
this.listenTo(this.menuView,'lesson:click',this.renderTitle); //this.listenTo(this.menuView,'lesson:click',this.renderTitle);
} }
this.updateMenu = function(){ this.updateMenu = function(){
this.menuView.updateMenu(); this.menuView.updateMenu();
},
//TODO: move title rendering into lessonContent/View pipeline once data can support it
this.renderTitle = function(title) {
this.titleView.render(title);
} }
}; };
return Controller; return Controller;

View File

@ -29,10 +29,15 @@ define(['jquery',
self.setContent(data); self.setContent(data);
}); });
}, },
setContent: function(content) {
setContent: function(content, loadHelps) {
if (typeof loadHelps === 'undefined') {
loadHelps = true;
}
this.set('content',content); this.set('content',content);
this.trigger('contentLoaded'); this.trigger('content:loaded',this,loadHelps);
}, },
fetch: function (options) { fetch: function (options) {
options = options || {}; options = options || {};
return Backbone.Model.prototype.fetch.call(this, _.extend({ dataType: "html"}, options)); return Backbone.Model.prototype.fetch.call(this, _.extend({ dataType: "html"}, options));

View File

@ -0,0 +1,21 @@
define(['jquery',
'underscore',
'backbone'],
function($,
_,
Backbone){
return Backbone.Model.extend({
url:'service/lessoninfo.mvc',
initialize: function (options) {
this.fetch().then(this.infoLoaded.bind(this));
},
infoLoaded: function(data) {
console.log (data);
this.trigger('info:loaded',this,data);
}
});
});

View File

@ -4,19 +4,22 @@ define(['jquery',
'goatApp/controller/LessonController', 'goatApp/controller/LessonController',
'goatApp/controller/MenuController', 'goatApp/controller/MenuController',
'goatApp/view/LessonContentView', 'goatApp/view/LessonContentView',
'goatApp/view/MenuView', 'goatApp/view/MenuView'
'goatApp/view/TitleView' ], function ($,
], function ($,_,Backbone,LessonController,MenuController,LessonContentView,MenuView,TitleView) { _,
Backbone,
LessonController,
MenuController,
LessonContentView,
MenuView) {
var lessonView = new LessonContentView(); var lessonView = new LessonContentView();
var menuView = new MenuView(); var menuView = new MenuView();
var titleView = new TitleView();
var GoatAppRouter = Backbone.Router.extend({ var GoatAppRouter = Backbone.Router.extend({
routes: { routes: {
//#....
'welcome':'welcomeRoute', 'welcome':'welcomeRoute',
'attack/:scr/:menu(/:stage)':'attackRoute' // 'attack/:scr/:menu(/:stage)':'attackRoute',
}, },
lessonController: new LessonController({ lessonController: new LessonController({
@ -24,8 +27,7 @@ define(['jquery',
}), }),
menuController: new MenuController({ menuController: new MenuController({
menuView:menuView, menuView:menuView
titleView:titleView
}), }),
init:function() { init:function() {
@ -39,7 +41,7 @@ define(['jquery',
//update menu //update menu
}); });
goatRouter.on('route:welcomeRoute', function() { goatRouter.on('route:welcomeRoute', function() {
alert('welcome route'); this.lessonController.loadWelcome();
}); });
Backbone.history.start(); Backbone.history.start();

View File

@ -7,7 +7,6 @@ function($,_,Backbone) {
render:function(title) { render:function(title) {
var lessonTitleEl = $('<h1>',{id:'lesson-title',text:title}); var lessonTitleEl = $('<h1>',{id:'lesson-title',text:title});
this.$el.html(lessonTitleEl); this.$el.html(lessonTitleEl);
//this.$el.append(lessonTitleEl);
} }
}); });
}); });

View File

@ -0,0 +1,391 @@
/**
* @license RequireJS text 2.0.14 Copyright (c) 2010-2014, The Dojo Foundation All Rights Reserved.
* Available via the MIT or new BSD license.
* see: http://github.com/requirejs/text for details
*/
/*jslint regexp: true */
/*global require, XMLHttpRequest, ActiveXObject,
define, window, process, Packages,
java, location, Components, FileUtils */
define(['module'], function (module) {
'use strict';
var text, fs, Cc, Ci, xpcIsWindows,
progIds = ['Msxml2.XMLHTTP', 'Microsoft.XMLHTTP', 'Msxml2.XMLHTTP.4.0'],
xmlRegExp = /^\s*<\?xml(\s)+version=[\'\"](\d)*.(\d)*[\'\"](\s)*\?>/im,
bodyRegExp = /<body[^>]*>\s*([\s\S]+)\s*<\/body>/im,
hasLocation = typeof location !== 'undefined' && location.href,
defaultProtocol = hasLocation && location.protocol && location.protocol.replace(/\:/, ''),
defaultHostName = hasLocation && location.hostname,
defaultPort = hasLocation && (location.port || undefined),
buildMap = {},
masterConfig = (module.config && module.config()) || {};
text = {
version: '2.0.14',
strip: function (content) {
//Strips <?xml ...?> declarations so that external SVG and XML
//documents can be added to a document without worry. Also, if the string
//is an HTML document, only the part inside the body tag is returned.
if (content) {
content = content.replace(xmlRegExp, "");
var matches = content.match(bodyRegExp);
if (matches) {
content = matches[1];
}
} else {
content = "";
}
return content;
},
jsEscape: function (content) {
return content.replace(/(['\\])/g, '\\$1')
.replace(/[\f]/g, "\\f")
.replace(/[\b]/g, "\\b")
.replace(/[\n]/g, "\\n")
.replace(/[\t]/g, "\\t")
.replace(/[\r]/g, "\\r")
.replace(/[\u2028]/g, "\\u2028")
.replace(/[\u2029]/g, "\\u2029");
},
createXhr: masterConfig.createXhr || function () {
//Would love to dump the ActiveX crap in here. Need IE 6 to die first.
var xhr, i, progId;
if (typeof XMLHttpRequest !== "undefined") {
return new XMLHttpRequest();
} else if (typeof ActiveXObject !== "undefined") {
for (i = 0; i < 3; i += 1) {
progId = progIds[i];
try {
xhr = new ActiveXObject(progId);
} catch (e) {}
if (xhr) {
progIds = [progId]; // so faster next time
break;
}
}
}
return xhr;
},
/**
* Parses a resource name into its component parts. Resource names
* look like: module/name.ext!strip, where the !strip part is
* optional.
* @param {String} name the resource name
* @returns {Object} with properties "moduleName", "ext" and "strip"
* where strip is a boolean.
*/
parseName: function (name) {
var modName, ext, temp,
strip = false,
index = name.lastIndexOf("."),
isRelative = name.indexOf('./') === 0 ||
name.indexOf('../') === 0;
if (index !== -1 && (!isRelative || index > 1)) {
modName = name.substring(0, index);
ext = name.substring(index + 1);
} else {
modName = name;
}
temp = ext || modName;
index = temp.indexOf("!");
if (index !== -1) {
//Pull off the strip arg.
strip = temp.substring(index + 1) === "strip";
temp = temp.substring(0, index);
if (ext) {
ext = temp;
} else {
modName = temp;
}
}
return {
moduleName: modName,
ext: ext,
strip: strip
};
},
xdRegExp: /^((\w+)\:)?\/\/([^\/\\]+)/,
/**
* Is an URL on another domain. Only works for browser use, returns
* false in non-browser environments. Only used to know if an
* optimized .js version of a text resource should be loaded
* instead.
* @param {String} url
* @returns Boolean
*/
useXhr: function (url, protocol, hostname, port) {
var uProtocol, uHostName, uPort,
match = text.xdRegExp.exec(url);
if (!match) {
return true;
}
uProtocol = match[2];
uHostName = match[3];
uHostName = uHostName.split(':');
uPort = uHostName[1];
uHostName = uHostName[0];
return (!uProtocol || uProtocol === protocol) &&
(!uHostName || uHostName.toLowerCase() === hostname.toLowerCase()) &&
((!uPort && !uHostName) || uPort === port);
},
finishLoad: function (name, strip, content, onLoad) {
content = strip ? text.strip(content) : content;
if (masterConfig.isBuild) {
buildMap[name] = content;
}
onLoad(content);
},
load: function (name, req, onLoad, config) {
//Name has format: some.module.filext!strip
//The strip part is optional.
//if strip is present, then that means only get the string contents
//inside a body tag in an HTML string. For XML/SVG content it means
//removing the <?xml ...?> declarations so the content can be inserted
//into the current doc without problems.
// Do not bother with the work if a build and text will
// not be inlined.
if (config && config.isBuild && !config.inlineText) {
onLoad();
return;
}
masterConfig.isBuild = config && config.isBuild;
var parsed = text.parseName(name),
nonStripName = parsed.moduleName +
(parsed.ext ? '.' + parsed.ext : ''),
url = req.toUrl(nonStripName),
useXhr = (masterConfig.useXhr) ||
text.useXhr;
// Do not load if it is an empty: url
if (url.indexOf('empty:') === 0) {
onLoad();
return;
}
//Load the text. Use XHR if possible and in a browser.
if (!hasLocation || useXhr(url, defaultProtocol, defaultHostName, defaultPort)) {
text.get(url, function (content) {
text.finishLoad(name, parsed.strip, content, onLoad);
}, function (err) {
if (onLoad.error) {
onLoad.error(err);
}
});
} else {
//Need to fetch the resource across domains. Assume
//the resource has been optimized into a JS module. Fetch
//by the module name + extension, but do not include the
//!strip part to avoid file system issues.
req([nonStripName], function (content) {
text.finishLoad(parsed.moduleName + '.' + parsed.ext,
parsed.strip, content, onLoad);
});
}
},
write: function (pluginName, moduleName, write, config) {
if (buildMap.hasOwnProperty(moduleName)) {
var content = text.jsEscape(buildMap[moduleName]);
write.asModule(pluginName + "!" + moduleName,
"define(function () { return '" +
content +
"';});\n");
}
},
writeFile: function (pluginName, moduleName, req, write, config) {
var parsed = text.parseName(moduleName),
extPart = parsed.ext ? '.' + parsed.ext : '',
nonStripName = parsed.moduleName + extPart,
//Use a '.js' file name so that it indicates it is a
//script that can be loaded across domains.
fileName = req.toUrl(parsed.moduleName + extPart) + '.js';
//Leverage own load() method to load plugin value, but only
//write out values that do not have the strip argument,
//to avoid any potential issues with ! in file names.
text.load(nonStripName, req, function (value) {
//Use own write() method to construct full module value.
//But need to create shell that translates writeFile's
//write() to the right interface.
var textWrite = function (contents) {
return write(fileName, contents);
};
textWrite.asModule = function (moduleName, contents) {
return write.asModule(moduleName, fileName, contents);
};
text.write(pluginName, nonStripName, textWrite, config);
}, config);
}
};
if (masterConfig.env === 'node' || (!masterConfig.env &&
typeof process !== "undefined" &&
process.versions &&
!!process.versions.node &&
!process.versions['node-webkit'] &&
!process.versions['atom-shell'])) {
//Using special require.nodeRequire, something added by r.js.
fs = require.nodeRequire('fs');
text.get = function (url, callback, errback) {
try {
var file = fs.readFileSync(url, 'utf8');
//Remove BOM (Byte Mark Order) from utf8 files if it is there.
if (file[0] === '\uFEFF') {
file = file.substring(1);
}
callback(file);
} catch (e) {
if (errback) {
errback(e);
}
}
};
} else if (masterConfig.env === 'xhr' || (!masterConfig.env &&
text.createXhr())) {
text.get = function (url, callback, errback, headers) {
var xhr = text.createXhr(), header;
xhr.open('GET', url, true);
//Allow plugins direct access to xhr headers
if (headers) {
for (header in headers) {
if (headers.hasOwnProperty(header)) {
xhr.setRequestHeader(header.toLowerCase(), headers[header]);
}
}
}
//Allow overrides specified in config
if (masterConfig.onXhr) {
masterConfig.onXhr(xhr, url);
}
xhr.onreadystatechange = function (evt) {
var status, err;
//Do not explicitly handle errors, those should be
//visible via console output in the browser.
if (xhr.readyState === 4) {
status = xhr.status || 0;
if (status > 399 && status < 600) {
//An http 4xx or 5xx error. Signal an error.
err = new Error(url + ' HTTP status: ' + status);
err.xhr = xhr;
if (errback) {
errback(err);
}
} else {
callback(xhr.responseText);
}
if (masterConfig.onXhrComplete) {
masterConfig.onXhrComplete(xhr, url);
}
}
};
xhr.send(null);
};
} else if (masterConfig.env === 'rhino' || (!masterConfig.env &&
typeof Packages !== 'undefined' && typeof java !== 'undefined')) {
//Why Java, why is this so awkward?
text.get = function (url, callback) {
var stringBuffer, line,
encoding = "utf-8",
file = new java.io.File(url),
lineSeparator = java.lang.System.getProperty("line.separator"),
input = new java.io.BufferedReader(new java.io.InputStreamReader(new java.io.FileInputStream(file), encoding)),
content = '';
try {
stringBuffer = new java.lang.StringBuffer();
line = input.readLine();
// Byte Order Mark (BOM) - The Unicode Standard, version 3.0, page 324
// http://www.unicode.org/faq/utf_bom.html
// Note that when we use utf-8, the BOM should appear as "EF BB BF", but it doesn't due to this bug in the JDK:
// http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4508058
if (line && line.length() && line.charAt(0) === 0xfeff) {
// Eat the BOM, since we've already found the encoding on this file,
// and we plan to concatenating this buffer with others; the BOM should
// only appear at the top of a file.
line = line.substring(1);
}
if (line !== null) {
stringBuffer.append(line);
}
while ((line = input.readLine()) !== null) {
stringBuffer.append(lineSeparator);
stringBuffer.append(line);
}
//Make sure we return a JavaScript string and not a Java string.
content = String(stringBuffer.toString()); //String
} finally {
input.close();
}
callback(content);
};
} else if (masterConfig.env === 'xpconnect' || (!masterConfig.env &&
typeof Components !== 'undefined' && Components.classes &&
Components.interfaces)) {
//Avert your gaze!
Cc = Components.classes;
Ci = Components.interfaces;
Components.utils['import']('resource://gre/modules/FileUtils.jsm');
xpcIsWindows = ('@mozilla.org/windows-registry-key;1' in Cc);
text.get = function (url, callback) {
var inStream, convertStream, fileObj,
readData = {};
if (xpcIsWindows) {
url = url.replace(/\//g, '\\');
}
fileObj = new FileUtils.File(url);
//XPCOM, you so crazy
try {
inStream = Cc['@mozilla.org/network/file-input-stream;1']
.createInstance(Ci.nsIFileInputStream);
inStream.init(fileObj, 1, 0, false);
convertStream = Cc['@mozilla.org/intl/converter-input-stream;1']
.createInstance(Ci.nsIConverterInputStream);
convertStream.init(inStream, "utf-8", inStream.available(),
Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER);
convertStream.readString(inStream.available(), readData);
convertStream.close();
inStream.close();
callback(readData.value);
} catch (e) {
throw new Error((fileObj && fileObj.path || '') + ': ' + e);
}
};
}
return text;
});

View File

@ -15,7 +15,8 @@ require.config({
paths: { paths: {
jquery: 'libs/jquery-1.10.2.min', jquery: 'libs/jquery-1.10.2.min',
underscore: 'libs/underscore-min', underscore: 'libs/underscore-min',
backbone: 'libs/backbone-min' backbone: 'libs/backbone-min',
templates: 'goatApp/templates'
} }
, ,
shim: { shim: {