##// END OF EJS Templates
Fix ansispan() to ignore stray [0m
Mateusz Paprocki -
Show More
@@ -1,570 +1,579 b''
1 // Copyright (c) IPython Development Team.
1 // Copyright (c) IPython Development Team.
2 // Distributed under the terms of the Modified BSD License.
2 // Distributed under the terms of the Modified BSD License.
3
3
4 define([
4 define([
5 'base/js/namespace',
5 'base/js/namespace',
6 'jquery',
6 'jquery',
7 ], function(IPython, $){
7 ], function(IPython, $){
8 "use strict";
8 "use strict";
9
9
10 IPython.load_extensions = function () {
10 IPython.load_extensions = function () {
11 // load one or more IPython notebook extensions with requirejs
11 // load one or more IPython notebook extensions with requirejs
12
12
13 var extensions = [];
13 var extensions = [];
14 var extension_names = arguments;
14 var extension_names = arguments;
15 for (var i = 0; i < extension_names.length; i++) {
15 for (var i = 0; i < extension_names.length; i++) {
16 extensions.push("nbextensions/" + arguments[i]);
16 extensions.push("nbextensions/" + arguments[i]);
17 }
17 }
18
18
19 require(extensions,
19 require(extensions,
20 function () {
20 function () {
21 for (var i = 0; i < arguments.length; i++) {
21 for (var i = 0; i < arguments.length; i++) {
22 var ext = arguments[i];
22 var ext = arguments[i];
23 var ext_name = extension_names[i];
23 var ext_name = extension_names[i];
24 // success callback
24 // success callback
25 console.log("Loaded extension: " + ext_name);
25 console.log("Loaded extension: " + ext_name);
26 if (ext && ext.load_ipython_extension !== undefined) {
26 if (ext && ext.load_ipython_extension !== undefined) {
27 ext.load_ipython_extension();
27 ext.load_ipython_extension();
28 }
28 }
29 }
29 }
30 },
30 },
31 function (err) {
31 function (err) {
32 // failure callback
32 // failure callback
33 console.log("Failed to load extension(s):", err.requireModules, err);
33 console.log("Failed to load extension(s):", err.requireModules, err);
34 }
34 }
35 );
35 );
36 };
36 };
37
37
38 //============================================================================
38 //============================================================================
39 // Cross-browser RegEx Split
39 // Cross-browser RegEx Split
40 //============================================================================
40 //============================================================================
41
41
42 // This code has been MODIFIED from the code licensed below to not replace the
42 // This code has been MODIFIED from the code licensed below to not replace the
43 // default browser split. The license is reproduced here.
43 // default browser split. The license is reproduced here.
44
44
45 // see http://blog.stevenlevithan.com/archives/cross-browser-split for more info:
45 // see http://blog.stevenlevithan.com/archives/cross-browser-split for more info:
46 /*!
46 /*!
47 * Cross-Browser Split 1.1.1
47 * Cross-Browser Split 1.1.1
48 * Copyright 2007-2012 Steven Levithan <stevenlevithan.com>
48 * Copyright 2007-2012 Steven Levithan <stevenlevithan.com>
49 * Available under the MIT License
49 * Available under the MIT License
50 * ECMAScript compliant, uniform cross-browser split method
50 * ECMAScript compliant, uniform cross-browser split method
51 */
51 */
52
52
53 /**
53 /**
54 * Splits a string into an array of strings using a regex or string
54 * Splits a string into an array of strings using a regex or string
55 * separator. Matches of the separator are not included in the result array.
55 * separator. Matches of the separator are not included in the result array.
56 * However, if `separator` is a regex that contains capturing groups,
56 * However, if `separator` is a regex that contains capturing groups,
57 * backreferences are spliced into the result each time `separator` is
57 * backreferences are spliced into the result each time `separator` is
58 * matched. Fixes browser bugs compared to the native
58 * matched. Fixes browser bugs compared to the native
59 * `String.prototype.split` and can be used reliably cross-browser.
59 * `String.prototype.split` and can be used reliably cross-browser.
60 * @param {String} str String to split.
60 * @param {String} str String to split.
61 * @param {RegExp|String} separator Regex or string to use for separating
61 * @param {RegExp|String} separator Regex or string to use for separating
62 * the string.
62 * the string.
63 * @param {Number} [limit] Maximum number of items to include in the result
63 * @param {Number} [limit] Maximum number of items to include in the result
64 * array.
64 * array.
65 * @returns {Array} Array of substrings.
65 * @returns {Array} Array of substrings.
66 * @example
66 * @example
67 *
67 *
68 * // Basic use
68 * // Basic use
69 * regex_split('a b c d', ' ');
69 * regex_split('a b c d', ' ');
70 * // -> ['a', 'b', 'c', 'd']
70 * // -> ['a', 'b', 'c', 'd']
71 *
71 *
72 * // With limit
72 * // With limit
73 * regex_split('a b c d', ' ', 2);
73 * regex_split('a b c d', ' ', 2);
74 * // -> ['a', 'b']
74 * // -> ['a', 'b']
75 *
75 *
76 * // Backreferences in result array
76 * // Backreferences in result array
77 * regex_split('..word1 word2..', /([a-z]+)(\d+)/i);
77 * regex_split('..word1 word2..', /([a-z]+)(\d+)/i);
78 * // -> ['..', 'word', '1', ' ', 'word', '2', '..']
78 * // -> ['..', 'word', '1', ' ', 'word', '2', '..']
79 */
79 */
80 var regex_split = function (str, separator, limit) {
80 var regex_split = function (str, separator, limit) {
81 // If `separator` is not a regex, use `split`
81 // If `separator` is not a regex, use `split`
82 if (Object.prototype.toString.call(separator) !== "[object RegExp]") {
82 if (Object.prototype.toString.call(separator) !== "[object RegExp]") {
83 return split.call(str, separator, limit);
83 return split.call(str, separator, limit);
84 }
84 }
85 var output = [],
85 var output = [],
86 flags = (separator.ignoreCase ? "i" : "") +
86 flags = (separator.ignoreCase ? "i" : "") +
87 (separator.multiline ? "m" : "") +
87 (separator.multiline ? "m" : "") +
88 (separator.extended ? "x" : "") + // Proposed for ES6
88 (separator.extended ? "x" : "") + // Proposed for ES6
89 (separator.sticky ? "y" : ""), // Firefox 3+
89 (separator.sticky ? "y" : ""), // Firefox 3+
90 lastLastIndex = 0,
90 lastLastIndex = 0,
91 // Make `global` and avoid `lastIndex` issues by working with a copy
91 // Make `global` and avoid `lastIndex` issues by working with a copy
92 separator = new RegExp(separator.source, flags + "g"),
92 separator = new RegExp(separator.source, flags + "g"),
93 separator2, match, lastIndex, lastLength;
93 separator2, match, lastIndex, lastLength;
94 str += ""; // Type-convert
94 str += ""; // Type-convert
95
95
96 var compliantExecNpcg = typeof(/()??/.exec("")[1]) === "undefined";
96 var compliantExecNpcg = typeof(/()??/.exec("")[1]) === "undefined";
97 if (!compliantExecNpcg) {
97 if (!compliantExecNpcg) {
98 // Doesn't need flags gy, but they don't hurt
98 // Doesn't need flags gy, but they don't hurt
99 separator2 = new RegExp("^" + separator.source + "$(?!\\s)", flags);
99 separator2 = new RegExp("^" + separator.source + "$(?!\\s)", flags);
100 }
100 }
101 /* Values for `limit`, per the spec:
101 /* Values for `limit`, per the spec:
102 * If undefined: 4294967295 // Math.pow(2, 32) - 1
102 * If undefined: 4294967295 // Math.pow(2, 32) - 1
103 * If 0, Infinity, or NaN: 0
103 * If 0, Infinity, or NaN: 0
104 * If positive number: limit = Math.floor(limit); if (limit > 4294967295) limit -= 4294967296;
104 * If positive number: limit = Math.floor(limit); if (limit > 4294967295) limit -= 4294967296;
105 * If negative number: 4294967296 - Math.floor(Math.abs(limit))
105 * If negative number: 4294967296 - Math.floor(Math.abs(limit))
106 * If other: Type-convert, then use the above rules
106 * If other: Type-convert, then use the above rules
107 */
107 */
108 limit = typeof(limit) === "undefined" ?
108 limit = typeof(limit) === "undefined" ?
109 -1 >>> 0 : // Math.pow(2, 32) - 1
109 -1 >>> 0 : // Math.pow(2, 32) - 1
110 limit >>> 0; // ToUint32(limit)
110 limit >>> 0; // ToUint32(limit)
111 while (match = separator.exec(str)) {
111 while (match = separator.exec(str)) {
112 // `separator.lastIndex` is not reliable cross-browser
112 // `separator.lastIndex` is not reliable cross-browser
113 lastIndex = match.index + match[0].length;
113 lastIndex = match.index + match[0].length;
114 if (lastIndex > lastLastIndex) {
114 if (lastIndex > lastLastIndex) {
115 output.push(str.slice(lastLastIndex, match.index));
115 output.push(str.slice(lastLastIndex, match.index));
116 // Fix browsers whose `exec` methods don't consistently return `undefined` for
116 // Fix browsers whose `exec` methods don't consistently return `undefined` for
117 // nonparticipating capturing groups
117 // nonparticipating capturing groups
118 if (!compliantExecNpcg && match.length > 1) {
118 if (!compliantExecNpcg && match.length > 1) {
119 match[0].replace(separator2, function () {
119 match[0].replace(separator2, function () {
120 for (var i = 1; i < arguments.length - 2; i++) {
120 for (var i = 1; i < arguments.length - 2; i++) {
121 if (typeof(arguments[i]) === "undefined") {
121 if (typeof(arguments[i]) === "undefined") {
122 match[i] = undefined;
122 match[i] = undefined;
123 }
123 }
124 }
124 }
125 });
125 });
126 }
126 }
127 if (match.length > 1 && match.index < str.length) {
127 if (match.length > 1 && match.index < str.length) {
128 Array.prototype.push.apply(output, match.slice(1));
128 Array.prototype.push.apply(output, match.slice(1));
129 }
129 }
130 lastLength = match[0].length;
130 lastLength = match[0].length;
131 lastLastIndex = lastIndex;
131 lastLastIndex = lastIndex;
132 if (output.length >= limit) {
132 if (output.length >= limit) {
133 break;
133 break;
134 }
134 }
135 }
135 }
136 if (separator.lastIndex === match.index) {
136 if (separator.lastIndex === match.index) {
137 separator.lastIndex++; // Avoid an infinite loop
137 separator.lastIndex++; // Avoid an infinite loop
138 }
138 }
139 }
139 }
140 if (lastLastIndex === str.length) {
140 if (lastLastIndex === str.length) {
141 if (lastLength || !separator.test("")) {
141 if (lastLength || !separator.test("")) {
142 output.push("");
142 output.push("");
143 }
143 }
144 } else {
144 } else {
145 output.push(str.slice(lastLastIndex));
145 output.push(str.slice(lastLastIndex));
146 }
146 }
147 return output.length > limit ? output.slice(0, limit) : output;
147 return output.length > limit ? output.slice(0, limit) : output;
148 };
148 };
149
149
150 //============================================================================
150 //============================================================================
151 // End contributed Cross-browser RegEx Split
151 // End contributed Cross-browser RegEx Split
152 //============================================================================
152 //============================================================================
153
153
154
154
155 var uuid = function () {
155 var uuid = function () {
156 // http://www.ietf.org/rfc/rfc4122.txt
156 // http://www.ietf.org/rfc/rfc4122.txt
157 var s = [];
157 var s = [];
158 var hexDigits = "0123456789ABCDEF";
158 var hexDigits = "0123456789ABCDEF";
159 for (var i = 0; i < 32; i++) {
159 for (var i = 0; i < 32; i++) {
160 s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
160 s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
161 }
161 }
162 s[12] = "4"; // bits 12-15 of the time_hi_and_version field to 0010
162 s[12] = "4"; // bits 12-15 of the time_hi_and_version field to 0010
163 s[16] = hexDigits.substr((s[16] & 0x3) | 0x8, 1); // bits 6-7 of the clock_seq_hi_and_reserved to 01
163 s[16] = hexDigits.substr((s[16] & 0x3) | 0x8, 1); // bits 6-7 of the clock_seq_hi_and_reserved to 01
164
164
165 var uuid = s.join("");
165 var uuid = s.join("");
166 return uuid;
166 return uuid;
167 };
167 };
168
168
169
169
170 //Fix raw text to parse correctly in crazy XML
170 //Fix raw text to parse correctly in crazy XML
171 function xmlencode(string) {
171 function xmlencode(string) {
172 return string.replace(/\&/g,'&'+'amp;')
172 return string.replace(/\&/g,'&'+'amp;')
173 .replace(/</g,'&'+'lt;')
173 .replace(/</g,'&'+'lt;')
174 .replace(/>/g,'&'+'gt;')
174 .replace(/>/g,'&'+'gt;')
175 .replace(/\'/g,'&'+'apos;')
175 .replace(/\'/g,'&'+'apos;')
176 .replace(/\"/g,'&'+'quot;')
176 .replace(/\"/g,'&'+'quot;')
177 .replace(/`/g,'&'+'#96;');
177 .replace(/`/g,'&'+'#96;');
178 }
178 }
179
179
180
180
181 //Map from terminal commands to CSS classes
181 //Map from terminal commands to CSS classes
182 var ansi_colormap = {
182 var ansi_colormap = {
183 "01":"ansibold",
183 "01":"ansibold",
184
184
185 "30":"ansiblack",
185 "30":"ansiblack",
186 "31":"ansired",
186 "31":"ansired",
187 "32":"ansigreen",
187 "32":"ansigreen",
188 "33":"ansiyellow",
188 "33":"ansiyellow",
189 "34":"ansiblue",
189 "34":"ansiblue",
190 "35":"ansipurple",
190 "35":"ansipurple",
191 "36":"ansicyan",
191 "36":"ansicyan",
192 "37":"ansigray",
192 "37":"ansigray",
193
193
194 "40":"ansibgblack",
194 "40":"ansibgblack",
195 "41":"ansibgred",
195 "41":"ansibgred",
196 "42":"ansibggreen",
196 "42":"ansibggreen",
197 "43":"ansibgyellow",
197 "43":"ansibgyellow",
198 "44":"ansibgblue",
198 "44":"ansibgblue",
199 "45":"ansibgpurple",
199 "45":"ansibgpurple",
200 "46":"ansibgcyan",
200 "46":"ansibgcyan",
201 "47":"ansibggray"
201 "47":"ansibggray"
202 };
202 };
203
203
204 function _process_numbers(attrs, numbers) {
204 function _process_numbers(attrs, numbers) {
205 // process ansi escapes
205 // process ansi escapes
206 var n = numbers.shift();
206 var n = numbers.shift();
207 if (ansi_colormap[n]) {
207 if (ansi_colormap[n]) {
208 if ( ! attrs["class"] ) {
208 if ( ! attrs["class"] ) {
209 attrs["class"] = ansi_colormap[n];
209 attrs["class"] = ansi_colormap[n];
210 } else {
210 } else {
211 attrs["class"] += " " + ansi_colormap[n];
211 attrs["class"] += " " + ansi_colormap[n];
212 }
212 }
213 } else if (n == "38" || n == "48") {
213 } else if (n == "38" || n == "48") {
214 // VT100 256 color or 24 bit RGB
214 // VT100 256 color or 24 bit RGB
215 if (numbers.length < 2) {
215 if (numbers.length < 2) {
216 console.log("Not enough fields for VT100 color", numbers);
216 console.log("Not enough fields for VT100 color", numbers);
217 return;
217 return;
218 }
218 }
219
219
220 var index_or_rgb = numbers.shift();
220 var index_or_rgb = numbers.shift();
221 var r,g,b;
221 var r,g,b;
222 if (index_or_rgb == "5") {
222 if (index_or_rgb == "5") {
223 // 256 color
223 // 256 color
224 var idx = parseInt(numbers.shift());
224 var idx = parseInt(numbers.shift());
225 if (idx < 16) {
225 if (idx < 16) {
226 // indexed ANSI
226 // indexed ANSI
227 // ignore bright / non-bright distinction
227 // ignore bright / non-bright distinction
228 idx = idx % 8;
228 idx = idx % 8;
229 var ansiclass = ansi_colormap[n[0] + (idx % 8).toString()];
229 var ansiclass = ansi_colormap[n[0] + (idx % 8).toString()];
230 if ( ! attrs["class"] ) {
230 if ( ! attrs["class"] ) {
231 attrs["class"] = ansiclass;
231 attrs["class"] = ansiclass;
232 } else {
232 } else {
233 attrs["class"] += " " + ansiclass;
233 attrs["class"] += " " + ansiclass;
234 }
234 }
235 return;
235 return;
236 } else if (idx < 232) {
236 } else if (idx < 232) {
237 // 216 color 6x6x6 RGB
237 // 216 color 6x6x6 RGB
238 idx = idx - 16;
238 idx = idx - 16;
239 b = idx % 6;
239 b = idx % 6;
240 g = Math.floor(idx / 6) % 6;
240 g = Math.floor(idx / 6) % 6;
241 r = Math.floor(idx / 36) % 6;
241 r = Math.floor(idx / 36) % 6;
242 // convert to rgb
242 // convert to rgb
243 r = (r * 51);
243 r = (r * 51);
244 g = (g * 51);
244 g = (g * 51);
245 b = (b * 51);
245 b = (b * 51);
246 } else {
246 } else {
247 // grayscale
247 // grayscale
248 idx = idx - 231;
248 idx = idx - 231;
249 // it's 1-24 and should *not* include black or white,
249 // it's 1-24 and should *not* include black or white,
250 // so a 26 point scale
250 // so a 26 point scale
251 r = g = b = Math.floor(idx * 256 / 26);
251 r = g = b = Math.floor(idx * 256 / 26);
252 }
252 }
253 } else if (index_or_rgb == "2") {
253 } else if (index_or_rgb == "2") {
254 // Simple 24 bit RGB
254 // Simple 24 bit RGB
255 if (numbers.length > 3) {
255 if (numbers.length > 3) {
256 console.log("Not enough fields for RGB", numbers);
256 console.log("Not enough fields for RGB", numbers);
257 return;
257 return;
258 }
258 }
259 r = numbers.shift();
259 r = numbers.shift();
260 g = numbers.shift();
260 g = numbers.shift();
261 b = numbers.shift();
261 b = numbers.shift();
262 } else {
262 } else {
263 console.log("unrecognized control", numbers);
263 console.log("unrecognized control", numbers);
264 return;
264 return;
265 }
265 }
266 if (r !== undefined) {
266 if (r !== undefined) {
267 // apply the rgb color
267 // apply the rgb color
268 var line;
268 var line;
269 if (n == "38") {
269 if (n == "38") {
270 line = "color: ";
270 line = "color: ";
271 } else {
271 } else {
272 line = "background-color: ";
272 line = "background-color: ";
273 }
273 }
274 line = line + "rgb(" + r + "," + g + "," + b + ");"
274 line = line + "rgb(" + r + "," + g + "," + b + ");"
275 if ( !attrs["style"] ) {
275 if ( !attrs["style"] ) {
276 attrs["style"] = line;
276 attrs["style"] = line;
277 } else {
277 } else {
278 attrs["style"] += " " + line;
278 attrs["style"] += " " + line;
279 }
279 }
280 }
280 }
281 }
281 }
282 }
282 }
283
283
284 function ansispan(str) {
284 function ansispan(str) {
285 // ansispan function adapted from github.com/mmalecki/ansispan (MIT License)
285 // ansispan function adapted from github.com/mmalecki/ansispan (MIT License)
286 // regular ansi escapes (using the table above)
286 // regular ansi escapes (using the table above)
287 var is_open = false
287 return str.replace(/\033\[(0?[01]|22|39)?([;\d]+)?m/g, function(match, prefix, pattern) {
288 return str.replace(/\033\[(0?[01]|22|39)?([;\d]+)?m/g, function(match, prefix, pattern) {
288 if (!pattern) {
289 if (!pattern) {
289 // [(01|22|39|)m close spans
290 // [(01|22|39|)m close spans
290 return "</span>";
291 if (is_open) {
291 }
292 is_open = false;
292 // consume sequence of color escapes
293 return "</span>";
293 var numbers = pattern.match(/\d+/g);
294 } else {
294 var attrs = {};
295 return "";
295 while (numbers.length > 0) {
296 }
296 _process_numbers(attrs, numbers);
297 } else {
297 }
298 is_open = true;
298
299
299 var span = "<span ";
300 // consume sequence of color escapes
300 for (var attr in attrs) {
301 var numbers = pattern.match(/\d+/g);
301 var value = attrs[attr];
302 var attrs = {};
302 span = span + " " + attr + '="' + attrs[attr] + '"';
303 while (numbers.length > 0) {
304 _process_numbers(attrs, numbers);
305 }
306
307 var span = "<span ";
308 for (var attr in attrs) {
309 var value = attrs[attr];
310 span = span + " " + attr + '="' + attrs[attr] + '"';
311 }
312 return span + ">";
303 }
313 }
304 return span + ">";
305 });
314 });
306 };
315 };
307
316
308 // Transform ANSI color escape codes into HTML <span> tags with css
317 // Transform ANSI color escape codes into HTML <span> tags with css
309 // classes listed in the above ansi_colormap object. The actual color used
318 // classes listed in the above ansi_colormap object. The actual color used
310 // are set in the css file.
319 // are set in the css file.
311 function fixConsole(txt) {
320 function fixConsole(txt) {
312 txt = xmlencode(txt);
321 txt = xmlencode(txt);
313 var re = /\033\[([\dA-Fa-f;]*?)m/;
322 var re = /\033\[([\dA-Fa-f;]*?)m/;
314 var opened = false;
323 var opened = false;
315 var cmds = [];
324 var cmds = [];
316 var opener = "";
325 var opener = "";
317 var closer = "";
326 var closer = "";
318
327
319 // Strip all ANSI codes that are not color related. Matches
328 // Strip all ANSI codes that are not color related. Matches
320 // all ANSI codes that do not end with "m".
329 // all ANSI codes that do not end with "m".
321 var ignored_re = /(?=(\033\[[\d;=]*[a-ln-zA-Z]{1}))\1(?!m)/g;
330 var ignored_re = /(?=(\033\[[\d;=]*[a-ln-zA-Z]{1}))\1(?!m)/g;
322 txt = txt.replace(ignored_re, "");
331 txt = txt.replace(ignored_re, "");
323
332
324 // color ansi codes
333 // color ansi codes
325 txt = ansispan(txt);
334 txt = ansispan(txt);
326 return txt;
335 return txt;
327 }
336 }
328
337
329 // Remove chunks that should be overridden by the effect of
338 // Remove chunks that should be overridden by the effect of
330 // carriage return characters
339 // carriage return characters
331 function fixCarriageReturn(txt) {
340 function fixCarriageReturn(txt) {
332 var tmp = txt;
341 var tmp = txt;
333 do {
342 do {
334 txt = tmp;
343 txt = tmp;
335 tmp = txt.replace(/\r+\n/gm, '\n'); // \r followed by \n --> newline
344 tmp = txt.replace(/\r+\n/gm, '\n'); // \r followed by \n --> newline
336 tmp = tmp.replace(/^.*\r+/gm, ''); // Other \r --> clear line
345 tmp = tmp.replace(/^.*\r+/gm, ''); // Other \r --> clear line
337 } while (tmp.length < txt.length);
346 } while (tmp.length < txt.length);
338 return txt;
347 return txt;
339 }
348 }
340
349
341 // Locate any URLs and convert them to a anchor tag
350 // Locate any URLs and convert them to a anchor tag
342 function autoLinkUrls(txt) {
351 function autoLinkUrls(txt) {
343 return txt.replace(/(^|\s)(https?|ftp)(:[^'">\s]+)/gi,
352 return txt.replace(/(^|\s)(https?|ftp)(:[^'">\s]+)/gi,
344 "$1<a target=\"_blank\" href=\"$2$3\">$2$3</a>");
353 "$1<a target=\"_blank\" href=\"$2$3\">$2$3</a>");
345 }
354 }
346
355
347 var points_to_pixels = function (points) {
356 var points_to_pixels = function (points) {
348 // A reasonably good way of converting between points and pixels.
357 // A reasonably good way of converting between points and pixels.
349 var test = $('<div style="display: none; width: 10000pt; padding:0; border:0;"></div>');
358 var test = $('<div style="display: none; width: 10000pt; padding:0; border:0;"></div>');
350 $(body).append(test);
359 $(body).append(test);
351 var pixel_per_point = test.width()/10000;
360 var pixel_per_point = test.width()/10000;
352 test.remove();
361 test.remove();
353 return Math.floor(points*pixel_per_point);
362 return Math.floor(points*pixel_per_point);
354 };
363 };
355
364
356 var always_new = function (constructor) {
365 var always_new = function (constructor) {
357 // wrapper around contructor to avoid requiring `var a = new constructor()`
366 // wrapper around contructor to avoid requiring `var a = new constructor()`
358 // useful for passing constructors as callbacks,
367 // useful for passing constructors as callbacks,
359 // not for programmer laziness.
368 // not for programmer laziness.
360 // from http://programmers.stackexchange.com/questions/118798
369 // from http://programmers.stackexchange.com/questions/118798
361 return function () {
370 return function () {
362 var obj = Object.create(constructor.prototype);
371 var obj = Object.create(constructor.prototype);
363 constructor.apply(obj, arguments);
372 constructor.apply(obj, arguments);
364 return obj;
373 return obj;
365 };
374 };
366 };
375 };
367
376
368 var url_path_join = function () {
377 var url_path_join = function () {
369 // join a sequence of url components with '/'
378 // join a sequence of url components with '/'
370 var url = '';
379 var url = '';
371 for (var i = 0; i < arguments.length; i++) {
380 for (var i = 0; i < arguments.length; i++) {
372 if (arguments[i] === '') {
381 if (arguments[i] === '') {
373 continue;
382 continue;
374 }
383 }
375 if (url.length > 0 && url[url.length-1] != '/') {
384 if (url.length > 0 && url[url.length-1] != '/') {
376 url = url + '/' + arguments[i];
385 url = url + '/' + arguments[i];
377 } else {
386 } else {
378 url = url + arguments[i];
387 url = url + arguments[i];
379 }
388 }
380 }
389 }
381 url = url.replace(/\/\/+/, '/');
390 url = url.replace(/\/\/+/, '/');
382 return url;
391 return url;
383 };
392 };
384
393
385 var parse_url = function (url) {
394 var parse_url = function (url) {
386 // an `a` element with an href allows attr-access to the parsed segments of a URL
395 // an `a` element with an href allows attr-access to the parsed segments of a URL
387 // a = parse_url("http://localhost:8888/path/name#hash")
396 // a = parse_url("http://localhost:8888/path/name#hash")
388 // a.protocol = "http:"
397 // a.protocol = "http:"
389 // a.host = "localhost:8888"
398 // a.host = "localhost:8888"
390 // a.hostname = "localhost"
399 // a.hostname = "localhost"
391 // a.port = 8888
400 // a.port = 8888
392 // a.pathname = "/path/name"
401 // a.pathname = "/path/name"
393 // a.hash = "#hash"
402 // a.hash = "#hash"
394 var a = document.createElement("a");
403 var a = document.createElement("a");
395 a.href = url;
404 a.href = url;
396 return a;
405 return a;
397 };
406 };
398
407
399 var encode_uri_components = function (uri) {
408 var encode_uri_components = function (uri) {
400 // encode just the components of a multi-segment uri,
409 // encode just the components of a multi-segment uri,
401 // leaving '/' separators
410 // leaving '/' separators
402 return uri.split('/').map(encodeURIComponent).join('/');
411 return uri.split('/').map(encodeURIComponent).join('/');
403 };
412 };
404
413
405 var url_join_encode = function () {
414 var url_join_encode = function () {
406 // join a sequence of url components with '/',
415 // join a sequence of url components with '/',
407 // encoding each component with encodeURIComponent
416 // encoding each component with encodeURIComponent
408 return encode_uri_components(url_path_join.apply(null, arguments));
417 return encode_uri_components(url_path_join.apply(null, arguments));
409 };
418 };
410
419
411
420
412 var splitext = function (filename) {
421 var splitext = function (filename) {
413 // mimic Python os.path.splitext
422 // mimic Python os.path.splitext
414 // Returns ['base', '.ext']
423 // Returns ['base', '.ext']
415 var idx = filename.lastIndexOf('.');
424 var idx = filename.lastIndexOf('.');
416 if (idx > 0) {
425 if (idx > 0) {
417 return [filename.slice(0, idx), filename.slice(idx)];
426 return [filename.slice(0, idx), filename.slice(idx)];
418 } else {
427 } else {
419 return [filename, ''];
428 return [filename, ''];
420 }
429 }
421 };
430 };
422
431
423
432
424 var escape_html = function (text) {
433 var escape_html = function (text) {
425 // escape text to HTML
434 // escape text to HTML
426 return $("<div/>").text(text).html();
435 return $("<div/>").text(text).html();
427 };
436 };
428
437
429
438
430 var get_body_data = function(key) {
439 var get_body_data = function(key) {
431 // get a url-encoded item from body.data and decode it
440 // get a url-encoded item from body.data and decode it
432 // we should never have any encoded URLs anywhere else in code
441 // we should never have any encoded URLs anywhere else in code
433 // until we are building an actual request
442 // until we are building an actual request
434 return decodeURIComponent($('body').data(key));
443 return decodeURIComponent($('body').data(key));
435 };
444 };
436
445
437 var to_absolute_cursor_pos = function (cm, cursor) {
446 var to_absolute_cursor_pos = function (cm, cursor) {
438 // get the absolute cursor position from CodeMirror's col, ch
447 // get the absolute cursor position from CodeMirror's col, ch
439 if (!cursor) {
448 if (!cursor) {
440 cursor = cm.getCursor();
449 cursor = cm.getCursor();
441 }
450 }
442 var cursor_pos = cursor.ch;
451 var cursor_pos = cursor.ch;
443 for (var i = 0; i < cursor.line; i++) {
452 for (var i = 0; i < cursor.line; i++) {
444 cursor_pos += cm.getLine(i).length + 1;
453 cursor_pos += cm.getLine(i).length + 1;
445 }
454 }
446 return cursor_pos;
455 return cursor_pos;
447 };
456 };
448
457
449 var from_absolute_cursor_pos = function (cm, cursor_pos) {
458 var from_absolute_cursor_pos = function (cm, cursor_pos) {
450 // turn absolute cursor postion into CodeMirror col, ch cursor
459 // turn absolute cursor postion into CodeMirror col, ch cursor
451 var i, line;
460 var i, line;
452 var offset = 0;
461 var offset = 0;
453 for (i = 0, line=cm.getLine(i); line !== undefined; i++, line=cm.getLine(i)) {
462 for (i = 0, line=cm.getLine(i); line !== undefined; i++, line=cm.getLine(i)) {
454 if (offset + line.length < cursor_pos) {
463 if (offset + line.length < cursor_pos) {
455 offset += line.length + 1;
464 offset += line.length + 1;
456 } else {
465 } else {
457 return {
466 return {
458 line : i,
467 line : i,
459 ch : cursor_pos - offset,
468 ch : cursor_pos - offset,
460 };
469 };
461 }
470 }
462 }
471 }
463 // reached end, return endpoint
472 // reached end, return endpoint
464 return {
473 return {
465 ch : line.length - 1,
474 ch : line.length - 1,
466 line : i - 1,
475 line : i - 1,
467 };
476 };
468 };
477 };
469
478
470 // http://stackoverflow.com/questions/2400935/browser-detection-in-javascript
479 // http://stackoverflow.com/questions/2400935/browser-detection-in-javascript
471 var browser = (function() {
480 var browser = (function() {
472 if (typeof navigator === 'undefined') {
481 if (typeof navigator === 'undefined') {
473 // navigator undefined in node
482 // navigator undefined in node
474 return 'None';
483 return 'None';
475 }
484 }
476 var N= navigator.appName, ua= navigator.userAgent, tem;
485 var N= navigator.appName, ua= navigator.userAgent, tem;
477 var M= ua.match(/(opera|chrome|safari|firefox|msie)\/?\s*(\.?\d+(\.\d+)*)/i);
486 var M= ua.match(/(opera|chrome|safari|firefox|msie)\/?\s*(\.?\d+(\.\d+)*)/i);
478 if (M && (tem= ua.match(/version\/([\.\d]+)/i)) !== null) M[2]= tem[1];
487 if (M && (tem= ua.match(/version\/([\.\d]+)/i)) !== null) M[2]= tem[1];
479 M= M? [M[1], M[2]]: [N, navigator.appVersion,'-?'];
488 M= M? [M[1], M[2]]: [N, navigator.appVersion,'-?'];
480 return M;
489 return M;
481 })();
490 })();
482
491
483 // http://stackoverflow.com/questions/11219582/how-to-detect-my-browser-version-and-operating-system-using-javascript
492 // http://stackoverflow.com/questions/11219582/how-to-detect-my-browser-version-and-operating-system-using-javascript
484 var platform = (function () {
493 var platform = (function () {
485 if (typeof navigator === 'undefined') {
494 if (typeof navigator === 'undefined') {
486 // navigator undefined in node
495 // navigator undefined in node
487 return 'None';
496 return 'None';
488 }
497 }
489 var OSName="None";
498 var OSName="None";
490 if (navigator.appVersion.indexOf("Win")!=-1) OSName="Windows";
499 if (navigator.appVersion.indexOf("Win")!=-1) OSName="Windows";
491 if (navigator.appVersion.indexOf("Mac")!=-1) OSName="MacOS";
500 if (navigator.appVersion.indexOf("Mac")!=-1) OSName="MacOS";
492 if (navigator.appVersion.indexOf("X11")!=-1) OSName="UNIX";
501 if (navigator.appVersion.indexOf("X11")!=-1) OSName="UNIX";
493 if (navigator.appVersion.indexOf("Linux")!=-1) OSName="Linux";
502 if (navigator.appVersion.indexOf("Linux")!=-1) OSName="Linux";
494 return OSName;
503 return OSName;
495 })();
504 })();
496
505
497 var is_or_has = function (a, b) {
506 var is_or_has = function (a, b) {
498 // Is b a child of a or a itself?
507 // Is b a child of a or a itself?
499 return a.has(b).length !==0 || a.is(b);
508 return a.has(b).length !==0 || a.is(b);
500 };
509 };
501
510
502 var is_focused = function (e) {
511 var is_focused = function (e) {
503 // Is element e, or one of its children focused?
512 // Is element e, or one of its children focused?
504 e = $(e);
513 e = $(e);
505 var target = $(document.activeElement);
514 var target = $(document.activeElement);
506 if (target.length > 0) {
515 if (target.length > 0) {
507 if (is_or_has(e, target)) {
516 if (is_or_has(e, target)) {
508 return true;
517 return true;
509 } else {
518 } else {
510 return false;
519 return false;
511 }
520 }
512 } else {
521 } else {
513 return false;
522 return false;
514 }
523 }
515 };
524 };
516
525
517 var mergeopt = function(_class, options, overwrite){
526 var mergeopt = function(_class, options, overwrite){
518 options = options || {};
527 options = options || {};
519 overwrite = overwrite || {};
528 overwrite = overwrite || {};
520 return $.extend(true, {}, _class.options_default, options, overwrite);
529 return $.extend(true, {}, _class.options_default, options, overwrite);
521 };
530 };
522
531
523 var ajax_error_msg = function (jqXHR) {
532 var ajax_error_msg = function (jqXHR) {
524 // Return a JSON error message if there is one,
533 // Return a JSON error message if there is one,
525 // otherwise the basic HTTP status text.
534 // otherwise the basic HTTP status text.
526 if (jqXHR.responseJSON && jqXHR.responseJSON.message) {
535 if (jqXHR.responseJSON && jqXHR.responseJSON.message) {
527 return jqXHR.responseJSON.message;
536 return jqXHR.responseJSON.message;
528 } else {
537 } else {
529 return jqXHR.statusText;
538 return jqXHR.statusText;
530 }
539 }
531 }
540 }
532 var log_ajax_error = function (jqXHR, status, error) {
541 var log_ajax_error = function (jqXHR, status, error) {
533 // log ajax failures with informative messages
542 // log ajax failures with informative messages
534 var msg = "API request failed (" + jqXHR.status + "): ";
543 var msg = "API request failed (" + jqXHR.status + "): ";
535 console.log(jqXHR);
544 console.log(jqXHR);
536 msg += ajax_error_msg(jqXHR);
545 msg += ajax_error_msg(jqXHR);
537 console.log(msg);
546 console.log(msg);
538 };
547 };
539
548
540 var utils = {
549 var utils = {
541 regex_split : regex_split,
550 regex_split : regex_split,
542 uuid : uuid,
551 uuid : uuid,
543 fixConsole : fixConsole,
552 fixConsole : fixConsole,
544 fixCarriageReturn : fixCarriageReturn,
553 fixCarriageReturn : fixCarriageReturn,
545 autoLinkUrls : autoLinkUrls,
554 autoLinkUrls : autoLinkUrls,
546 points_to_pixels : points_to_pixels,
555 points_to_pixels : points_to_pixels,
547 get_body_data : get_body_data,
556 get_body_data : get_body_data,
548 parse_url : parse_url,
557 parse_url : parse_url,
549 url_path_join : url_path_join,
558 url_path_join : url_path_join,
550 url_join_encode : url_join_encode,
559 url_join_encode : url_join_encode,
551 encode_uri_components : encode_uri_components,
560 encode_uri_components : encode_uri_components,
552 splitext : splitext,
561 splitext : splitext,
553 escape_html : escape_html,
562 escape_html : escape_html,
554 always_new : always_new,
563 always_new : always_new,
555 to_absolute_cursor_pos : to_absolute_cursor_pos,
564 to_absolute_cursor_pos : to_absolute_cursor_pos,
556 from_absolute_cursor_pos : from_absolute_cursor_pos,
565 from_absolute_cursor_pos : from_absolute_cursor_pos,
557 browser : browser,
566 browser : browser,
558 platform: platform,
567 platform: platform,
559 is_or_has : is_or_has,
568 is_or_has : is_or_has,
560 is_focused : is_focused,
569 is_focused : is_focused,
561 mergeopt: mergeopt,
570 mergeopt: mergeopt,
562 ajax_error_msg : ajax_error_msg,
571 ajax_error_msg : ajax_error_msg,
563 log_ajax_error : log_ajax_error,
572 log_ajax_error : log_ajax_error,
564 };
573 };
565
574
566 // Backwards compatability.
575 // Backwards compatability.
567 IPython.utils = utils;
576 IPython.utils = utils;
568
577
569 return utils;
578 return utils;
570 });
579 });
General Comments 0
You need to be logged in to leave comments. Login now