freeCodeCamp/client/commonFramework/update-preview.js

155 lines
4.1 KiB
JavaScript

window.common = (function(global) {
const {
Rx: { BehaviorSubject, Observable },
common = { init: [] }
} = global;
// the first script tag here is to proxy jQuery
// We use the same jQuery on the main window but we change the
// context to that of the iframe.
var libraryIncludes = `
<script>
window.loopProtect = parent.loopProtect;
window.__err = null;
window.loopProtect.hit = function(line) {
window.__err = new Error(
'Potential infinite loop at line ' +
line +
'. To disable loop protection, write:' +
' \\n\\/\\/ noprotect\\nas the first' +
' line. Beware that if you do have an infinite loop in your code' +
' this will crash your browser.'
);
};
</script>
<link
rel='stylesheet'
href='//cdnjs.cloudflare.com/ajax/libs/animate.css/3.2.0/animate.min.css'
/>
<link
rel='stylesheet'
href='//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css'
/>
<link
rel='stylesheet'
href='//maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css'
/>
<style>
body { padding: 0px 3px 0px 3px; }
/* FORM RESET: */
textarea,
select,
input[type="date"],
input[type="datetime"],
input[type="datetime-local"],
input[type="email"],
input[type="month"],
input[type="number"],
input[type="password"],
input[type="search"],
input[type="tel"],
input[type="text"],
input[type="time"],
input[type="url"],
input[type="week"] {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
-webkit-background-clip: padding;
-moz-background-clip: padding;
background-clip:padding-box;
-webkit-border-radius:0;
-moz-border-radius:0;
-ms-border-radius:0;
-o-border-radius:0;
border-radius:0;
-webkit-appearance:none;
background-color:#fff;
color:#000;
outline:0;
margin:0;
padding:0;
text-align: left;
font-size:1em;
height: 1.8em;
vertical-align: middle;
}
select, select, select {
background:#fff
url('data:image/png;base64,\
R0lGODlhDQAEAIAAAAAAAP8A/yH5BAEHAAEAL\
AAAAAANAAQAAAILhA+hG5jMDpxvhgIAOw==');
background-repeat: no-repeat;
background-position: 97% center;
padding:0 25px 0 8px;
font-size: .875em;
}
// ! FORM RESET
</style>
`;
const codeDisabledError = `
<script>
window.__err = new Error('code has been disabled');
</script>
`;
const iFrameScript$ =
common.getScriptContent$('/js/iFrameScripts.js').shareReplay();
const jQueryScript$ = common.getScriptContent$(
'/bower_components/jquery/dist/jquery.js'
).shareReplay();
// behavior subject allways remembers the last value
// we use this to determine if runPreviewTest$ is defined
// and prime it with false
common.previewReady$ = new BehaviorSubject(false);
// These should be set up in the preview window
// if this error is seen it is because the function tried to run
// before the iframe has completely loaded
common.runPreviewTests$ =
common.checkPreview$ =
() => Observable.throw(new Error('Preview not fully loaded'));
common.updatePreview$ = function updatePreview$(code = '') {
const preview = common.getIframe('preview');
return Observable.combineLatest(
iFrameScript$,
jQueryScript$,
(iframe, jQuery) => ({
iframeScript: `<script>${iframe}</script>`,
jQuery: `<script>${jQuery}</script>`
})
)
.first()
.flatMap(({ iframeScript, jQuery }) => {
// we make sure to override the last value in the
// subject to false here.
common.previewReady$.onNext(false);
preview.open();
preview.write(
libraryIncludes +
jQuery +
(common.shouldRun() ? code : codeDisabledError) +
'<!-- -->' +
iframeScript
);
preview.close();
// now we filter false values and wait for the first true
return common.previewReady$
.filter(ready => ready)
.first()
// the delay here is to give code within the iframe
// control to run
.delay(400);
})
.map(() => code);
};
return common;
}(window));