diff --git a/demo/assets/index.js b/demo/assets/index.js index fedf754..91d57cb 100644 --- a/demo/assets/index.js +++ b/demo/assets/index.js @@ -1,7 +1,7 @@ (function () { 'use strict'; - var mdHtml, mdSrc, permalink; + var mdHtml, mdSrc, permalink, scrollMap; var defaults = { html: false, // Enable HTML tags in source @@ -63,6 +63,24 @@ mdHtml.renderer.rules.table_open = function () { return '\n'; }; + + mdHtml.renderer.rules.paragraph_open = function (tokens, idx) { + var line; + if (tokens[idx].lines && tokens[idx].level === 0) { + line = tokens[idx].lines[0]; + return '

'; + } + return '

'; + }; + + mdHtml.renderer.rules.heading_open = function (tokens, idx) { + var line; + if (tokens[idx].lines && tokens[idx].level === 0) { + line = tokens[idx].lines[0]; + return ''; + } + return ''; + }; } function updateResult() { @@ -70,6 +88,7 @@ $('.result-html').html(mdHtml.render(source)); $('.result-src-content').html(window.hljs.highlight('html', mdSrc.render(source)).value); + scrollMap = null; var dump = JSON.stringify(mdSrc.parse(source, { references: {} }), null, 2); $('.result-debug-content').html(window.hljs.highlight('json', dump).value); @@ -89,6 +108,88 @@ } } + function recomputeScroll() { + var i, offset, nonEmptyList, pos, a, b, lineHeightMap, linesCount, + acc, sourceLikeDiv, textarea = $('.source'); + + sourceLikeDiv = $('

').css({ + position: 'absolute', + visibility: 'hidden', + height: 'auto', + width: textarea[0].clientWidth, + 'font-size': textarea.css('font-size'), + 'font-family': textarea.css('font-family'), + 'line-height': textarea.css('line-height'), + 'white-space': textarea.css('white-space') + }).appendTo('body'); + + offset = $('.result-html').scrollTop() - $('.result-html').offset().top; + scrollMap = []; + nonEmptyList = []; + lineHeightMap = []; + + acc = 0; + textarea.val().split('\n').forEach(function(str) { + var h, lh; + + lineHeightMap.push(acc); + + if (str.length === 0) { + acc++; + return; + } + + sourceLikeDiv.text(str); + h = parseFloat(sourceLikeDiv.css('height')); + lh = parseFloat(sourceLikeDiv.css('line-height')); + acc += Math.round(h / lh); + }); + sourceLikeDiv.remove(); + lineHeightMap.push(acc); + linesCount = acc; + + for (i = 0; i < linesCount; i++) { scrollMap.push(-1); } + + nonEmptyList.push(0); + scrollMap[0] = 0; + + $('.line').each(function(n, el) { + var $el = $(el), t = $el.data('line'); + if (t === '') { return; } + t = lineHeightMap[t]; + if (t !== 0) { nonEmptyList.push(t); } + scrollMap[t] = Math.round($el.offset().top + offset); + }); + + nonEmptyList.push(linesCount); + scrollMap[linesCount] = $('.result-html')[0].scrollHeight; + + pos = 0; + for (i = 1; i < linesCount; i++) { + if (scrollMap[i] !== -1) { + pos++; + continue; + } + + a = nonEmptyList[pos]; + b = nonEmptyList[pos + 1]; + scrollMap[i] = Math.round((scrollMap[b] * (i - a) + scrollMap[a] * (b - i)) / (b - a)); + } + + return scrollMap; + } + + function syncScroll() { + var textarea = $('.source'), + lineHeight = parseFloat(textarea.css('line-height')), + lineNo; + + lineNo = Math.floor(textarea.scrollTop() / lineHeight); + if (!scrollMap) { recomputeScroll(); } + $('.result-html').stop(true).animate({ + scrollTop: scrollMap[lineNo] + }, 100, 'linear'); + } $(function() { // highlight snippet @@ -180,6 +281,7 @@ // Setup listeners $('.source').on('keyup paste cut mouseup', updateResult); + $('.source').on('scroll', _.debounce(syncScroll, 50, { maxWait: 50 })); $('.source-clear').on('click', function (event) { $('.source').val('');