jsbin/public/js/render/live.js
2012-06-03 10:37:31 +02:00

173 lines
6.0 KiB
JavaScript

var $live = $('#live'),
$body = $('body'),
showlive = $('#showlive')[0],
throttledPreview = throttle(renderLivePreview, 200),
killAlerts = '<script>try{window.open=function(){};window.print=function(){};window.alert=function(){};window.prompt=function(){};window.confirm=function(){};}catch(e){}</script>',
restoreAlerts = '<script>try{delete window.print;delete window.alert;delete window.prompt;delete window.confirm;delete window.open;}catch(e){}</script>';
var iframedelay = (function () {
var iframedelay = { active : false },
iframe = document.createElement('iframe'),
doc,
callbackName = '__callback' + (+new Date);
iframe.style.height = iframe.style.width = '1px';
iframe.style.visibility = 'hidden';
document.body.appendChild(iframe);
doc = iframe.contentDocument || iframe.contentWindow.document;
window[callbackName] = function (width) {
iframedelay.active = width === 0;
try {
iframe.parentNode.removeChild(iframe);
delete window[callbackName];
} catch (e){};
};
try {
doc.open();
doc.write('<script>window.parent.' + callbackName + '(window.innerWidth)</script>');
doc.close();
} catch (e) {
iframedelay.active = true;
}
return iframedelay;
}());
var deferredLiveRender = null;
function codeChangeLive(event, data) {
clearTimeout(deferredLiveRender);
var editor,
line,
panel = jsbin.panels.panels.live;
if (jsbin.panels.ready) {
if (jsbin.settings.includejs === false && data.panelId === 'javascript') {
// ignore
} else if (panel.visible) {
// test to see if they're write a while loop
if (jsbin.panels.focused.id === 'javascript') {
// check the current line doesn't match a for or a while or a do - which could trip in to an infinite loop
editor = jsbin.panels.focused.editor;
line = editor.getLine(editor.getCursor().line);
if (ignoreDuringLive.test(line) === true) {
// ignore
throttledPreview.cancel();
deferredLiveRender = setTimeout(function () {
codeChangeLive(event, data);
}, 1000);
} else {
throttledPreview();
}
} else {
throttledPreview();
}
}
}
}
$document.bind('codeChange.live', codeChangeLive);
function two(s) {
return (s+'').length < 2 ? '0' + s : s;
}
function renderLivePreview(withalerts) {
var source = getPreparedCode(),
remove = $live.find('iframe').length > 0,
frame = $live.prepend('<iframe class="stretch" frameBorder="0"></iframe>').find('iframe:first')[0],
doc = frame.contentDocument || frame.contentWindow.document,
win = doc.defaultView || doc.parentWindow,
d = new Date();
// if (!useCustomConsole) console.log('--- refreshing live preview @ ' + [two(d.getHours()),two(d.getMinutes()),two(d.getSeconds())].join(':') + ' ---');
if (withalerts !== true && jsbin.settings.includejs === false) {
source = source.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '');
}
// strip autofocus from the markup - prevents the focus switching out of the editable area
source = source.replace(/(<.*?\s)(autofocus)/g, '$1');
var run = function () {
var jsbinConsole = jsbin.panels.panels.console.visible ? 'window.top._console' : false;
// we're wrapping the whole thing in a try/catch in case the doc.write breaks the
// execution and never reaches the point where it removes the previous iframe.
try {
doc.open();
if (debug) {
doc.write('<pre>' + source.replace(/[<>&]/g, function (m) {
if (m == '<') return '&lt;';
if (m == '>') return '&gt;';
if (m == '"') return '&quot;';
}) + '</pre>');
} else {
// nullify the blocking functions
// IE requires that this is done in the script, rather than off the window object outside of the doc.write
if (withalerts !== true) {
doc.write(killAlerts);
} else {
doc.write(restoreAlerts);
}
if (jsbinConsole) {
doc.write('<script>(function(){window.addEventListener && window.addEventListener("error", function (event) { window.top._console.error({ message: event.message }, event.filename + ":" + event.lineno);}, false);}());</script>');
// doc.write('<script>(function () { var fakeConsole = ' + jsbinConsole + '; if (console != undefined) { for (var k in fakeConsole) { console[k] = fakeConsole[k]; } } else { console = fakeConsole; } })(); window.onerror = function () { console.error.apply(console, arguments); }</script>');
}
// almost jQuery Mobile specific - when the page renders
// it moves the focus over to the live preview - since
// we no longer have a "render" panel, our code loses
// focus which is damn annoying. So, I cancel the iframe
// focus event...because I can :)
var click = false;
win.onmousedown = function () {
click = true;
setTimeout(function () {
click = false;
}, 10);
};
win.onfocus = function (event) {
// allow the iframe to be clicked to create a fake focus
if (click) {
$('#live').focus();
}
return false;
};
doc.write(source);
doc.write(restoreAlerts);
}
doc.close();
} catch (e) {
console.error(e);
}
delete jsbin.panels.panels.live.doc;
jsbin.panels.panels.live.doc = doc;
// by removing the previous iframe /after/ the newly created live iframe
// has run, it doesn't flicker - which fakes a smooth live update.
if (remove) {
$live.find('iframe:last').remove();
}
}
// WebKit requires a wait time before actually writing to the iframe
// annoyingly it's not consistent (I suspect WebKit is the buggy one)
if (iframedelay.active) {
// this setTimeout allows the iframe to be rendered before our code
// runs - thus allowing us access to the innerWidth, et al
setTimeout(run, 0);
} else {
run();
}
}