mirror of
https://github.com/jsbin/jsbin.git
synced 2026-01-18 15:18:04 +00:00
294 lines
9.6 KiB
JavaScript
294 lines
9.6 KiB
JavaScript
function tryToRender() {
|
|
// TODO re-enable this code. It's been disabled for now because it
|
|
// only works to detect infinite loops in very simple situations.
|
|
// what it needs is a few polyfills in the worker for DOM API
|
|
// and probably canvas API.
|
|
if (false && window.Worker) {
|
|
// this code creates a web worker, and if it doesn't complete the
|
|
// execution inside of 100ms, it'll return false suggesting there may
|
|
// be an infinite loop
|
|
testForHang(function (ok) {
|
|
if (ok) {
|
|
renderLivePreview();
|
|
}
|
|
});
|
|
} else {
|
|
renderLivePreview();
|
|
}
|
|
}
|
|
|
|
var $live = $('#live'),
|
|
showlive = $('#showlive')[0],
|
|
throttledPreview = throttle(tryToRender, 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>',
|
|
liveScrollTop = null;
|
|
|
|
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;
|
|
}());
|
|
|
|
/**
|
|
* Grab the doctype from a string.
|
|
*
|
|
* Returns an object with doctype and tail keys.
|
|
*/
|
|
var getDoctype = (function () {
|
|
// Cached regex
|
|
// [\s\S] matches multiline doctypes
|
|
var regex = /<!doctype [\s\S]*?>/i;
|
|
return function (str) {
|
|
var doctype = (str.match(regex) || [''])[0],
|
|
tail = str.substr(doctype.length);
|
|
return {
|
|
doctype: doctype,
|
|
tail: tail
|
|
};
|
|
};
|
|
}());
|
|
|
|
function sendReload() {
|
|
if (saveChecksum) {
|
|
$.ajax({
|
|
url: jsbin.getURL() + '/reload',
|
|
data: {
|
|
code: jsbin.state.code,
|
|
revision: jsbin.state.revision,
|
|
checksum: saveChecksum
|
|
},
|
|
type: 'post'
|
|
});
|
|
}
|
|
}
|
|
|
|
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.lameEditor && jsbin.panels.focused && 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(), //jsbin.panels.panels.console.visible),
|
|
remove = $live.find('iframe').length > 0,
|
|
$frame = $live.prepend('<iframe class="stretch" frameBorder="0"></iframe>').find('iframe:first'),
|
|
frame = $frame[0],
|
|
doc = frame.contentDocument || frame.contentWindow.document,
|
|
win = doc.defaultView || doc.parentWindow,
|
|
d = new Date(),
|
|
combinedSource = [];
|
|
|
|
// 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, '');
|
|
} else if (withalerts) {
|
|
// send an update to the server that we ran the code
|
|
sendReload();
|
|
}
|
|
|
|
// 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 (jsbin.settings.debug) {
|
|
doc.write('<pre>' + source.replace(/[<>&]/g, function (m) {
|
|
if (m == '<') return '<';
|
|
if (m == '>') return '>';
|
|
if (m == '"') return '"';
|
|
}) + '</pre>');
|
|
} else {
|
|
|
|
// Make sure the doctype is the first thing in the source
|
|
var doctypeObj = getDoctype(source),
|
|
doctype = doctypeObj.doctype;
|
|
source = doctypeObj.tail;
|
|
combinedSource.push(doctype);
|
|
|
|
// 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) {
|
|
combinedSource.push(killAlerts);
|
|
} else {
|
|
combinedSource.push(restoreAlerts);
|
|
}
|
|
|
|
if (jsbinConsole) {
|
|
combinedSource.push('<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();
|
|
// also close any open dropdowns
|
|
closedropdown();
|
|
}
|
|
return false;
|
|
};
|
|
|
|
win.onscroll = function () {
|
|
liveScrollTop = this.scrollY;
|
|
};
|
|
|
|
var size = $live.find('.size'),
|
|
timer = null;
|
|
|
|
var hide = throttle(function () {
|
|
size.fadeOut(200);
|
|
}, 2000);
|
|
$(win).on('resize', function () {
|
|
// clearTimeout(timer);
|
|
if (!jsbin.embed) {
|
|
size.show().html(this.innerWidth + 'px');
|
|
hide();
|
|
}
|
|
});
|
|
|
|
win.resizeJSBin = throttle(function () {
|
|
var height = ($body.outerHeight(true) - $frame.height()) + doc.documentElement.offsetHeight;
|
|
window.parent.postMessage({ height: height }, '*');
|
|
}, 20);
|
|
|
|
combinedSource.push(source);
|
|
combinedSource.push(restoreAlerts);
|
|
// Only one doc.write. Fixes IE crashing bug.
|
|
doc.write(combinedSource.join('\n'));
|
|
|
|
if (liveScrollTop !== null) {
|
|
win.scrollTo(0, liveScrollTop);
|
|
}
|
|
}
|
|
doc.close();
|
|
win.resizeJSBin();
|
|
if (jsbin.embed) { // allow the iframe to be resized
|
|
(function () {
|
|
var dragging = false,
|
|
height = false,
|
|
$window = $(win);
|
|
$(doc.documentElement).mousedown(function (event) {
|
|
if (event.pageY > $window.height() - 40) {
|
|
dragging = event.pageY;
|
|
height = $body.outerHeight();
|
|
}
|
|
}).mousemove(function (event) {
|
|
if (dragging !== false) {
|
|
window.parent.postMessage({ height: height + (event.pageY - dragging) }, '*');
|
|
}
|
|
}).mouseup(function () {
|
|
dragging = false;
|
|
});
|
|
})();
|
|
}
|
|
} catch (e) {
|
|
if (jsbinConsole) {
|
|
window.top._console.error({ message: e.message }, e.filename + ":" + e.lineno);
|
|
}
|
|
}
|
|
|
|
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();
|
|
}
|
|
|
|
// if (jsbin.panels.panels.console.visible) {
|
|
// jsbin.panels.panels.console.render(); // now run the JS
|
|
// }
|
|
};
|
|
|
|
// 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();
|
|
}
|
|
}
|