Skip to content

Commit 4ab966b

Browse files
authored
Merge pull request #30 from wcoder/bugfixes/multiline-nodes
Bug Fixes & refactoring Closed #21 #22 #29
2 parents b7f382a + 0290e33 commit 4ab966b

7 files changed

+198
-125
lines changed

.editorconfig

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
2+
root = true
3+
4+
[*]
5+
end_of_line = lf
6+
insert_final_newline = true
7+
charset = utf-8
8+
indent_style = space
9+
indent_size = 4

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# highlightjs-line-numbers.js [![version](http://img.shields.io/badge/release-v2.1.0-brightgreen.svg?style=flat)](https://github.com/wcoder/highlightjs-line-numbers.js/archive/master.zip)
1+
# highlightjs-line-numbers.js [![version](http://img.shields.io/badge/release-v2.2.0-brightgreen.svg?style=flat)](https://github.com/wcoder/highlightjs-line-numbers.js/archive/master.zip)
22

33
Highlight.js line numbers plugin.
44

@@ -18,7 +18,7 @@ npm install highlightjs-line-numbers.js
1818

1919
#### Getting the library from CDN
2020
```html
21-
<script src="//cdnjs.cloudflare.com/ajax/libs/highlightjs-line-numbers.js/2.1.0/highlightjs-line-numbers.min.js"></script>
21+
<script src="//cdnjs.cloudflare.com/ajax/libs/highlightjs-line-numbers.js/2.2.0/highlightjs-line-numbers.min.js"></script>
2222
```
2323

2424
## Usage

bower.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "highlightjs-line-numbers.js",
3-
"version": "2.1.0",
3+
"version": "2.2.0",
44
"homepage": "https://github.com/wcoder/highlightjs-line-numbers.js",
55
"authors": [
66
"Yauheni Pakala <evgeniy.pakalo@gmail.com>"

dist/highlightjs-line-numbers.min.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

gulpfile.js

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@ var rename = require("gulp-rename");
44
var replace = require('gulp-replace');
55

66
gulp.task('build', function() {
7-
return gulp.src('src/*.js')
8-
.pipe(uglify())
9-
.pipe(replace('\\t', ''))
10-
.pipe(rename({
11-
extname: '.min.js'
12-
}))
13-
.pipe(gulp.dest('dist'));
14-
});
7+
return gulp.src('src/*.js')
8+
.pipe(uglify())
9+
.pipe(replace(' ', ''))
10+
.pipe(rename({
11+
extname: '.min.js'
12+
}))
13+
.pipe(gulp.dest('dist'));
14+
});

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "highlightjs-line-numbers.js",
3-
"version": "2.1.0",
3+
"version": "2.2.0",
44
"description": "Highlight.js line numbers plugin.",
55
"main": "src/highlightjs-line-numbers.js",
66
"dependencies": {},

src/highlightjs-line-numbers.js

