From e25926619e0b964e47f690d606d658f284b624f6 Mon Sep 17 00:00:00 2001 From: Maciej Smolinski Date: Tue, 19 Jul 2016 18:57:59 +0200 Subject: [PATCH 01/17] Events: Allow for easier application extension with hook points. How does it work? Every time someone wants to extend the application, instead of modifying original `publishing.js` file, he or she can subscribe to given events, e.g. Scenario: Table Of Contents extension ``` // resources/public/js/toc.js events.subscribe('content:rendered', function () { // Prepend table of contents in edit mode on updates }); events.subscribe('document:loaded', function () { // Prepend table of contents on initial load in edit view and preview }); ``` Now, having structured way to hook into app allows to keep the main codebase maintainable and pretty stable and extremely extensible at the same time. --- resources/edit.html | 1 + resources/public/js/events.js | 62 +++++++++++++++++++++++++++++++ resources/public/js/publishing.js | 6 +++ resources/template.html | 6 +++ 4 files changed, 75 insertions(+) create mode 100644 resources/public/js/events.js diff --git a/resources/edit.html b/resources/edit.html index 1d5f65a..8c286ef 100644 --- a/resources/edit.html +++ b/resources/edit.html @@ -8,6 +8,7 @@ + diff --git a/resources/public/js/events.js b/resources/public/js/events.js new file mode 100644 index 0000000..9965745 --- /dev/null +++ b/resources/public/js/events.js @@ -0,0 +1,62 @@ +/** + * Simple Event Bus + * + * Allows to easily hook into various page rendering / markdown parsing stages with custom modules + * + * + * High Level API: + * + * // Subscribe + * events.subscribe(eventName:String, eventHandler:Function) + * + * // Publish + * events.publish(eventName:String, optionalArgument1, optionalArgument2, ..., optionalArgumentN); + * + * + * Sample Usage: + * + * event.subscribe('markdown:parsed', function () { + * console.log('Markdown Parsed'); + * }); + * + * event.subscribe('markdown:parsed', function (title) { + * console.log('Markdown Parsed For Document: ' + title); + * }); + * + * events.publish('markdown:parsed', 'SampleDocument.md'); + * // Markdown Parsed + * // Markdown Parsed For Document: SampleDocument.md + * + */ +(function (global) { + + var eventBus = { + subscribers: [], + }; + + global.events = global.events || { + + subscribe: function (eventName, eventHandler) { + // Initialize an array of event listeners if doesn't exist already + eventBus.subscribers[eventName] = eventBus.subscribers[eventName] || []; + + eventBus.subscribers[eventName].push(eventHandler); + }, + + publish: function (eventName /*, arg1, arg2, ..., argN */) { + var eventArguments = [].slice.call(arguments, 1); + + if (eventArguments.length) { + console.log('[Hooks] "%s" with args %O', eventName, eventArguments); + } else { + console.log('[Hooks] "%s"', eventName); + } + + // Call event handlers with given attributes + (eventBus.subscribers[eventName] || []).forEach(function (eventHandler) { + eventHandler.apply(null, eventArguments); + }); + }, + + }; +}(window)); diff --git a/resources/public/js/publishing.js b/resources/public/js/publishing.js index 56c6e90..e011564 100644 --- a/resources/public/js/publishing.js +++ b/resources/public/js/publishing.js @@ -25,6 +25,9 @@ function enableButton() { } function onLoad() { + // Hook point + events.publish('document:loaded'); + $note = $("note"); $action = $("action").value; $preview = $("draft"); @@ -37,6 +40,9 @@ function onLoad() { timer = setTimeout(function() { $preview.innerHTML = md2html(content); $tableau.innerHTML = content.split(/\s+/).length + " words"; + + // Hook point + events.publish('content:rendered'); }, delay); }; if ($action == "UPDATE") updatePreview(); diff --git a/resources/template.html b/resources/template.html index b127591..c88ce88 100644 --- a/resources/template.html +++ b/resources/template.html @@ -12,5 +12,11 @@ %CONTENT% %FOOTER% + + + + From 9690f717fe69e94b8fbe4e6d256086c1a9b6589f Mon Sep 17 00:00:00 2001 From: Maciej Smolinski Date: Tue, 19 Jul 2016 19:03:37 +0200 Subject: [PATCH 02/17] Modules: Add code highlighting using hooks. Achieved using Prism.js (MIT licensed) library and its language addons. Website: http://prismjs.com/ --- resources/edit.html | 1 + resources/public/js/highlight.js | 157 +++++++++++++++++++++++++++++++ resources/template.html | 1 + 3 files changed, 159 insertions(+) create mode 100644 resources/public/js/highlight.js diff --git a/resources/edit.html b/resources/edit.html index 8c286ef..6325934 100644 --- a/resources/edit.html +++ b/resources/edit.html @@ -10,6 +10,7 @@ + diff --git a/resources/public/js/highlight.js b/resources/public/js/highlight.js new file mode 100644 index 0000000..88bfdca --- /dev/null +++ b/resources/public/js/highlight.js @@ -0,0 +1,157 @@ +/** + * Highlight Module + * + * + * High Level API: + * + * // Load basic dependencies and language support for present code tags + * + * highlight.init() + * + * + * // Function that loads additional languages support + * // Useful when markdown has been parsed again + * // and new language occurred in the output for example + * + * highlight.update(); + * + * + * Hooks To: + * + * 'document:loaded' ~> highlight.init(); + * 'content:rendered' ~> highlight.update(); + * + */ +(function (global) { + + // Simplified debounce version (no args support) + function debounce(callback, milliseconds) { + var timeout; + + return function () { + clearTimeout(timeout); + + timeout = setTimeout(function () { + callback(); + }, milliseconds); + }; + } + + // Converts arguments-like / querySelector results data to a simple array + function toArray(data) { + return [].slice.call(data); + } + + // Highlight callback + function highlight() { + if (!('Prism' in global)) { + throw new Error( + '[Highlight] Prism not detected. Please run `highlight.init` to load all dependencies' + ); + } + + global.Prism.highlightAll(); + } + + // Debounced highlight callback + var debouncedHighlight = debounce(highlight, 300); + + // Collect a list of unique langages to be highlighted + function collectLanguages() { + return [] + .concat(toArray(document.querySelectorAll('code[class*="lang-"]'))) + .concat(toArray(document.querySelectorAll('code[class*="language-"]'))) + .map(function (element) { + // Collect languages from code elements, e.g. `lang-css`, `language-javascript` + // and then remove `lang-` and `language-` parts + return (element.className.match(/lang(uage)?\-\w+/g) || []) + .map(function (languageClass) { + return languageClass.replace(/lang(uage)?\-/g, '').trim(); + }) + .map(function (language) { + // Common language abbreviations mapped to full language names + var mappings = { + js: 'javascript', + }; + + return mappings[language] || language; + }); + }) + .reduce(function (uniqueLanguages, elementLanguages) { + elementLanguages.forEach(function (language) { + // Add language to the pool if not detected already + if (uniqueLanguages.indexOf(language) === -1) { + uniqueLanguages.push(language); + } + }); + + return uniqueLanguages; + }, []); + } + + // Load scripts for additional languages support + function loadAdditionalLanguageSupport() { + collectLanguages() + .forEach(function (language) { + var resource = 'https://cdnjs.cloudflare.com/ajax/libs/prism/1.5.1/components/prism-%s.min.js' + .replace('%s', language); + + // Escape early if language support is already loaded + if (document.querySelector('script[src="' + resource + '"]')) { + debouncedHighlight(); + return; + } + + // Load language support file otherwise + var script = document.createElement('script'); + + script.src = resource; + + script.addEventListener('load', debouncedHighlight); + script.addEventListener('error', function (event) { + // Remove element that wasn't successful + document.body.removeChild(event.srcElement); + + // Highlight code anyway + debouncedHighlight(); + }); + + document.body.appendChild(script); + }); + } + + // Load minimal requirements + function loadInitialScriptsAndStyles() { + var link = document.createElement('link'); + var script = document.createElement('script'); + + link.rel = 'stylesheet'; + link.href = 'https://cdnjs.cloudflare.com/ajax/libs/prism/1.5.1/themes/prism.min.css'; + script.src = 'https://cdnjs.cloudflare.com/ajax/libs/prism/1.5.1/prism.min.js'; + + script.addEventListener('load', global.highlight.update); + + document.head.appendChild(link); + document.body.appendChild(script); + } + + // High Level API + global.highlight = global.highlight || { + + init: function () { + loadInitialScriptsAndStyles(); + }, + + update: function () { + loadAdditionalLanguageSupport(); + }, + + }; + + // Hooks + if ('events' in global) { + events.subscribe('document:loaded', global.highlight.init); + events.subscribe('content:rendered', global.highlight.update); + } + +}(window)); diff --git a/resources/template.html b/resources/template.html index c88ce88..01c8461 100644 --- a/resources/template.html +++ b/resources/template.html @@ -14,6 +14,7 @@ %FOOTER% + From 38e61080007193928898c14477e9b0698e2d5af0 Mon Sep 17 00:00:00 2001 From: Maciej Smolinski Date: Wed, 20 Jul 2016 09:42:22 +0200 Subject: [PATCH 03/17] Module: Use Prism own autoloader. --- resources/public/js/highlight.js | 106 ++++++++++--------------------- 1 file changed, 32 insertions(+), 74 deletions(-) diff --git a/resources/public/js/highlight.js b/resources/public/js/highlight.js index 88bfdca..bbf8959 100644 --- a/resources/public/js/highlight.js +++ b/resources/public/js/highlight.js @@ -1,3 +1,5 @@ +// jscs:disable maximumLineLength + /** * Highlight Module * @@ -9,7 +11,7 @@ * highlight.init() * * - * // Function that loads additional languages support + * // Re-highlights all code tags on demand * // Useful when markdown has been parsed again * // and new language occurred in the output for example * @@ -37,11 +39,6 @@ }; } - // Converts arguments-like / querySelector results data to a simple array - function toArray(data) { - return [].slice.call(data); - } - // Highlight callback function highlight() { if (!('Prism' in global)) { @@ -54,85 +51,46 @@ } // Debounced highlight callback - var debouncedHighlight = debounce(highlight, 300); - - // Collect a list of unique langages to be highlighted - function collectLanguages() { - return [] - .concat(toArray(document.querySelectorAll('code[class*="lang-"]'))) - .concat(toArray(document.querySelectorAll('code[class*="language-"]'))) - .map(function (element) { - // Collect languages from code elements, e.g. `lang-css`, `language-javascript` - // and then remove `lang-` and `language-` parts - return (element.className.match(/lang(uage)?\-\w+/g) || []) - .map(function (languageClass) { - return languageClass.replace(/lang(uage)?\-/g, '').trim(); - }) - .map(function (language) { - // Common language abbreviations mapped to full language names - var mappings = { - js: 'javascript', - }; - - return mappings[language] || language; - }); - }) - .reduce(function (uniqueLanguages, elementLanguages) { - elementLanguages.forEach(function (language) { - // Add language to the pool if not detected already - if (uniqueLanguages.indexOf(language) === -1) { - uniqueLanguages.push(language); - } - }); - - return uniqueLanguages; - }, []); - } + var debouncedHighlight = + debounce(highlight, 300); + + // Load minimal requirements + function loadInitialScriptsAndStyles() { + var link = + document.createElement('link'); - // Load scripts for additional languages support - function loadAdditionalLanguageSupport() { - collectLanguages() - .forEach(function (language) { - var resource = 'https://cdnjs.cloudflare.com/ajax/libs/prism/1.5.1/components/prism-%s.min.js' - .replace('%s', language); + var mainScript = + document.createElement('script'); - // Escape early if language support is already loaded - if (document.querySelector('script[src="' + resource + '"]')) { - debouncedHighlight(); - return; - } + var autoloaderScript = + document.createElement('script'); - // Load language support file otherwise - var script = document.createElement('script'); + link.rel = + 'stylesheet'; - script.src = resource; + link.href = + 'https://cdnjs.cloudflare.com/ajax/libs/prism/1.5.1/themes/prism-tomorrow.min.css'; - script.addEventListener('load', debouncedHighlight); - script.addEventListener('error', function (event) { - // Remove element that wasn't successful - document.body.removeChild(event.srcElement); + mainScript.src = + 'https://cdnjs.cloudflare.com/ajax/libs/prism/1.5.1/prism.min.js'; - // Highlight code anyway - debouncedHighlight(); - }); + autoloaderScript.src = + 'https://cdnjs.cloudflare.com/ajax/libs/prism/1.5.1/plugins/autoloader/prism-autoloader.min.js'; - document.body.appendChild(script); + mainScript.addEventListener('load', function () { + // Load autoloader after Prism loads + document.body.appendChild(autoloaderScript); }); - } - - // Load minimal requirements - function loadInitialScriptsAndStyles() { - var link = document.createElement('link'); - var script = document.createElement('script'); - link.rel = 'stylesheet'; - link.href = 'https://cdnjs.cloudflare.com/ajax/libs/prism/1.5.1/themes/prism.min.css'; - script.src = 'https://cdnjs.cloudflare.com/ajax/libs/prism/1.5.1/prism.min.js'; + autoloaderScript.addEventListener('load', function () { + global.Prism.plugins.autoloader.languages_path = + 'https://cdnjs.cloudflare.com/ajax/libs/prism/1.5.1/components/'; - script.addEventListener('load', global.highlight.update); + global.highlight.update(); + }); document.head.appendChild(link); - document.body.appendChild(script); + document.body.appendChild(mainScript); } // High Level API @@ -143,7 +101,7 @@ }, update: function () { - loadAdditionalLanguageSupport(); + debouncedHighlight(); }, }; From 262202fbad21eb1dca0c8dc5a9a73c2f3e246adf Mon Sep 17 00:00:00 2001 From: Maciej Smolinski Date: Wed, 20 Jul 2016 16:55:58 +0200 Subject: [PATCH 04/17] Styles: Prevent syntax-highlighted code from being over-sized. --- resources/public/style.css | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/resources/public/style.css b/resources/public/style.css index 6705e3b..8e3faaa 100644 --- a/resources/public/style.css +++ b/resources/public/style.css @@ -186,11 +186,20 @@ blockquote { } code, pre { - font-size: 1.05em; background: #efefef; font-family: monospace; } +/* Code tags alone to be slightly bigger than defaults */ +code { + font-size: 1.05em; +} + +/* Prevent over-sized code in when syntax-highlighted */ +pre code { + font-size: 1em; +} + pre { border: 1px solid #aaa; padding: 0.5em; From 74ed0744ec4510f817e1d2b6743a144210939606 Mon Sep 17 00:00:00 2001 From: Maciej Smolinski Date: Tue, 26 Jul 2016 18:21:06 +0200 Subject: [PATCH 05/17] Highlight: Use marked options. --- resources/public/js/highlight.js | 100 ++++++++++++------------------ resources/public/js/publishing.js | 6 +- 2 files changed, 42 insertions(+), 64 deletions(-) diff --git a/resources/public/js/highlight.js b/resources/public/js/highlight.js index bbf8959..a687a04 100644 --- a/resources/public/js/highlight.js +++ b/resources/public/js/highlight.js @@ -7,53 +7,16 @@ * High Level API: * * // Load basic dependencies and language support for present code tags + * api.init() * - * highlight.init() * - * - * // Re-highlights all code tags on demand - * // Useful when markdown has been parsed again - * // and new language occurred in the output for example - * - * highlight.update(); - * - * - * Hooks To: + * Hooks To: * * 'document:loaded' ~> highlight.init(); - * 'content:rendered' ~> highlight.update(); * */ (function (global) { - // Simplified debounce version (no args support) - function debounce(callback, milliseconds) { - var timeout; - - return function () { - clearTimeout(timeout); - - timeout = setTimeout(function () { - callback(); - }, milliseconds); - }; - } - - // Highlight callback - function highlight() { - if (!('Prism' in global)) { - throw new Error( - '[Highlight] Prism not detected. Please run `highlight.init` to load all dependencies' - ); - } - - global.Prism.highlightAll(); - } - - // Debounced highlight callback - var debouncedHighlight = - debounce(highlight, 300); - // Load minimal requirements function loadInitialScriptsAndStyles() { var link = @@ -62,9 +25,6 @@ var mainScript = document.createElement('script'); - var autoloaderScript = - document.createElement('script'); - link.rel = 'stylesheet'; @@ -74,19 +34,42 @@ mainScript.src = 'https://cdnjs.cloudflare.com/ajax/libs/prism/1.5.1/prism.min.js'; - autoloaderScript.src = - 'https://cdnjs.cloudflare.com/ajax/libs/prism/1.5.1/plugins/autoloader/prism-autoloader.min.js'; - mainScript.addEventListener('load', function () { - // Load autoloader after Prism loads - document.body.appendChild(autoloaderScript); - }); + // Escape early if back-end rendering is used + if (!('marked' in global)) { + // @TODO Autoload er + return Prism.highlightAll(); + } + + // Set up autoloading + marked.setOptions({ + highlight: function (code, language) { + if (!(language in Prism.languages)) { + var additionalLanguageScript = + document.createElement('script'); + + additionalLanguageScript.src = + 'https://cdnjs.cloudflare.com/ajax/libs/prism/1.5.1/components/prism-' + language + '.min.js'; + + // On success, highlight code for given language + additionalLanguageScript.addEventListener('load', function () { + [].forEach.call(document.querySelectorAll('.lang-' + language), function (element) { + Prism.highlightElement(element); + }); + }); + + // Remove if language not available + additionalLanguageScript.addEventListener('error', function () { + document.body.removeChild(additionalLanguageScript); + }); + + document.body.appendChild(additionalLanguageScript); + } + + return Prism.highlight(code, Prism.languages[language] || Prism.languages.markup); + }, + }); - autoloaderScript.addEventListener('load', function () { - global.Prism.plugins.autoloader.languages_path = - 'https://cdnjs.cloudflare.com/ajax/libs/prism/1.5.1/components/'; - - global.highlight.update(); }); document.head.appendChild(link); @@ -94,22 +77,15 @@ } // High Level API - global.highlight = global.highlight || { - + var api = { init: function () { loadInitialScriptsAndStyles(); }, - - update: function () { - debouncedHighlight(); - }, - }; // Hooks if ('events' in global) { - events.subscribe('document:loaded', global.highlight.init); - events.subscribe('content:rendered', global.highlight.update); + events.subscribe('document:loaded', api.init); } }(window)); diff --git a/resources/public/js/publishing.js b/resources/public/js/publishing.js index e011564..4cedae8 100644 --- a/resources/public/js/publishing.js +++ b/resources/public/js/publishing.js @@ -21,13 +21,15 @@ function saveDraft() { function enableButton() { var checkbox = $('tos'); var button = $('publish-button'); - button.disabled = !checkbox.checked; + button.disabled = !checkbox.checked; } -function onLoad() { +document.addEventListener('DOMContentLoaded', function () { // Hook point events.publish('document:loaded'); +}); +function onLoad() { $note = $("note"); $action = $("action").value; $preview = $("draft"); From dc7ecb762132f2166ff0cf174977b450e1a95d3c Mon Sep 17 00:00:00 2001 From: Maciej Smolinski Date: Tue, 26 Jul 2016 19:22:30 +0200 Subject: [PATCH 06/17] Highlight: Use highlight.js. --- resources/public/js/highlight.js | 50 +++++++++----------------------- resources/public/style.css | 27 ++++++++--------- 2 files changed, 27 insertions(+), 50 deletions(-) diff --git a/resources/public/js/highlight.js b/resources/public/js/highlight.js index a687a04..9091294 100644 --- a/resources/public/js/highlight.js +++ b/resources/public/js/highlight.js @@ -6,18 +6,16 @@ * * High Level API: * - * // Load basic dependencies and language support for present code tags * api.init() * * * Hooks To: * - * 'document:loaded' ~> highlight.init(); + * 'document:loaded' ~> highlight.init(); * */ (function (global) { - // Load minimal requirements function loadInitialScriptsAndStyles() { var link = document.createElement('link'); @@ -29,51 +27,29 @@ 'stylesheet'; link.href = - 'https://cdnjs.cloudflare.com/ajax/libs/prism/1.5.1/themes/prism-tomorrow.min.css'; + 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.5.0/styles/railscasts.min.css'; mainScript.src = - 'https://cdnjs.cloudflare.com/ajax/libs/prism/1.5.1/prism.min.js'; + 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.5.0/highlight.min.js'; + // On main script load, configure marked mainScript.addEventListener('load', function () { - // Escape early if back-end rendering is used - if (!('marked' in global)) { - // @TODO Autoload er - return Prism.highlightAll(); - } - - // Set up autoloading marked.setOptions({ - highlight: function (code, language) { - if (!(language in Prism.languages)) { - var additionalLanguageScript = - document.createElement('script'); - - additionalLanguageScript.src = - 'https://cdnjs.cloudflare.com/ajax/libs/prism/1.5.1/components/prism-' + language + '.min.js'; - - // On success, highlight code for given language - additionalLanguageScript.addEventListener('load', function () { - [].forEach.call(document.querySelectorAll('.lang-' + language), function (element) { - Prism.highlightElement(element); - }); - }); - - // Remove if language not available - additionalLanguageScript.addEventListener('error', function () { - document.body.removeChild(additionalLanguageScript); - }); - - document.body.appendChild(additionalLanguageScript); - } - - return Prism.highlight(code, Prism.languages[language] || Prism.languages.markup); + langPrefix: 'hljs lang-', + highlight: function (code) { + return hljs.highlightAuto(code).value; }, }); }); + // Extend marked.js on edit page only + if ('marked' in global) { + document.body.appendChild(mainScript); + } + + // Preview page requires scripts only document.head.appendChild(link); - document.body.appendChild(mainScript); } // High Level API diff --git a/resources/public/style.css b/resources/public/style.css index 8e3faaa..7be1505 100644 --- a/resources/public/style.css +++ b/resources/public/style.css @@ -185,25 +185,26 @@ blockquote { margin-bottom: 7em; } -code, pre { - background: #efefef; +/* Code tags and pre tags alone to be monospaced */ +pre, code { font-family: monospace; } -/* Code tags alone to be slightly bigger than defaults */ -code { - font-size: 1.05em; +/* Non-syntax highlighted code and pre tags to get default background */ +pre, code:not([class*="lang-"]) { + background: #efefef; } -/* Prevent over-sized code in when syntax-highlighted */ -pre code { - font-size: 1em; +/* Non-syntax highlighted code tag to be slightly bigger so it outstands in text */ +code:not([class*="lang-"]) { + font-size: 1.05em; } -pre { - border: 1px solid #aaa; - padding: 0.5em; +/* Syntax highlighted code tag to get some padding, nice rounded corners and keep font-size */ +code[class*="lang-"] { border-radius: 3px; + font-size: 1em; + padding: 1em; } *:focus { @@ -231,7 +232,7 @@ td { padding: 0.5em; } -/* MEDIA QUERIES */ +/* MEDIA QUERIES */ @media screen and (min-width: 1024px) { article, .central-element { @@ -317,4 +318,4 @@ fieldset { #plain-password { width: 10em; -} \ No newline at end of file +} From 032b0885296dea611a1669d91f72b742192fbcb5 Mon Sep 17 00:00:00 2001 From: Maciej Smolinski Date: Tue, 26 Jul 2016 19:33:04 +0200 Subject: [PATCH 07/17] Highlight: Node.js highlighting. --- package.json | 1 + src/view.js | 10 +++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 846c461..b627010 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "dependencies": { "body-parser": "^1.15.0", "express": "^4.13.4", + "highlight.js": "^9.5.0", "lru-cache": "^4.0.0", "marked": "^0.3.5", "md5": "^2.1.0", diff --git a/src/view.js b/src/view.js index 4d99a51..a588002 100644 --- a/src/view.js +++ b/src/view.js @@ -1,5 +1,6 @@ var marked = require("marked"); var fs = require("fs"); +var hljs = require("highlight.js"); var TOS = fs.readFileSync("resources/TOS.md", "utf-8"); var pageTemplate = fs.readFileSync("resources/template.html", "utf-8"); @@ -17,7 +18,14 @@ var renderPage = (id, title, content, footer, blackList) => pageTemplate .replace("%TITLE%", title) .replace("%CONTENT%", content.replace(//gi, "").replace(//gi, "")) .replace("%FOOTER%", footer || ""); - + +marked.setOptions({ + langPrefix: 'hljs lang-', + highlight: function (code) { + return hljs.highlightAuto(code).value; + }, +}); + module.exports.renderPage = renderPage; module.exports.renderStats = note => renderPage(note.id, deriveTitle(note.text), From 55bfeeb15659d19c57ef180f8a3b2c5c1fb7b97f Mon Sep 17 00:00:00 2001 From: Maciej Smolinski Date: Tue, 26 Jul 2016 19:58:36 +0200 Subject: [PATCH 08/17] Highlight: Use lighter theme with good contrast. --- resources/public/js/highlight.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/public/js/highlight.js b/resources/public/js/highlight.js index 9091294..b46112d 100644 --- a/resources/public/js/highlight.js +++ b/resources/public/js/highlight.js @@ -27,7 +27,7 @@ 'stylesheet'; link.href = - 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.5.0/styles/railscasts.min.css'; + 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.5.0/styles/zenburn.min.css'; mainScript.src = 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.5.0/highlight.min.js'; From dd2c69afdb4eb2c23a9bb493a6d0a3946b83aa96 Mon Sep 17 00:00:00 2001 From: Maciej Smolinski Date: Mon, 1 Aug 2016 19:15:42 +0200 Subject: [PATCH 09/17] Highlight: As per PR comments - preview becomes static (back-end rendered), edit more dynamic. No hooks. --- resources/edit.html | 4 +- resources/public/js/events.js | 62 ---------------------------- resources/public/js/highlight.js | 67 ------------------------------- resources/public/js/publishing.js | 17 ++++---- resources/template.html | 8 +--- 5 files changed, 12 insertions(+), 146 deletions(-) delete mode 100644 resources/public/js/events.js delete mode 100644 resources/public/js/highlight.js diff --git a/resources/edit.html b/resources/edit.html index 6325934..7d5c821 100644 --- a/resources/edit.html +++ b/resources/edit.html @@ -6,11 +6,11 @@ + - + - diff --git a/resources/public/js/events.js b/resources/public/js/events.js deleted file mode 100644 index 9965745..0000000 --- a/resources/public/js/events.js +++ /dev/null @@ -1,62 +0,0 @@ -/** - * Simple Event Bus - * - * Allows to easily hook into various page rendering / markdown parsing stages with custom modules - * - * - * High Level API: - * - * // Subscribe - * events.subscribe(eventName:String, eventHandler:Function) - * - * // Publish - * events.publish(eventName:String, optionalArgument1, optionalArgument2, ..., optionalArgumentN); - * - * - * Sample Usage: - * - * event.subscribe('markdown:parsed', function () { - * console.log('Markdown Parsed'); - * }); - * - * event.subscribe('markdown:parsed', function (title) { - * console.log('Markdown Parsed For Document: ' + title); - * }); - * - * events.publish('markdown:parsed', 'SampleDocument.md'); - * // Markdown Parsed - * // Markdown Parsed For Document: SampleDocument.md - * - */ -(function (global) { - - var eventBus = { - subscribers: [], - }; - - global.events = global.events || { - - subscribe: function (eventName, eventHandler) { - // Initialize an array of event listeners if doesn't exist already - eventBus.subscribers[eventName] = eventBus.subscribers[eventName] || []; - - eventBus.subscribers[eventName].push(eventHandler); - }, - - publish: function (eventName /*, arg1, arg2, ..., argN */) { - var eventArguments = [].slice.call(arguments, 1); - - if (eventArguments.length) { - console.log('[Hooks] "%s" with args %O', eventName, eventArguments); - } else { - console.log('[Hooks] "%s"', eventName); - } - - // Call event handlers with given attributes - (eventBus.subscribers[eventName] || []).forEach(function (eventHandler) { - eventHandler.apply(null, eventArguments); - }); - }, - - }; -}(window)); diff --git a/resources/public/js/highlight.js b/resources/public/js/highlight.js deleted file mode 100644 index b46112d..0000000 --- a/resources/public/js/highlight.js +++ /dev/null @@ -1,67 +0,0 @@ -// jscs:disable maximumLineLength - -/** - * Highlight Module - * - * - * High Level API: - * - * api.init() - * - * - * Hooks To: - * - * 'document:loaded' ~> highlight.init(); - * - */ -(function (global) { - - function loadInitialScriptsAndStyles() { - var link = - document.createElement('link'); - - var mainScript = - document.createElement('script'); - - link.rel = - 'stylesheet'; - - link.href = - 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.5.0/styles/zenburn.min.css'; - - mainScript.src = - 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.5.0/highlight.min.js'; - - // On main script load, configure marked - mainScript.addEventListener('load', function () { - marked.setOptions({ - langPrefix: 'hljs lang-', - highlight: function (code) { - return hljs.highlightAuto(code).value; - }, - }); - - }); - - // Extend marked.js on edit page only - if ('marked' in global) { - document.body.appendChild(mainScript); - } - - // Preview page requires scripts only - document.head.appendChild(link); - } - - // High Level API - var api = { - init: function () { - loadInitialScriptsAndStyles(); - }, - }; - - // Hooks - if ('events' in global) { - events.subscribe('document:loaded', api.init); - } - -}(window)); diff --git a/resources/public/js/publishing.js b/resources/public/js/publishing.js index 4cedae8..3faa8e7 100644 --- a/resources/public/js/publishing.js +++ b/resources/public/js/publishing.js @@ -7,6 +7,15 @@ var timerDelay = iosDetected ? 800 : 400; var $note, $action, $preview, $plain_password, $tableau; var backendTimer; +document.addEventListener('DOMContentLoaded', function () { + marked.setOptions({ + langPrefix: 'hljs lang-', + highlight: function (code) { + return hljs.highlightAuto(code).value; + }, + }); +}); + function md2html(input) { return marked(input); } @@ -24,11 +33,6 @@ function enableButton() { button.disabled = !checkbox.checked; } -document.addEventListener('DOMContentLoaded', function () { - // Hook point - events.publish('document:loaded'); -}); - function onLoad() { $note = $("note"); $action = $("action").value; @@ -42,9 +46,6 @@ function onLoad() { timer = setTimeout(function() { $preview.innerHTML = md2html(content); $tableau.innerHTML = content.split(/\s+/).length + " words"; - - // Hook point - events.publish('content:rendered'); }, delay); }; if ($action == "UPDATE") updatePreview(); diff --git a/resources/template.html b/resources/template.html index 01c8461..16e13e1 100644 --- a/resources/template.html +++ b/resources/template.html @@ -5,6 +5,7 @@ + %HEADER% @@ -12,12 +13,5 @@ %CONTENT% %FOOTER% - - - - - From 093b0c95e12314bfbdb78e93fd125ae00bd092a4 Mon Sep 17 00:00:00 2001 From: Maciej Smolinski Date: Mon, 1 Aug 2016 19:38:29 +0200 Subject: [PATCH 10/17] Highlight: Use `tomorrow` (light) theme. --- resources/edit.html | 2 +- resources/template.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/edit.html b/resources/edit.html index 7d5c821..4641fb6 100644 --- a/resources/edit.html +++ b/resources/edit.html @@ -6,7 +6,7 @@ - + diff --git a/resources/template.html b/resources/template.html index 16e13e1..4c34c4f 100644 --- a/resources/template.html +++ b/resources/template.html @@ -5,7 +5,7 @@ - + %HEADER% From 0f618cd50f6946e8a19c318facd30e5e781164e9 Mon Sep 17 00:00:00 2001 From: Maciej Smolinski Date: Mon, 1 Aug 2016 19:38:45 +0200 Subject: [PATCH 11/17] Highlight: Apply some defaults to hljs themes (background, border radius). --- resources/public/style.css | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/resources/public/style.css b/resources/public/style.css index 7be1505..c0b9a6d 100644 --- a/resources/public/style.css +++ b/resources/public/style.css @@ -188,11 +188,8 @@ blockquote { /* Code tags and pre tags alone to be monospaced */ pre, code { font-family: monospace; -} - -/* Non-syntax highlighted code and pre tags to get default background */ -pre, code:not([class*="lang-"]) { - background: #efefef; + border-radius: 3px; + background: #fcfcfc !important; /* required to override theme defaults */ } /* Non-syntax highlighted code tag to be slightly bigger so it outstands in text */ @@ -200,9 +197,8 @@ code:not([class*="lang-"]) { font-size: 1.05em; } -/* Syntax highlighted code tag to get some padding, nice rounded corners and keep font-size */ +/* Syntax highlighted code tag to get some padding and keep font-size */ code[class*="lang-"] { - border-radius: 3px; font-size: 1em; padding: 1em; } From 4676d46a153ac105b340cf55354090414c5c8f10 Mon Sep 17 00:00:00 2001 From: Maciej Smolinski Date: Mon, 1 Aug 2016 19:52:51 +0200 Subject: [PATCH 12/17] Highlight: Inline highlight.js styles so even more transfer is saved and notes render faster. --- resources/edit.html | 1 - resources/public/style.css | 40 +++++++++++++++++++++----------------- resources/template.html | 1 - 3 files changed, 22 insertions(+), 20 deletions(-) diff --git a/resources/edit.html b/resources/edit.html index 4641fb6..d866019 100644 --- a/resources/edit.html +++ b/resources/edit.html @@ -6,7 +6,6 @@ - diff --git a/resources/public/style.css b/resources/public/style.css index c0b9a6d..2a76e64 100644 --- a/resources/public/style.css +++ b/resources/public/style.css @@ -185,24 +185,6 @@ blockquote { margin-bottom: 7em; } -/* Code tags and pre tags alone to be monospaced */ -pre, code { - font-family: monospace; - border-radius: 3px; - background: #fcfcfc !important; /* required to override theme defaults */ -} - -/* Non-syntax highlighted code tag to be slightly bigger so it outstands in text */ -code:not([class*="lang-"]) { - font-size: 1.05em; -} - -/* Syntax highlighted code tag to get some padding and keep font-size */ -code[class*="lang-"] { - font-size: 1em; - padding: 1em; -} - *:focus { outline: 0px none transparent; } @@ -315,3 +297,25 @@ fieldset { #plain-password { width: 10em; } + +/* SYNTAX HIGHLIGHTING */ + +/* Theme: tomorrow, source: http://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.5.0/styles/tomorrow.min.css */ +.hljs-comment,.hljs-quote{color:#8e908c}.hljs-variable,.hljs-template-variable,.hljs-tag,.hljs-name,.hljs-selector-id,.hljs-selector-class,.hljs-regexp,.hljs-deletion{color:#c82829}.hljs-number,.hljs-built_in,.hljs-builtin-name,.hljs-literal,.hljs-type,.hljs-params,.hljs-meta,.hljs-link{color:#f5871f}.hljs-attribute{color:#eab700}.hljs-string,.hljs-symbol,.hljs-bullet,.hljs-addition{color:#718c00}.hljs-title,.hljs-section{color:#4271ae}.hljs-keyword,.hljs-selector-tag{color:#8959a8}.hljs{display:block;overflow-x:auto;background:white;color:#4d4d4c;padding:0.5em}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:bold} + +pre, code, code.hljs { + font-family: monospace; + border-radius: 3px; + background: #fcfcfc; +} + +/* Non-syntax highlighted code tag to be slightly bigger so it outstands in text */ +code:not(.hljs) { + font-size: 1.05em; +} + +/* Syntax highlighted code tag to get some padding and keep font-size */ +code.hljs { + font-size: 1em; + padding: 1em; +} diff --git a/resources/template.html b/resources/template.html index 4c34c4f..b127591 100644 --- a/resources/template.html +++ b/resources/template.html @@ -5,7 +5,6 @@ - %HEADER% From 29d036c0fd128f7ef93c1dbaf15ec4a4906a7f67 Mon Sep 17 00:00:00 2001 From: Maciej Smolinski Date: Mon, 1 Aug 2016 19:57:51 +0200 Subject: [PATCH 13/17] Highlight: Use default theme. --- resources/public/style.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/public/style.css b/resources/public/style.css index 2a76e64..ad0c4a9 100644 --- a/resources/public/style.css +++ b/resources/public/style.css @@ -300,8 +300,8 @@ fieldset { /* SYNTAX HIGHLIGHTING */ -/* Theme: tomorrow, source: http://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.5.0/styles/tomorrow.min.css */ -.hljs-comment,.hljs-quote{color:#8e908c}.hljs-variable,.hljs-template-variable,.hljs-tag,.hljs-name,.hljs-selector-id,.hljs-selector-class,.hljs-regexp,.hljs-deletion{color:#c82829}.hljs-number,.hljs-built_in,.hljs-builtin-name,.hljs-literal,.hljs-type,.hljs-params,.hljs-meta,.hljs-link{color:#f5871f}.hljs-attribute{color:#eab700}.hljs-string,.hljs-symbol,.hljs-bullet,.hljs-addition{color:#718c00}.hljs-title,.hljs-section{color:#4271ae}.hljs-keyword,.hljs-selector-tag{color:#8959a8}.hljs{display:block;overflow-x:auto;background:white;color:#4d4d4c;padding:0.5em}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:bold} +/* Theme: default, source: http://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.5.0/styles/default.min.css */ +.hljs{display:block;overflow-x:auto;padding:0.5em;background:#F0F0F0}.hljs,.hljs-subst{color:#444}.hljs-comment{color:#888888}.hljs-keyword,.hljs-attribute,.hljs-selector-tag,.hljs-meta-keyword,.hljs-doctag,.hljs-name{font-weight:bold}.hljs-type,.hljs-string,.hljs-number,.hljs-selector-id,.hljs-selector-class,.hljs-quote,.hljs-template-tag,.hljs-deletion{color:#880000}.hljs-title,.hljs-section{color:#880000;font-weight:bold}.hljs-regexp,.hljs-symbol,.hljs-variable,.hljs-template-variable,.hljs-link,.hljs-selector-attr,.hljs-selector-pseudo{color:#BC6060}.hljs-literal{color:#78A960}.hljs-built_in,.hljs-bullet,.hljs-code,.hljs-addition{color:#397300}.hljs-meta{color:#1f7199}.hljs-meta-string{color:#4d99bf}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:bold} pre, code, code.hljs { font-family: monospace; From 6879b08fd05030b8e872e3b86fb5b95faea248e4 Mon Sep 17 00:00:00 2001 From: Maciej Smolinski Date: Wed, 3 Aug 2016 07:53:51 +0200 Subject: [PATCH 14/17] Highlight: Make hljs theme code easily readable. --- resources/public/style.css | 68 +++++++++++++++++++++++++++++++++++++- 1 file changed, 67 insertions(+), 1 deletion(-) diff --git a/resources/public/style.css b/resources/public/style.css index ad0c4a9..bbfd584 100644 --- a/resources/public/style.css +++ b/resources/public/style.css @@ -301,7 +301,73 @@ fieldset { /* SYNTAX HIGHLIGHTING */ /* Theme: default, source: http://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.5.0/styles/default.min.css */ -.hljs{display:block;overflow-x:auto;padding:0.5em;background:#F0F0F0}.hljs,.hljs-subst{color:#444}.hljs-comment{color:#888888}.hljs-keyword,.hljs-attribute,.hljs-selector-tag,.hljs-meta-keyword,.hljs-doctag,.hljs-name{font-weight:bold}.hljs-type,.hljs-string,.hljs-number,.hljs-selector-id,.hljs-selector-class,.hljs-quote,.hljs-template-tag,.hljs-deletion{color:#880000}.hljs-title,.hljs-section{color:#880000;font-weight:bold}.hljs-regexp,.hljs-symbol,.hljs-variable,.hljs-template-variable,.hljs-link,.hljs-selector-attr,.hljs-selector-pseudo{color:#BC6060}.hljs-literal{color:#78A960}.hljs-built_in,.hljs-bullet,.hljs-code,.hljs-addition{color:#397300}.hljs-meta{color:#1f7199}.hljs-meta-string{color:#4d99bf}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:bold} +.hljs { + display:block; + overflow-x:auto; + padding:0.5em; + background:#F0F0F0 +} +.hljs, +.hljs-subst { + color:#444 +} +.hljs-comment { + color:#888888 +} +.hljs-keyword, +.hljs-attribute, +.hljs-selector-tag, +.hljs-meta-keyword, +.hljs-doctag, +.hljs-name { + font-weight:bold +} +.hljs-type, +.hljs-string, +.hljs-number, +.hljs-selector-id, +.hljs-selector-class, +.hljs-quote, +.hljs-template-tag, +.hljs-deletion { + color:#880000 +} +.hljs-title, +.hljs-section { + color:#880000; + font-weight:bold +} +.hljs-regexp, +.hljs-symbol, +.hljs-variable, +.hljs-template-variable, +.hljs-link, +.hljs-selector-attr, +.hljs-selector-pseudo { + color:#BC6060 +} +.hljs-literal { + color:#78A960 +} +.hljs-built_in, +.hljs-bullet, +.hljs-code, +.hljs-addition { + color:#397300 +} +.hljs-meta { + color:#1f7199 +} +.hljs-meta-string { + color:#4d99bf +} +.hljs-emphasis { + font-style:italic +} +.hljs-strong { + font-weight:bold +} + pre, code, code.hljs { font-family: monospace; From 7f0fefc9009120155d9113eb7e492c334ccd38fc Mon Sep 17 00:00:00 2001 From: Maciej Smolinski Date: Wed, 3 Aug 2016 07:54:34 +0200 Subject: [PATCH 15/17] Highlight: Increase the contrast between the text background and code background. --- resources/public/style.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/public/style.css b/resources/public/style.css index bbfd584..82a435b 100644 --- a/resources/public/style.css +++ b/resources/public/style.css @@ -372,7 +372,7 @@ fieldset { pre, code, code.hljs { font-family: monospace; border-radius: 3px; - background: #fcfcfc; + background: #f0f0f0; } /* Non-syntax highlighted code tag to be slightly bigger so it outstands in text */ From 5aa889a63ac8a22f69e4b4d667395a4d2fd44863 Mon Sep 17 00:00:00 2001 From: Maciej Smolinski Date: Wed, 3 Aug 2016 07:55:23 +0200 Subject: [PATCH 16/17] Highlight: Use arrow functions (back-end rendering). --- src/view.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/view.js b/src/view.js index a588002..389420c 100644 --- a/src/view.js +++ b/src/view.js @@ -20,10 +20,8 @@ var renderPage = (id, title, content, footer, blackList) => pageTemplate .replace("%FOOTER%", footer || ""); marked.setOptions({ - langPrefix: 'hljs lang-', - highlight: function (code) { - return hljs.highlightAuto(code).value; - }, + langPrefix: "hljs lang-", + highlight: code => hljs.highlightAuto(code).value, }); module.exports.renderPage = renderPage; From f2c77b6c0e7e90d453fd40b9a2fdaf6b6d154b13 Mon Sep 17 00:00:00 2001 From: Maciej Smolinski Date: Wed, 3 Aug 2016 08:00:23 +0200 Subject: [PATCH 17/17] Highlight: Keep highlighted code tags at 1.05em font-size. --- resources/public/style.css | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/resources/public/style.css b/resources/public/style.css index 82a435b..150d14f 100644 --- a/resources/public/style.css +++ b/resources/public/style.css @@ -373,15 +373,15 @@ pre, code, code.hljs { font-family: monospace; border-radius: 3px; background: #f0f0f0; + font-size: 1.05em; } -/* Non-syntax highlighted code tag to be slightly bigger so it outstands in text */ -code:not(.hljs) { - font-size: 1.05em; +/* Don't increase the size of code in pre tag, pre tag already did it. + Otherwise code size would be even bigger (1.05em * 1.05em = 1.1025em) */ +pre code { + font-size: 1em; } -/* Syntax highlighted code tag to get some padding and keep font-size */ code.hljs { - font-size: 1em; padding: 1em; }