##// END OF EJS Templates
Simplify code by using Promises in a better way; try_load -> load
Jason Grout -
Show More
@@ -1,672 +1,672 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 ], function(IPython, $, CodeMirror){
8 ], function(IPython, $, CodeMirror){
9 "use strict";
9 "use strict";
10
10
11 IPython.load_extensions = function () {
11 IPython.load_extensions = function () {
12 // load one or more IPython notebook extensions with requirejs
12 // load one or more IPython notebook extensions with requirejs
13
13
14 var extensions = [];
14 var extensions = [];
15 var extension_names = arguments;
15 var extension_names = arguments;
16 for (var i = 0; i < extension_names.length; i++) {
16 for (var i = 0; i < extension_names.length; i++) {
17 extensions.push("nbextensions/" + arguments[i]);
17 extensions.push("nbextensions/" + arguments[i]);
18 }
18 }
19
19
20 require(extensions,
20 require(extensions,
21 function () {
21 function () {
22 for (var i = 0; i < arguments.length; i++) {
22 for (var i = 0; i < arguments.length; i++) {
23 var ext = arguments[i];
23 var ext = arguments[i];
24 var ext_name = extension_names[i];
24 var ext_name = extension_names[i];
25 // success callback
25 // success callback
26 console.log("Loaded extension: " + ext_name);
26 console.log("Loaded extension: " + ext_name);
27 if (ext && ext.load_ipython_extension !== undefined) {
27 if (ext && ext.load_ipython_extension !== undefined) {
28 ext.load_ipython_extension();
28 ext.load_ipython_extension();
29 }
29 }
30 }
30 }
31 },
31 },
32 function (err) {
32 function (err) {
33 // failure callback
33 // failure callback
34 console.log("Failed to load extension(s):", err.requireModules, err);
34 console.log("Failed to load extension(s):", err.requireModules, err);
35 }
35 }
36 );
36 );
37 };
37 };
38
38
39 //============================================================================
39 //============================================================================
40 // Cross-browser RegEx Split
40 // Cross-browser RegEx Split
41 //============================================================================
41 //============================================================================
42
42
43 // This code has been MODIFIED from the code licensed below to not replace the
43 // This code has been MODIFIED from the code licensed below to not replace the
44 // default browser split. The license is reproduced here.
44 // default browser split. The license is reproduced here.
45
45
46 // see http://blog.stevenlevithan.com/archives/cross-browser-split for more info:
46 // see http://blog.stevenlevithan.com/archives/cross-browser-split for more info:
47 /*!
47 /*!
48 * Cross-Browser Split 1.1.1
48 * Cross-Browser Split 1.1.1
49 * Copyright 2007-2012 Steven Levithan <stevenlevithan.com>
49 * Copyright 2007-2012 Steven Levithan <stevenlevithan.com>
50 * Available under the MIT License
50 * Available under the MIT License
51 * ECMAScript compliant, uniform cross-browser split method
51 * ECMAScript compliant, uniform cross-browser split method
52 */
52 */
53
53
54 /**
54 /**
55 * Splits a string into an array of strings using a regex or string
55 * Splits a string into an array of strings using a regex or string
56 * separator. Matches of the separator are not included in the result array.
56 * separator. Matches of the separator are not included in the result array.
57 * However, if `separator` is a regex that contains capturing groups,
57 * However, if `separator` is a regex that contains capturing groups,
58 * backreferences are spliced into the result each time `separator` is
58 * backreferences are spliced into the result each time `separator` is
59 * matched. Fixes browser bugs compared to the native
59 * matched. Fixes browser bugs compared to the native
60 * `String.prototype.split` and can be used reliably cross-browser.
60 * `String.prototype.split` and can be used reliably cross-browser.
61 * @param {String} str String to split.
61 * @param {String} str String to split.
62 * @param {RegExp|String} separator Regex or string to use for separating
62 * @param {RegExp|String} separator Regex or string to use for separating
63 * the string.
63 * the string.
64 * @param {Number} [limit] Maximum number of items to include in the result
64 * @param {Number} [limit] Maximum number of items to include in the result
65 * array.
65 * array.
66 * @returns {Array} Array of substrings.
66 * @returns {Array} Array of substrings.
67 * @example
67 * @example
68 *
68 *
69 * // Basic use
69 * // Basic use
70 * regex_split('a b c d', ' ');
70 * regex_split('a b c d', ' ');
71 * // -> ['a', 'b', 'c', 'd']
71 * // -> ['a', 'b', 'c', 'd']
72 *
72 *
73 * // With limit
73 * // With limit
74 * regex_split('a b c d', ' ', 2);
74 * regex_split('a b c d', ' ', 2);
75 * // -> ['a', 'b']
75 * // -> ['a', 'b']
76 *
76 *
77 * // Backreferences in result array
77 * // Backreferences in result array
78 * regex_split('..word1 word2..', /([a-z]+)(\d+)/i);
78 * regex_split('..word1 word2..', /([a-z]+)(\d+)/i);
79 * // -> ['..', 'word', '1', ' ', 'word', '2', '..']
79 * // -> ['..', 'word', '1', ' ', 'word', '2', '..']
80 */
80 */
81 var regex_split = function (str, separator, limit) {
81 var regex_split = function (str, separator, limit) {
82 // If `separator` is not a regex, use `split`
82 // If `separator` is not a regex, use `split`
83 if (Object.prototype.toString.call(separator) !== "[object RegExp]") {
83 if (Object.prototype.toString.call(separator) !== "[object RegExp]") {
84 return split.call(str, separator, limit);
84 return split.call(str, separator, limit);
85 }
85 }
86 var output = [],
86 var output = [],
87 flags = (separator.ignoreCase ? "i" : "") +
87 flags = (separator.ignoreCase ? "i" : "") +
88 (separator.multiline ? "m" : "") +
88 (separator.multiline ? "m" : "") +
89 (separator.extended ? "x" : "") + // Proposed for ES6
89 (separator.extended ? "x" : "") + // Proposed for ES6
90 (separator.sticky ? "y" : ""), // Firefox 3+
90 (separator.sticky ? "y" : ""), // Firefox 3+
91 lastLastIndex = 0,
91 lastLastIndex = 0,
92 // Make `global` and avoid `lastIndex` issues by working with a copy
92 // Make `global` and avoid `lastIndex` issues by working with a copy
93 separator = new RegExp(separator.source, flags + "g"),
93 separator = new RegExp(separator.source, flags + "g"),
94 separator2, match, lastIndex, lastLength;
94 separator2, match, lastIndex, lastLength;
95 str += ""; // Type-convert
95 str += ""; // Type-convert
96
96
97 var compliantExecNpcg = typeof(/()??/.exec("")[1]) === "undefined";
97 var compliantExecNpcg = typeof(/()??/.exec("")[1]) === "undefined";
98 if (!compliantExecNpcg) {
98 if (!compliantExecNpcg) {
99 // Doesn't need flags gy, but they don't hurt
99 // Doesn't need flags gy, but they don't hurt
100 separator2 = new RegExp("^" + separator.source + "$(?!\\s)", flags);
100 separator2 = new RegExp("^" + separator.source + "$(?!\\s)", flags);
101 }
101 }
102 /* Values for `limit`, per the spec:
102 /* Values for `limit`, per the spec:
103 * If undefined: 4294967295 // Math.pow(2, 32) - 1
103 * If undefined: 4294967295 // Math.pow(2, 32) - 1
104 * If 0, Infinity, or NaN: 0
104 * If 0, Infinity, or NaN: 0
105 * If positive number: limit = Math.floor(limit); if (limit > 4294967295) limit -= 4294967296;
105 * If positive number: limit = Math.floor(limit); if (limit > 4294967295) limit -= 4294967296;
106 * If negative number: 4294967296 - Math.floor(Math.abs(limit))
106 * If negative number: 4294967296 - Math.floor(Math.abs(limit))
107 * If other: Type-convert, then use the above rules
107 * If other: Type-convert, then use the above rules
108 */
108 */
109 limit = typeof(limit) === "undefined" ?
109 limit = typeof(limit) === "undefined" ?
110 -1 >>> 0 : // Math.pow(2, 32) - 1
110 -1 >>> 0 : // Math.pow(2, 32) - 1
111 limit >>> 0; // ToUint32(limit)
111 limit >>> 0; // ToUint32(limit)
112 while (match = separator.exec(str)) {
112 while (match = separator.exec(str)) {
113 // `separator.lastIndex` is not reliable cross-browser
113 // `separator.lastIndex` is not reliable cross-browser
114 lastIndex = match.index + match[0].length;
114 lastIndex = match.index + match[0].length;
115 if (lastIndex > lastLastIndex) {
115 if (lastIndex > lastLastIndex) {
116 output.push(str.slice(lastLastIndex, match.index));
116 output.push(str.slice(lastLastIndex, match.index));
117 // Fix browsers whose `exec` methods don't consistently return `undefined` for
117 // Fix browsers whose `exec` methods don't consistently return `undefined` for
118 // nonparticipating capturing groups
118 // nonparticipating capturing groups
119 if (!compliantExecNpcg && match.length > 1) {
119 if (!compliantExecNpcg && match.length > 1) {
120 match[0].replace(separator2, function () {
120 match[0].replace(separator2, function () {
121 for (var i = 1; i < arguments.length - 2; i++) {
121 for (var i = 1; i < arguments.length - 2; i++) {
122 if (typeof(arguments[i]) === "undefined") {
122 if (typeof(arguments[i]) === "undefined") {
123 match[i] = undefined;
123 match[i] = undefined;
124 }
124 }
125 }
125 }
126 });
126 });
127 }
127 }
128 if (match.length > 1 && match.index < str.length) {
128 if (match.length > 1 && match.index < str.length) {
129 Array.prototype.push.apply(output, match.slice(1));
129 Array.prototype.push.apply(output, match.slice(1));
130 }
130 }
131 lastLength = match[0].length;
131 lastLength = match[0].length;
132 lastLastIndex = lastIndex;
132 lastLastIndex = lastIndex;
133 if (output.length >= limit) {
133 if (output.length >= limit) {
134 break;
134 break;
135 }
135 }
136 }
136 }
137 if (separator.lastIndex === match.index) {
137 if (separator.lastIndex === match.index) {
138 separator.lastIndex++; // Avoid an infinite loop
138 separator.lastIndex++; // Avoid an infinite loop
139 }
139 }
140 }
140 }
141 if (lastLastIndex === str.length) {
141 if (lastLastIndex === str.length) {
142 if (lastLength || !separator.test("")) {
142 if (lastLength || !separator.test("")) {
143 output.push("");
143 output.push("");
144 }
144 }
145 } else {
145 } else {
146 output.push(str.slice(lastLastIndex));
146 output.push(str.slice(lastLastIndex));
147 }
147 }
148 return output.length > limit ? output.slice(0, limit) : output;
148 return output.length > limit ? output.slice(0, limit) : output;
149 };
149 };
150
150
151 //============================================================================
151 //============================================================================
152 // End contributed Cross-browser RegEx Split
152 // End contributed Cross-browser RegEx Split
153 //============================================================================
153 //============================================================================
154
154
155
155
156 var uuid = function () {
156 var uuid = function () {
157 // http://www.ietf.org/rfc/rfc4122.txt
157 // http://www.ietf.org/rfc/rfc4122.txt
158 var s = [];
158 var s = [];
159 var hexDigits = "0123456789ABCDEF";
159 var hexDigits = "0123456789ABCDEF";
160 for (var i = 0; i < 32; i++) {
160 for (var i = 0; i < 32; i++) {
161 s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
161 s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
162 }
162 }
163 s[12] = "4"; // bits 12-15 of the time_hi_and_version field to 0010
163 s[12] = "4"; // bits 12-15 of the time_hi_and_version field to 0010
164 s[16] = hexDigits.substr((s[16] & 0x3) | 0x8, 1); // bits 6-7 of the clock_seq_hi_and_reserved to 01
164 s[16] = hexDigits.substr((s[16] & 0x3) | 0x8, 1); // bits 6-7 of the clock_seq_hi_and_reserved to 01
165
165
166 var uuid = s.join("");
166 var uuid = s.join("");
167 return uuid;
167 return uuid;
168 };
168 };
169
169
170
170
171 //Fix raw text to parse correctly in crazy XML
171 //Fix raw text to parse correctly in crazy XML
172 function xmlencode(string) {
172 function xmlencode(string) {
173 return string.replace(/\&/g,'&'+'amp;')
173 return string.replace(/\&/g,'&'+'amp;')
174 .replace(/</g,'&'+'lt;')
174 .replace(/</g,'&'+'lt;')
175 .replace(/>/g,'&'+'gt;')
175 .replace(/>/g,'&'+'gt;')
176 .replace(/\'/g,'&'+'apos;')
176 .replace(/\'/g,'&'+'apos;')
177 .replace(/\"/g,'&'+'quot;')
177 .replace(/\"/g,'&'+'quot;')
178 .replace(/`/g,'&'+'#96;');
178 .replace(/`/g,'&'+'#96;');
179 }
179 }
180
180
181
181
182 //Map from terminal commands to CSS classes
182 //Map from terminal commands to CSS classes
183 var ansi_colormap = {
183 var ansi_colormap = {
184 "01":"ansibold",
184 "01":"ansibold",
185
185
186 "30":"ansiblack",
186 "30":"ansiblack",
187 "31":"ansired",
187 "31":"ansired",
188 "32":"ansigreen",
188 "32":"ansigreen",
189 "33":"ansiyellow",
189 "33":"ansiyellow",
190 "34":"ansiblue",
190 "34":"ansiblue",
191 "35":"ansipurple",
191 "35":"ansipurple",
192 "36":"ansicyan",
192 "36":"ansicyan",
193 "37":"ansigray",
193 "37":"ansigray",
194
194
195 "40":"ansibgblack",
195 "40":"ansibgblack",
196 "41":"ansibgred",
196 "41":"ansibgred",
197 "42":"ansibggreen",
197 "42":"ansibggreen",
198 "43":"ansibgyellow",
198 "43":"ansibgyellow",
199 "44":"ansibgblue",
199 "44":"ansibgblue",
200 "45":"ansibgpurple",
200 "45":"ansibgpurple",
201 "46":"ansibgcyan",
201 "46":"ansibgcyan",
202 "47":"ansibggray"
202 "47":"ansibggray"
203 };
203 };
204
204
205 function _process_numbers(attrs, numbers) {
205 function _process_numbers(attrs, numbers) {
206 // process ansi escapes
206 // process ansi escapes
207 var n = numbers.shift();
207 var n = numbers.shift();
208 if (ansi_colormap[n]) {
208 if (ansi_colormap[n]) {
209 if ( ! attrs["class"] ) {
209 if ( ! attrs["class"] ) {
210 attrs["class"] = ansi_colormap[n];
210 attrs["class"] = ansi_colormap[n];
211 } else {
211 } else {
212 attrs["class"] += " " + ansi_colormap[n];
212 attrs["class"] += " " + ansi_colormap[n];
213 }
213 }
214 } else if (n == "38" || n == "48") {
214 } else if (n == "38" || n == "48") {
215 // VT100 256 color or 24 bit RGB
215 // VT100 256 color or 24 bit RGB
216 if (numbers.length < 2) {
216 if (numbers.length < 2) {
217 console.log("Not enough fields for VT100 color", numbers);
217 console.log("Not enough fields for VT100 color", numbers);
218 return;
218 return;
219 }
219 }
220
220
221 var index_or_rgb = numbers.shift();
221 var index_or_rgb = numbers.shift();
222 var r,g,b;
222 var r,g,b;
223 if (index_or_rgb == "5") {
223 if (index_or_rgb == "5") {
224 // 256 color
224 // 256 color
225 var idx = parseInt(numbers.shift());
225 var idx = parseInt(numbers.shift());
226 if (idx < 16) {
226 if (idx < 16) {
227 // indexed ANSI
227 // indexed ANSI
228 // ignore bright / non-bright distinction
228 // ignore bright / non-bright distinction
229 idx = idx % 8;
229 idx = idx % 8;
230 var ansiclass = ansi_colormap[n[0] + (idx % 8).toString()];
230 var ansiclass = ansi_colormap[n[0] + (idx % 8).toString()];
231 if ( ! attrs["class"] ) {
231 if ( ! attrs["class"] ) {
232 attrs["class"] = ansiclass;
232 attrs["class"] = ansiclass;
233 } else {
233 } else {
234 attrs["class"] += " " + ansiclass;
234 attrs["class"] += " " + ansiclass;
235 }
235 }
236 return;
236 return;
237 } else if (idx < 232) {
237 } else if (idx < 232) {
238 // 216 color 6x6x6 RGB
238 // 216 color 6x6x6 RGB
239 idx = idx - 16;
239 idx = idx - 16;
240 b = idx % 6;
240 b = idx % 6;
241 g = Math.floor(idx / 6) % 6;
241 g = Math.floor(idx / 6) % 6;
242 r = Math.floor(idx / 36) % 6;
242 r = Math.floor(idx / 36) % 6;
243 // convert to rgb
243 // convert to rgb
244 r = (r * 51);
244 r = (r * 51);
245 g = (g * 51);
245 g = (g * 51);
246 b = (b * 51);
246 b = (b * 51);
247 } else {
247 } else {
248 // grayscale
248 // grayscale
249 idx = idx - 231;
249 idx = idx - 231;
250 // it's 1-24 and should *not* include black or white,
250 // it's 1-24 and should *not* include black or white,
251 // so a 26 point scale
251 // so a 26 point scale
252 r = g = b = Math.floor(idx * 256 / 26);
252 r = g = b = Math.floor(idx * 256 / 26);
253 }
253 }
254 } else if (index_or_rgb == "2") {
254 } else if (index_or_rgb == "2") {
255 // Simple 24 bit RGB
255 // Simple 24 bit RGB
256 if (numbers.length > 3) {
256 if (numbers.length > 3) {
257 console.log("Not enough fields for RGB", numbers);
257 console.log("Not enough fields for RGB", numbers);
258 return;
258 return;
259 }
259 }
260 r = numbers.shift();
260 r = numbers.shift();
261 g = numbers.shift();
261 g = numbers.shift();
262 b = numbers.shift();
262 b = numbers.shift();
263 } else {
263 } else {
264 console.log("unrecognized control", numbers);
264 console.log("unrecognized control", numbers);
265 return;
265 return;
266 }
266 }
267 if (r !== undefined) {
267 if (r !== undefined) {
268 // apply the rgb color
268 // apply the rgb color
269 var line;
269 var line;
270 if (n == "38") {
270 if (n == "38") {
271 line = "color: ";
271 line = "color: ";
272 } else {
272 } else {
273 line = "background-color: ";
273 line = "background-color: ";
274 }
274 }
275 line = line + "rgb(" + r + "," + g + "," + b + ");";
275 line = line + "rgb(" + r + "," + g + "," + b + ");";
276 if ( !attrs.style ) {
276 if ( !attrs.style ) {
277 attrs.style = line;
277 attrs.style = line;
278 } else {
278 } else {
279 attrs.style += " " + line;
279 attrs.style += " " + line;
280 }
280 }
281 }
281 }
282 }
282 }
283 }
283 }
284
284
285 function ansispan(str) {
285 function ansispan(str) {
286 // ansispan function adapted from github.com/mmalecki/ansispan (MIT License)
286 // ansispan function adapted from github.com/mmalecki/ansispan (MIT License)
287 // regular ansi escapes (using the table above)
287 // regular ansi escapes (using the table above)
288 var is_open = false;
288 var is_open = false;
289 return str.replace(/\033\[(0?[01]|22|39)?([;\d]+)?m/g, function(match, prefix, pattern) {
289 return str.replace(/\033\[(0?[01]|22|39)?([;\d]+)?m/g, function(match, prefix, pattern) {
290 if (!pattern) {
290 if (!pattern) {
291 // [(01|22|39|)m close spans
291 // [(01|22|39|)m close spans
292 if (is_open) {
292 if (is_open) {
293 is_open = false;
293 is_open = false;
294 return "</span>";
294 return "</span>";
295 } else {
295 } else {
296 return "";
296 return "";
297 }
297 }
298 } else {
298 } else {
299 is_open = true;
299 is_open = true;
300
300
301 // consume sequence of color escapes
301 // consume sequence of color escapes
302 var numbers = pattern.match(/\d+/g);
302 var numbers = pattern.match(/\d+/g);
303 var attrs = {};
303 var attrs = {};
304 while (numbers.length > 0) {
304 while (numbers.length > 0) {
305 _process_numbers(attrs, numbers);
305 _process_numbers(attrs, numbers);
306 }
306 }
307
307
308 var span = "<span ";
308 var span = "<span ";
309 for (var attr in attrs) {
309 for (var attr in attrs) {
310 var value = attrs[attr];
310 var value = attrs[attr];
311 span = span + " " + attr + '="' + attrs[attr] + '"';
311 span = span + " " + attr + '="' + attrs[attr] + '"';
312 }
312 }
313 return span + ">";
313 return span + ">";
314 }
314 }
315 });
315 });
316 }
316 }
317
317
318 // Transform ANSI color escape codes into HTML <span> tags with css
318 // Transform ANSI color escape codes into HTML <span> tags with css
319 // classes listed in the above ansi_colormap object. The actual color used
319 // classes listed in the above ansi_colormap object. The actual color used
320 // are set in the css file.
320 // are set in the css file.
321 function fixConsole(txt) {
321 function fixConsole(txt) {
322 txt = xmlencode(txt);
322 txt = xmlencode(txt);
323 var re = /\033\[([\dA-Fa-f;]*?)m/;
323 var re = /\033\[([\dA-Fa-f;]*?)m/;
324 var opened = false;
324 var opened = false;
325 var cmds = [];
325 var cmds = [];
326 var opener = "";
326 var opener = "";
327 var closer = "";
327 var closer = "";
328
328
329 // Strip all ANSI codes that are not color related. Matches
329 // Strip all ANSI codes that are not color related. Matches
330 // all ANSI codes that do not end with "m".
330 // all ANSI codes that do not end with "m".
331 var ignored_re = /(?=(\033\[[\d;=]*[a-ln-zA-Z]{1}))\1(?!m)/g;
331 var ignored_re = /(?=(\033\[[\d;=]*[a-ln-zA-Z]{1}))\1(?!m)/g;
332 txt = txt.replace(ignored_re, "");
332 txt = txt.replace(ignored_re, "");
333
333
334 // color ansi codes
334 // color ansi codes
335 txt = ansispan(txt);
335 txt = ansispan(txt);
336 return txt;
336 return txt;
337 }
337 }
338
338
339 // Remove chunks that should be overridden by the effect of
339 // Remove chunks that should be overridden by the effect of
340 // carriage return characters
340 // carriage return characters
341 function fixCarriageReturn(txt) {
341 function fixCarriageReturn(txt) {
342 var tmp = txt;
342 var tmp = txt;
343 do {
343 do {
344 txt = tmp;
344 txt = tmp;
345 tmp = txt.replace(/\r+\n/gm, '\n'); // \r followed by \n --> newline
345 tmp = txt.replace(/\r+\n/gm, '\n'); // \r followed by \n --> newline
346 tmp = tmp.replace(/^.*\r+/gm, ''); // Other \r --> clear line
346 tmp = tmp.replace(/^.*\r+/gm, ''); // Other \r --> clear line
347 } while (tmp.length < txt.length);
347 } while (tmp.length < txt.length);
348 return txt;
348 return txt;
349 }
349 }
350
350
351 // Locate any URLs and convert them to a anchor tag
351 // Locate any URLs and convert them to a anchor tag
352 function autoLinkUrls(txt) {
352 function autoLinkUrls(txt) {
353 return txt.replace(/(^|\s)(https?|ftp)(:[^'">\s]+)/gi,
353 return txt.replace(/(^|\s)(https?|ftp)(:[^'">\s]+)/gi,
354 "$1<a target=\"_blank\" href=\"$2$3\">$2$3</a>");
354 "$1<a target=\"_blank\" href=\"$2$3\">$2$3</a>");
355 }
355 }
356
356
357 var points_to_pixels = function (points) {
357 var points_to_pixels = function (points) {
358 // A reasonably good way of converting between points and pixels.
358 // A reasonably good way of converting between points and pixels.
359 var test = $('<div style="display: none; width: 10000pt; padding:0; border:0;"></div>');
359 var test = $('<div style="display: none; width: 10000pt; padding:0; border:0;"></div>');
360 $(body).append(test);
360 $(body).append(test);
361 var pixel_per_point = test.width()/10000;
361 var pixel_per_point = test.width()/10000;
362 test.remove();
362 test.remove();
363 return Math.floor(points*pixel_per_point);
363 return Math.floor(points*pixel_per_point);
364 };
364 };
365
365
366 var always_new = function (constructor) {
366 var always_new = function (constructor) {
367 // wrapper around contructor to avoid requiring `var a = new constructor()`
367 // wrapper around contructor to avoid requiring `var a = new constructor()`
368 // useful for passing constructors as callbacks,
368 // useful for passing constructors as callbacks,
369 // not for programmer laziness.
369 // not for programmer laziness.
370 // from http://programmers.stackexchange.com/questions/118798
370 // from http://programmers.stackexchange.com/questions/118798
371 return function () {
371 return function () {
372 var obj = Object.create(constructor.prototype);
372 var obj = Object.create(constructor.prototype);
373 constructor.apply(obj, arguments);
373 constructor.apply(obj, arguments);
374 return obj;
374 return obj;
375 };
375 };
376 };
376 };
377
377
378 var url_path_join = function () {
378 var url_path_join = function () {
379 // join a sequence of url components with '/'
379 // join a sequence of url components with '/'
380 var url = '';
380 var url = '';
381 for (var i = 0; i < arguments.length; i++) {
381 for (var i = 0; i < arguments.length; i++) {
382 if (arguments[i] === '') {
382 if (arguments[i] === '') {
383 continue;
383 continue;
384 }
384 }
385 if (url.length > 0 && url[url.length-1] != '/') {
385 if (url.length > 0 && url[url.length-1] != '/') {
386 url = url + '/' + arguments[i];
386 url = url + '/' + arguments[i];
387 } else {
387 } else {
388 url = url + arguments[i];
388 url = url + arguments[i];
389 }
389 }
390 }
390 }
391 url = url.replace(/\/\/+/, '/');
391 url = url.replace(/\/\/+/, '/');
392 return url;
392 return url;
393 };
393 };
394
394
395 var url_path_split = function (path) {
395 var url_path_split = function (path) {
396 // Like os.path.split for URLs.
396 // Like os.path.split for URLs.
397 // Always returns two strings, the directory path and the base filename
397 // Always returns two strings, the directory path and the base filename
398
398
399 var idx = path.lastIndexOf('/');
399 var idx = path.lastIndexOf('/');
400 if (idx === -1) {
400 if (idx === -1) {
401 return ['', path];
401 return ['', path];
402 } else {
402 } else {
403 return [ path.slice(0, idx), path.slice(idx + 1) ];
403 return [ path.slice(0, idx), path.slice(idx + 1) ];
404 }
404 }
405 };
405 };
406
406
407 var parse_url = function (url) {
407 var parse_url = function (url) {
408 // an `a` element with an href allows attr-access to the parsed segments of a URL
408 // an `a` element with an href allows attr-access to the parsed segments of a URL
409 // a = parse_url("http://localhost:8888/path/name#hash")
409 // a = parse_url("http://localhost:8888/path/name#hash")
410 // a.protocol = "http:"
410 // a.protocol = "http:"
411 // a.host = "localhost:8888"
411 // a.host = "localhost:8888"
412 // a.hostname = "localhost"
412 // a.hostname = "localhost"
413 // a.port = 8888
413 // a.port = 8888
414 // a.pathname = "/path/name"
414 // a.pathname = "/path/name"
415 // a.hash = "#hash"
415 // a.hash = "#hash"
416 var a = document.createElement("a");
416 var a = document.createElement("a");
417 a.href = url;
417 a.href = url;
418 return a;
418 return a;
419 };
419 };
420
420
421 var encode_uri_components = function (uri) {
421 var encode_uri_components = function (uri) {
422 // encode just the components of a multi-segment uri,
422 // encode just the components of a multi-segment uri,
423 // leaving '/' separators
423 // leaving '/' separators
424 return uri.split('/').map(encodeURIComponent).join('/');
424 return uri.split('/').map(encodeURIComponent).join('/');
425 };
425 };
426
426
427 var url_join_encode = function () {
427 var url_join_encode = function () {
428 // join a sequence of url components with '/',
428 // join a sequence of url components with '/',
429 // encoding each component with encodeURIComponent
429 // encoding each component with encodeURIComponent
430 return encode_uri_components(url_path_join.apply(null, arguments));
430 return encode_uri_components(url_path_join.apply(null, arguments));
431 };
431 };
432
432
433
433
434 var splitext = function (filename) {
434 var splitext = function (filename) {
435 // mimic Python os.path.splitext
435 // mimic Python os.path.splitext
436 // Returns ['base', '.ext']
436 // Returns ['base', '.ext']
437 var idx = filename.lastIndexOf('.');
437 var idx = filename.lastIndexOf('.');
438 if (idx > 0) {
438 if (idx > 0) {
439 return [filename.slice(0, idx), filename.slice(idx)];
439 return [filename.slice(0, idx), filename.slice(idx)];
440 } else {
440 } else {
441 return [filename, ''];
441 return [filename, ''];
442 }
442 }
443 };
443 };
444
444
445
445
446 var escape_html = function (text) {
446 var escape_html = function (text) {
447 // escape text to HTML
447 // escape text to HTML
448 return $("<div/>").text(text).html();
448 return $("<div/>").text(text).html();
449 };
449 };
450
450
451
451
452 var get_body_data = function(key) {
452 var get_body_data = function(key) {
453 // get a url-encoded item from body.data and decode it
453 // get a url-encoded item from body.data and decode it
454 // we should never have any encoded URLs anywhere else in code
454 // we should never have any encoded URLs anywhere else in code
455 // until we are building an actual request
455 // until we are building an actual request
456 return decodeURIComponent($('body').data(key));
456 return decodeURIComponent($('body').data(key));
457 };
457 };
458
458
459 var to_absolute_cursor_pos = function (cm, cursor) {
459 var to_absolute_cursor_pos = function (cm, cursor) {
460 // get the absolute cursor position from CodeMirror's col, ch
460 // get the absolute cursor position from CodeMirror's col, ch
461 if (!cursor) {
461 if (!cursor) {
462 cursor = cm.getCursor();
462 cursor = cm.getCursor();
463 }
463 }
464 var cursor_pos = cursor.ch;
464 var cursor_pos = cursor.ch;
465 for (var i = 0; i < cursor.line; i++) {
465 for (var i = 0; i < cursor.line; i++) {
466 cursor_pos += cm.getLine(i).length + 1;
466 cursor_pos += cm.getLine(i).length + 1;
467 }
467 }
468 return cursor_pos;
468 return cursor_pos;
469 };
469 };
470
470
471 var from_absolute_cursor_pos = function (cm, cursor_pos) {
471 var from_absolute_cursor_pos = function (cm, cursor_pos) {
472 // turn absolute cursor postion into CodeMirror col, ch cursor
472 // turn absolute cursor postion into CodeMirror col, ch cursor
473 var i, line;
473 var i, line;
474 var offset = 0;
474 var offset = 0;
475 for (i = 0, line=cm.getLine(i); line !== undefined; i++, line=cm.getLine(i)) {
475 for (i = 0, line=cm.getLine(i); line !== undefined; i++, line=cm.getLine(i)) {
476 if (offset + line.length < cursor_pos) {
476 if (offset + line.length < cursor_pos) {
477 offset += line.length + 1;
477 offset += line.length + 1;
478 } else {
478 } else {
479 return {
479 return {
480 line : i,
480 line : i,
481 ch : cursor_pos - offset,
481 ch : cursor_pos - offset,
482 };
482 };
483 }
483 }
484 }
484 }
485 // reached end, return endpoint
485 // reached end, return endpoint
486 return {
486 return {
487 ch : line.length - 1,
487 ch : line.length - 1,
488 line : i - 1,
488 line : i - 1,
489 };
489 };
490 };
490 };
491
491
492 // http://stackoverflow.com/questions/2400935/browser-detection-in-javascript
492 // http://stackoverflow.com/questions/2400935/browser-detection-in-javascript
493 var browser = (function() {
493 var browser = (function() {
494 if (typeof navigator === 'undefined') {
494 if (typeof navigator === 'undefined') {
495 // navigator undefined in node
495 // navigator undefined in node
496 return 'None';
496 return 'None';
497 }
497 }
498 var N= navigator.appName, ua= navigator.userAgent, tem;
498 var N= navigator.appName, ua= navigator.userAgent, tem;
499 var M= ua.match(/(opera|chrome|safari|firefox|msie)\/?\s*(\.?\d+(\.\d+)*)/i);
499 var M= ua.match(/(opera|chrome|safari|firefox|msie)\/?\s*(\.?\d+(\.\d+)*)/i);
500 if (M && (tem= ua.match(/version\/([\.\d]+)/i)) !== null) M[2]= tem[1];
500 if (M && (tem= ua.match(/version\/([\.\d]+)/i)) !== null) M[2]= tem[1];
501 M= M? [M[1], M[2]]: [N, navigator.appVersion,'-?'];
501 M= M? [M[1], M[2]]: [N, navigator.appVersion,'-?'];
502 return M;
502 return M;
503 })();
503 })();
504
504
505 // http://stackoverflow.com/questions/11219582/how-to-detect-my-browser-version-and-operating-system-using-javascript
505 // http://stackoverflow.com/questions/11219582/how-to-detect-my-browser-version-and-operating-system-using-javascript
506 var platform = (function () {
506 var platform = (function () {
507 if (typeof navigator === 'undefined') {
507 if (typeof navigator === 'undefined') {
508 // navigator undefined in node
508 // navigator undefined in node
509 return 'None';
509 return 'None';
510 }
510 }
511 var OSName="None";
511 var OSName="None";
512 if (navigator.appVersion.indexOf("Win")!=-1) OSName="Windows";
512 if (navigator.appVersion.indexOf("Win")!=-1) OSName="Windows";
513 if (navigator.appVersion.indexOf("Mac")!=-1) OSName="MacOS";
513 if (navigator.appVersion.indexOf("Mac")!=-1) OSName="MacOS";
514 if (navigator.appVersion.indexOf("X11")!=-1) OSName="UNIX";
514 if (navigator.appVersion.indexOf("X11")!=-1) OSName="UNIX";
515 if (navigator.appVersion.indexOf("Linux")!=-1) OSName="Linux";
515 if (navigator.appVersion.indexOf("Linux")!=-1) OSName="Linux";
516 return OSName;
516 return OSName;
517 })();
517 })();
518
518
519 var is_or_has = function (a, b) {
519 var is_or_has = function (a, b) {
520 // Is b a child of a or a itself?
520 // Is b a child of a or a itself?
521 return a.has(b).length !==0 || a.is(b);
521 return a.has(b).length !==0 || a.is(b);
522 };
522 };
523
523
524 var is_focused = function (e) {
524 var is_focused = function (e) {
525 // Is element e, or one of its children focused?
525 // Is element e, or one of its children focused?
526 e = $(e);
526 e = $(e);
527 var target = $(document.activeElement);
527 var target = $(document.activeElement);
528 if (target.length > 0) {
528 if (target.length > 0) {
529 if (is_or_has(e, target)) {
529 if (is_or_has(e, target)) {
530 return true;
530 return true;
531 } else {
531 } else {
532 return false;
532 return false;
533 }
533 }
534 } else {
534 } else {
535 return false;
535 return false;
536 }
536 }
537 };
537 };
538
538
539 var mergeopt = function(_class, options, overwrite){
539 var mergeopt = function(_class, options, overwrite){
540 options = options || {};
540 options = options || {};
541 overwrite = overwrite || {};
541 overwrite = overwrite || {};
542 return $.extend(true, {}, _class.options_default, options, overwrite);
542 return $.extend(true, {}, _class.options_default, options, overwrite);
543 };
543 };
544
544
545 var ajax_error_msg = function (jqXHR) {
545 var ajax_error_msg = function (jqXHR) {
546 // Return a JSON error message if there is one,
546 // Return a JSON error message if there is one,
547 // otherwise the basic HTTP status text.
547 // otherwise the basic HTTP status text.
548 if (jqXHR.responseJSON && jqXHR.responseJSON.traceback) {
548 if (jqXHR.responseJSON && jqXHR.responseJSON.traceback) {
549 return jqXHR.responseJSON.traceback;
549 return jqXHR.responseJSON.traceback;
550 } else if (jqXHR.responseJSON && jqXHR.responseJSON.message) {
550 } else if (jqXHR.responseJSON && jqXHR.responseJSON.message) {
551 return jqXHR.responseJSON.message;
551 return jqXHR.responseJSON.message;
552 } else {
552 } else {
553 return jqXHR.statusText;
553 return jqXHR.statusText;
554 }
554 }
555 };
555 };
556 var log_ajax_error = function (jqXHR, status, error) {
556 var log_ajax_error = function (jqXHR, status, error) {
557 // log ajax failures with informative messages
557 // log ajax failures with informative messages
558 var msg = "API request failed (" + jqXHR.status + "): ";
558 var msg = "API request failed (" + jqXHR.status + "): ";
559 console.log(jqXHR);
559 console.log(jqXHR);
560 msg += ajax_error_msg(jqXHR);
560 msg += ajax_error_msg(jqXHR);
561 console.log(msg);
561 console.log(msg);
562 };
562 };
563
563
564 var requireCodeMirrorMode = function (mode, callback, errback) {
564 var requireCodeMirrorMode = function (mode, callback, errback) {
565 // load a mode with requirejs
565 // load a mode with requirejs
566 if (typeof mode != "string") mode = mode.name;
566 if (typeof mode != "string") mode = mode.name;
567 if (CodeMirror.modes.hasOwnProperty(mode)) {
567 if (CodeMirror.modes.hasOwnProperty(mode)) {
568 callback(CodeMirror.modes.mode);
568 callback(CodeMirror.modes.mode);
569 return;
569 return;
570 }
570 }
571 require([
571 require([
572 // might want to use CodeMirror.modeURL here
572 // might want to use CodeMirror.modeURL here
573 ['codemirror/mode', mode, mode].join('/'),
573 ['codemirror/mode', mode, mode].join('/'),
574 ], callback, errback
574 ], callback, errback
575 );
575 );
576 };
576 };
577
577
578 /** Error type for wrapped XHR errors. */
578 /** Error type for wrapped XHR errors. */
579 var XHR_ERROR = 'XhrError';
579 var XHR_ERROR = 'XhrError';
580
580
581 /**
581 /**
582 * Wraps an AJAX error as an Error object.
582 * Wraps an AJAX error as an Error object.
583 */
583 */
584 var wrap_ajax_error = function (jqXHR, status, error) {
584 var wrap_ajax_error = function (jqXHR, status, error) {
585 var wrapped_error = new Error(ajax_error_msg(jqXHR));
585 var wrapped_error = new Error(ajax_error_msg(jqXHR));
586 wrapped_error.name = XHR_ERROR;
586 wrapped_error.name = XHR_ERROR;
587 // provide xhr response
587 // provide xhr response
588 wrapped_error.xhr = jqXHR;
588 wrapped_error.xhr = jqXHR;
589 wrapped_error.xhr_status = status;
589 wrapped_error.xhr_status = status;
590 wrapped_error.xhr_error = error;
590 wrapped_error.xhr_error = error;
591 return wrapped_error;
591 return wrapped_error;
592 };
592 };
593
593
594 var promising_ajax = function(url, settings) {
594 var promising_ajax = function(url, settings) {
595 // Like $.ajax, but returning an ES6 promise. success and error settings
595 // Like $.ajax, but returning an ES6 promise. success and error settings
596 // will be ignored.
596 // will be ignored.
597 return new Promise(function(resolve, reject) {
597 return new Promise(function(resolve, reject) {
598 settings.success = function(data, status, jqXHR) {
598 settings.success = function(data, status, jqXHR) {
599 resolve(data);
599 resolve(data);
600 };
600 };
601 settings.error = function(jqXHR, status, error) {
601 settings.error = function(jqXHR, status, error) {
602 log_ajax_error(jqXHR, status, error);
602 log_ajax_error(jqXHR, status, error);
603 reject(wrap_ajax_error(jqXHR, status, error));
603 reject(wrap_ajax_error(jqXHR, status, error));
604 };
604 };
605 $.ajax(url, settings);
605 $.ajax(url, settings);
606 });
606 });
607 };
607 };
608
608
609 var try_load = function(class_name, module_name, registry) {
609 var load = function(class_name, module_name, registry) {
610 // Tries to load a class
610 // Tries to load a class
611 //
611 //
612 // Tries to load a class from a module using require.js, if a module
612 // Tries to load a class from a module using require.js, if a module
613 // is specified, otherwise tries to load a class from the global
613 // is specified, otherwise tries to load a class from the global
614 // registry, if the global registry is provided.
614 // registry, if the global registry is provided.
615 return new Promise(function(resolve, reject) {
615 return new Promise(function(resolve, reject) {
616
616
617 // Try loading the view module using require.js
617 // Try loading the view module using require.js
618 if (module_name) {
618 if (module_name) {
619 require([module_name], function(module) {
619 require([module_name], function(module) {
620 if (module[class_name] === undefined) {
620 if (module[class_name] === undefined) {
621 reject(Error('Class not found in module.'));
621 reject(new Error('Class not found in module.'));
622 } else {
622 } else {
623 resolve(module[class_name]);
623 resolve(module[class_name]);
624 }
624 }
625 }, reject);
625 }, reject);
626 } else {
626 } else {
627 if (registry && registry[class_name]) {
627 if (registry && registry[class_name]) {
628 resolve(registry[class_name]);
628 resolve(registry[class_name]);
629 } else {
629 } else {
630 reject(Error('Class not found in registry.'));
630 reject(new Error('Class not found in registry.'));
631 }
631 }
632 }
632 }
633 });
633 });
634 };
634 };
635
635
636 var utils = {
636 var utils = {
637 regex_split : regex_split,
637 regex_split : regex_split,
638 uuid : uuid,
638 uuid : uuid,
639 fixConsole : fixConsole,
639 fixConsole : fixConsole,
640 fixCarriageReturn : fixCarriageReturn,
640 fixCarriageReturn : fixCarriageReturn,
641 autoLinkUrls : autoLinkUrls,
641 autoLinkUrls : autoLinkUrls,
642 points_to_pixels : points_to_pixels,
642 points_to_pixels : points_to_pixels,
643 get_body_data : get_body_data,
643 get_body_data : get_body_data,
644 parse_url : parse_url,
644 parse_url : parse_url,
645 url_path_split : url_path_split,
645 url_path_split : url_path_split,
646 url_path_join : url_path_join,
646 url_path_join : url_path_join,
647 url_join_encode : url_join_encode,
647 url_join_encode : url_join_encode,
648 encode_uri_components : encode_uri_components,
648 encode_uri_components : encode_uri_components,
649 splitext : splitext,
649 splitext : splitext,
650 escape_html : escape_html,
650 escape_html : escape_html,
651 always_new : always_new,
651 always_new : always_new,
652 to_absolute_cursor_pos : to_absolute_cursor_pos,
652 to_absolute_cursor_pos : to_absolute_cursor_pos,
653 from_absolute_cursor_pos : from_absolute_cursor_pos,
653 from_absolute_cursor_pos : from_absolute_cursor_pos,
654 browser : browser,
654 browser : browser,
655 platform: platform,
655 platform: platform,
656 is_or_has : is_or_has,
656 is_or_has : is_or_has,
657 is_focused : is_focused,
657 is_focused : is_focused,
658 mergeopt: mergeopt,
658 mergeopt: mergeopt,
659 ajax_error_msg : ajax_error_msg,
659 ajax_error_msg : ajax_error_msg,
660 log_ajax_error : log_ajax_error,
660 log_ajax_error : log_ajax_error,
661 requireCodeMirrorMode : requireCodeMirrorMode,
661 requireCodeMirrorMode : requireCodeMirrorMode,
662 XHR_ERROR : XHR_ERROR,
662 XHR_ERROR : XHR_ERROR,
663 wrap_ajax_error : wrap_ajax_error,
663 wrap_ajax_error : wrap_ajax_error,
664 promising_ajax : promising_ajax,
664 promising_ajax : promising_ajax,
665 try_load: try_load,
665 load: load,
666 };
666 };
667
667
668 // Backwards compatability.
668 // Backwards compatability.
669 IPython.utils = utils;
669 IPython.utils = utils;
670
670
671 return utils;
671 return utils;
672 });
672 });
@@ -1,256 +1,231 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 "underscore",
5 "underscore",
6 "backbone",
6 "backbone",
7 "jquery",
7 "jquery",
8 "base/js/utils",
8 "base/js/utils",
9 "base/js/namespace"
9 "base/js/namespace"
10 ], function (_, Backbone, $, utils, IPython) {
10 ], function (_, Backbone, $, utils, IPython) {
11 "use strict";
11 "use strict";
12 //--------------------------------------------------------------------
12 //--------------------------------------------------------------------
13 // WidgetManager class
13 // WidgetManager class
14 //--------------------------------------------------------------------
14 //--------------------------------------------------------------------
15 var WidgetManager = function (comm_manager, notebook) {
15 var WidgetManager = function (comm_manager, notebook) {
16 // Public constructor
16 // Public constructor
17 WidgetManager._managers.push(this);
17 WidgetManager._managers.push(this);
18
18
19 // Attach a comm manager to the
19 // Attach a comm manager to the
20 this.keyboard_manager = notebook.keyboard_manager;
20 this.keyboard_manager = notebook.keyboard_manager;
21 this.notebook = notebook;
21 this.notebook = notebook;
22 this.comm_manager = comm_manager;
22 this.comm_manager = comm_manager;
23 this._models = {}; /* Dictionary of model ids and model instances */
23 this._models = {}; /* Dictionary of model ids and model instances */
24
24
25 // Register with the comm manager.
25 // Register with the comm manager.
26 this.comm_manager.register_target('ipython.widget', $.proxy(this._handle_comm_open, this));
26 this.comm_manager.register_target('ipython.widget', $.proxy(this._handle_comm_open, this));
27 };
27 };
28
28
29 //--------------------------------------------------------------------
29 //--------------------------------------------------------------------
30 // Class level
30 // Class level
31 //--------------------------------------------------------------------
31 //--------------------------------------------------------------------
32 WidgetManager._model_types = {}; /* Dictionary of model type names (target_name) and model types. */
32 WidgetManager._model_types = {}; /* Dictionary of model type names (target_name) and model types. */
33 WidgetManager._view_types = {}; /* Dictionary of view names and view types. */
33 WidgetManager._view_types = {}; /* Dictionary of view names and view types. */
34 WidgetManager._managers = []; /* List of widget managers */
34 WidgetManager._managers = []; /* List of widget managers */
35
35
36 WidgetManager.register_widget_model = function (model_name, model_type) {
36 WidgetManager.register_widget_model = function (model_name, model_type) {
37 // Registers a widget model by name.
37 // Registers a widget model by name.
38 WidgetManager._model_types[model_name] = model_type;
38 WidgetManager._model_types[model_name] = model_type;
39 };
39 };
40
40
41 WidgetManager.register_widget_view = function (view_name, view_type) {
41 WidgetManager.register_widget_view = function (view_name, view_type) {
42 // Registers a widget view by name.
42 // Registers a widget view by name.
43 WidgetManager._view_types[view_name] = view_type;
43 WidgetManager._view_types[view_name] = view_type;
44 };
44 };
45
45
46 //--------------------------------------------------------------------
46 //--------------------------------------------------------------------
47 // Instance level
47 // Instance level
48 //--------------------------------------------------------------------
48 //--------------------------------------------------------------------
49 WidgetManager.prototype.display_view = function(msg, model) {
49 WidgetManager.prototype.display_view = function(msg, model) {
50 // Displays a view for a particular model.
50 // Displays a view for a particular model.
51 var cell = this.get_msg_cell(msg.parent_header.msg_id);
51 var cell = this.get_msg_cell(msg.parent_header.msg_id);
52 if (cell === null) {
52 if (cell === null) {
53 console.log("Could not determine where the display" +
53 console.log("Could not determine where the display" +
54 " message was from. Widget will not be displayed");
54 " message was from. Widget will not be displayed");
55 } else {
55 } else {
56 var dummy = null;
56 var dummy = null;
57 if (cell.widget_subarea) {
57 if (cell.widget_subarea) {
58 dummy = $('<div />');
58 dummy = $('<div />');
59 cell.widget_subarea.append(dummy);
59 cell.widget_subarea.append(dummy);
60 }
60 }
61
61
62 var that = this;
62 var that = this;
63 this.create_view(model, {cell: cell}).then(function(view) {
63 this.create_view(model, {cell: cell}).then(function(view) {
64 that._handle_display_view(view);
64 that._handle_display_view(view);
65 if (dummy) {
65 if (dummy) {
66 dummy.replaceWith(view.$el);
66 dummy.replaceWith(view.$el);
67 }
67 }
68 view.trigger('displayed');
68 view.trigger('displayed');
69 }, $.proxy(console.error, console));
69 }, $.proxy(console.error, console));
70 }
70 }
71 };
71 };
72
72
73 WidgetManager.prototype._handle_display_view = function (view) {
73 WidgetManager.prototype._handle_display_view = function (view) {
74 // Have the IPython keyboard manager disable its event
74 // Have the IPython keyboard manager disable its event
75 // handling so the widget can capture keyboard input.
75 // handling so the widget can capture keyboard input.
76 // Note, this is only done on the outer most widgets.
76 // Note, this is only done on the outer most widgets.
77 if (this.keyboard_manager) {
77 if (this.keyboard_manager) {
78 this.keyboard_manager.register_events(view.$el);
78 this.keyboard_manager.register_events(view.$el);
79
79
80 if (view.additional_elements) {
80 if (view.additional_elements) {
81 for (var i = 0; i < view.additional_elements.length; i++) {
81 for (var i = 0; i < view.additional_elements.length; i++) {
82 this.keyboard_manager.register_events(view.additional_elements[i]);
82 this.keyboard_manager.register_events(view.additional_elements[i]);
83 }
83 }
84 }
84 }
85 }
85 }
86 };
86 };
87
87
88
88
89 WidgetManager.prototype.create_view = function(model, options) {
89 WidgetManager.prototype.create_view = function(model, options) {
90 // Creates a view for a particular model.
90 // Creates a promise for a view of a given model
91 return new Promise(function(resolve, reject) {
91 return utils.load(model.get('_view_name'), model.get('_view_module'),
92 var view_name = model.get('_view_name');
92 WidgetManager._view_types).then(function(ViewType) {
93 var view_module = model.get('_view_module');
93 // If a view is passed into the method, use that view's cell as
94 utils.try_load(view_name, view_module, WidgetManager._view_types).then(function(ViewType){
94 // the cell for the view that is created.
95
95 options = options || {};
96 // If a view is passed into the method, use that view's cell as
96 if (options.parent !== undefined) {
97 // the cell for the view that is created.
97 options.cell = options.parent.options.cell;
98 options = options || {};
98 }
99 if (options.parent !== undefined) {
99 // Create and render the view...
100 options.cell = options.parent.options.cell;
100 var parameters = {model: model, options: options};
101 }
101 var view = new ViewType(parameters);
102
102 view.listenTo(model, 'destroy', view.remove);
103 // Create and render the view...
103 view.render();
104 var parameters = {model: model, options: options};
104 return view;
105 var view = new ViewType(parameters);
105 });
106 view.render();
107 model.on('destroy', view.remove, view);
108 resolve(view);
109 }, reject);
110 });
111 };
106 };
112
107
113 WidgetManager.prototype.get_msg_cell = function (msg_id) {
108 WidgetManager.prototype.get_msg_cell = function (msg_id) {
114 var cell = null;
109 var cell = null;
115 // First, check to see if the msg was triggered by cell execution.
110 // First, check to see if the msg was triggered by cell execution.
116 if (this.notebook) {
111 if (this.notebook) {
117 cell = this.notebook.get_msg_cell(msg_id);
112 cell = this.notebook.get_msg_cell(msg_id);
118 }
113 }
119 if (cell !== null) {
114 if (cell !== null) {
120 return cell;
115 return cell;
121 }
116 }
122 // Second, check to see if a get_cell callback was defined
117 // Second, check to see if a get_cell callback was defined
123 // for the message. get_cell callbacks are registered for
118 // for the message. get_cell callbacks are registered for
124 // widget messages, so this block is actually checking to see if the
119 // widget messages, so this block is actually checking to see if the
125 // message was triggered by a widget.
120 // message was triggered by a widget.
126 var kernel = this.comm_manager.kernel;
121 var kernel = this.comm_manager.kernel;
127 if (kernel) {
122 if (kernel) {
128 var callbacks = kernel.get_callbacks_for_msg(msg_id);
123 var callbacks = kernel.get_callbacks_for_msg(msg_id);
129 if (callbacks && callbacks.iopub &&
124 if (callbacks && callbacks.iopub &&
130 callbacks.iopub.get_cell !== undefined) {
125 callbacks.iopub.get_cell !== undefined) {
131 return callbacks.iopub.get_cell();
126 return callbacks.iopub.get_cell();
132 }
127 }
133 }
128 }
134
129
135 // Not triggered by a cell or widget (no get_cell callback
130 // Not triggered by a cell or widget (no get_cell callback
136 // exists).
131 // exists).
137 return null;
132 return null;
138 };
133 };
139
134
140 WidgetManager.prototype.callbacks = function (view) {
135 WidgetManager.prototype.callbacks = function (view) {
141 // callback handlers specific a view
136 // callback handlers specific a view
142 var callbacks = {};
137 var callbacks = {};
143 if (view && view.options.cell) {
138 if (view && view.options.cell) {
144
139
145 // Try to get output handlers
140 // Try to get output handlers
146 var cell = view.options.cell;
141 var cell = view.options.cell;
147 var handle_output = null;
142 var handle_output = null;
148 var handle_clear_output = null;
143 var handle_clear_output = null;
149 if (cell.output_area) {
144 if (cell.output_area) {
150 handle_output = $.proxy(cell.output_area.handle_output, cell.output_area);
145 handle_output = $.proxy(cell.output_area.handle_output, cell.output_area);
151 handle_clear_output = $.proxy(cell.output_area.handle_clear_output, cell.output_area);
146 handle_clear_output = $.proxy(cell.output_area.handle_clear_output, cell.output_area);
152 }
147 }
153
148
154 // Create callback dictionary using what is known
149 // Create callback dictionary using what is known
155 var that = this;
150 var that = this;
156 callbacks = {
151 callbacks = {
157 iopub : {
152 iopub : {
158 output : handle_output,
153 output : handle_output,
159 clear_output : handle_clear_output,
154 clear_output : handle_clear_output,
160
155
161 // Special function only registered by widget messages.
156 // Special function only registered by widget messages.
162 // Allows us to get the cell for a message so we know
157 // Allows us to get the cell for a message so we know
163 // where to add widgets if the code requires it.
158 // where to add widgets if the code requires it.
164 get_cell : function () {
159 get_cell : function () {
165 return cell;
160 return cell;
166 },
161 },
167 },
162 },
168 };
163 };
169 }
164 }
170 return callbacks;
165 return callbacks;
171 };
166 };
172
167
173 WidgetManager.prototype.get_model = function (model_id) {
168 WidgetManager.prototype.get_model = function (model_id) {
174 // Look-up a model instance by its id.
169 return that._models[model_id];
175 var that = this;
176 var model = that._models[model_id];
177 if (model !== undefined) {
178 return new Promise(function(resolve, reject){
179 if (model instanceof Promise) {
180 model.then(resolve, reject);
181 } else {
182 resolve(model);
183 }
184 });
185 } else {
186 return undefined;
187 }
188 };
170 };
189
171
190 WidgetManager.prototype._handle_comm_open = function (comm, msg) {
172 WidgetManager.prototype._handle_comm_open = function (comm, msg) {
191 // Handle when a comm is opened.
173 // Handle when a comm is opened.
192 this.create_model({
174 this.create_model({
193 model_name: msg.content.data.model_name,
175 model_name: msg.content.data.model_name,
194 model_module: msg.content.data.model_module,
176 model_module: msg.content.data.model_module,
195 comm: comm});
177 comm: comm});
196 };
178 };
197
179
198 WidgetManager.prototype.create_model = function (options) {
180 WidgetManager.prototype.create_model = function (options) {
199 // Create and return a promise to create a new widget model.
181 // Create and return a promise for a new widget model
200 //
182 //
201 // Minimally, one must provide the model_name and widget_class
183 // Minimally, one must provide the model_name and widget_class
202 // parameters to create a model from Javascript.
184 // parameters to create a model from Javascript.
203 //
185 //
204 // Example
186 // Example
205 // --------
187 // --------
206 // JS:
188 // JS:
207 // IPython.notebook.kernel.widget_manager.create_model({
189 // IPython.notebook.kernel.widget_manager.create_model({
208 // model_name: 'WidgetModel',
190 // model_name: 'WidgetModel',
209 // widget_class: 'IPython.html.widgets.widget_int.IntSlider'})
191 // widget_class: 'IPython.html.widgets.widget_int.IntSlider'})
210 // .then(function(model) { console.log('Create success!', model); },
192 // .then(function(model) { console.log('Create success!', model); },
211 // $.proxy(console.error, console));
193 // $.proxy(console.error, console));
212 //
194 //
213 // Parameters
195 // Parameters
214 // ----------
196 // ----------
215 // options: dictionary
197 // options: dictionary
216 // Dictionary of options with the following contents:
198 // Dictionary of options with the following contents:
217 // model_name: string
199 // model_name: string
218 // Target name of the widget model to create.
200 // Target name of the widget model to create.
219 // model_module: (optional) string
201 // model_module: (optional) string
220 // Module name of the widget model to create.
202 // Module name of the widget model to create.
221 // widget_class: (optional) string
203 // widget_class: (optional) string
222 // Target name of the widget in the back-end.
204 // Target name of the widget in the back-end.
223 // comm: (optional) Comm
205 // comm: (optional) Comm
224
206
225 // Create a comm if it wasn't provided.
207 // Create a comm if it wasn't provided.
226 var comm = options.comm;
208 var comm = options.comm;
227 if (!comm) {
209 if (!comm) {
228 comm = this.comm_manager.new_comm('ipython.widget', {'widget_class': options.widget_class});
210 comm = this.comm_manager.new_comm('ipython.widget', {'widget_class': options.widget_class});
229 }
211 }
230
212
231 var that = this;
213 var that = this;
232 var model_id = comm.comm_id;
214 var model_id = comm.comm_id;
233 var promise = new Promise(function(resolve, reject) {
215 var model_promise = utils.load(options.model_name, options.model_module, WidgetManager._model_types)
234
216 .then(function(ModelType) {
235 // Get the model type using require or through the registry.
217 var widget_model = new ModelType(that, model_id, comm);
236 var widget_type_name = options.model_name;
218 widget_model.once('comm:close', function () {
237 var widget_module = options.model_module;
219 delete that._models[model_id];
238 utils.try_load(widget_type_name, widget_module, WidgetManager._model_types)
220 });
239 .then(function(ModelType) {
221 return widget_model;
240 var widget_model = new ModelType(that, model_id, comm);
222 });
241 widget_model.on('comm:close', function () {
223 this._models[model_id] = model_promise;
242 delete that._models[model_id];
224 return model_promise;
243 });
244 that._models[model_id] = widget_model;
245 resolve(widget_model);
246 }, reject);
247 });
248 this._models[model_id] = promise;
249 return promise;
250 };
225 };
251
226
252 // Backwards compatibility.
227 // Backwards compatibility.
253 IPython.WidgetManager = WidgetManager;
228 IPython.WidgetManager = WidgetManager;
254
229
255 return {'WidgetManager': WidgetManager};
230 return {'WidgetManager': WidgetManager};
256 });
231 });
General Comments 0
You need to be logged in to leave comments. Login now