Lines changed: 176 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -1,113 +1,177 @@
1+
// jshint multistr:true
2+
13
(function (w, d) {
2-
'use strict';
3-
4-
var TABLE_NAME = 'hljs-ln',
5-
LINE_NAME = 'hljs-ln-line',
6-
CODE_BLOCK_NAME = 'hljs-ln-code',
7-
NUMBERS_BLOCK_NAME = 'hljs-ln-numbers',
8-
NUMBER_LINE_NAME = 'hljs-ln-n',
9-
DATA_ATTR_NAME = 'data-line-number';
10-
11-
// string format
12-
// https://wcoder.github.io/notes/string-format-for-string-formating-in-javascript
13-
var format = function (str, args) {
14-
return str.replace(/\{(\d+)\}/g, function(m, n){
15-
return args[n] ? args[n] : m;
16-
});
17-
};
18-
19-
if (w.hljs) {
20-
w.hljs.initLineNumbersOnLoad = initLineNumbersOnLoad;
21-
w.hljs.lineNumbersBlock = lineNumbersBlock;
22-
23-
addStyles();
24-
} else {
25-
w.console.error('highlight.js not detected!');
26-
}
27-
28-
function addStyles () {
29-
var css = d.createElement('style');
30-
css.type = 'text/css';
31-
css.innerHTML = format(
32-
'.{0}{border-collapse:collapse}\
33-
.{0} td{padding:0}\
34-
.{1}:before{content:attr({2})}',
35-
[
36-
TABLE_NAME,
37-
NUMBER_LINE_NAME,
38-
DATA_ATTR_NAME
39-
]);
40-
d.getElementsByTagName('head')[0].appendChild(css);
41-
}
42-
43-
function initLineNumbersOnLoad (options) {
44-
if (d.readyState === 'complete') {
45-
documentReady(options);
46-
} else {
47-
w.addEventListener('DOMContentLoaded', function () {
48-
documentReady(options);
49-
});
50-
}
51-
}
52-
53-
function documentReady (options) {
54-
try {
55-
var blocks = d.querySelectorAll('code.hljs');
56-
57-
for (var i in blocks) {
58-
if (blocks.hasOwnProperty(i)) {
59-
lineNumbersBlock(blocks[i], options);
60-
}
61-
}
62-
} catch (e) {
63-
w.console.error('LineNumbers error: ', e);
64-
}
65-
}
66-
67-
function lineNumbersBlock (element, options) {
68-
if (typeof element !== 'object') return;
69-
70-
// define options or set default
71-
options = options || {
72-
singleLine: false
73-
};
74-
75-
// convert options
76-
var firstLineIndex = !!options.singleLine ? 0 : 1;
77-
78-
var lines = getLines(element.innerHTML);
79-
80-
if (lines.length > firstLineIndex) {
81-
var html = '';
82-
83-
for (var i = 0, l = lines.length; i < l; i++) {
84-
html += format(
85-
'<tr>\
86-
<td class="{0}">\
87-
<div class="{1} {2}" {3}="{5}"></div>\
88-
</td>\
89-
<td class="{4}">\
90-
<div class="{1}">{6}</div>\
91-
</td>\
92-
</tr>',
93-
[
94-
NUMBERS_BLOCK_NAME,
95-
LINE_NAME,
96-
NUMBER_LINE_NAME,
97-
DATA_ATTR_NAME,
98-
CODE_BLOCK_NAME,
99-
i + 1,
100-
lines[i].length > 0 ? lines[i] : ' '
101-
]);
102-
}
103-
104-
element.innerHTML = format('<table class="{0}">{1}</table>', [ TABLE_NAME, html ]);
105-
}
106-
}
107-
108-
function getLines(text) {
109-
if (text.length === 0) return [];
110-
return text.split(/\r\n|\r|\n/g);
111-
}
112-
113-
}(window, document));
4+
'use strict';
5+
6+
var TABLE_NAME = 'hljs-ln',
7+
LINE_NAME = 'hljs-ln-line',
8+
CODE_BLOCK_NAME = 'hljs-ln-code',
9+
NUMBERS_BLOCK_NAME = 'hljs-ln-numbers',
10+
NUMBER_LINE_NAME = 'hljs-ln-n',
11+
DATA_ATTR_NAME = 'data-line-number',
12+
BREAK_LINE_REGEXP = /\r\n|\r|\n/g;
13+
14+
if (w.hljs) {
15+
w.hljs.initLineNumbersOnLoad = initLineNumbersOnLoad;
16+
w.hljs.lineNumbersBlock = lineNumbersBlock;
17+
18+
addStyles();
19+
} else {
20+
w.console.error('highlight.js not detected!');
21+
}
22+
23+
function addStyles () {
24+
var css = d.createElement('style');
25+
css.type = 'text/css';
26+
css.innerHTML = format(
27+
'.{0}{border-collapse:collapse}\
28+
.{0} td{padding:0}\
29+
.{1}:before{content:attr({2})}',
30+
[
31+
TABLE_NAME,
32+
NUMBER_LINE_NAME,
33+
DATA_ATTR_NAME
34+
]);
35+
d.getElementsByTagName('head')[0].appendChild(css);
36+
}
37+
38+
function initLineNumbersOnLoad (options) {
39+
if (d.readyState === 'complete') {
40+
documentReady(options);
41+
} else {
42+
w.addEventListener('DOMContentLoaded', function () {
43+
documentReady(options);
44+
});
45+
}
46+
}
47+
48+
function documentReady (options) {
49+
try {
50+
var blocks = d.querySelectorAll('code.hljs');
51+
52+
for (var i in blocks) {
53+
if (blocks.hasOwnProperty(i)) {
54+
lineNumbersBlock(blocks[i], options);
55+
}
56+
}
57+
} catch (e) {
58+
w.console.error('LineNumbers error: ', e);
59+
}
60+
}
61+
62+
function lineNumbersBlock (element, options) {
63+
if (typeof element !== 'object') return;
64+
65+
// define options or set default
66+
options = options || {
67+
singleLine: false
68+
};
69+
70+
// convert options
71+
var firstLineIndex = !!options.singleLine ? 0 : 1;
72+
73+
async(function () {
74+
75+
duplicateMultilineNodes(element);
76+
77+
element.innerHTML = addLineNumbersBlockFor(element.innerHTML, firstLineIndex);
78+
});
79+
}
80+
81+
function addLineNumbersBlockFor (inputHtml, firstLineIndex) {
82+
83+
var lines = getLines(inputHtml);
84+
85+
if (lines.length > firstLineIndex) {
86+
var html = '';
87+
88+
for (var i = 0, l = lines.length; i < l; i++) {
89+
html += format(
90+
'<tr>\
91+
<td class="{0}">\
92+
<div class="{1} {2}" {3}="{5}"></div>\
93+
</td>\
94+
<td class="{4}">\
95+
<div class="{1}">{6}</div>\
96+
</td>\
97+
</tr>',
98+
[
99+
NUMBERS_BLOCK_NAME,
100+
LINE_NAME,
101+
NUMBER_LINE_NAME,
102+
DATA_ATTR_NAME,
103+
CODE_BLOCK_NAME,
104+
i + 1,
105+
lines[i].length > 0 ? lines[i] : ' '
106+
]);
107+
}
108+
109+
return format('<table class="{0}">{1}</table>', [ TABLE_NAME, html ]);
110+
}
111+
112+
return inputHtml;
113+
}
114+
115+
/**
116+
* Recursive method for fix multi-line elements implementation in highlight.js
117+
* Doing deep passage on child nodes.
118+
* @param {HTMLElement} element
119+
*/
120+
function duplicateMultilineNodes (element) {
121+
var nodes = element.childNodes;
122+
for (var node in nodes){
123+
if (nodes.hasOwnProperty(node)) {
124+
var child = nodes[node];
125+
if (getLinesCount(child.textContent) > 0) {
126+
if (child.childNodes.length > 0) {
127+
duplicateMultilineNodes(child);
128+
} else {
129+
duplicateMultilineNode(child);
130+
}
131+
}
132+
}
133+
}
134+
}
135+
136+
/**
137+
* Method for fix multi-line elements implementation in highlight.js
138+
* @param {HTMLElement} element
139+
*/
140+
function duplicateMultilineNode (element) {
141+
var className = element.parentNode.className;
142+
143+
if ( ! /hljs-/.test(className)) return;
144+
145+
var lines = getLines(element.textContent);
146+
147+
for (var i = 0, result = ''; i < lines.length; i++) {
148+
result += format('<span class="{0}">{1}</span>\n', [ className, lines[i] ]);
149+
}
150+
element.parentNode.innerHTML = result.trim();
151+
}
152+
153+
function getLines (text) {
154+
if (text.length === 0) return [];
155+
return text.split(BREAK_LINE_REGEXP);
156+
}
157+
158+
function getLinesCount (text) {
159+
return (text.trim().match(BREAK_LINE_REGEXP) || []).length;
160+
}
161+
162+
function async (func) {
163+
w.setTimeout(func, 0);
164+
}
165+
166+
/**
167+
* {@link https://wcoder.github.io/notes/string-format-for-string-formating-in-javascript}
168+
* @param {string} format
169+
* @param {array} args
170+
*/
171+
function format (format, args) {
172+
return format.replace(/\{(\d+)\}/g, function(m, n){
173+
return args[n] ? args[n] : m;
174+
});
175+
}
176+
177+
}(window, document));

0 commit comments

Comments
 (0)