diff --git a/client/sagas/execute-challenge-saga.js b/client/sagas/execute-challenge-saga.js
index ab7b9ac8a4..96b7c6e566 100644
--- a/client/sagas/execute-challenge-saga.js
+++ b/client/sagas/execute-challenge-saga.js
@@ -1,5 +1,8 @@
import { Scheduler, Observable } from 'rx';
+import {
+ challengeSelector
+} from '../../common/app/routes/challenges/redux/selectors';
import { ajax$ } from '../../common/utils/ajax-stream';
import throwers from '../rechallenge/throwers';
import transformers from '../rechallenge/transformers';
@@ -25,10 +28,7 @@ const globalRequires = [{
link: 'https://cdnjs.cloudflare.com/' +
'ajax/libs/normalize/4.2.0/normalize.min.css'
}, {
- src: '/bower_components/jquery/dist/jquery.js',
- script: true,
- type: 'global',
- crossDomain: false
+ src: 'https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.js'
}];
const scriptCache = new Map();
@@ -36,7 +36,7 @@ const linkCache = new Map();
function cacheScript({ src } = {}, crossDomain = true) {
if (!src) {
- return Observable.throw(new Error('No source provided for script'));
+ throw new Error('No source provided for script');
}
if (scriptCache.has(src)) {
return scriptCache.get(src);
@@ -49,7 +49,6 @@ function cacheScript({ src } = {}, crossDomain = true) {
})
.map(({ response }) => response)
.map(script => ``)
- .catch(createErrorObservable)
.shareReplay();
scriptCache.set(src, script$);
@@ -71,7 +70,7 @@ function cacheLink({ link } = {}, crossDomain = true) {
})
.map(({ response }) => response)
.map(script => ``)
- .catch(createErrorObservable)
+ .catch(() => Observable.just(''))
.shareReplay();
linkCache.set(link, link$);
@@ -94,8 +93,10 @@ export default function executeChallengeSaga(action$, getState) {
))
.debounce(750)
.flatMapLatest(({ type }) => {
- const { files, required = [] } = getState().challengesApp;
- const finalRequires = [...required, ...globalRequires ];
+ const state = getState();
+ const { files } = state.challengesApp;
+ const { challenge: { required = [] } } = challengeSelector(state);
+ const finalRequires = [...globalRequires, ...required ];
return createFileStream(files)
::throwers()
::transformers()
@@ -121,7 +122,7 @@ export default function executeChallengeSaga(action$, getState) {
.flatMap(source => {
const head$ = Observable.from(finalRequires)
.flatMap(required => {
- if (required.script) {
+ if (required.src) {
return cacheScript(required, required.crossDomain);
}
if (required.link) {
diff --git a/common/models/challenge.json b/common/models/challenge.json
index 9243713cee..94d3c3f21b 100644
--- a/common/models/challenge.json
+++ b/common/models/challenge.json
@@ -106,6 +106,25 @@
"translations": {
"type": "object",
"default": "{}"
+ },
+ "required": {
+ "type": [{
+ "type": {
+ "link": {
+ "type": "string",
+ "description": "Used for css files"
+ },
+ "src": {
+ "type": "string",
+ "description": "Used for script files"
+ },
+ "crossDomain": {
+ "type": "boolean",
+ "description": "Files coming from FreeCodeCamp must mark this true"
+ }
+ }
+ }],
+ "default": []
}
},
"validations": [],
diff --git a/seed/challenges/01-front-end-development-certification/bootstrap.json b/seed/challenges/01-front-end-development-certification/bootstrap.json
index 0209cf800d..1b88adc1ec 100644
--- a/seed/challenges/01-front-end-development-certification/bootstrap.json
+++ b/seed/challenges/01-front-end-development-certification/bootstrap.json
@@ -3,6 +3,9 @@
"order": 3,
"time": "5 hours",
"helpRoom": "Help",
+ "required": [
+ { "link": "https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.css" }
+ ],
"challenges": [
{
"id": "bad87fee1348bd9acde08712",