##// END OF EJS Templates
Merge pull request #7763 from ctaf42/cg/utils/get_body_data...
Min RK -
r20415:ff9803fb merge
parent child Browse files
Show More
@@ -1,884 +1,887 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 'codemirror/lib/codemirror',
7 'codemirror/lib/codemirror',
8 'moment',
8 'moment',
9 // silently upgrades CodeMirror
9 // silently upgrades CodeMirror
10 'codemirror/mode/meta',
10 'codemirror/mode/meta',
11 ], function(IPython, $, CodeMirror, moment){
11 ], function(IPython, $, CodeMirror, moment){
12 "use strict";
12 "use strict";
13
13
14 var load_extensions = function () {
14 var load_extensions = function () {
15 // load one or more IPython notebook extensions with requirejs
15 // load one or more IPython notebook extensions with requirejs
16
16
17 var extensions = [];
17 var extensions = [];
18 var extension_names = arguments;
18 var extension_names = arguments;
19 for (var i = 0; i < extension_names.length; i++) {
19 for (var i = 0; i < extension_names.length; i++) {
20 extensions.push("nbextensions/" + arguments[i]);
20 extensions.push("nbextensions/" + arguments[i]);
21 }
21 }
22
22
23 require(extensions,
23 require(extensions,
24 function () {
24 function () {
25 for (var i = 0; i < arguments.length; i++) {
25 for (var i = 0; i < arguments.length; i++) {
26 var ext = arguments[i];
26 var ext = arguments[i];
27 var ext_name = extension_names[i];
27 var ext_name = extension_names[i];
28 // success callback
28 // success callback
29 console.log("Loaded extension: " + ext_name);
29 console.log("Loaded extension: " + ext_name);
30 if (ext && ext.load_ipython_extension !== undefined) {
30 if (ext && ext.load_ipython_extension !== undefined) {
31 ext.load_ipython_extension();
31 ext.load_ipython_extension();
32 }
32 }
33 }
33 }
34 },
34 },
35 function (err) {
35 function (err) {
36 // failure callback
36 // failure callback
37 console.log("Failed to load extension(s):", err.requireModules, err);
37 console.log("Failed to load extension(s):", err.requireModules, err);
38 }
38 }
39 );
39 );
40 };
40 };
41
41
42 IPython.load_extensions = load_extensions;
42 IPython.load_extensions = load_extensions;
43
43
44 /**
44 /**
45 * Wait for a config section to load, and then load the extensions specified
45 * Wait for a config section to load, and then load the extensions specified
46 * in a 'load_extensions' key inside it.
46 * in a 'load_extensions' key inside it.
47 */
47 */
48 function load_extensions_from_config(section) {
48 function load_extensions_from_config(section) {
49 section.loaded.then(function() {
49 section.loaded.then(function() {
50 if (section.data.load_extensions) {
50 if (section.data.load_extensions) {
51 var nbextension_paths = Object.getOwnPropertyNames(
51 var nbextension_paths = Object.getOwnPropertyNames(
52 section.data.load_extensions);
52 section.data.load_extensions);
53 load_extensions.apply(this, nbextension_paths);
53 load_extensions.apply(this, nbextension_paths);
54 }
54 }
55 });
55 });
56 }
56 }
57
57
58 //============================================================================
58 //============================================================================
59 // Cross-browser RegEx Split
59 // Cross-browser RegEx Split
60 //============================================================================
60 //============================================================================
61
61
62 // This code has been MODIFIED from the code licensed below to not replace the
62 // This code has been MODIFIED from the code licensed below to not replace the
63 // default browser split. The license is reproduced here.
63 // default browser split. The license is reproduced here.
64
64
65 // see http://blog.stevenlevithan.com/archives/cross-browser-split for more info:
65 // see http://blog.stevenlevithan.com/archives/cross-browser-split for more info:
66 /*!
66 /*!
67 * Cross-Browser Split 1.1.1
67 * Cross-Browser Split 1.1.1
68 * Copyright 2007-2012 Steven Levithan <stevenlevithan.com>
68 * Copyright 2007-2012 Steven Levithan <stevenlevithan.com>
69 * Available under the MIT License
69 * Available under the MIT License
70 * ECMAScript compliant, uniform cross-browser split method
70 * ECMAScript compliant, uniform cross-browser split method
71 */
71 */
72
72
73 /**
73 /**
74 * Splits a string into an array of strings using a regex or string
74 * Splits a string into an array of strings using a regex or string
75 * separator. Matches of the separator are not included in the result array.
75 * separator. Matches of the separator are not included in the result array.
76 * However, if `separator` is a regex that contains capturing groups,
76 * However, if `separator` is a regex that contains capturing groups,
77 * backreferences are spliced into the result each time `separator` is
77 * backreferences are spliced into the result each time `separator` is
78 * matched. Fixes browser bugs compared to the native
78 * matched. Fixes browser bugs compared to the native
79 * `String.prototype.split` and can be used reliably cross-browser.
79 * `String.prototype.split` and can be used reliably cross-browser.
80 * @param {String} str String to split.
80 * @param {String} str String to split.
81 * @param {RegExp} separator Regex to use for separating
81 * @param {RegExp} separator Regex to use for separating
82 * the string.
82 * the string.
83 * @param {Number} [limit] Maximum number of items to include in the result
83 * @param {Number} [limit] Maximum number of items to include in the result
84 * array.
84 * array.
85 * @returns {Array} Array of substrings.
85 * @returns {Array} Array of substrings.
86 * @example
86 * @example
87 *
87 *
88 * // Basic use
88 * // Basic use
89 * regex_split('a b c d', ' ');
89 * regex_split('a b c d', ' ');
90 * // -> ['a', 'b', 'c', 'd']
90 * // -> ['a', 'b', 'c', 'd']
91 *
91 *
92 * // With limit
92 * // With limit
93 * regex_split('a b c d', ' ', 2);
93 * regex_split('a b c d', ' ', 2);
94 * // -> ['a', 'b']
94 * // -> ['a', 'b']
95 *
95 *
96 * // Backreferences in result array
96 * // Backreferences in result array
97 * regex_split('..word1 word2..', /([a-z]+)(\d+)/i);
97 * regex_split('..word1 word2..', /([a-z]+)(\d+)/i);
98 * // -> ['..', 'word', '1', ' ', 'word', '2', '..']
98 * // -> ['..', 'word', '1', ' ', 'word', '2', '..']
99 */
99 */
100 var regex_split = function (str, separator, limit) {
100 var regex_split = function (str, separator, limit) {
101 var output = [],
101 var output = [],
102 flags = (separator.ignoreCase ? "i" : "") +
102 flags = (separator.ignoreCase ? "i" : "") +
103 (separator.multiline ? "m" : "") +
103 (separator.multiline ? "m" : "") +
104 (separator.extended ? "x" : "") + // Proposed for ES6
104 (separator.extended ? "x" : "") + // Proposed for ES6
105 (separator.sticky ? "y" : ""), // Firefox 3+
105 (separator.sticky ? "y" : ""), // Firefox 3+
106 lastLastIndex = 0,
106 lastLastIndex = 0,
107 separator2, match, lastIndex, lastLength;
107 separator2, match, lastIndex, lastLength;
108 // Make `global` and avoid `lastIndex` issues by working with a copy
108 // Make `global` and avoid `lastIndex` issues by working with a copy
109 separator = new RegExp(separator.source, flags + "g");
109 separator = new RegExp(separator.source, flags + "g");
110
110
111 var compliantExecNpcg = typeof(/()??/.exec("")[1]) === "undefined";
111 var compliantExecNpcg = typeof(/()??/.exec("")[1]) === "undefined";
112 if (!compliantExecNpcg) {
112 if (!compliantExecNpcg) {
113 // Doesn't need flags gy, but they don't hurt
113 // Doesn't need flags gy, but they don't hurt
114 separator2 = new RegExp("^" + separator.source + "$(?!\\s)", flags);
114 separator2 = new RegExp("^" + separator.source + "$(?!\\s)", flags);
115 }
115 }
116 /* Values for `limit`, per the spec:
116 /* Values for `limit`, per the spec:
117 * If undefined: 4294967295 // Math.pow(2, 32) - 1
117 * If undefined: 4294967295 // Math.pow(2, 32) - 1
118 * If 0, Infinity, or NaN: 0
118 * If 0, Infinity, or NaN: 0
119 * If positive number: limit = Math.floor(limit); if (limit > 4294967295) limit -= 4294967296;
119 * If positive number: limit = Math.floor(limit); if (limit > 4294967295) limit -= 4294967296;
120 * If negative number: 4294967296 - Math.floor(Math.abs(limit))
120 * If negative number: 4294967296 - Math.floor(Math.abs(limit))
121 * If other: Type-convert, then use the above rules
121 * If other: Type-convert, then use the above rules
122 */
122 */
123 limit = typeof(limit) === "undefined" ?
123 limit = typeof(limit) === "undefined" ?
124 -1 >>> 0 : // Math.pow(2, 32) - 1
124 -1 >>> 0 : // Math.pow(2, 32) - 1
125 limit >>> 0; // ToUint32(limit)
125 limit >>> 0; // ToUint32(limit)
126 for (match = separator.exec(str); match; match = separator.exec(str)) {
126 for (match = separator.exec(str); match; match = separator.exec(str)) {
127 // `separator.lastIndex` is not reliable cross-browser
127 // `separator.lastIndex` is not reliable cross-browser
128 lastIndex = match.index + match[0].length;
128 lastIndex = match.index + match[0].length;
129 if (lastIndex > lastLastIndex) {
129 if (lastIndex > lastLastIndex) {
130 output.push(str.slice(lastLastIndex, match.index));
130 output.push(str.slice(lastLastIndex, match.index));
131 // Fix browsers whose `exec` methods don't consistently return `undefined` for
131 // Fix browsers whose `exec` methods don't consistently return `undefined` for
132 // nonparticipating capturing groups
132 // nonparticipating capturing groups
133 if (!compliantExecNpcg && match.length > 1) {
133 if (!compliantExecNpcg && match.length > 1) {
134 match[0].replace(separator2, function () {
134 match[0].replace(separator2, function () {
135 for (var i = 1; i < arguments.length - 2; i++) {
135 for (var i = 1; i < arguments.length - 2; i++) {
136 if (typeof(arguments[i]) === "undefined") {
136 if (typeof(arguments[i]) === "undefined") {
137 match[i] = undefined;
137 match[i] = undefined;
138 }
138 }
139 }
139 }
140 });
140 });
141 }
141 }
142 if (match.length > 1 && match.index < str.length) {
142 if (match.length > 1 && match.index < str.length) {
143 Array.prototype.push.apply(output, match.slice(1));
143 Array.prototype.push.apply(output, match.slice(1));
144 }
144 }
145 lastLength = match[0].length;
145 lastLength = match[0].length;
146 lastLastIndex = lastIndex;
146 lastLastIndex = lastIndex;
147 if (output.length >= limit) {
147 if (output.length >= limit) {
148 break;
148 break;
149 }
149 }
150 }
150 }
151 if (separator.lastIndex === match.index) {
151 if (separator.lastIndex === match.index) {
152 separator.lastIndex++; // Avoid an infinite loop
152 separator.lastIndex++; // Avoid an infinite loop
153 }
153 }
154 }
154 }
155 if (lastLastIndex === str.length) {
155 if (lastLastIndex === str.length) {
156 if (lastLength || !separator.test("")) {
156 if (lastLength || !separator.test("")) {
157 output.push("");
157 output.push("");
158 }
158 }
159 } else {
159 } else {
160 output.push(str.slice(lastLastIndex));
160 output.push(str.slice(lastLastIndex));
161 }
161 }
162 return output.length > limit ? output.slice(0, limit) : output;
162 return output.length > limit ? output.slice(0, limit) : output;
163 };
163 };
164
164
165 //============================================================================
165 //============================================================================
166 // End contributed Cross-browser RegEx Split
166 // End contributed Cross-browser RegEx Split
167 //============================================================================
167 //============================================================================
168
168
169
169
170 var uuid = function () {
170 var uuid = function () {
171 /**
171 /**
172 * http://www.ietf.org/rfc/rfc4122.txt
172 * http://www.ietf.org/rfc/rfc4122.txt
173 */
173 */
174 var s = [];
174 var s = [];
175 var hexDigits = "0123456789ABCDEF";
175 var hexDigits = "0123456789ABCDEF";
176 for (var i = 0; i < 32; i++) {
176 for (var i = 0; i < 32; i++) {
177 s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
177 s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
178 }
178 }
179 s[12] = "4"; // bits 12-15 of the time_hi_and_version field to 0010
179 s[12] = "4"; // bits 12-15 of the time_hi_and_version field to 0010
180 s[16] = hexDigits.substr((s[16] & 0x3) | 0x8, 1); // bits 6-7 of the clock_seq_hi_and_reserved to 01
180 s[16] = hexDigits.substr((s[16] & 0x3) | 0x8, 1); // bits 6-7 of the clock_seq_hi_and_reserved to 01
181
181
182 var uuid = s.join("");
182 var uuid = s.join("");
183 return uuid;
183 return uuid;
184 };
184 };
185
185
186
186
187 //Fix raw text to parse correctly in crazy XML
187 //Fix raw text to parse correctly in crazy XML
188 function xmlencode(string) {
188 function xmlencode(string) {
189 return string.replace(/\&/g,'&'+'amp;')
189 return string.replace(/\&/g,'&'+'amp;')
190 .replace(/</g,'&'+'lt;')
190 .replace(/</g,'&'+'lt;')
191 .replace(/>/g,'&'+'gt;')
191 .replace(/>/g,'&'+'gt;')
192 .replace(/\'/g,'&'+'apos;')
192 .replace(/\'/g,'&'+'apos;')
193 .replace(/\"/g,'&'+'quot;')
193 .replace(/\"/g,'&'+'quot;')
194 .replace(/`/g,'&'+'#96;');
194 .replace(/`/g,'&'+'#96;');
195 }
195 }
196
196
197
197
198 //Map from terminal commands to CSS classes
198 //Map from terminal commands to CSS classes
199 var ansi_colormap = {
199 var ansi_colormap = {
200 "01":"ansibold",
200 "01":"ansibold",
201
201
202 "30":"ansiblack",
202 "30":"ansiblack",
203 "31":"ansired",
203 "31":"ansired",
204 "32":"ansigreen",
204 "32":"ansigreen",
205 "33":"ansiyellow",
205 "33":"ansiyellow",
206 "34":"ansiblue",
206 "34":"ansiblue",
207 "35":"ansipurple",
207 "35":"ansipurple",
208 "36":"ansicyan",
208 "36":"ansicyan",
209 "37":"ansigray",
209 "37":"ansigray",
210
210
211 "40":"ansibgblack",
211 "40":"ansibgblack",
212 "41":"ansibgred",
212 "41":"ansibgred",
213 "42":"ansibggreen",
213 "42":"ansibggreen",
214 "43":"ansibgyellow",
214 "43":"ansibgyellow",
215 "44":"ansibgblue",
215 "44":"ansibgblue",
216 "45":"ansibgpurple",
216 "45":"ansibgpurple",
217 "46":"ansibgcyan",
217 "46":"ansibgcyan",
218 "47":"ansibggray"
218 "47":"ansibggray"
219 };
219 };
220
220
221 function _process_numbers(attrs, numbers) {
221 function _process_numbers(attrs, numbers) {
222 // process ansi escapes
222 // process ansi escapes
223 var n = numbers.shift();
223 var n = numbers.shift();
224 if (ansi_colormap[n]) {
224 if (ansi_colormap[n]) {
225 if ( ! attrs["class"] ) {
225 if ( ! attrs["class"] ) {
226 attrs["class"] = ansi_colormap[n];
226 attrs["class"] = ansi_colormap[n];
227 } else {
227 } else {
228 attrs["class"] += " " + ansi_colormap[n];
228 attrs["class"] += " " + ansi_colormap[n];
229 }
229 }
230 } else if (n == "38" || n == "48") {
230 } else if (n == "38" || n == "48") {
231 // VT100 256 color or 24 bit RGB
231 // VT100 256 color or 24 bit RGB
232 if (numbers.length < 2) {
232 if (numbers.length < 2) {
233 console.log("Not enough fields for VT100 color", numbers);
233 console.log("Not enough fields for VT100 color", numbers);
234 return;
234 return;
235 }
235 }
236
236
237 var index_or_rgb = numbers.shift();
237 var index_or_rgb = numbers.shift();
238 var r,g,b;
238 var r,g,b;
239 if (index_or_rgb == "5") {
239 if (index_or_rgb == "5") {
240 // 256 color
240 // 256 color
241 var idx = parseInt(numbers.shift(), 10);
241 var idx = parseInt(numbers.shift(), 10);
242 if (idx < 16) {
242 if (idx < 16) {
243 // indexed ANSI
243 // indexed ANSI
244 // ignore bright / non-bright distinction
244 // ignore bright / non-bright distinction
245 idx = idx % 8;
245 idx = idx % 8;
246 var ansiclass = ansi_colormap[n[0] + (idx % 8).toString()];
246 var ansiclass = ansi_colormap[n[0] + (idx % 8).toString()];
247 if ( ! attrs["class"] ) {
247 if ( ! attrs["class"] ) {
248 attrs["class"] = ansiclass;
248 attrs["class"] = ansiclass;
249 } else {
249 } else {
250 attrs["class"] += " " + ansiclass;
250 attrs["class"] += " " + ansiclass;
251 }
251 }
252 return;
252 return;
253 } else if (idx < 232) {
253 } else if (idx < 232) {
254 // 216 color 6x6x6 RGB
254 // 216 color 6x6x6 RGB
255 idx = idx - 16;
255 idx = idx - 16;
256 b = idx % 6;
256 b = idx % 6;
257 g = Math.floor(idx / 6) % 6;
257 g = Math.floor(idx / 6) % 6;
258 r = Math.floor(idx / 36) % 6;
258 r = Math.floor(idx / 36) % 6;
259 // convert to rgb
259 // convert to rgb
260 r = (r * 51);
260 r = (r * 51);
261 g = (g * 51);
261 g = (g * 51);
262 b = (b * 51);
262 b = (b * 51);
263 } else {
263 } else {
264 // grayscale
264 // grayscale
265 idx = idx - 231;
265 idx = idx - 231;
266 // it's 1-24 and should *not* include black or white,
266 // it's 1-24 and should *not* include black or white,
267 // so a 26 point scale
267 // so a 26 point scale
268 r = g = b = Math.floor(idx * 256 / 26);
268 r = g = b = Math.floor(idx * 256 / 26);
269 }
269 }
270 } else if (index_or_rgb == "2") {
270 } else if (index_or_rgb == "2") {
271 // Simple 24 bit RGB
271 // Simple 24 bit RGB
272 if (numbers.length > 3) {
272 if (numbers.length > 3) {
273 console.log("Not enough fields for RGB", numbers);
273 console.log("Not enough fields for RGB", numbers);
274 return;
274 return;
275 }
275 }
276 r = numbers.shift();
276 r = numbers.shift();
277 g = numbers.shift();
277 g = numbers.shift();
278 b = numbers.shift();
278 b = numbers.shift();
279 } else {
279 } else {
280 console.log("unrecognized control", numbers);
280 console.log("unrecognized control", numbers);
281 return;
281 return;
282 }
282 }
283 if (r !== undefined) {
283 if (r !== undefined) {
284 // apply the rgb color
284 // apply the rgb color
285 var line;
285 var line;
286 if (n == "38") {
286 if (n == "38") {
287 line = "color: ";
287 line = "color: ";
288 } else {
288 } else {
289 line = "background-color: ";
289 line = "background-color: ";
290 }
290 }
291 line = line + "rgb(" + r + "," + g + "," + b + ");";
291 line = line + "rgb(" + r + "," + g + "," + b + ");";
292 if ( !attrs.style ) {
292 if ( !attrs.style ) {
293 attrs.style = line;
293 attrs.style = line;
294 } else {
294 } else {
295 attrs.style += " " + line;
295 attrs.style += " " + line;
296 }
296 }
297 }
297 }
298 }
298 }
299 }
299 }
300
300
301 function ansispan(str) {
301 function ansispan(str) {
302 // ansispan function adapted from github.com/mmalecki/ansispan (MIT License)
302 // ansispan function adapted from github.com/mmalecki/ansispan (MIT License)
303 // regular ansi escapes (using the table above)
303 // regular ansi escapes (using the table above)
304 var is_open = false;
304 var is_open = false;
305 return str.replace(/\033\[(0?[01]|22|39)?([;\d]+)?m/g, function(match, prefix, pattern) {
305 return str.replace(/\033\[(0?[01]|22|39)?([;\d]+)?m/g, function(match, prefix, pattern) {
306 if (!pattern) {
306 if (!pattern) {
307 // [(01|22|39|)m close spans
307 // [(01|22|39|)m close spans
308 if (is_open) {
308 if (is_open) {
309 is_open = false;
309 is_open = false;
310 return "</span>";
310 return "</span>";
311 } else {
311 } else {
312 return "";
312 return "";
313 }
313 }
314 } else {
314 } else {
315 is_open = true;
315 is_open = true;
316
316
317 // consume sequence of color escapes
317 // consume sequence of color escapes
318 var numbers = pattern.match(/\d+/g);
318 var numbers = pattern.match(/\d+/g);
319 var attrs = {};
319 var attrs = {};
320 while (numbers.length > 0) {
320 while (numbers.length > 0) {
321 _process_numbers(attrs, numbers);
321 _process_numbers(attrs, numbers);
322 }
322 }
323
323
324 var span = "<span ";
324 var span = "<span ";
325 Object.keys(attrs).map(function (attr) {
325 Object.keys(attrs).map(function (attr) {
326 span = span + " " + attr + '="' + attrs[attr] + '"';
326 span = span + " " + attr + '="' + attrs[attr] + '"';
327 });
327 });
328 return span + ">";
328 return span + ">";
329 }
329 }
330 });
330 });
331 }
331 }
332
332
333 // Transform ANSI color escape codes into HTML <span> tags with css
333 // Transform ANSI color escape codes into HTML <span> tags with css
334 // classes listed in the above ansi_colormap object. The actual color used
334 // classes listed in the above ansi_colormap object. The actual color used
335 // are set in the css file.
335 // are set in the css file.
336 function fixConsole(txt) {
336 function fixConsole(txt) {
337 txt = xmlencode(txt);
337 txt = xmlencode(txt);
338
338
339 // Strip all ANSI codes that are not color related. Matches
339 // Strip all ANSI codes that are not color related. Matches
340 // all ANSI codes that do not end with "m".
340 // all ANSI codes that do not end with "m".
341 var ignored_re = /(?=(\033\[[\d;=]*[a-ln-zA-Z]{1}))\1(?!m)/g;
341 var ignored_re = /(?=(\033\[[\d;=]*[a-ln-zA-Z]{1}))\1(?!m)/g;
342 txt = txt.replace(ignored_re, "");
342 txt = txt.replace(ignored_re, "");
343
343
344 // color ansi codes
344 // color ansi codes
345 txt = ansispan(txt);
345 txt = ansispan(txt);
346 return txt;
346 return txt;
347 }
347 }
348
348
349 // Remove chunks that should be overridden by the effect of
349 // Remove chunks that should be overridden by the effect of
350 // carriage return characters
350 // carriage return characters
351 function fixCarriageReturn(txt) {
351 function fixCarriageReturn(txt) {
352 var tmp = txt;
352 var tmp = txt;
353 do {
353 do {
354 txt = tmp;
354 txt = tmp;
355 tmp = txt.replace(/\r+\n/gm, '\n'); // \r followed by \n --> newline
355 tmp = txt.replace(/\r+\n/gm, '\n'); // \r followed by \n --> newline
356 tmp = tmp.replace(/^.*\r+/gm, ''); // Other \r --> clear line
356 tmp = tmp.replace(/^.*\r+/gm, ''); // Other \r --> clear line
357 } while (tmp.length < txt.length);
357 } while (tmp.length < txt.length);
358 return txt;
358 return txt;
359 }
359 }
360
360
361 // Locate any URLs and convert them to a anchor tag
361 // Locate any URLs and convert them to a anchor tag
362 function autoLinkUrls(txt) {
362 function autoLinkUrls(txt) {
363 return txt.replace(/(^|\s)(https?|ftp)(:[^'">\s]+)/gi,
363 return txt.replace(/(^|\s)(https?|ftp)(:[^'">\s]+)/gi,
364 "$1<a target=\"_blank\" href=\"$2$3\">$2$3</a>");
364 "$1<a target=\"_blank\" href=\"$2$3\">$2$3</a>");
365 }
365 }
366
366
367 var points_to_pixels = function (points) {
367 var points_to_pixels = function (points) {
368 /**
368 /**
369 * A reasonably good way of converting between points and pixels.
369 * A reasonably good way of converting between points and pixels.
370 */
370 */
371 var test = $('<div style="display: none; width: 10000pt; padding:0; border:0;"></div>');
371 var test = $('<div style="display: none; width: 10000pt; padding:0; border:0;"></div>');
372 $('body').append(test);
372 $('body').append(test);
373 var pixel_per_point = test.width()/10000;
373 var pixel_per_point = test.width()/10000;
374 test.remove();
374 test.remove();
375 return Math.floor(points*pixel_per_point);
375 return Math.floor(points*pixel_per_point);
376 };
376 };
377
377
378 var always_new = function (constructor) {
378 var always_new = function (constructor) {
379 /**
379 /**
380 * wrapper around contructor to avoid requiring `var a = new constructor()`
380 * wrapper around contructor to avoid requiring `var a = new constructor()`
381 * useful for passing constructors as callbacks,
381 * useful for passing constructors as callbacks,
382 * not for programmer laziness.
382 * not for programmer laziness.
383 * from http://programmers.stackexchange.com/questions/118798
383 * from http://programmers.stackexchange.com/questions/118798
384 */
384 */
385 return function () {
385 return function () {
386 var obj = Object.create(constructor.prototype);
386 var obj = Object.create(constructor.prototype);
387 constructor.apply(obj, arguments);
387 constructor.apply(obj, arguments);
388 return obj;
388 return obj;
389 };
389 };
390 };
390 };
391
391
392 var url_path_join = function () {
392 var url_path_join = function () {
393 /**
393 /**
394 * join a sequence of url components with '/'
394 * join a sequence of url components with '/'
395 */
395 */
396 var url = '';
396 var url = '';
397 for (var i = 0; i < arguments.length; i++) {
397 for (var i = 0; i < arguments.length; i++) {
398 if (arguments[i] === '') {
398 if (arguments[i] === '') {
399 continue;
399 continue;
400 }
400 }
401 if (url.length > 0 && url[url.length-1] != '/') {
401 if (url.length > 0 && url[url.length-1] != '/') {
402 url = url + '/' + arguments[i];
402 url = url + '/' + arguments[i];
403 } else {
403 } else {
404 url = url + arguments[i];
404 url = url + arguments[i];
405 }
405 }
406 }
406 }
407 url = url.replace(/\/\/+/, '/');
407 url = url.replace(/\/\/+/, '/');
408 return url;
408 return url;
409 };
409 };
410
410
411 var url_path_split = function (path) {
411 var url_path_split = function (path) {
412 /**
412 /**
413 * Like os.path.split for URLs.
413 * Like os.path.split for URLs.
414 * Always returns two strings, the directory path and the base filename
414 * Always returns two strings, the directory path and the base filename
415 */
415 */
416
416
417 var idx = path.lastIndexOf('/');
417 var idx = path.lastIndexOf('/');
418 if (idx === -1) {
418 if (idx === -1) {
419 return ['', path];
419 return ['', path];
420 } else {
420 } else {
421 return [ path.slice(0, idx), path.slice(idx + 1) ];
421 return [ path.slice(0, idx), path.slice(idx + 1) ];
422 }
422 }
423 };
423 };
424
424
425 var parse_url = function (url) {
425 var parse_url = function (url) {
426 /**
426 /**
427 * an `a` element with an href allows attr-access to the parsed segments of a URL
427 * an `a` element with an href allows attr-access to the parsed segments of a URL
428 * a = parse_url("http://localhost:8888/path/name#hash")
428 * a = parse_url("http://localhost:8888/path/name#hash")
429 * a.protocol = "http:"
429 * a.protocol = "http:"
430 * a.host = "localhost:8888"
430 * a.host = "localhost:8888"
431 * a.hostname = "localhost"
431 * a.hostname = "localhost"
432 * a.port = 8888
432 * a.port = 8888
433 * a.pathname = "/path/name"
433 * a.pathname = "/path/name"
434 * a.hash = "#hash"
434 * a.hash = "#hash"
435 */
435 */
436 var a = document.createElement("a");
436 var a = document.createElement("a");
437 a.href = url;
437 a.href = url;
438 return a;
438 return a;
439 };
439 };
440
440
441 var encode_uri_components = function (uri) {
441 var encode_uri_components = function (uri) {
442 /**
442 /**
443 * encode just the components of a multi-segment uri,
443 * encode just the components of a multi-segment uri,
444 * leaving '/' separators
444 * leaving '/' separators
445 */
445 */
446 return uri.split('/').map(encodeURIComponent).join('/');
446 return uri.split('/').map(encodeURIComponent).join('/');
447 };
447 };
448
448
449 var url_join_encode = function () {
449 var url_join_encode = function () {
450 /**
450 /**
451 * join a sequence of url components with '/',
451 * join a sequence of url components with '/',
452 * encoding each component with encodeURIComponent
452 * encoding each component with encodeURIComponent
453 */
453 */
454 return encode_uri_components(url_path_join.apply(null, arguments));
454 return encode_uri_components(url_path_join.apply(null, arguments));
455 };
455 };
456
456
457
457
458 var splitext = function (filename) {
458 var splitext = function (filename) {
459 /**
459 /**
460 * mimic Python os.path.splitext
460 * mimic Python os.path.splitext
461 * Returns ['base', '.ext']
461 * Returns ['base', '.ext']
462 */
462 */
463 var idx = filename.lastIndexOf('.');
463 var idx = filename.lastIndexOf('.');
464 if (idx > 0) {
464 if (idx > 0) {
465 return [filename.slice(0, idx), filename.slice(idx)];
465 return [filename.slice(0, idx), filename.slice(idx)];
466 } else {
466 } else {
467 return [filename, ''];
467 return [filename, ''];
468 }
468 }
469 };
469 };
470
470
471
471
472 var escape_html = function (text) {
472 var escape_html = function (text) {
473 /**
473 /**
474 * escape text to HTML
474 * escape text to HTML
475 */
475 */
476 return $("<div/>").text(text).html();
476 return $("<div/>").text(text).html();
477 };
477 };
478
478
479
479
480 var get_body_data = function(key) {
480 var get_body_data = function(key) {
481 /**
481 /**
482 * get a url-encoded item from body.data and decode it
482 * get a url-encoded item from body.data and decode it
483 * we should never have any encoded URLs anywhere else in code
483 * we should never have any encoded URLs anywhere else in code
484 * until we are building an actual request
484 * until we are building an actual request
485 */
485 */
486 return decodeURIComponent($('body').data(key));
486 var val = $('body').data(key);
487 if (!val)
488 return val;
489 return decodeURIComponent(val);
487 };
490 };
488
491
489 var to_absolute_cursor_pos = function (cm, cursor) {
492 var to_absolute_cursor_pos = function (cm, cursor) {
490 /**
493 /**
491 * get the absolute cursor position from CodeMirror's col, ch
494 * get the absolute cursor position from CodeMirror's col, ch
492 */
495 */
493 if (!cursor) {
496 if (!cursor) {
494 cursor = cm.getCursor();
497 cursor = cm.getCursor();
495 }
498 }
496 var cursor_pos = cursor.ch;
499 var cursor_pos = cursor.ch;
497 for (var i = 0; i < cursor.line; i++) {
500 for (var i = 0; i < cursor.line; i++) {
498 cursor_pos += cm.getLine(i).length + 1;
501 cursor_pos += cm.getLine(i).length + 1;
499 }
502 }
500 return cursor_pos;
503 return cursor_pos;
501 };
504 };
502
505
503 var from_absolute_cursor_pos = function (cm, cursor_pos) {
506 var from_absolute_cursor_pos = function (cm, cursor_pos) {
504 /**
507 /**
505 * turn absolute cursor postion into CodeMirror col, ch cursor
508 * turn absolute cursor postion into CodeMirror col, ch cursor
506 */
509 */
507 var i, line;
510 var i, line;
508 var offset = 0;
511 var offset = 0;
509 for (i = 0, line=cm.getLine(i); line !== undefined; i++, line=cm.getLine(i)) {
512 for (i = 0, line=cm.getLine(i); line !== undefined; i++, line=cm.getLine(i)) {
510 if (offset + line.length < cursor_pos) {
513 if (offset + line.length < cursor_pos) {
511 offset += line.length + 1;
514 offset += line.length + 1;
512 } else {
515 } else {
513 return {
516 return {
514 line : i,
517 line : i,
515 ch : cursor_pos - offset,
518 ch : cursor_pos - offset,
516 };
519 };
517 }
520 }
518 }
521 }
519 // reached end, return endpoint
522 // reached end, return endpoint
520 return {
523 return {
521 ch : line.length - 1,
524 ch : line.length - 1,
522 line : i - 1,
525 line : i - 1,
523 };
526 };
524 };
527 };
525
528
526 // http://stackoverflow.com/questions/2400935/browser-detection-in-javascript
529 // http://stackoverflow.com/questions/2400935/browser-detection-in-javascript
527 var browser = (function() {
530 var browser = (function() {
528 if (typeof navigator === 'undefined') {
531 if (typeof navigator === 'undefined') {
529 // navigator undefined in node
532 // navigator undefined in node
530 return 'None';
533 return 'None';
531 }
534 }
532 var N= navigator.appName, ua= navigator.userAgent, tem;
535 var N= navigator.appName, ua= navigator.userAgent, tem;
533 var M= ua.match(/(opera|chrome|safari|firefox|msie)\/?\s*(\.?\d+(\.\d+)*)/i);
536 var M= ua.match(/(opera|chrome|safari|firefox|msie)\/?\s*(\.?\d+(\.\d+)*)/i);
534 if (M && (tem= ua.match(/version\/([\.\d]+)/i)) !== null) M[2]= tem[1];
537 if (M && (tem= ua.match(/version\/([\.\d]+)/i)) !== null) M[2]= tem[1];
535 M= M? [M[1], M[2]]: [N, navigator.appVersion,'-?'];
538 M= M? [M[1], M[2]]: [N, navigator.appVersion,'-?'];
536 return M;
539 return M;
537 })();
540 })();
538
541
539 // http://stackoverflow.com/questions/11219582/how-to-detect-my-browser-version-and-operating-system-using-javascript
542 // http://stackoverflow.com/questions/11219582/how-to-detect-my-browser-version-and-operating-system-using-javascript
540 var platform = (function () {
543 var platform = (function () {
541 if (typeof navigator === 'undefined') {
544 if (typeof navigator === 'undefined') {
542 // navigator undefined in node
545 // navigator undefined in node
543 return 'None';
546 return 'None';
544 }
547 }
545 var OSName="None";
548 var OSName="None";
546 if (navigator.appVersion.indexOf("Win")!=-1) OSName="Windows";
549 if (navigator.appVersion.indexOf("Win")!=-1) OSName="Windows";
547 if (navigator.appVersion.indexOf("Mac")!=-1) OSName="MacOS";
550 if (navigator.appVersion.indexOf("Mac")!=-1) OSName="MacOS";
548 if (navigator.appVersion.indexOf("X11")!=-1) OSName="UNIX";
551 if (navigator.appVersion.indexOf("X11")!=-1) OSName="UNIX";
549 if (navigator.appVersion.indexOf("Linux")!=-1) OSName="Linux";
552 if (navigator.appVersion.indexOf("Linux")!=-1) OSName="Linux";
550 return OSName;
553 return OSName;
551 })();
554 })();
552
555
553 var get_url_param = function (name) {
556 var get_url_param = function (name) {
554 // get a URL parameter. I cannot believe we actually need this.
557 // get a URL parameter. I cannot believe we actually need this.
555 // Based on http://stackoverflow.com/a/25359264/938949
558 // Based on http://stackoverflow.com/a/25359264/938949
556 var match = new RegExp('[?&]' + name + '=([^&]*)').exec(window.location.search);
559 var match = new RegExp('[?&]' + name + '=([^&]*)').exec(window.location.search);
557 if (match){
560 if (match){
558 return decodeURIComponent(match[1] || '');
561 return decodeURIComponent(match[1] || '');
559 }
562 }
560 };
563 };
561
564
562 var is_or_has = function (a, b) {
565 var is_or_has = function (a, b) {
563 /**
566 /**
564 * Is b a child of a or a itself?
567 * Is b a child of a or a itself?
565 */
568 */
566 return a.has(b).length !==0 || a.is(b);
569 return a.has(b).length !==0 || a.is(b);
567 };
570 };
568
571
569 var is_focused = function (e) {
572 var is_focused = function (e) {
570 /**
573 /**
571 * Is element e, or one of its children focused?
574 * Is element e, or one of its children focused?
572 */
575 */
573 e = $(e);
576 e = $(e);
574 var target = $(document.activeElement);
577 var target = $(document.activeElement);
575 if (target.length > 0) {
578 if (target.length > 0) {
576 if (is_or_has(e, target)) {
579 if (is_or_has(e, target)) {
577 return true;
580 return true;
578 } else {
581 } else {
579 return false;
582 return false;
580 }
583 }
581 } else {
584 } else {
582 return false;
585 return false;
583 }
586 }
584 };
587 };
585
588
586 var mergeopt = function(_class, options, overwrite){
589 var mergeopt = function(_class, options, overwrite){
587 options = options || {};
590 options = options || {};
588 overwrite = overwrite || {};
591 overwrite = overwrite || {};
589 return $.extend(true, {}, _class.options_default, options, overwrite);
592 return $.extend(true, {}, _class.options_default, options, overwrite);
590 };
593 };
591
594
592 var ajax_error_msg = function (jqXHR) {
595 var ajax_error_msg = function (jqXHR) {
593 /**
596 /**
594 * Return a JSON error message if there is one,
597 * Return a JSON error message if there is one,
595 * otherwise the basic HTTP status text.
598 * otherwise the basic HTTP status text.
596 */
599 */
597 if (jqXHR.responseJSON && jqXHR.responseJSON.traceback) {
600 if (jqXHR.responseJSON && jqXHR.responseJSON.traceback) {
598 return jqXHR.responseJSON.traceback;
601 return jqXHR.responseJSON.traceback;
599 } else if (jqXHR.responseJSON && jqXHR.responseJSON.message) {
602 } else if (jqXHR.responseJSON && jqXHR.responseJSON.message) {
600 return jqXHR.responseJSON.message;
603 return jqXHR.responseJSON.message;
601 } else {
604 } else {
602 return jqXHR.statusText;
605 return jqXHR.statusText;
603 }
606 }
604 };
607 };
605 var log_ajax_error = function (jqXHR, status, error) {
608 var log_ajax_error = function (jqXHR, status, error) {
606 /**
609 /**
607 * log ajax failures with informative messages
610 * log ajax failures with informative messages
608 */
611 */
609 var msg = "API request failed (" + jqXHR.status + "): ";
612 var msg = "API request failed (" + jqXHR.status + "): ";
610 console.log(jqXHR);
613 console.log(jqXHR);
611 msg += ajax_error_msg(jqXHR);
614 msg += ajax_error_msg(jqXHR);
612 console.log(msg);
615 console.log(msg);
613 };
616 };
614
617
615 var requireCodeMirrorMode = function (mode, callback, errback) {
618 var requireCodeMirrorMode = function (mode, callback, errback) {
616 /**
619 /**
617 * find a predefined mode or detect from CM metadata then
620 * find a predefined mode or detect from CM metadata then
618 * require and callback with the resolveable mode string: mime or
621 * require and callback with the resolveable mode string: mime or
619 * custom name
622 * custom name
620 */
623 */
621
624
622 var modename = (typeof mode == "string") ? mode :
625 var modename = (typeof mode == "string") ? mode :
623 mode.mode || mode.name;
626 mode.mode || mode.name;
624
627
625 // simplest, cheapest check by mode name: mode may also have config
628 // simplest, cheapest check by mode name: mode may also have config
626 if (CodeMirror.modes.hasOwnProperty(modename)) {
629 if (CodeMirror.modes.hasOwnProperty(modename)) {
627 // return the full mode object, if it has a name
630 // return the full mode object, if it has a name
628 callback(mode.name ? mode : modename);
631 callback(mode.name ? mode : modename);
629 return;
632 return;
630 }
633 }
631
634
632 // *somehow* get back a CM.modeInfo-like object that has .mode and
635 // *somehow* get back a CM.modeInfo-like object that has .mode and
633 // .mime
636 // .mime
634 var info = (mode && mode.mode && mode.mime && mode) ||
637 var info = (mode && mode.mode && mode.mime && mode) ||
635 CodeMirror.findModeByName(modename) ||
638 CodeMirror.findModeByName(modename) ||
636 CodeMirror.findModeByExtension(modename.split(".").slice(-1)) ||
639 CodeMirror.findModeByExtension(modename.split(".").slice(-1)) ||
637 CodeMirror.findModeByMIME(modename) ||
640 CodeMirror.findModeByMIME(modename) ||
638 {mode: modename, mime: modename};
641 {mode: modename, mime: modename};
639
642
640 require([
643 require([
641 // might want to use CodeMirror.modeURL here
644 // might want to use CodeMirror.modeURL here
642 ['codemirror/mode', info.mode, info.mode].join('/'),
645 ['codemirror/mode', info.mode, info.mode].join('/'),
643 ], function() {
646 ], function() {
644 // return the original mode, as from a kernelspec on first load
647 // return the original mode, as from a kernelspec on first load
645 // or the mimetype, as for most highlighting
648 // or the mimetype, as for most highlighting
646 callback(mode.name ? mode : info.mime);
649 callback(mode.name ? mode : info.mime);
647 }, errback
650 }, errback
648 );
651 );
649 };
652 };
650
653
651 /** Error type for wrapped XHR errors. */
654 /** Error type for wrapped XHR errors. */
652 var XHR_ERROR = 'XhrError';
655 var XHR_ERROR = 'XhrError';
653
656
654 /**
657 /**
655 * Wraps an AJAX error as an Error object.
658 * Wraps an AJAX error as an Error object.
656 */
659 */
657 var wrap_ajax_error = function (jqXHR, status, error) {
660 var wrap_ajax_error = function (jqXHR, status, error) {
658 var wrapped_error = new Error(ajax_error_msg(jqXHR));
661 var wrapped_error = new Error(ajax_error_msg(jqXHR));
659 wrapped_error.name = XHR_ERROR;
662 wrapped_error.name = XHR_ERROR;
660 // provide xhr response
663 // provide xhr response
661 wrapped_error.xhr = jqXHR;
664 wrapped_error.xhr = jqXHR;
662 wrapped_error.xhr_status = status;
665 wrapped_error.xhr_status = status;
663 wrapped_error.xhr_error = error;
666 wrapped_error.xhr_error = error;
664 return wrapped_error;
667 return wrapped_error;
665 };
668 };
666
669
667 var promising_ajax = function(url, settings) {
670 var promising_ajax = function(url, settings) {
668 /**
671 /**
669 * Like $.ajax, but returning an ES6 promise. success and error settings
672 * Like $.ajax, but returning an ES6 promise. success and error settings
670 * will be ignored.
673 * will be ignored.
671 */
674 */
672 settings = settings || {};
675 settings = settings || {};
673 return new Promise(function(resolve, reject) {
676 return new Promise(function(resolve, reject) {
674 settings.success = function(data, status, jqXHR) {
677 settings.success = function(data, status, jqXHR) {
675 resolve(data);
678 resolve(data);
676 };
679 };
677 settings.error = function(jqXHR, status, error) {
680 settings.error = function(jqXHR, status, error) {
678 log_ajax_error(jqXHR, status, error);
681 log_ajax_error(jqXHR, status, error);
679 reject(wrap_ajax_error(jqXHR, status, error));
682 reject(wrap_ajax_error(jqXHR, status, error));
680 };
683 };
681 $.ajax(url, settings);
684 $.ajax(url, settings);
682 });
685 });
683 };
686 };
684
687
685 var WrappedError = function(message, error){
688 var WrappedError = function(message, error){
686 /**
689 /**
687 * Wrappable Error class
690 * Wrappable Error class
688 *
691 *
689 * The Error class doesn't actually act on `this`. Instead it always
692 * The Error class doesn't actually act on `this`. Instead it always
690 * returns a new instance of Error. Here we capture that instance so we
693 * returns a new instance of Error. Here we capture that instance so we
691 * can apply it's properties to `this`.
694 * can apply it's properties to `this`.
692 */
695 */
693 var tmp = Error.apply(this, [message]);
696 var tmp = Error.apply(this, [message]);
694
697
695 // Copy the properties of the error over to this.
698 // Copy the properties of the error over to this.
696 var properties = Object.getOwnPropertyNames(tmp);
699 var properties = Object.getOwnPropertyNames(tmp);
697 for (var i = 0; i < properties.length; i++) {
700 for (var i = 0; i < properties.length; i++) {
698 this[properties[i]] = tmp[properties[i]];
701 this[properties[i]] = tmp[properties[i]];
699 }
702 }
700
703
701 // Keep a stack of the original error messages.
704 // Keep a stack of the original error messages.
702 if (error instanceof WrappedError) {
705 if (error instanceof WrappedError) {
703 this.error_stack = error.error_stack;
706 this.error_stack = error.error_stack;
704 } else {
707 } else {
705 this.error_stack = [error];
708 this.error_stack = [error];
706 }
709 }
707 this.error_stack.push(tmp);
710 this.error_stack.push(tmp);
708
711
709 return this;
712 return this;
710 };
713 };
711
714
712 WrappedError.prototype = Object.create(Error.prototype, {});
715 WrappedError.prototype = Object.create(Error.prototype, {});
713
716
714
717
715 var load_class = function(class_name, module_name, registry) {
718 var load_class = function(class_name, module_name, registry) {
716 /**
719 /**
717 * Tries to load a class
720 * Tries to load a class
718 *
721 *
719 * Tries to load a class from a module using require.js, if a module
722 * Tries to load a class from a module using require.js, if a module
720 * is specified, otherwise tries to load a class from the global
723 * is specified, otherwise tries to load a class from the global
721 * registry, if the global registry is provided.
724 * registry, if the global registry is provided.
722 */
725 */
723 return new Promise(function(resolve, reject) {
726 return new Promise(function(resolve, reject) {
724
727
725 // Try loading the view module using require.js
728 // Try loading the view module using require.js
726 if (module_name) {
729 if (module_name) {
727 require([module_name], function(module) {
730 require([module_name], function(module) {
728 if (module[class_name] === undefined) {
731 if (module[class_name] === undefined) {
729 reject(new Error('Class '+class_name+' not found in module '+module_name));
732 reject(new Error('Class '+class_name+' not found in module '+module_name));
730 } else {
733 } else {
731 resolve(module[class_name]);
734 resolve(module[class_name]);
732 }
735 }
733 }, reject);
736 }, reject);
734 } else {
737 } else {
735 if (registry && registry[class_name]) {
738 if (registry && registry[class_name]) {
736 resolve(registry[class_name]);
739 resolve(registry[class_name]);
737 } else {
740 } else {
738 reject(new Error('Class '+class_name+' not found in registry '));
741 reject(new Error('Class '+class_name+' not found in registry '));
739 }
742 }
740 }
743 }
741 });
744 });
742 };
745 };
743
746
744 var resolve_promises_dict = function(d) {
747 var resolve_promises_dict = function(d) {
745 /**
748 /**
746 * Resolve a promiseful dictionary.
749 * Resolve a promiseful dictionary.
747 * Returns a single Promise.
750 * Returns a single Promise.
748 */
751 */
749 var keys = Object.keys(d);
752 var keys = Object.keys(d);
750 var values = [];
753 var values = [];
751 keys.forEach(function(key) {
754 keys.forEach(function(key) {
752 values.push(d[key]);
755 values.push(d[key]);
753 });
756 });
754 return Promise.all(values).then(function(v) {
757 return Promise.all(values).then(function(v) {
755 d = {};
758 d = {};
756 for(var i=0; i<keys.length; i++) {
759 for(var i=0; i<keys.length; i++) {
757 d[keys[i]] = v[i];
760 d[keys[i]] = v[i];
758 }
761 }
759 return d;
762 return d;
760 });
763 });
761 };
764 };
762
765
763 var reject = function(message, log) {
766 var reject = function(message, log) {
764 /**
767 /**
765 * Creates a wrappable Promise rejection function.
768 * Creates a wrappable Promise rejection function.
766 *
769 *
767 * Creates a function that returns a Promise.reject with a new WrappedError
770 * Creates a function that returns a Promise.reject with a new WrappedError
768 * that has the provided message and wraps the original error that
771 * that has the provided message and wraps the original error that
769 * caused the promise to reject.
772 * caused the promise to reject.
770 */
773 */
771 return function(error) {
774 return function(error) {
772 var wrapped_error = new WrappedError(message, error);
775 var wrapped_error = new WrappedError(message, error);
773 if (log) console.error(wrapped_error);
776 if (log) console.error(wrapped_error);
774 return Promise.reject(wrapped_error);
777 return Promise.reject(wrapped_error);
775 };
778 };
776 };
779 };
777
780
778 var typeset = function(element, text) {
781 var typeset = function(element, text) {
779 /**
782 /**
780 * Apply MathJax rendering to an element, and optionally set its text
783 * Apply MathJax rendering to an element, and optionally set its text
781 *
784 *
782 * If MathJax is not available, make no changes.
785 * If MathJax is not available, make no changes.
783 *
786 *
784 * Returns the output any number of typeset elements, or undefined if
787 * Returns the output any number of typeset elements, or undefined if
785 * MathJax was not available.
788 * MathJax was not available.
786 *
789 *
787 * Parameters
790 * Parameters
788 * ----------
791 * ----------
789 * element: Node, NodeList, or jQuery selection
792 * element: Node, NodeList, or jQuery selection
790 * text: option string
793 * text: option string
791 */
794 */
792 var $el = element.jquery ? element : $(element);
795 var $el = element.jquery ? element : $(element);
793 if(arguments.length > 1){
796 if(arguments.length > 1){
794 $el.text(text);
797 $el.text(text);
795 }
798 }
796 if(!window.MathJax){
799 if(!window.MathJax){
797 return;
800 return;
798 }
801 }
799 return $el.map(function(){
802 return $el.map(function(){
800 // MathJax takes a DOM node: $.map makes `this` the context
803 // MathJax takes a DOM node: $.map makes `this` the context
801 return MathJax.Hub.Queue(["Typeset", MathJax.Hub, this]);
804 return MathJax.Hub.Queue(["Typeset", MathJax.Hub, this]);
802 });
805 });
803 };
806 };
804
807
805 var time = {};
808 var time = {};
806 time.milliseconds = {};
809 time.milliseconds = {};
807 time.milliseconds.s = 1000;
810 time.milliseconds.s = 1000;
808 time.milliseconds.m = 60 * time.milliseconds.s;
811 time.milliseconds.m = 60 * time.milliseconds.s;
809 time.milliseconds.h = 60 * time.milliseconds.m;
812 time.milliseconds.h = 60 * time.milliseconds.m;
810 time.milliseconds.d = 24 * time.milliseconds.h;
813 time.milliseconds.d = 24 * time.milliseconds.h;
811
814
812 time.thresholds = {
815 time.thresholds = {
813 // moment.js thresholds in milliseconds
816 // moment.js thresholds in milliseconds
814 s: moment.relativeTimeThreshold('s') * time.milliseconds.s,
817 s: moment.relativeTimeThreshold('s') * time.milliseconds.s,
815 m: moment.relativeTimeThreshold('m') * time.milliseconds.m,
818 m: moment.relativeTimeThreshold('m') * time.milliseconds.m,
816 h: moment.relativeTimeThreshold('h') * time.milliseconds.h,
819 h: moment.relativeTimeThreshold('h') * time.milliseconds.h,
817 d: moment.relativeTimeThreshold('d') * time.milliseconds.d,
820 d: moment.relativeTimeThreshold('d') * time.milliseconds.d,
818 };
821 };
819
822
820 time.timeout_from_dt = function (dt) {
823 time.timeout_from_dt = function (dt) {
821 /** compute a timeout based on dt
824 /** compute a timeout based on dt
822
825
823 input and output both in milliseconds
826 input and output both in milliseconds
824
827
825 use moment's relative time thresholds:
828 use moment's relative time thresholds:
826
829
827 - 10 seconds if in 'seconds ago' territory
830 - 10 seconds if in 'seconds ago' territory
828 - 1 minute if in 'minutes ago'
831 - 1 minute if in 'minutes ago'
829 - 1 hour otherwise
832 - 1 hour otherwise
830 */
833 */
831 if (dt < time.thresholds.s) {
834 if (dt < time.thresholds.s) {
832 return 10 * time.milliseconds.s;
835 return 10 * time.milliseconds.s;
833 } else if (dt < time.thresholds.m) {
836 } else if (dt < time.thresholds.m) {
834 return time.milliseconds.m;
837 return time.milliseconds.m;
835 } else {
838 } else {
836 return time.milliseconds.h;
839 return time.milliseconds.h;
837 }
840 }
838 };
841 };
839
842
840 var utils = {
843 var utils = {
841 load_extensions: load_extensions,
844 load_extensions: load_extensions,
842 load_extensions_from_config: load_extensions_from_config,
845 load_extensions_from_config: load_extensions_from_config,
843 regex_split : regex_split,
846 regex_split : regex_split,
844 uuid : uuid,
847 uuid : uuid,
845 fixConsole : fixConsole,
848 fixConsole : fixConsole,
846 fixCarriageReturn : fixCarriageReturn,
849 fixCarriageReturn : fixCarriageReturn,
847 autoLinkUrls : autoLinkUrls,
850 autoLinkUrls : autoLinkUrls,
848 points_to_pixels : points_to_pixels,
851 points_to_pixels : points_to_pixels,
849 get_body_data : get_body_data,
852 get_body_data : get_body_data,
850 parse_url : parse_url,
853 parse_url : parse_url,
851 url_path_split : url_path_split,
854 url_path_split : url_path_split,
852 url_path_join : url_path_join,
855 url_path_join : url_path_join,
853 url_join_encode : url_join_encode,
856 url_join_encode : url_join_encode,
854 encode_uri_components : encode_uri_components,
857 encode_uri_components : encode_uri_components,
855 splitext : splitext,
858 splitext : splitext,
856 escape_html : escape_html,
859 escape_html : escape_html,
857 always_new : always_new,
860 always_new : always_new,
858 to_absolute_cursor_pos : to_absolute_cursor_pos,
861 to_absolute_cursor_pos : to_absolute_cursor_pos,
859 from_absolute_cursor_pos : from_absolute_cursor_pos,
862 from_absolute_cursor_pos : from_absolute_cursor_pos,
860 browser : browser,
863 browser : browser,
861 platform: platform,
864 platform: platform,
862 get_url_param: get_url_param,
865 get_url_param: get_url_param,
863 is_or_has : is_or_has,
866 is_or_has : is_or_has,
864 is_focused : is_focused,
867 is_focused : is_focused,
865 mergeopt: mergeopt,
868 mergeopt: mergeopt,
866 ajax_error_msg : ajax_error_msg,
869 ajax_error_msg : ajax_error_msg,
867 log_ajax_error : log_ajax_error,
870 log_ajax_error : log_ajax_error,
868 requireCodeMirrorMode : requireCodeMirrorMode,
871 requireCodeMirrorMode : requireCodeMirrorMode,
869 XHR_ERROR : XHR_ERROR,
872 XHR_ERROR : XHR_ERROR,
870 wrap_ajax_error : wrap_ajax_error,
873 wrap_ajax_error : wrap_ajax_error,
871 promising_ajax : promising_ajax,
874 promising_ajax : promising_ajax,
872 WrappedError: WrappedError,
875 WrappedError: WrappedError,
873 load_class: load_class,
876 load_class: load_class,
874 resolve_promises_dict: resolve_promises_dict,
877 resolve_promises_dict: resolve_promises_dict,
875 reject: reject,
878 reject: reject,
876 typeset: typeset,
879 typeset: typeset,
877 time: time,
880 time: time,
878 };
881 };
879
882
880 // Backwards compatability.
883 // Backwards compatability.
881 IPython.utils = utils;
884 IPython.utils = utils;
882
885
883 return utils;
886 return utils;
884 });
887 });
General Comments 0
You need to be logged in to leave comments. Login now