UNPKG

6 kBJavaScriptView Raw
1(function () {
2
3 if (typeof Prism === 'undefined' || typeof document === 'undefined') {
4 return;
5 }
6
7 var CLASS_PATTERN = /(?:^|\s)command-line(?:\s|$)/;
8 var PROMPT_CLASS = 'command-line-prompt';
9
10 /** @type {(str: string, prefix: string) => boolean} */
11 var startsWith = ''.startsWith
12 ? function (s, p) { return s.startsWith(p); }
13 : function (s, p) { return s.indexOf(p) === 0; };
14
15 /**
16 * Repeats the given string some number of times.
17 *
18 * This is just a polyfill for `String.prototype.repeat`.
19 *
20 * @param {string} str
21 * @param {number} times
22 * @returns {string}
23 */
24 function repeat(str, times) {
25 var s = '';
26 for (var i = 0; i < times; i++) {
27 s += str;
28 }
29 return s;
30 }
31
32 /**
33 * Returns whether the given hook environment has a command line info object.
34 *
35 * @param {any} env
36 * @returns {boolean}
37 */
38 function hasCommandLineInfo(env) {
39 var vars = env.vars = env.vars || {};
40 return 'command-line' in vars;
41 }
42 /**
43 * Returns the command line info object from the given hook environment.
44 *
45 * @param {any} env
46 * @returns {CommandLineInfo}
47 *
48 * @typedef CommandLineInfo
49 * @property {boolean} [complete]
50 * @property {number} [numberOfLines]
51 * @property {string[]} [outputLines]
52 */
53 function getCommandLineInfo(env) {
54 var vars = env.vars = env.vars || {};
55 return vars['command-line'] = vars['command-line'] || {};
56 }
57
58
59 Prism.hooks.add('before-highlight', function (env) {
60 var commandLine = getCommandLineInfo(env);
61
62 if (commandLine.complete || !env.code) {
63 commandLine.complete = true;
64 return;
65 }
66
67 // Works only for <code> wrapped inside <pre> (not inline).
68 var pre = env.element.parentElement;
69 if (!pre || !/pre/i.test(pre.nodeName) || // Abort only if neither the <pre> nor the <code> have the class
70 (!CLASS_PATTERN.test(pre.className) && !CLASS_PATTERN.test(env.element.className))) {
71 commandLine.complete = true;
72 return;
73 }
74
75 // The element might be highlighted multiple times, so we just remove the previous prompt
76 var existingPrompt = env.element.querySelector('.' + PROMPT_CLASS);
77 if (existingPrompt) {
78 existingPrompt.remove();
79 }
80
81 var codeLines = env.code.split('\n');
82 commandLine.numberOfLines = codeLines.length;
83 /** @type {string[]} */
84 var outputLines = commandLine.outputLines = [];
85
86 var outputSections = pre.getAttribute('data-output');
87 var outputFilter = pre.getAttribute('data-filter-output');
88 if (outputSections !== null) { // The user specified the output lines. -- cwells
89 outputSections.split(',').forEach(function (section) {
90 var range = section.split('-');
91 var outputStart = parseInt(range[0], 10);
92 var outputEnd = range.length === 2 ? parseInt(range[1], 10) : outputStart;
93
94 if (!isNaN(outputStart) && !isNaN(outputEnd)) {
95 if (outputStart < 1) {
96 outputStart = 1;
97 }
98 if (outputEnd > codeLines.length) {
99 outputEnd = codeLines.length;
100 }
101 // Convert start and end to 0-based to simplify the arrays. -- cwells
102 outputStart--;
103 outputEnd--;
104 // Save the output line in an array and clear it in the code so it's not highlighted. -- cwells
105 for (var j = outputStart; j <= outputEnd; j++) {
106 outputLines[j] = codeLines[j];
107 codeLines[j] = '';
108 }
109 }
110 });
111 } else if (outputFilter) { // Treat lines beginning with this string as output. -- cwells
112 for (var i = 0; i < codeLines.length; i++) {
113 if (startsWith(codeLines[i], outputFilter)) { // This line is output. -- cwells
114 outputLines[i] = codeLines[i].slice(outputFilter.length);
115 codeLines[i] = '';
116 }
117 }
118 }
119
120 env.code = codeLines.join('\n');
121 });
122
123 Prism.hooks.add('before-insert', function (env) {
124 var commandLine = getCommandLineInfo(env);
125
126 if (commandLine.complete) {
127 return;
128 }
129
130 // Reinsert the output lines into the highlighted code. -- cwells
131 var codeLines = env.highlightedCode.split('\n');
132 var outputLines = commandLine.outputLines || [];
133 for (var i = 0, l = outputLines.length; i < l; i++) {
134 if (outputLines.hasOwnProperty(i)) {
135 codeLines[i] = outputLines[i];
136 }
137 }
138 env.highlightedCode = codeLines.join('\n');
139 });
140
141 Prism.hooks.add('complete', function (env) {
142 if (!hasCommandLineInfo(env)) {
143 // the previous hooks never ran
144 return;
145 }
146
147 var commandLine = getCommandLineInfo(env);
148
149 if (commandLine.complete) {
150 return;
151 }
152
153 var pre = env.element.parentElement;
154 if (CLASS_PATTERN.test(env.element.className)) { // Remove the class "command-line" from the <code>
155 env.element.className = env.element.className.replace(CLASS_PATTERN, ' ');
156 }
157 if (!CLASS_PATTERN.test(pre.className)) { // Add the class "command-line" to the <pre>
158 pre.className += ' command-line';
159 }
160
161 function getAttribute(key, defaultValue) {
162 return (pre.getAttribute(key) || defaultValue).replace(/"/g, '&quot');
163 }
164
165 // Create the "rows" that will become the command-line prompts. -- cwells
166 var promptLines;
167 var rowCount = commandLine.numberOfLines || 0;
168 var promptText = getAttribute('data-prompt', '');
169 if (promptText !== '') {
170 promptLines = repeat('<span data-prompt="' + promptText + '"></span>', rowCount);
171 } else {
172 var user = getAttribute('data-user', 'user');
173 var host = getAttribute('data-host', 'localhost');
174 promptLines = repeat('<span data-user="' + user + '" data-host="' + host + '"></span>', rowCount);
175 }
176
177 // Create the wrapper element. -- cwells
178 var prompt = document.createElement('span');
179 prompt.className = PROMPT_CLASS;
180 prompt.innerHTML = promptLines;
181
182 // Remove the prompt from the output lines. -- cwells
183 var outputLines = commandLine.outputLines || [];
184 for (var i = 0, l = outputLines.length; i < l; i++) {
185 if (outputLines.hasOwnProperty(i)) {
186 var node = prompt.children[i];
187 node.removeAttribute('data-user');
188 node.removeAttribute('data-host');
189 node.removeAttribute('data-prompt');
190 }
191 }
192
193 env.element.insertBefore(prompt, env.element.firstChild);
194 commandLine.complete = true;
195 });
196
197}());