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