Move script tags to bottom. Refactor livepreview logic into livepreview.js.

This commit is contained in:
bootstraponline
2012-05-20 12:34:32 -06:00
parent f7d3f6b3f0
commit d5155273df
2 changed files with 366 additions and 372 deletions
@@ -1,19 +1,9 @@
<html>
<head>
<title>Live Preview</title>
<link rel="stylesheet" type="text/css" href="../css/template.css" />
<link rel="stylesheet" type="text/css" href="css/highlightjs/github.css" />
<link rel="stylesheet" type="text/css" href="css/custom.css" />
<script src="js/ace/ace.js" type="text/javascript" charset="utf-8"></script>
<script src="js/ace/theme-twilight.js" type="text/javascript" charset="utf-8"></script>
<script src="js/ace/mode-markdown.js" type="text/javascript" charset="utf-8"></script>
<script src="js/pagedown/Markdown.Converter.js" type="text/javascript" charset="utf-8"></script>
<script src="js/pagedown/Markdown.Sanitizer.js" type="text/javascript" charset="utf-8"></script>
<script src="js/jquery/jquery-1.7.2.min.js" type="text/javascript" charset="utf-8"></script>
<script src="js/highlightjs/highlight.pack.js" type="text/javascript" charset="utf-8"></script>
<script src="js/debounce/jquery.ba-throttle-debounce.js" type="text/javascript" charset="utf-8"></script>
</head>
<body>
@@ -34,367 +24,14 @@
<div id="comment"></div>
<div id="darkness"></div>
<script type="text/javascript">
(function () {
var converter = Markdown.getSanitizingConverter();
var editor = ace.edit("editor");
var editorSession = editor.getSession();
var editorContainer = editor.container;
var preview = document.getElementById("previewframe");
var content = document.getElementById("contentframe");
var toolPanel = document.getElementById("toolpanel");
var comment = document.getElementById("comment");
var commentToolPanel = document.getElementById("commenttoolpanel");
// dim the page
var darkness = document.getElementById("darkness");
var leftRight = true;
var jsm = {}; // JavaScript Markdown
window.jsm = jsm;
window.jsm.toggleLeftRight = function() {
leftRight = leftRight === false ? true : false;
jsm.resize();
}
var MarkdownMode = require("ace/mode/markdown").Mode;
function initAce(editor, editorSession) {
editor.setTheme("ace/theme/twilight");
editorSession.setMode(new MarkdownMode());
// Gutter shows line numbers
editor.renderer.setShowGutter(true);
editor.renderer.setHScrollBarAlwaysVisible(false);
editorSession.setUseSoftTabs(true);
editorSession.setTabSize(2);
editorSession.setUseWrapMode(true);
editor.setShowPrintMargin(false);
editor.setBehavioursEnabled(true);
}
initAce(editor, editorSession);
// Setup comment ace.
var commentEditor = ace.edit("comment");
var commentEditorSession = commentEditor.getSession();
var commentEditorContainer = commentEditor.container;
initAce(commentEditor, commentEditorSession);
// RegExp from http://stackoverflow.com/questions/901115/get-query-string-values-in-javascript
$.key = function(key){
var value = new RegExp('[\\?&]' + key + '=([^&#]*)').exec(window.location.href);
return (!value) ? 0 : value[1] || 0;
}
// True if &create=true
var create = $.key("create");
// The name of the page being edited.
var pageName = $.key("page");
defaultCommitMessage = function() {
var msg = pageName + " (markdown)";
if (create) {
return "Created " + msg;
} else {
return "Updated " + msg;
}
}
// Set comment using the default commit message.
commentEditorSession.setValue( defaultCommitMessage() );
$.save = function( commitMessage ) {
var POST = "POST";
var markdown = "markdown";
var txt = editorSession.getValue();
var msg = defaultCommitMessage();
var newLocation = window.location.origin + "/" + pageName;
// if &create=true then handle create instead of edit.
if (create) {
jQuery.ajax({
type: POST,
url: "/create",
data: { page: pageName, format: markdown, content: txt, message: commitMessage || msg },
success: function() {
window.location = newLocation;
}
});
} else {
jQuery.ajax({
type: POST,
url: "/edit/" + pageName,
data: { format: markdown, content: txt, message: commitMessage || msg },
success: function() {
window.location = newLocation;
}
});
} // end else
}
/* Load markdown from /data/page into the ace editor. */
jQuery.ajax({
type: "GET",
url: "/data/" + $.key("page"),
success: function(data) {
editorSession.setValue(data);
}
});
$("#save").click(function() {
$.save();
});
// Hide dimmer, comment tool panel, and comment.
$("#commentcancel").click(function() {
// Restore focus on commentcancel but not on
// savecommentconfirm because the latter loads
// a new page.
hideCommentWindow();
editor.focus();
});
var isCommentHidden = true;
function hideCommentWindow() {
isCommentHidden = true;
darkness.style.visibility = "hidden";
commentToolPanel.style.visibility = "hidden";
comment.style.visibility = "hidden";
}
// Show dimmer, comment tool panel, and comment.
$("#savecomment").click(function() {
isCommentHidden = false;
darkness.style.visibility = "visible";
commentToolPanel.style.visibility = "visible";
comment.style.visibility = "visible";
// Set focus so typing can begin immediately.
commentEditor.focus();
});
$("#savecommentconfirm").click(function() {
$.save(commentEditorSession.getValue());
hideCommentWindow();
});
// onChange calls applyTimeout which rate limits the calling of makePreviewHtml based on render time.
editor.on('change', applyTimeout);
var cssTextSet = function( element, css ){
element.style.cssText = css;
};
var cssAttrSet = function( element, css ){
element.setAttribute( 'style', css );
};
/*
Redefine the function based on browser support.
element - the element to set the css on
css - a fully formed css string. ex: "top: 0; left: 0;"
Avoid reflow by batching CSS changes.
http://dev.opera.com/articles/view/efficient-javascript/?page=3#stylechanges
*/
var cssSet = function( element, css ) {
if( typeof( element.style.cssText ) != 'undefined' ) {
cssTextSet( element, css );
cssSet = cssTextSet;
} else {
cssAttrSet( element, css );
cssSet = cssAttrSet;
}
};
function resize() {
var width = $(window).width();
var widthHalf = width / 2;
var widthFourth = widthHalf / 2;
var height = $(window).height();
var heightHalf = height / 2;
// height minus 50 so the end of document text doesn't flow off the page.
var editorContainerStyle = "width:" + widthHalf + "px;" +
"height:" + (height - 50) + "px;" +
"left:" + (leftRight === false ? widthHalf + "px;" : "0px;") +
"top:" + "40px;"; // use 40px for tool menu
cssSet( editorContainer, editorContainerStyle );
editor.resize();
// width -2 for scroll bar & -10 for left offset
var previewStyle = "width:" + (widthHalf - 2 - 10) + "px;" +
"height:" + height + "px;" +
"left:" + (leftRight === false ? "10px;" : widthHalf + "px;") +
"top:" + "0px;";
cssSet( preview, previewStyle );
// Resize tool panel
var toolPanelStyle = "width:" + widthHalf + "px;" +
"left:" + (leftRight === false ? widthHalf + "px;" : "0px;");
cssSet( toolPanel, toolPanelStyle );
// Resize comment related elements.
var commentHidden = "visibility:" + ( isCommentHidden === true ? "hidden;" : "visible;" );
// Adjust comment editor
var commentEditorContainerStyle = "height:" + heightHalf + "px;" +
"width:" + widthHalf + "px;" +
"left:" + widthFourth + "px;" +
"top:" + (heightHalf / 2) + "px;" +
commentHidden;
cssSet( commentEditorContainer, commentEditorContainerStyle );
commentEditor.resize();
// In top subtract height (40px) of comment tool panel.
var commentToolPanelStyle = "width:" + widthHalf + "px;" +
"left:" + widthFourth + "px;" +
"top:" + (height / 4 - 40) + "px;" +
commentHidden;
cssSet( commentToolPanel, commentToolPanelStyle );
// Resize dimmer.
var darknessStyle = "width:" + width + "px;" +
"height:" + height + "px;" +
commentHidden;
cssSet(darkness, darknessStyle);
}
window.jsm.resize = resize;
/*
Resize can be called an absurd amount of times
and will crash the page without debouncing.
http://benalman.com/projects/jquery-throttle-debounce-plugin/
https://github.com/cowboy/jquery-throttle-debounce
http://unscriptable.com/2009/03/20/debouncing-javascript-methods/
*/
$(window).resize( $.debounce( 100, resize ) );
// resize for the intial page load
resize();
var elapsedTime;
var oldInputText = "";
// ---- from Markdown.Editor
var timeout;
var nonSuckyBrowserPreviewSet = function (text) {
content.innerHTML = text;
}
// IE doesn't let you use innerHTML if the element is contained somewhere in a table
// (which is the case for inline editing) -- in that case, detach the element, set the
// value, and reattach. Yes, that *is* ridiculous.
var ieSafePreviewSet = function (text) {
var parent = content.parentNode;
var sibling = content.nextSibling;
parent.removeChild(content);
content.innerHTML = text;
if (!sibling)
parent.appendChild(content);
else
parent.insertBefore(content, sibling);
}
var previewSet = function( text ) {
try {
nonSuckyBrowserPreviewSet( text );
previewSet = nonSuckyBrowserPreviewSet;
} catch (e) {
ieSafePreviewSet( text );
previewSet = ieSafePreviewSet;
}
};
var languages = [ "1c", "actionscript", "apache", "avrasm", "axapta",
"bash", "cmake", "coffeescript", "cpp", "cs", "css", "delphi", "diff",
"django", "d", "dos", "erlang", "erlang-repl", "go", "haskell", "ini",
"java", "javascript", "languages", "lisp", "lua", "markdown", "matlab",
"mel", "nginx", "objectivec", "parser3", "perl", "php", "profile",
"python", "renderman", "ruby", "rust", "scala", "smalltalk", "sql",
"tex", "vala", "vbscript", "vhdl", "xml" ];
var makePreviewHtml = function () {
var text = editorSession.getValue();
if (text && text == oldInputText) {
return; // Input text hasn't changed.
}
else {
oldInputText = text;
}
var prevTime = new Date().getTime();
text = converter.makeHtml(text);
// Calculate the processing time of the HTML creation.
// It's used as the delay time in the event listener.
var currTime = new Date().getTime();
elapsedTime = currTime - prevTime;
// Update the text using feature detection to support IE.
// preview.innerHTML = text; // this doesn't work on IE.
previewSet(text);
// highlight code blocks.
var codeElements = preview.getElementsByTagName("pre");
var codeElementsLength = codeElements.length;
var hlSpace = " ";
if (codeElementsLength > 0) {
for (var idx = 0; idx < codeElementsLength; idx++) {
var element = codeElements[idx];
var codeHTML = element.innerHTML;
// Only use pre tags marked containing code.
if (codeHTML[0] !== "`")
continue;
var txt = codeHTML.split(/\b/);
// the syntax for code highlighting means all code, even one line, contains newlines.
if (txt.length > 1 && codeHTML.match(/\n/)) {
// txt[0] must be "`"
// txt[0] = "`"; txt[1] = "ruby"
if (txt[0] !== "`" || $.inArray(txt[1], languages) === -1) {
element.innerHTML = codeHTML.substring(1).trim();
hljs.highlightBlock(element, hlSpace);
continue;
}
element.className = txt[1] + " highlight";
// length + 1 for the marker character.
element.innerHTML = codeHTML.substring(txt[1].length+1).trim();
hljs.highlightBlock(element, hlSpace);
} else {
element.innerHTML = codeHTML.substring(1).trim();
hljs.highlightBlock(element, hlSpace);
}
}
}
};
// preview default text on load
makePreviewHtml();
// setTimeout is already used. Used as an event listener.
var applyTimeout = function () {
if (timeout) {
clearTimeout(timeout);
timeout = undefined;
}
// 3 second max delay
if (elapsedTime > 3000) {
elapsedTime = 3000;
}
timeout = setTimeout(makePreviewHtml, elapsedTime);
};
})();
</script>
<script src="js/ace/ace.js" type="text/javascript" charset="utf-8"></script>
<script src="js/ace/theme-twilight.js" type="text/javascript" charset="utf-8"></script>
<script src="js/ace/mode-markdown.js" type="text/javascript" charset="utf-8"></script>
<script src="js/pagedown/Markdown.Converter.js" type="text/javascript" charset="utf-8"></script>
<script src="js/pagedown/Markdown.Sanitizer.js" type="text/javascript" charset="utf-8"></script>
<script src="js/jquery/jquery-1.7.2.min.js" type="text/javascript" charset="utf-8"></script>
<script src="js/highlightjs/highlight.pack.js" type="text/javascript" charset="utf-8"></script>
<script src="js/debounce/jquery.ba-throttle-debounce.js" type="text/javascript" charset="utf-8"></script>
<script src="js/livepreview/livepreview.js" type="text/javascript" charset="utf-8"></script>
</body>
</html>
@@ -0,0 +1,357 @@
(function () {
var converter = Markdown.getSanitizingConverter();
var editor = ace.edit("editor");
var editorSession = editor.getSession();
var editorContainer = editor.container;
var preview = document.getElementById("previewframe");
var content = document.getElementById("contentframe");
var toolPanel = document.getElementById("toolpanel");
var comment = document.getElementById("comment");
var commentToolPanel = document.getElementById("commenttoolpanel");
// dim the page
var darkness = document.getElementById("darkness");
var leftRight = true;
var jsm = {}; // JavaScript Markdown
window.jsm = jsm;
window.jsm.toggleLeftRight = function() {
leftRight = leftRight === false ? true : false;
jsm.resize();
}
var MarkdownMode = require("ace/mode/markdown").Mode;
function initAce(editor, editorSession) {
editor.setTheme("ace/theme/twilight");
editorSession.setMode(new MarkdownMode());
// Gutter shows line numbers
editor.renderer.setShowGutter(true);
editor.renderer.setHScrollBarAlwaysVisible(false);
editorSession.setUseSoftTabs(true);
editorSession.setTabSize(2);
editorSession.setUseWrapMode(true);
editor.setShowPrintMargin(false);
editor.setBehavioursEnabled(true);
}
initAce(editor, editorSession);
// Setup comment ace.
var commentEditor = ace.edit("comment");
var commentEditorSession = commentEditor.getSession();
var commentEditorContainer = commentEditor.container;
initAce(commentEditor, commentEditorSession);
// RegExp from http://stackoverflow.com/questions/901115/get-query-string-values-in-javascript
$.key = function(key){
var value = new RegExp('[\\?&]' + key + '=([^&#]*)').exec(window.location.href);
return (!value) ? 0 : value[1] || 0;
}
// True if &create=true
var create = $.key("create");
// The name of the page being edited.
var pageName = $.key("page");
defaultCommitMessage = function() {
var msg = pageName + " (markdown)";
if (create) {
return "Created " + msg;
} else {
return "Updated " + msg;
}
}
// Set comment using the default commit message.
commentEditorSession.setValue( defaultCommitMessage() );
$.save = function( commitMessage ) {
var POST = "POST";
var markdown = "markdown";
var txt = editorSession.getValue();
var msg = defaultCommitMessage();
var newLocation = window.location.origin + "/" + pageName;
// if &create=true then handle create instead of edit.
if (create) {
jQuery.ajax({
type: POST,
url: "/create",
data: { page: pageName, format: markdown, content: txt, message: commitMessage || msg },
success: function() {
window.location = newLocation;
}
});
} else {
jQuery.ajax({
type: POST,
url: "/edit/" + pageName,
data: { format: markdown, content: txt, message: commitMessage || msg },
success: function() {
window.location = newLocation;
}
});
} // end else
}
var elapsedTime;
var oldInputText = "";
// ---- from Markdown.Editor
var timeout;
var nonSuckyBrowserPreviewSet = function (text) {
content.innerHTML = text;
}
// IE doesn't let you use innerHTML if the element is contained somewhere in a table
// (which is the case for inline editing) -- in that case, detach the element, set the
// value, and reattach. Yes, that *is* ridiculous.
var ieSafePreviewSet = function (text) {
var parent = content.parentNode;
var sibling = content.nextSibling;
parent.removeChild(content);
content.innerHTML = text;
if (!sibling)
parent.appendChild(content);
else
parent.insertBefore(content, sibling);
}
var cssTextSet = function( element, css ){
element.style.cssText = css;
}
var cssAttrSet = function( element, css ){
element.setAttribute( 'style', css );
}
/*
Redefine the function based on browser support.
element - the element to set the css on
css - a fully formed css string. ex: "top: 0; left: 0;"
Avoid reflow by batching CSS changes.
http://dev.opera.com/articles/view/efficient-javascript/?page=3#stylechanges
*/
var cssSet = function( element, css ) {
if( typeof( element.style.cssText ) != 'undefined' ) {
cssTextSet( element, css );
cssSet = cssTextSet;
} else {
cssAttrSet( element, css );
cssSet = cssAttrSet;
}
}
var previewSet = function( text ) {
try {
nonSuckyBrowserPreviewSet( text );
previewSet = nonSuckyBrowserPreviewSet;
} catch (e) {
ieSafePreviewSet( text );
previewSet = ieSafePreviewSet;
}
};
var languages = [ "1c", "actionscript", "apache", "avrasm", "axapta",
"bash", "cmake", "coffeescript", "cpp", "cs", "css", "delphi", "diff",
"django", "d", "dos", "erlang", "erlang-repl", "go", "haskell", "ini",
"java", "javascript", "languages", "lisp", "lua", "markdown", "matlab",
"mel", "nginx", "objectivec", "parser3", "perl", "php", "profile",
"python", "renderman", "ruby", "rust", "scala", "smalltalk", "sql",
"tex", "vala", "vbscript", "vhdl", "xml" ];
var makePreviewHtml = function () {
var text = editorSession.getValue();
if (text && text == oldInputText) {
return; // Input text hasn't changed.
}
else {
oldInputText = text;
}
var prevTime = new Date().getTime();
text = converter.makeHtml(text);
// Calculate the processing time of the HTML creation.
// It's used as the delay time in the event listener.
var currTime = new Date().getTime();
elapsedTime = currTime - prevTime;
// Update the text using feature detection to support IE.
// preview.innerHTML = text; // this doesn't work on IE.
previewSet(text);
// highlight code blocks.
var codeElements = preview.getElementsByTagName("pre");
var codeElementsLength = codeElements.length;
var hlSpace = " ";
if (codeElementsLength > 0) {
for (var idx = 0; idx < codeElementsLength; idx++) {
var element = codeElements[idx];
var codeHTML = element.innerHTML;
// Only use pre tags marked containing code.
if (codeHTML[0] !== "`")
continue;
var txt = codeHTML.split(/\b/);
// the syntax for code highlighting means all code, even one line, contains newlines.
if (txt.length > 1 && codeHTML.match(/\n/)) {
// txt[0] must be "`"
// txt[0] = "`"; txt[1] = "ruby"
if (txt[0] !== "`" || $.inArray(txt[1], languages) === -1) {
element.innerHTML = codeHTML.substring(1).trim();
hljs.highlightBlock(element, hlSpace);
continue;
}
element.className = txt[1] + " highlight";
// length + 1 for the marker character.
element.innerHTML = codeHTML.substring(txt[1].length+1).trim();
hljs.highlightBlock(element, hlSpace);
} else {
element.innerHTML = codeHTML.substring(1).trim();
hljs.highlightBlock(element, hlSpace);
}
}
}
};
// setTimeout is already used. Used as an event listener.
var applyTimeout = function () {
if (timeout) {
clearTimeout(timeout);
timeout = undefined;
}
// 3 second max delay
if (elapsedTime > 3000) {
elapsedTime = 3000;
}
timeout = setTimeout(makePreviewHtml, elapsedTime);
};
/* Load markdown from /data/page into the ace editor. */
jQuery.ajax({
type: "GET",
url: "/data/" + $.key("page"),
success: function(data) {
editorSession.setValue(data);
}
});
$("#save").click(function() {
$.save();
});
// Hide dimmer, comment tool panel, and comment.
$("#commentcancel").click(function() {
// Restore focus on commentcancel but not on
// savecommentconfirm because the latter loads
// a new page.
hideCommentWindow();
editor.focus();
});
var isCommentHidden = true;
function hideCommentWindow() {
isCommentHidden = true;
darkness.style.visibility = "hidden";
commentToolPanel.style.visibility = "hidden";
comment.style.visibility = "hidden";
}
// Show dimmer, comment tool panel, and comment.
$("#savecomment").click(function() {
isCommentHidden = false;
darkness.style.visibility = "visible";
commentToolPanel.style.visibility = "visible";
comment.style.visibility = "visible";
// Set focus so typing can begin immediately.
commentEditor.focus();
});
$("#savecommentconfirm").click(function() {
$.save(commentEditorSession.getValue());
hideCommentWindow();
});
// onChange calls applyTimeout which rate limits the calling of makePreviewHtml based on render time.
editor.on('change', applyTimeout);
makePreviewHtml(); // preview default text on load
function resize() {
var width = $(window).width();
var widthHalf = width / 2;
var widthFourth = widthHalf / 2;
var height = $(window).height();
var heightHalf = height / 2;
// height minus 50 so the end of document text doesn't flow off the page.
var editorContainerStyle = "width:" + widthHalf + "px;" +
"height:" + (height - 50) + "px;" +
"left:" + (leftRight === false ? widthHalf + "px;" : "0px;") +
"top:" + "40px;"; // use 40px for tool menu
cssSet( editorContainer, editorContainerStyle );
editor.resize();
// width -2 for scroll bar & -10 for left offset
var previewStyle = "width:" + (widthHalf - 2 - 10) + "px;" +
"height:" + height + "px;" +
"left:" + (leftRight === false ? "10px;" : widthHalf + "px;") +
"top:" + "0px;";
cssSet( preview, previewStyle );
// Resize tool panel
var toolPanelStyle = "width:" + widthHalf + "px;" +
"left:" + (leftRight === false ? widthHalf + "px;" : "0px;");
cssSet( toolPanel, toolPanelStyle );
// Resize comment related elements.
var commentHidden = "visibility:" + ( isCommentHidden === true ? "hidden;" : "visible;" );
// Adjust comment editor
var commentEditorContainerStyle = "height:" + heightHalf + "px;" +
"width:" + widthHalf + "px;" +
"left:" + widthFourth + "px;" +
"top:" + (heightHalf / 2) + "px;" +
commentHidden;
cssSet( commentEditorContainer, commentEditorContainerStyle );
commentEditor.resize();
// In top subtract height (40px) of comment tool panel.
var commentToolPanelStyle = "width:" + widthHalf + "px;" +
"left:" + widthFourth + "px;" +
"top:" + (height / 4 - 40) + "px;" +
commentHidden;
cssSet( commentToolPanel, commentToolPanelStyle );
// Resize dimmer.
var darknessStyle = "width:" + width + "px;" +
"height:" + height + "px;" +
commentHidden;
cssSet(darkness, darknessStyle);
}
window.jsm.resize = resize;
/*
Resize can be called an absurd amount of times
and will crash the page without debouncing.
http://benalman.com/projects/jquery-throttle-debounce-plugin/
https://github.com/cowboy/jquery-throttle-debounce
http://unscriptable.com/2009/03/20/debouncing-javascript-methods/
*/
$(window).resize( $.debounce( 100, resize ) );
// resize for the intial page load
resize();
})();