Commit e05043f32282cfa1e0bca238524b7a25d8430d98

Vitaliy Ribachenko 2015-05-25T15:26:10

Added support of soft wrap lines for line numbers plugin

diff --git a/plugins/line-numbers/index.html b/plugins/line-numbers/index.html
index f22653a..598ec93 100644
--- a/plugins/line-numbers/index.html
+++ b/plugins/line-numbers/index.html
@@ -29,6 +29,7 @@
   <p>Obviously, this is supposed to work only for code blocks (<code>&lt;pre>&lt;code></code>) and not for inline code.</p>
   <p>Add class <strong>line-numbers</strong> to your desired <code>&lt;pre></code> and line-numbers plugin will take care.</p>
   <p>Optional: You can specify the <code>data-start</code> (Number) attribute on the <code>&lt;pre></code> element. It will shift the line counter.</p>
+  <p>Optional: To support multiline line numbers add <code>line-numbers-break-word</code> class.</p>
 </section>
 
 <section>
@@ -42,7 +43,7 @@
 
   <h2>HTML</h2>
   <p>Please note the <code>data-start="-5"</code> in the code below.</p>
-  <pre class="line-numbers" data-src="plugins/line-numbers/index.html" data-start="-5"></pre>
+  <pre class="line-numbers line-numbers-break-word" data-src="plugins/line-numbers/index.html" data-start="-5"></pre>
 </section>
 
 <footer data-src="templates/footer.html" data-type="text/html"></footer>
diff --git a/plugins/line-numbers/prism-line-numbers.css b/plugins/line-numbers/prism-line-numbers.css
index 284cc0a..861d9af 100644
--- a/plugins/line-numbers/prism-line-numbers.css
+++ b/plugins/line-numbers/prism-line-numbers.css
@@ -4,6 +4,11 @@ pre.line-numbers {
 	counter-reset: linenumber;
 }
 
+pre.line-numbers-break-word > code {
+	white-space: pre-wrap;
+	display: inline-block;
+}
+
 pre.line-numbers > code {
 	position: relative;
 }
diff --git a/plugins/line-numbers/prism-line-numbers.js b/plugins/line-numbers/prism-line-numbers.js
index 9443deb..5d7535f 100644
--- a/plugins/line-numbers/prism-line-numbers.js
+++ b/plugins/line-numbers/prism-line-numbers.js
@@ -1,38 +1,102 @@
-Prism.hooks.add('after-highlight', function (env) {
-	// works only for <code> wrapped inside <pre> (not inline)
-	var pre = env.element.parentNode;
-	var clsReg = /\s*\bline-numbers\b\s*/;
-	if (
-		!pre || !/pre/i.test(pre.nodeName) ||
-		// Abort only if nor the <pre> nor the <code> have the class
-		(!clsReg.test(pre.className) && !clsReg.test(env.element.className))
-	) {
-		return;
-	}
-
-	if (clsReg.test(env.element.className)) {
-		// Remove the class "line-numbers" from the <code>
-		env.element.className = env.element.className.replace(clsReg, '');
-	}
-	if (!clsReg.test(pre.className)) {
-		// Add the class "line-numbers" to the <pre>
-		pre.className += ' line-numbers';
-	}
-
-	var linesNum = (1 + env.code.split('\n').length);
-	var lineNumbersWrapper;
-
-	var lines = new Array(linesNum);
-	lines = lines.join('<span></span>');
-
-	lineNumbersWrapper = document.createElement('span');
-	lineNumbersWrapper.className = 'line-numbers-rows';
-	lineNumbersWrapper.innerHTML = lines;
-
-	if (pre.hasAttribute('data-start')) {
-		pre.style.counterReset = 'linenumber ' + (parseInt(pre.getAttribute('data-start'), 10) - 1);
-	}
-
-	env.element.appendChild(lineNumbersWrapper);
-
-});
\ No newline at end of file
+(function(){
+
+	/**
+	 * Class name which is used to flag this code field has break-word css attr
+	 *
+	 * @type {String}
+	 */
+	var BREAK_WORD_CLASS = 'line-numbers-break-word';
+
+	/**
+	 * Returns style declaration for element
+	 *
+	 * @param  {Node} el
+	 * @return {Object}
+	 */
+	var getStyle = function(el){
+		if (!el){
+			return {};
+		}
+		if (el.currentStyle){
+		  return el.currentStyle;
+		}
+	 	else if (document.defaultView && document.defaultView.getComputedStyle){
+	 		return document.defaultView.getComputedStyle(el, '');
+	 	} else {
+		  return el.style;
+	 	}
+	};
+
+	var _resizeElement = function(element){
+		var lineNumbersWrapper = element.querySelector('.line-numbers-rows');
+		var lineNumberSizer = element.querySelector('.line-numbers-sizer');
+		var codeLines = element.textContent.split('\n');
+		var lineHeight;
+
+		lineNumberSizer.style.display = 'block';
+
+		// parse line height in such way because of problems with zoom & line height css attr
+		lineNumberSizer.innerText = '\n';
+		lineHeight = lineNumberSizer.getBoundingClientRect().height;
+
+		codeLines.forEach(function(line, lineNumber){
+			lineNumberSizer.innerText = line;
+			var lineSize = lineNumberSizer.getBoundingClientRect().height || lineHeight;
+			lineNumbersWrapper.children[lineNumber].style.height = lineSize + 'px';
+		});
+
+		lineNumberSizer.innerText = '';
+		lineNumberSizer.style.display = 'none';
+	};
+
+	window.addEventListener('resize', function(){
+		Array.prototype.forEach.call(document.querySelectorAll('pre.' + BREAK_WORD_CLASS), _resizeElement);
+	});
+
+	Prism.hooks.add('after-highlight', function (env) {
+		// works only for <code> wrapped inside <pre> (not inline)
+		var pre = env.element.parentNode;
+		var clsReg = /\s*\bline-numbers\b\s*/;
+		if (
+			!pre || !/pre/i.test(pre.nodeName) ||
+			// Abort only if nor the <pre> nor the <code> have the class
+			(!clsReg.test(pre.className) && !clsReg.test(env.element.className))
+		) {
+			return;
+		}
+
+		if (clsReg.test(env.element.className)) {
+			// Remove the class "line-numbers" from the <code>
+			env.element.className = env.element.className.replace(clsReg, '');
+		}
+		if (!clsReg.test(pre.className)) {
+			// Add the class "line-numbers" to the <pre>
+			pre.className += ' line-numbers';
+		}
+
+		var linesNum = (1 + env.code.split('\n').length);
+		var lineNumbersWrapper = document.createElement('span');
+		lineNumbersWrapper.className = 'line-numbers-rows';
+
+		var lines = new Array(linesNum);
+		lines = lines.join('<span></span>');
+
+		lineNumbersWrapper.innerHTML = lines;
+
+		if (pre.hasAttribute('data-start')) {
+			pre.style.counterReset = 'linenumber ' + (parseInt(pre.getAttribute('data-start'), 10) - 1);
+		}
+
+		env.element.appendChild(lineNumbersWrapper);
+
+		if (pre.classList.contains(BREAK_WORD_CLASS)){
+			var lineHeightSizer = document.createElement('span');
+			lineHeightSizer.className = 'line-numbers-sizer';
+
+			env.element.appendChild(lineHeightSizer);
+			_resizeElement(env.element);
+		}
+
+	});
+
+})();
\ No newline at end of file