1 | (function () {
|
---|
2 |
|
---|
3 | if (typeof Prism === 'undefined' || typeof document === 'undefined') {
|
---|
4 | return;
|
---|
5 | }
|
---|
6 |
|
---|
7 | /**
|
---|
8 | * Plugin name which is used as a class name for <pre> which is activating the plugin
|
---|
9 | *
|
---|
10 | * @type {string}
|
---|
11 | */
|
---|
12 | var PLUGIN_NAME = 'line-numbers';
|
---|
13 |
|
---|
14 | /**
|
---|
15 | * Regular expression used for determining line breaks
|
---|
16 | *
|
---|
17 | * @type {RegExp}
|
---|
18 | */
|
---|
19 | var NEW_LINE_EXP = /\n(?!$)/g;
|
---|
20 |
|
---|
21 |
|
---|
22 | /**
|
---|
23 | * Global exports
|
---|
24 | */
|
---|
25 | var config = Prism.plugins.lineNumbers = {
|
---|
26 | /**
|
---|
27 | * Get node for provided line number
|
---|
28 | *
|
---|
29 | * @param {Element} element pre element
|
---|
30 | * @param {number} number line number
|
---|
31 | * @returns {Element|undefined}
|
---|
32 | */
|
---|
33 | getLine: function (element, number) {
|
---|
34 | if (element.tagName !== 'PRE' || !element.classList.contains(PLUGIN_NAME)) {
|
---|
35 | return;
|
---|
36 | }
|
---|
37 |
|
---|
38 | var lineNumberRows = element.querySelector('.line-numbers-rows');
|
---|
39 | if (!lineNumberRows) {
|
---|
40 | return;
|
---|
41 | }
|
---|
42 | var lineNumberStart = parseInt(element.getAttribute('data-start'), 10) || 1;
|
---|
43 | var lineNumberEnd = lineNumberStart + (lineNumberRows.children.length - 1);
|
---|
44 |
|
---|
45 | if (number < lineNumberStart) {
|
---|
46 | number = lineNumberStart;
|
---|
47 | }
|
---|
48 | if (number > lineNumberEnd) {
|
---|
49 | number = lineNumberEnd;
|
---|
50 | }
|
---|
51 |
|
---|
52 | var lineIndex = number - lineNumberStart;
|
---|
53 |
|
---|
54 | return lineNumberRows.children[lineIndex];
|
---|
55 | },
|
---|
56 |
|
---|
57 | /**
|
---|
58 | * Resizes the line numbers of the given element.
|
---|
59 | *
|
---|
60 | * This function will not add line numbers. It will only resize existing ones.
|
---|
61 | *
|
---|
62 | * @param {HTMLElement} element A `<pre>` element with line numbers.
|
---|
63 | * @returns {void}
|
---|
64 | */
|
---|
65 | resize: function (element) {
|
---|
66 | resizeElements([element]);
|
---|
67 | },
|
---|
68 |
|
---|
69 | /**
|
---|
70 | * Whether the plugin can assume that the units font sizes and margins are not depended on the size of
|
---|
71 | * the current viewport.
|
---|
72 | *
|
---|
73 | * Setting this to `true` will allow the plugin to do certain optimizations for better performance.
|
---|
74 | *
|
---|
75 | * Set this to `false` if you use any of the following CSS units: `vh`, `vw`, `vmin`, `vmax`.
|
---|
76 | *
|
---|
77 | * @type {boolean}
|
---|
78 | */
|
---|
79 | assumeViewportIndependence: true
|
---|
80 | };
|
---|
81 |
|
---|
82 | /**
|
---|
83 | * Resizes the given elements.
|
---|
84 | *
|
---|
85 | * @param {HTMLElement[]} elements
|
---|
86 | */
|
---|
87 | function resizeElements(elements) {
|
---|
88 | elements = elements.filter(function (e) {
|
---|
89 | var codeStyles = getStyles(e);
|
---|
90 | var whiteSpace = codeStyles['white-space'];
|
---|
91 | return whiteSpace === 'pre-wrap' || whiteSpace === 'pre-line';
|
---|
92 | });
|
---|
93 |
|
---|
94 | if (elements.length == 0) {
|
---|
95 | return;
|
---|
96 | }
|
---|
97 |
|
---|
98 | var infos = elements.map(function (element) {
|
---|
99 | var codeElement = element.querySelector('code');
|
---|
100 | var lineNumbersWrapper = element.querySelector('.line-numbers-rows');
|
---|
101 | if (!codeElement || !lineNumbersWrapper) {
|
---|
102 | return undefined;
|
---|
103 | }
|
---|
104 |
|
---|
105 | /** @type {HTMLElement} */
|
---|
106 | var lineNumberSizer = element.querySelector('.line-numbers-sizer');
|
---|
107 | var codeLines = codeElement.textContent.split(NEW_LINE_EXP);
|
---|
108 |
|
---|
109 | if (!lineNumberSizer) {
|
---|
110 | lineNumberSizer = document.createElement('span');
|
---|
111 | lineNumberSizer.className = 'line-numbers-sizer';
|
---|
112 |
|
---|
113 | codeElement.appendChild(lineNumberSizer);
|
---|
114 | }
|
---|
115 |
|
---|
116 | lineNumberSizer.innerHTML = '0';
|
---|
117 | lineNumberSizer.style.display = 'block';
|
---|
118 |
|
---|
119 | var oneLinerHeight = lineNumberSizer.getBoundingClientRect().height;
|
---|
120 | lineNumberSizer.innerHTML = '';
|
---|
121 |
|
---|
122 | return {
|
---|
123 | element: element,
|
---|
124 | lines: codeLines,
|
---|
125 | lineHeights: [],
|
---|
126 | oneLinerHeight: oneLinerHeight,
|
---|
127 | sizer: lineNumberSizer,
|
---|
128 | };
|
---|
129 | }).filter(Boolean);
|
---|
130 |
|
---|
131 | infos.forEach(function (info) {
|
---|
132 | var lineNumberSizer = info.sizer;
|
---|
133 | var lines = info.lines;
|
---|
134 | var lineHeights = info.lineHeights;
|
---|
135 | var oneLinerHeight = info.oneLinerHeight;
|
---|
136 |
|
---|
137 | lineHeights[lines.length - 1] = undefined;
|
---|
138 | lines.forEach(function (line, index) {
|
---|
139 | if (line && line.length > 1) {
|
---|
140 | var e = lineNumberSizer.appendChild(document.createElement('span'));
|
---|
141 | e.style.display = 'block';
|
---|
142 | e.textContent = line;
|
---|
143 | } else {
|
---|
144 | lineHeights[index] = oneLinerHeight;
|
---|
145 | }
|
---|
146 | });
|
---|
147 | });
|
---|
148 |
|
---|
149 | infos.forEach(function (info) {
|
---|
150 | var lineNumberSizer = info.sizer;
|
---|
151 | var lineHeights = info.lineHeights;
|
---|
152 |
|
---|
153 | var childIndex = 0;
|
---|
154 | for (var i = 0; i < lineHeights.length; i++) {
|
---|
155 | if (lineHeights[i] === undefined) {
|
---|
156 | lineHeights[i] = lineNumberSizer.children[childIndex++].getBoundingClientRect().height;
|
---|
157 | }
|
---|
158 | }
|
---|
159 | });
|
---|
160 |
|
---|
161 | infos.forEach(function (info) {
|
---|
162 | var lineNumberSizer = info.sizer;
|
---|
163 | var wrapper = info.element.querySelector('.line-numbers-rows');
|
---|
164 |
|
---|
165 | lineNumberSizer.style.display = 'none';
|
---|
166 | lineNumberSizer.innerHTML = '';
|
---|
167 |
|
---|
168 | info.lineHeights.forEach(function (height, lineNumber) {
|
---|
169 | wrapper.children[lineNumber].style.height = height + 'px';
|
---|
170 | });
|
---|
171 | });
|
---|
172 | }
|
---|
173 |
|
---|
174 | /**
|
---|
175 | * Returns style declarations for the element
|
---|
176 | *
|
---|
177 | * @param {Element} element
|
---|
178 | */
|
---|
179 | function getStyles(element) {
|
---|
180 | if (!element) {
|
---|
181 | return null;
|
---|
182 | }
|
---|
183 |
|
---|
184 | return window.getComputedStyle ? getComputedStyle(element) : (element.currentStyle || null);
|
---|
185 | }
|
---|
186 |
|
---|
187 | var lastWidth = undefined;
|
---|
188 | window.addEventListener('resize', function () {
|
---|
189 | if (config.assumeViewportIndependence && lastWidth === window.innerWidth) {
|
---|
190 | return;
|
---|
191 | }
|
---|
192 | lastWidth = window.innerWidth;
|
---|
193 |
|
---|
194 | resizeElements(Array.prototype.slice.call(document.querySelectorAll('pre.' + PLUGIN_NAME)));
|
---|
195 | });
|
---|
196 |
|
---|
197 | Prism.hooks.add('complete', function (env) {
|
---|
198 | if (!env.code) {
|
---|
199 | return;
|
---|
200 | }
|
---|
201 |
|
---|
202 | var code = /** @type {Element} */ (env.element);
|
---|
203 | var pre = /** @type {HTMLElement} */ (code.parentNode);
|
---|
204 |
|
---|
205 | // works only for <code> wrapped inside <pre> (not inline)
|
---|
206 | if (!pre || !/pre/i.test(pre.nodeName)) {
|
---|
207 | return;
|
---|
208 | }
|
---|
209 |
|
---|
210 | // Abort if line numbers already exists
|
---|
211 | if (code.querySelector('.line-numbers-rows')) {
|
---|
212 | return;
|
---|
213 | }
|
---|
214 |
|
---|
215 | // only add line numbers if <code> or one of its ancestors has the `line-numbers` class
|
---|
216 | if (!Prism.util.isActive(code, PLUGIN_NAME)) {
|
---|
217 | return;
|
---|
218 | }
|
---|
219 |
|
---|
220 | // Remove the class 'line-numbers' from the <code>
|
---|
221 | code.classList.remove(PLUGIN_NAME);
|
---|
222 | // Add the class 'line-numbers' to the <pre>
|
---|
223 | pre.classList.add(PLUGIN_NAME);
|
---|
224 |
|
---|
225 | var match = env.code.match(NEW_LINE_EXP);
|
---|
226 | var linesNum = match ? match.length + 1 : 1;
|
---|
227 | var lineNumbersWrapper;
|
---|
228 |
|
---|
229 | var lines = new Array(linesNum + 1).join('<span></span>');
|
---|
230 |
|
---|
231 | lineNumbersWrapper = document.createElement('span');
|
---|
232 | lineNumbersWrapper.setAttribute('aria-hidden', 'true');
|
---|
233 | lineNumbersWrapper.className = 'line-numbers-rows';
|
---|
234 | lineNumbersWrapper.innerHTML = lines;
|
---|
235 |
|
---|
236 | if (pre.hasAttribute('data-start')) {
|
---|
237 | pre.style.counterReset = 'linenumber ' + (parseInt(pre.getAttribute('data-start'), 10) - 1);
|
---|
238 | }
|
---|
239 |
|
---|
240 | env.element.appendChild(lineNumbersWrapper);
|
---|
241 |
|
---|
242 | resizeElements([pre]);
|
---|
243 |
|
---|
244 | Prism.hooks.run('line-numbers', env);
|
---|
245 | });
|
---|
246 |
|
---|
247 | Prism.hooks.add('line-numbers', function (env) {
|
---|
248 | env.plugins = env.plugins || {};
|
---|
249 | env.plugins.lineNumbers = true;
|
---|
250 | });
|
---|
251 |
|
---|
252 | }());
|
---|