##// END OF EJS Templates
use es6
Jonathan Frederic -
Show More
@@ -1,761 +1,760
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 'rsvp',
8 ], function(IPython, $, CodeMirror){
9 ], function(IPython, $, CodeMirror, rsvp){
10 "use strict";
9 "use strict";
11
10
12 IPython.load_extensions = function () {
11 IPython.load_extensions = function () {
13 // load one or more IPython notebook extensions with requirejs
12 // load one or more IPython notebook extensions with requirejs
14
13
15 var extensions = [];
14 var extensions = [];
16 var extension_names = arguments;
15 var extension_names = arguments;
17 for (var i = 0; i < extension_names.length; i++) {
16 for (var i = 0; i < extension_names.length; i++) {
18 extensions.push("nbextensions/" + arguments[i]);
17 extensions.push("nbextensions/" + arguments[i]);
19 }
18 }
20
19
21 require(extensions,
20 require(extensions,
22 function () {
21 function () {
23 for (var i = 0; i < arguments.length; i++) {
22 for (var i = 0; i < arguments.length; i++) {
24 var ext = arguments[i];
23 var ext = arguments[i];
25 var ext_name = extension_names[i];
24 var ext_name = extension_names[i];
26 // success callback
25 // success callback
27 console.log("Loaded extension: " + ext_name);
26 console.log("Loaded extension: " + ext_name);
28 if (ext && ext.load_ipython_extension !== undefined) {
27 if (ext && ext.load_ipython_extension !== undefined) {
29 ext.load_ipython_extension();
28 ext.load_ipython_extension();
30 }
29 }
31 }
30 }
32 },
31 },
33 function (err) {
32 function (err) {
34 // failure callback
33 // failure callback
35 console.log("Failed to load extension(s):", err.requireModules, err);
34 console.log("Failed to load extension(s):", err.requireModules, err);
36 }
35 }
37 );
36 );
38 };
37 };
39
38
40 //============================================================================
39 //============================================================================
41 // Cross-browser RegEx Split
40 // Cross-browser RegEx Split
42 //============================================================================
41 //============================================================================
43
42
44 // 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
45 // default browser split. The license is reproduced here.
44 // default browser split. The license is reproduced here.
46
45
47 // 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:
48 /*!
47 /*!
49 * Cross-Browser Split 1.1.1
48 * Cross-Browser Split 1.1.1
50 * Copyright 2007-2012 Steven Levithan <stevenlevithan.com>
49 * Copyright 2007-2012 Steven Levithan <stevenlevithan.com>
51 * Available under the MIT License
50 * Available under the MIT License
52 * ECMAScript compliant, uniform cross-browser split method
51 * ECMAScript compliant, uniform cross-browser split method
53 */
52 */
54
53
55 /**
54 /**
56 * 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
57 * 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.
58 * However, if `separator` is a regex that contains capturing groups,
57 * However, if `separator` is a regex that contains capturing groups,
59 * backreferences are spliced into the result each time `separator` is
58 * backreferences are spliced into the result each time `separator` is
60 * matched. Fixes browser bugs compared to the native
59 * matched. Fixes browser bugs compared to the native
61 * `String.prototype.split` and can be used reliably cross-browser.
60 * `String.prototype.split` and can be used reliably cross-browser.
62 * @param {String} str String to split.
61 * @param {String} str String to split.
63 * @param {RegExp|String} separator Regex or string to use for separating
62 * @param {RegExp|String} separator Regex or string to use for separating
64 * the string.
63 * the string.
65 * @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
66 * array.
65 * array.
67 * @returns {Array} Array of substrings.
66 * @returns {Array} Array of substrings.
68 * @example
67 * @example
69 *
68 *
70 * // Basic use
69 * // Basic use
71 * regex_split('a b c d', ' ');
70 * regex_split('a b c d', ' ');
72 * // -> ['a', 'b', 'c', 'd']
71 * // -> ['a', 'b', 'c', 'd']
73 *
72 *
74 * // With limit
73 * // With limit
75 * regex_split('a b c d', ' ', 2);
74 * regex_split('a b c d', ' ', 2);
76 * // -> ['a', 'b']
75 * // -> ['a', 'b']
77 *
76 *
78 * // Backreferences in result array
77 * // Backreferences in result array
79 * regex_split('..word1 word2..', /([a-z]+)(\d+)/i);
78 * regex_split('..word1 word2..', /([a-z]+)(\d+)/i);
80 * // -> ['..', 'word', '1', ' ', 'word', '2', '..']
79 * // -> ['..', 'word', '1', ' ', 'word', '2', '..']
81 */
80 */
82 var regex_split = function (str, separator, limit) {
81 var regex_split = function (str, separator, limit) {
83 // If `separator` is not a regex, use `split`
82 // If `separator` is not a regex, use `split`
84 if (Object.prototype.toString.call(separator) !== "[object RegExp]") {
83 if (Object.prototype.toString.call(separator) !== "[object RegExp]") {
85 return split.call(str, separator, limit);
84 return split.call(str, separator, limit);
86 }
85 }
87 var output = [],
86 var output = [],
88 flags = (separator.ignoreCase ? "i" : "") +
87 flags = (separator.ignoreCase ? "i" : "") +
89 (separator.multiline ? "m" : "") +
88 (separator.multiline ? "m" : "") +
90 (separator.extended ? "x" : "") + // Proposed for ES6
89 (separator.extended ? "x" : "") + // Proposed for ES6
91 (separator.sticky ? "y" : ""), // Firefox 3+
90 (separator.sticky ? "y" : ""), // Firefox 3+
92 lastLastIndex = 0,
91 lastLastIndex = 0,
93 // Make `global` and avoid `lastIndex` issues by working with a copy
92 // Make `global` and avoid `lastIndex` issues by working with a copy
94 separator = new RegExp(separator.source, flags + "g"),
93 separator = new RegExp(separator.source, flags + "g"),
95 separator2, match, lastIndex, lastLength;
94 separator2, match, lastIndex, lastLength;
96 str += ""; // Type-convert
95 str += ""; // Type-convert
97
96
98 var compliantExecNpcg = typeof(/()??/.exec("")[1]) === "undefined";
97 var compliantExecNpcg = typeof(/()??/.exec("")[1]) === "undefined";
99 if (!compliantExecNpcg) {
98 if (!compliantExecNpcg) {
100 // Doesn't need flags gy, but they don't hurt
99 // Doesn't need flags gy, but they don't hurt
101 separator2 = new RegExp("^" + separator.source + "$(?!\\s)", flags);
100 separator2 = new RegExp("^" + separator.source + "$(?!\\s)", flags);
102 }
101 }
103 /* Values for `limit`, per the spec:
102 /* Values for `limit`, per the spec:
104 * If undefined: 4294967295 // Math.pow(2, 32) - 1
103 * If undefined: 4294967295 // Math.pow(2, 32) - 1
105 * If 0, Infinity, or NaN: 0
104 * If 0, Infinity, or NaN: 0
106 * 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;
107 * If negative number: 4294967296 - Math.floor(Math.abs(limit))
106 * If negative number: 4294967296 - Math.floor(Math.abs(limit))
108 * If other: Type-convert, then use the above rules
107 * If other: Type-convert, then use the above rules
109 */
108 */
110 limit = typeof(limit) === "undefined" ?
109 limit = typeof(limit) === "undefined" ?
111 -1 >>> 0 : // Math.pow(2, 32) - 1
110 -1 >>> 0 : // Math.pow(2, 32) - 1
112 limit >>> 0; // ToUint32(limit)
111 limit >>> 0; // ToUint32(limit)
113 while (match = separator.exec(str)) {
112 while (match = separator.exec(str)) {
114 // `separator.lastIndex` is not reliable cross-browser
113 // `separator.lastIndex` is not reliable cross-browser
115 lastIndex = match.index + match[0].length;
114 lastIndex = match.index + match[0].length;
116 if (lastIndex > lastLastIndex) {
115 if (lastIndex > lastLastIndex) {
117 output.push(str.slice(lastLastIndex, match.index));
116 output.push(str.slice(lastLastIndex, match.index));
118 // Fix browsers whose `exec` methods don't consistently return `undefined` for
117 // Fix browsers whose `exec` methods don't consistently return `undefined` for
119 // nonparticipating capturing groups
118 // nonparticipating capturing groups
120 if (!compliantExecNpcg && match.length > 1) {
119 if (!compliantExecNpcg && match.length > 1) {
121 match[0].replace(separator2, function () {
120 match[0].replace(separator2, function () {
122 for (var i = 1; i < arguments.length - 2; i++) {
121 for (var i = 1; i < arguments.length - 2; i++) {
123 if (typeof(arguments[i]) === "undefined") {
122 if (typeof(arguments[i]) === "undefined") {
124 match[i] = undefined;
123 match[i] = undefined;
125 }
124 }
126 }
125 }
127 });
126 });
128 }
127 }
129 if (match.length > 1 && match.index < str.length) {
128 if (match.length > 1 && match.index < str.length) {
130 Array.prototype.push.apply(output, match.slice(1));
129 Array.prototype.push.apply(output, match.slice(1));
131 }
130 }
132 lastLength = match[0].length;
131 lastLength = match[0].length;
133 lastLastIndex = lastIndex;
132 lastLastIndex = lastIndex;
134 if (output.length >= limit) {
133 if (output.length >= limit) {
135 break;
134 break;
136 }
135 }
137 }
136 }
138 if (separator.lastIndex === match.index) {
137 if (separator.lastIndex === match.index) {
139 separator.lastIndex++; // Avoid an infinite loop
138 separator.lastIndex++; // Avoid an infinite loop
140 }
139 }
141 }
140 }
142 if (lastLastIndex === str.length) {
141 if (lastLastIndex === str.length) {
143 if (lastLength || !separator.test("")) {
142 if (lastLength || !separator.test("")) {
144 output.push("");
143 output.push("");
145 }
144 }
146 } else {
145 } else {
147 output.push(str.slice(lastLastIndex));
146 output.push(str.slice(lastLastIndex));
148 }
147 }
149 return output.length > limit ? output.slice(0, limit) : output;
148 return output.length > limit ? output.slice(0, limit) : output;
150 };
149 };
151
150
152 //============================================================================
151 //============================================================================
153 // End contributed Cross-browser RegEx Split
152 // End contributed Cross-browser RegEx Split
154 //============================================================================
153 //============================================================================
155
154
156
155
157 var uuid = function () {
156 var uuid = function () {
158 // http://www.ietf.org/rfc/rfc4122.txt
157 // http://www.ietf.org/rfc/rfc4122.txt
159 var s = [];
158 var s = [];
160 var hexDigits = "0123456789ABCDEF";
159 var hexDigits = "0123456789ABCDEF";
161 for (var i = 0; i < 32; i++) {
160 for (var i = 0; i < 32; i++) {
162 s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
161 s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
163 }
162 }
164 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
165 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
166
165
167 var uuid = s.join("");
166 var uuid = s.join("");
168 return uuid;
167 return uuid;
169 };
168 };
170
169
171
170
172 //Fix raw text to parse correctly in crazy XML
171 //Fix raw text to parse correctly in crazy XML
173 function xmlencode(string) {
172 function xmlencode(string) {
174 return string.replace(/\&/g,'&'+'amp;')
173 return string.replace(/\&/g,'&'+'amp;')
175 .replace(/</g,'&'+'lt;')
174 .replace(/</g,'&'+'lt;')
176 .replace(/>/g,'&'+'gt;')
175 .replace(/>/g,'&'+'gt;')
177 .replace(/\'/g,'&'+'apos;')
176 .replace(/\'/g,'&'+'apos;')
178 .replace(/\"/g,'&'+'quot;')
177 .replace(/\"/g,'&'+'quot;')
179 .replace(/`/g,'&'+'#96;');
178 .replace(/`/g,'&'+'#96;');
180 }
179 }
181
180
182
181
183 //Map from terminal commands to CSS classes
182 //Map from terminal commands to CSS classes
184 var ansi_colormap = {
183 var ansi_colormap = {
185 "01":"ansibold",
184 "01":"ansibold",
186
185
187 "30":"ansiblack",
186 "30":"ansiblack",
188 "31":"ansired",
187 "31":"ansired",
189 "32":"ansigreen",
188 "32":"ansigreen",
190 "33":"ansiyellow",
189 "33":"ansiyellow",
191 "34":"ansiblue",
190 "34":"ansiblue",
192 "35":"ansipurple",
191 "35":"ansipurple",
193 "36":"ansicyan",
192 "36":"ansicyan",
194 "37":"ansigray",
193 "37":"ansigray",
195
194
196 "40":"ansibgblack",
195 "40":"ansibgblack",
197 "41":"ansibgred",
196 "41":"ansibgred",
198 "42":"ansibggreen",
197 "42":"ansibggreen",
199 "43":"ansibgyellow",
198 "43":"ansibgyellow",
200 "44":"ansibgblue",
199 "44":"ansibgblue",
201 "45":"ansibgpurple",
200 "45":"ansibgpurple",
202 "46":"ansibgcyan",
201 "46":"ansibgcyan",
203 "47":"ansibggray"
202 "47":"ansibggray"
204 };
203 };
205
204
206 function _process_numbers(attrs, numbers) {
205 function _process_numbers(attrs, numbers) {
207 // process ansi escapes
206 // process ansi escapes
208 var n = numbers.shift();
207 var n = numbers.shift();
209 if (ansi_colormap[n]) {
208 if (ansi_colormap[n]) {
210 if ( ! attrs["class"] ) {
209 if ( ! attrs["class"] ) {
211 attrs["class"] = ansi_colormap[n];
210 attrs["class"] = ansi_colormap[n];
212 } else {
211 } else {
213 attrs["class"] += " " + ansi_colormap[n];
212 attrs["class"] += " " + ansi_colormap[n];
214 }
213 }
215 } else if (n == "38" || n == "48") {
214 } else if (n == "38" || n == "48") {
216 // VT100 256 color or 24 bit RGB
215 // VT100 256 color or 24 bit RGB
217 if (numbers.length < 2) {
216 if (numbers.length < 2) {
218 console.log("Not enough fields for VT100 color", numbers);
217 console.log("Not enough fields for VT100 color", numbers);
219 return;
218 return;
220 }
219 }
221
220
222 var index_or_rgb = numbers.shift();
221 var index_or_rgb = numbers.shift();
223 var r,g,b;
222 var r,g,b;
224 if (index_or_rgb == "5") {
223 if (index_or_rgb == "5") {
225 // 256 color
224 // 256 color
226 var idx = parseInt(numbers.shift());
225 var idx = parseInt(numbers.shift());
227 if (idx < 16) {
226 if (idx < 16) {
228 // indexed ANSI
227 // indexed ANSI
229 // ignore bright / non-bright distinction
228 // ignore bright / non-bright distinction
230 idx = idx % 8;
229 idx = idx % 8;
231 var ansiclass = ansi_colormap[n[0] + (idx % 8).toString()];
230 var ansiclass = ansi_colormap[n[0] + (idx % 8).toString()];
232 if ( ! attrs["class"] ) {
231 if ( ! attrs["class"] ) {
233 attrs["class"] = ansiclass;
232 attrs["class"] = ansiclass;
234 } else {
233 } else {
235 attrs["class"] += " " + ansiclass;
234 attrs["class"] += " " + ansiclass;
236 }
235 }
237 return;
236 return;
238 } else if (idx < 232) {
237 } else if (idx < 232) {
239 // 216 color 6x6x6 RGB
238 // 216 color 6x6x6 RGB
240 idx = idx - 16;
239 idx = idx - 16;
241 b = idx % 6;
240 b = idx % 6;
242 g = Math.floor(idx / 6) % 6;
241 g = Math.floor(idx / 6) % 6;
243 r = Math.floor(idx / 36) % 6;
242 r = Math.floor(idx / 36) % 6;
244 // convert to rgb
243 // convert to rgb
245 r = (r * 51);
244 r = (r * 51);
246 g = (g * 51);
245 g = (g * 51);
247 b = (b * 51);
246 b = (b * 51);
248 } else {
247 } else {
249 // grayscale
248 // grayscale
250 idx = idx - 231;
249 idx = idx - 231;
251 // it's 1-24 and should *not* include black or white,
250 // it's 1-24 and should *not* include black or white,
252 // so a 26 point scale
251 // so a 26 point scale
253 r = g = b = Math.floor(idx * 256 / 26);
252 r = g = b = Math.floor(idx * 256 / 26);
254 }
253 }
255 } else if (index_or_rgb == "2") {
254 } else if (index_or_rgb == "2") {
256 // Simple 24 bit RGB
255 // Simple 24 bit RGB
257 if (numbers.length > 3) {
256 if (numbers.length > 3) {
258 console.log("Not enough fields for RGB", numbers);
257 console.log("Not enough fields for RGB", numbers);
259 return;
258 return;
260 }
259 }
261 r = numbers.shift();
260 r = numbers.shift();
262 g = numbers.shift();
261 g = numbers.shift();
263 b = numbers.shift();
262 b = numbers.shift();
264 } else {
263 } else {
265 console.log("unrecognized control", numbers);
264 console.log("unrecognized control", numbers);
266 return;
265 return;
267 }
266 }
268 if (r !== undefined) {
267 if (r !== undefined) {
269 // apply the rgb color
268 // apply the rgb color
270 var line;
269 var line;
271 if (n == "38") {
270 if (n == "38") {
272 line = "color: ";
271 line = "color: ";
273 } else {
272 } else {
274 line = "background-color: ";
273 line = "background-color: ";
275 }
274 }
276 line = line + "rgb(" + r + "," + g + "," + b + ");";
275 line = line + "rgb(" + r + "," + g + "," + b + ");";
277 if ( !attrs.style ) {
276 if ( !attrs.style ) {
278 attrs.style = line;
277 attrs.style = line;
279 } else {
278 } else {
280 attrs.style += " " + line;
279 attrs.style += " " + line;
281 }
280 }
282 }
281 }
283 }
282 }
284 }
283 }
285
284
286 function ansispan(str) {
285 function ansispan(str) {
287 // ansispan function adapted from github.com/mmalecki/ansispan (MIT License)
286 // ansispan function adapted from github.com/mmalecki/ansispan (MIT License)
288 // regular ansi escapes (using the table above)
287 // regular ansi escapes (using the table above)
289 var is_open = false;
288 var is_open = false;
290 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) {
291 if (!pattern) {
290 if (!pattern) {
292 // [(01|22|39|)m close spans
291 // [(01|22|39|)m close spans
293 if (is_open) {
292 if (is_open) {
294 is_open = false;
293 is_open = false;
295 return "</span>";
294 return "</span>";
296 } else {
295 } else {
297 return "";
296 return "";
298 }
297 }
299 } else {
298 } else {
300 is_open = true;
299 is_open = true;
301
300
302 // consume sequence of color escapes
301 // consume sequence of color escapes
303 var numbers = pattern.match(/\d+/g);
302 var numbers = pattern.match(/\d+/g);
304 var attrs = {};
303 var attrs = {};
305 while (numbers.length > 0) {
304 while (numbers.length > 0) {
306 _process_numbers(attrs, numbers);
305 _process_numbers(attrs, numbers);
307 }
306 }
308
307
309 var span = "<span ";
308 var span = "<span ";
310 for (var attr in attrs) {
309 for (var attr in attrs) {
311 var value = attrs[attr];
310 var value = attrs[attr];
312 span = span + " " + attr + '="' + attrs[attr] + '"';
311 span = span + " " + attr + '="' + attrs[attr] + '"';
313 }
312 }
314 return span + ">";
313 return span + ">";
315 }
314 }
316 });
315 });
317 }
316 }
318
317
319 // Transform ANSI color escape codes into HTML <span> tags with css
318 // Transform ANSI color escape codes into HTML <span> tags with css
320 // 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
321 // are set in the css file.
320 // are set in the css file.
322 function fixConsole(txt) {
321 function fixConsole(txt) {
323 txt = xmlencode(txt);
322 txt = xmlencode(txt);
324 var re = /\033\[([\dA-Fa-f;]*?)m/;
323 var re = /\033\[([\dA-Fa-f;]*?)m/;
325 var opened = false;
324 var opened = false;
326 var cmds = [];
325 var cmds = [];
327 var opener = "";
326 var opener = "";
328 var closer = "";
327 var closer = "";
329
328
330 // Strip all ANSI codes that are not color related. Matches
329 // Strip all ANSI codes that are not color related. Matches
331 // all ANSI codes that do not end with "m".
330 // all ANSI codes that do not end with "m".
332 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;
333 txt = txt.replace(ignored_re, "");
332 txt = txt.replace(ignored_re, "");
334
333
335 // color ansi codes
334 // color ansi codes
336 txt = ansispan(txt);
335 txt = ansispan(txt);
337 return txt;
336 return txt;
338 }
337 }
339
338
340 // Remove chunks that should be overridden by the effect of
339 // Remove chunks that should be overridden by the effect of
341 // carriage return characters
340 // carriage return characters
342 function fixCarriageReturn(txt) {
341 function fixCarriageReturn(txt) {
343 var tmp = txt;
342 var tmp = txt;
344 do {
343 do {
345 txt = tmp;
344 txt = tmp;
346 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
347 tmp = tmp.replace(/^.*\r+/gm, ''); // Other \r --> clear line
346 tmp = tmp.replace(/^.*\r+/gm, ''); // Other \r --> clear line
348 } while (tmp.length < txt.length);
347 } while (tmp.length < txt.length);
349 return txt;
348 return txt;
350 }
349 }
351
350
352 // Locate any URLs and convert them to a anchor tag
351 // Locate any URLs and convert them to a anchor tag
353 function autoLinkUrls(txt) {
352 function autoLinkUrls(txt) {
354 return txt.replace(/(^|\s)(https?|ftp)(:[^'">\s]+)/gi,
353 return txt.replace(/(^|\s)(https?|ftp)(:[^'">\s]+)/gi,
355 "$1<a target=\"_blank\" href=\"$2$3\">$2$3</a>");
354 "$1<a target=\"_blank\" href=\"$2$3\">$2$3</a>");
356 }
355 }
357
356
358 var points_to_pixels = function (points) {
357 var points_to_pixels = function (points) {
359 // A reasonably good way of converting between points and pixels.
358 // A reasonably good way of converting between points and pixels.
360 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>');
361 $(body).append(test);
360 $(body).append(test);
362 var pixel_per_point = test.width()/10000;
361 var pixel_per_point = test.width()/10000;
363 test.remove();
362 test.remove();
364 return Math.floor(points*pixel_per_point);
363 return Math.floor(points*pixel_per_point);
365 };
364 };
366
365
367 var always_new = function (constructor) {
366 var always_new = function (constructor) {
368 // wrapper around contructor to avoid requiring `var a = new constructor()`
367 // wrapper around contructor to avoid requiring `var a = new constructor()`
369 // useful for passing constructors as callbacks,
368 // useful for passing constructors as callbacks,
370 // not for programmer laziness.
369 // not for programmer laziness.
371 // from http://programmers.stackexchange.com/questions/118798
370 // from http://programmers.stackexchange.com/questions/118798
372 return function () {
371 return function () {
373 var obj = Object.create(constructor.prototype);
372 var obj = Object.create(constructor.prototype);
374 constructor.apply(obj, arguments);
373 constructor.apply(obj, arguments);
375 return obj;
374 return obj;
376 };
375 };
377 };
376 };
378
377
379 var url_path_join = function () {
378 var url_path_join = function () {
380 // join a sequence of url components with '/'
379 // join a sequence of url components with '/'
381 var url = '';
380 var url = '';
382 for (var i = 0; i < arguments.length; i++) {
381 for (var i = 0; i < arguments.length; i++) {
383 if (arguments[i] === '') {
382 if (arguments[i] === '') {
384 continue;
383 continue;
385 }
384 }
386 if (url.length > 0 && url[url.length-1] != '/') {
385 if (url.length > 0 && url[url.length-1] != '/') {
387 url = url + '/' + arguments[i];
386 url = url + '/' + arguments[i];
388 } else {
387 } else {
389 url = url + arguments[i];
388 url = url + arguments[i];
390 }
389 }
391 }
390 }
392 url = url.replace(/\/\/+/, '/');
391 url = url.replace(/\/\/+/, '/');
393 return url;
392 return url;
394 };
393 };
395
394
396 var url_path_split = function (path) {
395 var url_path_split = function (path) {
397 // Like os.path.split for URLs.
396 // Like os.path.split for URLs.
398 // Always returns two strings, the directory path and the base filename
397 // Always returns two strings, the directory path and the base filename
399
398
400 var idx = path.lastIndexOf('/');
399 var idx = path.lastIndexOf('/');
401 if (idx === -1) {
400 if (idx === -1) {
402 return ['', path];
401 return ['', path];
403 } else {
402 } else {
404 return [ path.slice(0, idx), path.slice(idx + 1) ];
403 return [ path.slice(0, idx), path.slice(idx + 1) ];
405 }
404 }
406 };
405 };
407
406
408 var parse_url = function (url) {
407 var parse_url = function (url) {
409 // 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
410 // a = parse_url("http://localhost:8888/path/name#hash")
409 // a = parse_url("http://localhost:8888/path/name#hash")
411 // a.protocol = "http:"
410 // a.protocol = "http:"
412 // a.host = "localhost:8888"
411 // a.host = "localhost:8888"
413 // a.hostname = "localhost"
412 // a.hostname = "localhost"
414 // a.port = 8888
413 // a.port = 8888
415 // a.pathname = "/path/name"
414 // a.pathname = "/path/name"
416 // a.hash = "#hash"
415 // a.hash = "#hash"
417 var a = document.createElement("a");
416 var a = document.createElement("a");
418 a.href = url;
417 a.href = url;
419 return a;
418 return a;
420 };
419 };
421
420
422 var encode_uri_components = function (uri) {
421 var encode_uri_components = function (uri) {
423 // encode just the components of a multi-segment uri,
422 // encode just the components of a multi-segment uri,
424 // leaving '/' separators
423 // leaving '/' separators
425 return uri.split('/').map(encodeURIComponent).join('/');
424 return uri.split('/').map(encodeURIComponent).join('/');
426 };
425 };
427
426
428 var url_join_encode = function () {
427 var url_join_encode = function () {
429 // join a sequence of url components with '/',
428 // join a sequence of url components with '/',
430 // encoding each component with encodeURIComponent
429 // encoding each component with encodeURIComponent
431 return encode_uri_components(url_path_join.apply(null, arguments));
430 return encode_uri_components(url_path_join.apply(null, arguments));
432 };
431 };
433
432
434
433
435 var splitext = function (filename) {
434 var splitext = function (filename) {
436 // mimic Python os.path.splitext
435 // mimic Python os.path.splitext
437 // Returns ['base', '.ext']
436 // Returns ['base', '.ext']
438 var idx = filename.lastIndexOf('.');
437 var idx = filename.lastIndexOf('.');
439 if (idx > 0) {
438 if (idx > 0) {
440 return [filename.slice(0, idx), filename.slice(idx)];
439 return [filename.slice(0, idx), filename.slice(idx)];
441 } else {
440 } else {
442 return [filename, ''];
441 return [filename, ''];
443 }
442 }
444 };
443 };
445
444
446
445
447 var escape_html = function (text) {
446 var escape_html = function (text) {
448 // escape text to HTML
447 // escape text to HTML
449 return $("<div/>").text(text).html();
448 return $("<div/>").text(text).html();
450 };
449 };
451
450
452
451
453 var get_body_data = function(key) {
452 var get_body_data = function(key) {
454 // get a url-encoded item from body.data and decode it
453 // get a url-encoded item from body.data and decode it
455 // we should never have any encoded URLs anywhere else in code
454 // we should never have any encoded URLs anywhere else in code
456 // until we are building an actual request
455 // until we are building an actual request
457 return decodeURIComponent($('body').data(key));
456 return decodeURIComponent($('body').data(key));
458 };
457 };
459
458
460 var to_absolute_cursor_pos = function (cm, cursor) {
459 var to_absolute_cursor_pos = function (cm, cursor) {
461 // get the absolute cursor position from CodeMirror's col, ch
460 // get the absolute cursor position from CodeMirror's col, ch
462 if (!cursor) {
461 if (!cursor) {
463 cursor = cm.getCursor();
462 cursor = cm.getCursor();
464 }
463 }
465 var cursor_pos = cursor.ch;
464 var cursor_pos = cursor.ch;
466 for (var i = 0; i < cursor.line; i++) {
465 for (var i = 0; i < cursor.line; i++) {
467 cursor_pos += cm.getLine(i).length + 1;
466 cursor_pos += cm.getLine(i).length + 1;
468 }
467 }
469 return cursor_pos;
468 return cursor_pos;
470 };
469 };
471
470
472 var from_absolute_cursor_pos = function (cm, cursor_pos) {
471 var from_absolute_cursor_pos = function (cm, cursor_pos) {
473 // turn absolute cursor postion into CodeMirror col, ch cursor
472 // turn absolute cursor postion into CodeMirror col, ch cursor
474 var i, line;
473 var i, line;
475 var offset = 0;
474 var offset = 0;
476 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)) {
477 if (offset + line.length < cursor_pos) {
476 if (offset + line.length < cursor_pos) {
478 offset += line.length + 1;
477 offset += line.length + 1;
479 } else {
478 } else {
480 return {
479 return {
481 line : i,
480 line : i,
482 ch : cursor_pos - offset,
481 ch : cursor_pos - offset,
483 };
482 };
484 }
483 }
485 }
484 }
486 // reached end, return endpoint
485 // reached end, return endpoint
487 return {
486 return {
488 ch : line.length - 1,
487 ch : line.length - 1,
489 line : i - 1,
488 line : i - 1,
490 };
489 };
491 };
490 };
492
491
493 // http://stackoverflow.com/questions/2400935/browser-detection-in-javascript
492 // http://stackoverflow.com/questions/2400935/browser-detection-in-javascript
494 var browser = (function() {
493 var browser = (function() {
495 if (typeof navigator === 'undefined') {
494 if (typeof navigator === 'undefined') {
496 // navigator undefined in node
495 // navigator undefined in node
497 return 'None';
496 return 'None';
498 }
497 }
499 var N= navigator.appName, ua= navigator.userAgent, tem;
498 var N= navigator.appName, ua= navigator.userAgent, tem;
500 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);
501 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];
502 M= M? [M[1], M[2]]: [N, navigator.appVersion,'-?'];
501 M= M? [M[1], M[2]]: [N, navigator.appVersion,'-?'];
503 return M;
502 return M;
504 })();
503 })();
505
504
506 // 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
507 var platform = (function () {
506 var platform = (function () {
508 if (typeof navigator === 'undefined') {
507 if (typeof navigator === 'undefined') {
509 // navigator undefined in node
508 // navigator undefined in node
510 return 'None';
509 return 'None';
511 }
510 }
512 var OSName="None";
511 var OSName="None";
513 if (navigator.appVersion.indexOf("Win")!=-1) OSName="Windows";
512 if (navigator.appVersion.indexOf("Win")!=-1) OSName="Windows";
514 if (navigator.appVersion.indexOf("Mac")!=-1) OSName="MacOS";
513 if (navigator.appVersion.indexOf("Mac")!=-1) OSName="MacOS";
515 if (navigator.appVersion.indexOf("X11")!=-1) OSName="UNIX";
514 if (navigator.appVersion.indexOf("X11")!=-1) OSName="UNIX";
516 if (navigator.appVersion.indexOf("Linux")!=-1) OSName="Linux";
515 if (navigator.appVersion.indexOf("Linux")!=-1) OSName="Linux";
517 return OSName;
516 return OSName;
518 })();
517 })();
519
518
520 var is_or_has = function (a, b) {
519 var is_or_has = function (a, b) {
521 // Is b a child of a or a itself?
520 // Is b a child of a or a itself?
522 return a.has(b).length !==0 || a.is(b);
521 return a.has(b).length !==0 || a.is(b);
523 };
522 };
524
523
525 var is_focused = function (e) {
524 var is_focused = function (e) {
526 // Is element e, or one of its children focused?
525 // Is element e, or one of its children focused?
527 e = $(e);
526 e = $(e);
528 var target = $(document.activeElement);
527 var target = $(document.activeElement);
529 if (target.length > 0) {
528 if (target.length > 0) {
530 if (is_or_has(e, target)) {
529 if (is_or_has(e, target)) {
531 return true;
530 return true;
532 } else {
531 } else {
533 return false;
532 return false;
534 }
533 }
535 } else {
534 } else {
536 return false;
535 return false;
537 }
536 }
538 };
537 };
539
538
540 var mergeopt = function(_class, options, overwrite){
539 var mergeopt = function(_class, options, overwrite){
541 options = options || {};
540 options = options || {};
542 overwrite = overwrite || {};
541 overwrite = overwrite || {};
543 return $.extend(true, {}, _class.options_default, options, overwrite);
542 return $.extend(true, {}, _class.options_default, options, overwrite);
544 };
543 };
545
544
546 var ajax_error_msg = function (jqXHR) {
545 var ajax_error_msg = function (jqXHR) {
547 // Return a JSON error message if there is one,
546 // Return a JSON error message if there is one,
548 // otherwise the basic HTTP status text.
547 // otherwise the basic HTTP status text.
549 if (jqXHR.responseJSON && jqXHR.responseJSON.traceback) {
548 if (jqXHR.responseJSON && jqXHR.responseJSON.traceback) {
550 return jqXHR.responseJSON.traceback;
549 return jqXHR.responseJSON.traceback;
551 } else if (jqXHR.responseJSON && jqXHR.responseJSON.message) {
550 } else if (jqXHR.responseJSON && jqXHR.responseJSON.message) {
552 return jqXHR.responseJSON.message;
551 return jqXHR.responseJSON.message;
553 } else {
552 } else {
554 return jqXHR.statusText;
553 return jqXHR.statusText;
555 }
554 }
556 };
555 };
557 var log_ajax_error = function (jqXHR, status, error) {
556 var log_ajax_error = function (jqXHR, status, error) {
558 // log ajax failures with informative messages
557 // log ajax failures with informative messages
559 var msg = "API request failed (" + jqXHR.status + "): ";
558 var msg = "API request failed (" + jqXHR.status + "): ";
560 console.log(jqXHR);
559 console.log(jqXHR);
561 msg += ajax_error_msg(jqXHR);
560 msg += ajax_error_msg(jqXHR);
562 console.log(msg);
561 console.log(msg);
563 };
562 };
564
563
565 var requireCodeMirrorMode = function (mode, callback, errback) {
564 var requireCodeMirrorMode = function (mode, callback, errback) {
566 // load a mode with requirejs
565 // load a mode with requirejs
567 if (typeof mode != "string") mode = mode.name;
566 if (typeof mode != "string") mode = mode.name;
568 if (CodeMirror.modes.hasOwnProperty(mode)) {
567 if (CodeMirror.modes.hasOwnProperty(mode)) {
569 callback(CodeMirror.modes.mode);
568 callback(CodeMirror.modes.mode);
570 return;
569 return;
571 }
570 }
572 require([
571 require([
573 // might want to use CodeMirror.modeURL here
572 // might want to use CodeMirror.modeURL here
574 ['codemirror/mode', mode, mode].join('/'),
573 ['codemirror/mode', mode, mode].join('/'),
575 ], callback, errback
574 ], callback, errback
576 );
575 );
577 };
576 };
578
577
579 /** Error type for wrapped XHR errors. */
578 /** Error type for wrapped XHR errors. */
580 var XHR_ERROR = 'XhrError';
579 var XHR_ERROR = 'XhrError';
581
580
582 /**
581 /**
583 * Wraps an AJAX error as an Error object.
582 * Wraps an AJAX error as an Error object.
584 */
583 */
585 var wrap_ajax_error = function (jqXHR, status, error) {
584 var wrap_ajax_error = function (jqXHR, status, error) {
586 var wrapped_error = new Error(ajax_error_msg(jqXHR));
585 var wrapped_error = new Error(ajax_error_msg(jqXHR));
587 wrapped_error.name = XHR_ERROR;
586 wrapped_error.name = XHR_ERROR;
588 // provide xhr response
587 // provide xhr response
589 wrapped_error.xhr = jqXHR;
588 wrapped_error.xhr = jqXHR;
590 wrapped_error.xhr_status = status;
589 wrapped_error.xhr_status = status;
591 wrapped_error.xhr_error = error;
590 wrapped_error.xhr_error = error;
592 return wrapped_error;
591 return wrapped_error;
593 };
592 };
594
593
595 var promising_ajax = function(url, settings) {
594 var promising_ajax = function(url, settings) {
596 // Like $.ajax, but returning an ES6 promise. success and error settings
595 // Like $.ajax, but returning an ES6 promise. success and error settings
597 // will be ignored.
596 // will be ignored.
598 return new Promise(function(resolve, reject) {
597 return new Promise(function(resolve, reject) {
599 settings.success = function(data, status, jqXHR) {
598 settings.success = function(data, status, jqXHR) {
600 resolve(data);
599 resolve(data);
601 };
600 };
602 settings.error = function(jqXHR, status, error) {
601 settings.error = function(jqXHR, status, error) {
603 log_ajax_error(jqXHR, status, error);
602 log_ajax_error(jqXHR, status, error);
604 reject(wrap_ajax_error(jqXHR, status, error));
603 reject(wrap_ajax_error(jqXHR, status, error));
605 };
604 };
606 $.ajax(url, settings);
605 $.ajax(url, settings);
607 });
606 });
608 };
607 };
609
608
610 var WrappedError = function(message, error){
609 var WrappedError = function(message, error){
611 // Wrappable Error class
610 // Wrappable Error class
612
611
613 // The Error class doesn't actually act on `this`. Instead it always
612 // The Error class doesn't actually act on `this`. Instead it always
614 // returns a new instance of Error. Here we capture that instance so we
613 // returns a new instance of Error. Here we capture that instance so we
615 // can apply it's properties to `this`.
614 // can apply it's properties to `this`.
616 var tmp = Error.apply(this, [message]);
615 var tmp = Error.apply(this, [message]);
617
616
618 // Copy the properties of the error over to this.
617 // Copy the properties of the error over to this.
619 var properties = Object.getOwnPropertyNames(tmp);
618 var properties = Object.getOwnPropertyNames(tmp);
620 for (var i = 0; i < properties.length; i++) {
619 for (var i = 0; i < properties.length; i++) {
621 this[properties[i]] = tmp[properties[i]];
620 this[properties[i]] = tmp[properties[i]];
622 }
621 }
623
622
624 // Keep a stack of the original error messages.
623 // Keep a stack of the original error messages.
625 if (error instanceof WrappedError) {
624 if (error instanceof WrappedError) {
626 this.error_stack = error.error_stack;
625 this.error_stack = error.error_stack;
627 } else {
626 } else {
628 this.error_stack = [error];
627 this.error_stack = [error];
629 }
628 }
630 this.error_stack.push(tmp);
629 this.error_stack.push(tmp);
631
630
632 return this;
631 return this;
633 };
632 };
634
633
635 WrappedError.prototype = Object.create(Error.prototype, {});
634 WrappedError.prototype = Object.create(Error.prototype, {});
636
635
637
636
638 var load_class = function(class_name, module_name, registry) {
637 var load_class = function(class_name, module_name, registry) {
639 // Tries to load a class
638 // Tries to load a class
640 //
639 //
641 // Tries to load a class from a module using require.js, if a module
640 // Tries to load a class from a module using require.js, if a module
642 // is specified, otherwise tries to load a class from the global
641 // is specified, otherwise tries to load a class from the global
643 // registry, if the global registry is provided.
642 // registry, if the global registry is provided.
644 return new rsvp.Promise(function(resolve, reject) {
643 return new Promise(function(resolve, reject) {
645
644
646 // Try loading the view module using require.js
645 // Try loading the view module using require.js
647 if (module_name) {
646 if (module_name) {
648 require([module_name], function(module) {
647 require([module_name], function(module) {
649 if (module[class_name] === undefined) {
648 if (module[class_name] === undefined) {
650 reject(new Error('Class '+class_name+' not found in module '+module_name));
649 reject(new Error('Class '+class_name+' not found in module '+module_name));
651 } else {
650 } else {
652 resolve(module[class_name]);
651 resolve(module[class_name]);
653 }
652 }
654 }, reject);
653 }, reject);
655 } else {
654 } else {
656 if (registry && registry[class_name]) {
655 if (registry && registry[class_name]) {
657 resolve(registry[class_name]);
656 resolve(registry[class_name]);
658 } else {
657 } else {
659 reject(new Error('Class '+class_name+' not found in registry '));
658 reject(new Error('Class '+class_name+' not found in registry '));
660 }
659 }
661 }
660 }
662 });
661 });
663 };
662 };
664
663
665 var resolve_dict = function(d) {
664 var resolve_dict = function(d) {
666 // Resolve a promiseful dictionary.
665 // Resolve a promiseful dictionary.
667 // Returns a single rsvp.Promise.
666 // Returns a single Promise.
668 var keys = Object.keys(d);
667 var keys = Object.keys(d);
669 var values = [];
668 var values = [];
670 keys.forEach(function(key) {
669 keys.forEach(function(key) {
671 values.push(d[key]);
670 values.push(d[key]);
672 });
671 });
673 return rsvp.Promise.all(values).then(function(v) {
672 return Promise.all(values).then(function(v) {
674 d = {};
673 d = {};
675 for(var i=0; i<keys.length; i++) {
674 for(var i=0; i<keys.length; i++) {
676 d[keys[i]] = v[i];
675 d[keys[i]] = v[i];
677 }
676 }
678 return d;
677 return d;
679 });
678 });
680 };
679 };
681
680
682 var WrappedError = function(message, error){
681 var WrappedError = function(message, error){
683 // Wrappable Error class
682 // Wrappable Error class
684
683
685 // The Error class doesn't actually act on `this`. Instead it always
684 // The Error class doesn't actually act on `this`. Instead it always
686 // returns a new instance of Error. Here we capture that instance so we
685 // returns a new instance of Error. Here we capture that instance so we
687 // can apply it's properties to `this`.
686 // can apply it's properties to `this`.
688 var tmp = Error.apply(this, [message]);
687 var tmp = Error.apply(this, [message]);
689
688
690 // Copy the properties of the error over to this.
689 // Copy the properties of the error over to this.
691 var properties = Object.getOwnPropertyNames(tmp);
690 var properties = Object.getOwnPropertyNames(tmp);
692 for (var i = 0; i < properties.length; i++) {
691 for (var i = 0; i < properties.length; i++) {
693 this[properties[i]] = tmp[properties[i]];
692 this[properties[i]] = tmp[properties[i]];
694 }
693 }
695
694
696 // Keep a stack of the original error messages.
695 // Keep a stack of the original error messages.
697 if (error instanceof WrappedError) {
696 if (error instanceof WrappedError) {
698 this.error_stack = error.error_stack;
697 this.error_stack = error.error_stack;
699 } else {
698 } else {
700 this.error_stack = [error];
699 this.error_stack = [error];
701 }
700 }
702 this.error_stack.push(tmp);
701 this.error_stack.push(tmp);
703
702
704 return this;
703 return this;
705 };
704 };
706
705
707 WrappedError.prototype = Object.create(Error.prototype, {});
706 WrappedError.prototype = Object.create(Error.prototype, {});
708
707
709 var reject = function(message, log) {
708 var reject = function(message, log) {
710 // Creates a wrappable rsvp.Promise rejection function.
709 // Creates a wrappable Promise rejection function.
711 //
710 //
712 // Creates a function that returns a rsvp.Promise.reject with a new WrappedError
711 // Creates a function that returns a Promise.reject with a new WrappedError
713 // that has the provided message and wraps the original error that
712 // that has the provided message and wraps the original error that
714 // caused the promise to reject.
713 // caused the promise to reject.
715 return function(error) {
714 return function(error) {
716 var wrapped_error = new WrappedError(message, error);
715 var wrapped_error = new WrappedError(message, error);
717 if (log) console.error(wrapped_error);
716 if (log) console.error(wrapped_error);
718 return rsvp.Promise.reject(wrapped_error);
717 return Promise.reject(wrapped_error);
719 };
718 };
720 };
719 };
721
720
722 var utils = {
721 var utils = {
723 regex_split : regex_split,
722 regex_split : regex_split,
724 uuid : uuid,
723 uuid : uuid,
725 fixConsole : fixConsole,
724 fixConsole : fixConsole,
726 fixCarriageReturn : fixCarriageReturn,
725 fixCarriageReturn : fixCarriageReturn,
727 autoLinkUrls : autoLinkUrls,
726 autoLinkUrls : autoLinkUrls,
728 points_to_pixels : points_to_pixels,
727 points_to_pixels : points_to_pixels,
729 get_body_data : get_body_data,
728 get_body_data : get_body_data,
730 parse_url : parse_url,
729 parse_url : parse_url,
731 url_path_split : url_path_split,
730 url_path_split : url_path_split,
732 url_path_join : url_path_join,
731 url_path_join : url_path_join,
733 url_join_encode : url_join_encode,
732 url_join_encode : url_join_encode,
734 encode_uri_components : encode_uri_components,
733 encode_uri_components : encode_uri_components,
735 splitext : splitext,
734 splitext : splitext,
736 escape_html : escape_html,
735 escape_html : escape_html,
737 always_new : always_new,
736 always_new : always_new,
738 to_absolute_cursor_pos : to_absolute_cursor_pos,
737 to_absolute_cursor_pos : to_absolute_cursor_pos,
739 from_absolute_cursor_pos : from_absolute_cursor_pos,
738 from_absolute_cursor_pos : from_absolute_cursor_pos,
740 browser : browser,
739 browser : browser,
741 platform: platform,
740 platform: platform,
742 is_or_has : is_or_has,
741 is_or_has : is_or_has,
743 is_focused : is_focused,
742 is_focused : is_focused,
744 mergeopt: mergeopt,
743 mergeopt: mergeopt,
745 ajax_error_msg : ajax_error_msg,
744 ajax_error_msg : ajax_error_msg,
746 log_ajax_error : log_ajax_error,
745 log_ajax_error : log_ajax_error,
747 requireCodeMirrorMode : requireCodeMirrorMode,
746 requireCodeMirrorMode : requireCodeMirrorMode,
748 XHR_ERROR : XHR_ERROR,
747 XHR_ERROR : XHR_ERROR,
749 wrap_ajax_error : wrap_ajax_error,
748 wrap_ajax_error : wrap_ajax_error,
750 promising_ajax : promising_ajax,
749 promising_ajax : promising_ajax,
751 WrappedError: WrappedError,
750 WrappedError: WrappedError,
752 load_class: load_class,
751 load_class: load_class,
753 resolve_dict: resolve_dict,
752 resolve_dict: resolve_dict,
754 reject: reject,
753 reject: reject,
755 };
754 };
756
755
757 // Backwards compatability.
756 // Backwards compatability.
758 IPython.utils = utils;
757 IPython.utils = utils;
759
758
760 return utils;
759 return utils;
761 });
760 });
@@ -1,156 +1,157
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 require([
4 require([
5 'base/js/namespace',
5 'base/js/namespace',
6 'jquery',
6 'jquery',
7 'notebook/js/notebook',
7 'notebook/js/notebook',
8 'contents',
8 'contents',
9 'base/js/utils',
9 'base/js/utils',
10 'base/js/page',
10 'base/js/page',
11 'notebook/js/layoutmanager',
11 'notebook/js/layoutmanager',
12 'base/js/events',
12 'base/js/events',
13 'auth/js/loginwidget',
13 'auth/js/loginwidget',
14 'notebook/js/maintoolbar',
14 'notebook/js/maintoolbar',
15 'notebook/js/pager',
15 'notebook/js/pager',
16 'notebook/js/quickhelp',
16 'notebook/js/quickhelp',
17 'notebook/js/menubar',
17 'notebook/js/menubar',
18 'notebook/js/notificationarea',
18 'notebook/js/notificationarea',
19 'notebook/js/savewidget',
19 'notebook/js/savewidget',
20 'notebook/js/keyboardmanager',
20 'notebook/js/keyboardmanager',
21 'notebook/js/config',
21 'notebook/js/config',
22 'notebook/js/kernelselector',
22 'notebook/js/kernelselector',
23 'codemirror/lib/codemirror',
23 'codemirror/lib/codemirror',
24 'notebook/js/about',
24 'notebook/js/about',
25 // only loaded, not used, please keep sure this is loaded last
25 // only loaded, not used, please keep sure this is loaded last
26 'custom/custom'
26 'custom/custom'
27 ], function(
27 ], function(
28 IPython,
28 IPython,
29 $,
29 $,
30 notebook,
30 notebook,
31 contents,
31 contents,
32 utils,
32 utils,
33 page,
33 page,
34 layoutmanager,
34 layoutmanager,
35 events,
35 events,
36 loginwidget,
36 loginwidget,
37 maintoolbar,
37 maintoolbar,
38 pager,
38 pager,
39 quickhelp,
39 quickhelp,
40 menubar,
40 menubar,
41 notificationarea,
41 notificationarea,
42 savewidget,
42 savewidget,
43 keyboardmanager,
43 keyboardmanager,
44 config,
44 config,
45 kernelselector,
45 kernelselector,
46 CodeMirror,
46 CodeMirror,
47 about,
47 about,
48 // please keep sure that even if not used, this is loaded last
48 // please keep sure that even if not used, this is loaded last
49 custom
49 custom
50 ) {
50 ) {
51 "use strict";
51 "use strict";
52 console.log(promise);
52
53
53 // compat with old IPython, remove for IPython > 3.0
54 // compat with old IPython, remove for IPython > 3.0
54 window.CodeMirror = CodeMirror;
55 window.CodeMirror = CodeMirror;
55
56
56 var common_options = {
57 var common_options = {
57 ws_url : utils.get_body_data("wsUrl"),
58 ws_url : utils.get_body_data("wsUrl"),
58 base_url : utils.get_body_data("baseUrl"),
59 base_url : utils.get_body_data("baseUrl"),
59 notebook_path : utils.get_body_data("notebookPath"),
60 notebook_path : utils.get_body_data("notebookPath"),
60 notebook_name : utils.get_body_data('notebookName')
61 notebook_name : utils.get_body_data('notebookName')
61 };
62 };
62
63
63 var user_config = $.extend({}, config.default_config);
64 var user_config = $.extend({}, config.default_config);
64 var page = new page.Page();
65 var page = new page.Page();
65 var layout_manager = new layoutmanager.LayoutManager();
66 var layout_manager = new layoutmanager.LayoutManager();
66 var pager = new pager.Pager('div#pager', 'div#pager_splitter', {
67 var pager = new pager.Pager('div#pager', 'div#pager_splitter', {
67 layout_manager: layout_manager,
68 layout_manager: layout_manager,
68 events: events});
69 events: events});
69 var keyboard_manager = new keyboardmanager.KeyboardManager({
70 var keyboard_manager = new keyboardmanager.KeyboardManager({
70 pager: pager,
71 pager: pager,
71 events: events});
72 events: events});
72 var save_widget = new savewidget.SaveWidget('span#save_widget', {
73 var save_widget = new savewidget.SaveWidget('span#save_widget', {
73 events: events,
74 events: events,
74 keyboard_manager: keyboard_manager});
75 keyboard_manager: keyboard_manager});
75 var contents = new contents.Contents($.extend({
76 var contents = new contents.Contents($.extend({
76 events: events},
77 events: events},
77 common_options));
78 common_options));
78 var notebook = new notebook.Notebook('div#notebook', $.extend({
79 var notebook = new notebook.Notebook('div#notebook', $.extend({
79 events: events,
80 events: events,
80 keyboard_manager: keyboard_manager,
81 keyboard_manager: keyboard_manager,
81 save_widget: save_widget,
82 save_widget: save_widget,
82 contents: contents,
83 contents: contents,
83 config: user_config},
84 config: user_config},
84 common_options));
85 common_options));
85 var login_widget = new loginwidget.LoginWidget('span#login_widget', common_options);
86 var login_widget = new loginwidget.LoginWidget('span#login_widget', common_options);
86 var toolbar = new maintoolbar.MainToolBar('#maintoolbar-container', {
87 var toolbar = new maintoolbar.MainToolBar('#maintoolbar-container', {
87 notebook: notebook,
88 notebook: notebook,
88 events: events});
89 events: events});
89 var quick_help = new quickhelp.QuickHelp({
90 var quick_help = new quickhelp.QuickHelp({
90 keyboard_manager: keyboard_manager,
91 keyboard_manager: keyboard_manager,
91 events: events,
92 events: events,
92 notebook: notebook});
93 notebook: notebook});
93 var menubar = new menubar.MenuBar('#menubar', $.extend({
94 var menubar = new menubar.MenuBar('#menubar', $.extend({
94 notebook: notebook,
95 notebook: notebook,
95 contents: contents,
96 contents: contents,
96 layout_manager: layout_manager,
97 layout_manager: layout_manager,
97 events: events,
98 events: events,
98 save_widget: save_widget,
99 save_widget: save_widget,
99 quick_help: quick_help},
100 quick_help: quick_help},
100 common_options));
101 common_options));
101 var notification_area = new notificationarea.NotificationArea(
102 var notification_area = new notificationarea.NotificationArea(
102 '#notification_area', {
103 '#notification_area', {
103 events: events,
104 events: events,
104 save_widget: save_widget,
105 save_widget: save_widget,
105 notebook: notebook,
106 notebook: notebook,
106 keyboard_manager: keyboard_manager});
107 keyboard_manager: keyboard_manager});
107 notification_area.init_notification_widgets();
108 notification_area.init_notification_widgets();
108 var kernel_selector = new kernelselector.KernelSelector(
109 var kernel_selector = new kernelselector.KernelSelector(
109 '#kernel_selector_widget', notebook);
110 '#kernel_selector_widget', notebook);
110
111
111 $('body').append('<div id="fonttest"><pre><span id="test1">x</span>'+
112 $('body').append('<div id="fonttest"><pre><span id="test1">x</span>'+
112 '<span id="test2" style="font-weight: bold;">x</span>'+
113 '<span id="test2" style="font-weight: bold;">x</span>'+
113 '<span id="test3" style="font-style: italic;">x</span></pre></div>');
114 '<span id="test3" style="font-style: italic;">x</span></pre></div>');
114 var nh = $('#test1').innerHeight();
115 var nh = $('#test1').innerHeight();
115 var bh = $('#test2').innerHeight();
116 var bh = $('#test2').innerHeight();
116 var ih = $('#test3').innerHeight();
117 var ih = $('#test3').innerHeight();
117 if(nh != bh || nh != ih) {
118 if(nh != bh || nh != ih) {
118 $('head').append('<style>.CodeMirror span { vertical-align: bottom; }</style>');
119 $('head').append('<style>.CodeMirror span { vertical-align: bottom; }</style>');
119 }
120 }
120 $('#fonttest').remove();
121 $('#fonttest').remove();
121
122
122 page.show();
123 page.show();
123
124
124 layout_manager.do_resize();
125 layout_manager.do_resize();
125 var first_load = function () {
126 var first_load = function () {
126 layout_manager.do_resize();
127 layout_manager.do_resize();
127 var hash = document.location.hash;
128 var hash = document.location.hash;
128 if (hash) {
129 if (hash) {
129 document.location.hash = '';
130 document.location.hash = '';
130 document.location.hash = hash;
131 document.location.hash = hash;
131 }
132 }
132 notebook.set_autosave_interval(notebook.minimum_autosave_interval);
133 notebook.set_autosave_interval(notebook.minimum_autosave_interval);
133 // only do this once
134 // only do this once
134 events.off('notebook_loaded.Notebook', first_load);
135 events.off('notebook_loaded.Notebook', first_load);
135 };
136 };
136 events.on('notebook_loaded.Notebook', first_load);
137 events.on('notebook_loaded.Notebook', first_load);
137
138
138 IPython.page = page;
139 IPython.page = page;
139 IPython.layout_manager = layout_manager;
140 IPython.layout_manager = layout_manager;
140 IPython.notebook = notebook;
141 IPython.notebook = notebook;
141 IPython.contents = contents;
142 IPython.contents = contents;
142 IPython.pager = pager;
143 IPython.pager = pager;
143 IPython.quick_help = quick_help;
144 IPython.quick_help = quick_help;
144 IPython.login_widget = login_widget;
145 IPython.login_widget = login_widget;
145 IPython.menubar = menubar;
146 IPython.menubar = menubar;
146 IPython.toolbar = toolbar;
147 IPython.toolbar = toolbar;
147 IPython.notification_area = notification_area;
148 IPython.notification_area = notification_area;
148 IPython.keyboard_manager = keyboard_manager;
149 IPython.keyboard_manager = keyboard_manager;
149 IPython.save_widget = save_widget;
150 IPython.save_widget = save_widget;
150 IPython.config = user_config;
151 IPython.config = user_config;
151 IPython.tooltip = notebook.tooltip;
152 IPython.tooltip = notebook.tooltip;
152
153
153 events.trigger('app_initialized.NotebookApp');
154 events.trigger('app_initialized.NotebookApp');
154 notebook.load_notebook(common_options.notebook_path);
155 notebook.load_notebook(common_options.notebook_path);
155
156
156 });
157 });
@@ -1,194 +1,193
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 'base/js/utils',
7 'base/js/utils',
8 'rsvp',
8 ], function(IPython, $, utils) {
9 ], function(IPython, $, utils, rsvp) {
10 "use strict";
9 "use strict";
11
10
12 //-----------------------------------------------------------------------
11 //-----------------------------------------------------------------------
13 // CommManager class
12 // CommManager class
14 //-----------------------------------------------------------------------
13 //-----------------------------------------------------------------------
15
14
16 var CommManager = function (kernel) {
15 var CommManager = function (kernel) {
17 this.comms = {};
16 this.comms = {};
18 this.targets = {};
17 this.targets = {};
19 if (kernel !== undefined) {
18 if (kernel !== undefined) {
20 this.init_kernel(kernel);
19 this.init_kernel(kernel);
21 }
20 }
22 };
21 };
23
22
24 CommManager.prototype.init_kernel = function (kernel) {
23 CommManager.prototype.init_kernel = function (kernel) {
25 // connect the kernel, and register message handlers
24 // connect the kernel, and register message handlers
26 this.kernel = kernel;
25 this.kernel = kernel;
27 var msg_types = ['comm_open', 'comm_msg', 'comm_close'];
26 var msg_types = ['comm_open', 'comm_msg', 'comm_close'];
28 for (var i = 0; i < msg_types.length; i++) {
27 for (var i = 0; i < msg_types.length; i++) {
29 var msg_type = msg_types[i];
28 var msg_type = msg_types[i];
30 kernel.register_iopub_handler(msg_type, $.proxy(this[msg_type], this));
29 kernel.register_iopub_handler(msg_type, $.proxy(this[msg_type], this));
31 }
30 }
32 };
31 };
33
32
34 CommManager.prototype.new_comm = function (target_name, data, callbacks, metadata) {
33 CommManager.prototype.new_comm = function (target_name, data, callbacks, metadata) {
35 // Create a new Comm, register it, and open its Kernel-side counterpart
34 // Create a new Comm, register it, and open its Kernel-side counterpart
36 // Mimics the auto-registration in `Comm.__init__` in the IPython Comm
35 // Mimics the auto-registration in `Comm.__init__` in the IPython Comm
37 var comm = new Comm(target_name);
36 var comm = new Comm(target_name);
38 this.register_comm(comm);
37 this.register_comm(comm);
39 comm.open(data, callbacks, metadata);
38 comm.open(data, callbacks, metadata);
40 return comm;
39 return comm;
41 };
40 };
42
41
43 CommManager.prototype.register_target = function (target_name, f) {
42 CommManager.prototype.register_target = function (target_name, f) {
44 // Register a target function for a given target name
43 // Register a target function for a given target name
45 this.targets[target_name] = f;
44 this.targets[target_name] = f;
46 };
45 };
47
46
48 CommManager.prototype.unregister_target = function (target_name, f) {
47 CommManager.prototype.unregister_target = function (target_name, f) {
49 // Unregister a target function for a given target name
48 // Unregister a target function for a given target name
50 delete this.targets[target_name];
49 delete this.targets[target_name];
51 };
50 };
52
51
53 CommManager.prototype.register_comm = function (comm) {
52 CommManager.prototype.register_comm = function (comm) {
54 // Register a comm in the mapping
53 // Register a comm in the mapping
55 this.comms[comm.comm_id] = comm;
54 this.comms[comm.comm_id] = comm;
56 comm.kernel = this.kernel;
55 comm.kernel = this.kernel;
57 return comm.comm_id;
56 return comm.comm_id;
58 };
57 };
59
58
60 CommManager.prototype.unregister_comm = function (comm) {
59 CommManager.prototype.unregister_comm = function (comm) {
61 // Remove a comm from the mapping
60 // Remove a comm from the mapping
62 delete this.comms[comm.comm_id];
61 delete this.comms[comm.comm_id];
63 };
62 };
64
63
65 // comm message handlers
64 // comm message handlers
66
65
67 CommManager.prototype.comm_open = function (msg) {
66 CommManager.prototype.comm_open = function (msg) {
68 var content = msg.content;
67 var content = msg.content;
69 var that = this;
68 var that = this;
70
69
71 return utils.load_class(content.target_name, content.target_module,
70 return utils.load_class(content.target_name, content.target_module,
72 this.targets).then(function(target) {
71 this.targets).then(function(target) {
73
72
74 var comm = new Comm(content.target_name, content.comm_id);
73 var comm = new Comm(content.target_name, content.comm_id);
75 that.register_comm(comm);
74 that.register_comm(comm);
76 try {
75 try {
77 target(comm, msg);
76 target(comm, msg);
78 } catch (e) {
77 } catch (e) {
79 comm.close();
78 comm.close();
80 that.unregister_comm(comm);
79 that.unregister_comm(comm);
81 var wrapped_error = new utils.WrappedError("Exception opening new comm", e);
80 var wrapped_error = new utils.WrappedError("Exception opening new comm", e);
82 console.error(wrapped_error);
81 console.error(wrapped_error);
83 return rsvp.Promise.reject(wrapped_error);
82 return Promise.reject(wrapped_error);
84 }
83 }
85 return comm;
84 return comm;
86 }, utils.reject('Could not open comm', true));
85 }, utils.reject('Could not open comm', true));
87 };
86 };
88
87
89 CommManager.prototype.comm_close = function (msg) {
88 CommManager.prototype.comm_close = function (msg) {
90 var content = msg.content;
89 var content = msg.content;
91 var comm = this.comms[content.comm_id];
90 var comm = this.comms[content.comm_id];
92 if (comm === undefined) {
91 if (comm === undefined) {
93 return;
92 return;
94 }
93 }
95 this.unregister_comm(comm);
94 this.unregister_comm(comm);
96 try {
95 try {
97 comm.handle_close(msg);
96 comm.handle_close(msg);
98 } catch (e) {
97 } catch (e) {
99 console.log("Exception closing comm: ", e, e.stack, msg);
98 console.log("Exception closing comm: ", e, e.stack, msg);
100 }
99 }
101 };
100 };
102
101
103 CommManager.prototype.comm_msg = function (msg) {
102 CommManager.prototype.comm_msg = function (msg) {
104 var content = msg.content;
103 var content = msg.content;
105 var comm = this.comms[content.comm_id];
104 var comm = this.comms[content.comm_id];
106 if (comm === undefined) {
105 if (comm === undefined) {
107 return;
106 return;
108 }
107 }
109 try {
108 try {
110 comm.handle_msg(msg);
109 comm.handle_msg(msg);
111 } catch (e) {
110 } catch (e) {
112 console.log("Exception handling comm msg: ", e, e.stack, msg);
111 console.log("Exception handling comm msg: ", e, e.stack, msg);
113 }
112 }
114 };
113 };
115
114
116 //-----------------------------------------------------------------------
115 //-----------------------------------------------------------------------
117 // Comm base class
116 // Comm base class
118 //-----------------------------------------------------------------------
117 //-----------------------------------------------------------------------
119
118
120 var Comm = function (target_name, comm_id) {
119 var Comm = function (target_name, comm_id) {
121 this.target_name = target_name;
120 this.target_name = target_name;
122 this.comm_id = comm_id || utils.uuid();
121 this.comm_id = comm_id || utils.uuid();
123 this._msg_callback = this._close_callback = null;
122 this._msg_callback = this._close_callback = null;
124 };
123 };
125
124
126 // methods for sending messages
125 // methods for sending messages
127 Comm.prototype.open = function (data, callbacks, metadata) {
126 Comm.prototype.open = function (data, callbacks, metadata) {
128 var content = {
127 var content = {
129 comm_id : this.comm_id,
128 comm_id : this.comm_id,
130 target_name : this.target_name,
129 target_name : this.target_name,
131 data : data || {},
130 data : data || {},
132 };
131 };
133 return this.kernel.send_shell_message("comm_open", content, callbacks, metadata);
132 return this.kernel.send_shell_message("comm_open", content, callbacks, metadata);
134 };
133 };
135
134
136 Comm.prototype.send = function (data, callbacks, metadata, buffers) {
135 Comm.prototype.send = function (data, callbacks, metadata, buffers) {
137 var content = {
136 var content = {
138 comm_id : this.comm_id,
137 comm_id : this.comm_id,
139 data : data || {},
138 data : data || {},
140 };
139 };
141 return this.kernel.send_shell_message("comm_msg", content, callbacks, metadata, buffers);
140 return this.kernel.send_shell_message("comm_msg", content, callbacks, metadata, buffers);
142 };
141 };
143
142
144 Comm.prototype.close = function (data, callbacks, metadata) {
143 Comm.prototype.close = function (data, callbacks, metadata) {
145 var content = {
144 var content = {
146 comm_id : this.comm_id,
145 comm_id : this.comm_id,
147 data : data || {},
146 data : data || {},
148 };
147 };
149 return this.kernel.send_shell_message("comm_close", content, callbacks, metadata);
148 return this.kernel.send_shell_message("comm_close", content, callbacks, metadata);
150 };
149 };
151
150
152 // methods for registering callbacks for incoming messages
151 // methods for registering callbacks for incoming messages
153 Comm.prototype._register_callback = function (key, callback) {
152 Comm.prototype._register_callback = function (key, callback) {
154 this['_' + key + '_callback'] = callback;
153 this['_' + key + '_callback'] = callback;
155 };
154 };
156
155
157 Comm.prototype.on_msg = function (callback) {
156 Comm.prototype.on_msg = function (callback) {
158 this._register_callback('msg', callback);
157 this._register_callback('msg', callback);
159 };
158 };
160
159
161 Comm.prototype.on_close = function (callback) {
160 Comm.prototype.on_close = function (callback) {
162 this._register_callback('close', callback);
161 this._register_callback('close', callback);
163 };
162 };
164
163
165 // methods for handling incoming messages
164 // methods for handling incoming messages
166
165
167 Comm.prototype._maybe_callback = function (key, msg) {
166 Comm.prototype._maybe_callback = function (key, msg) {
168 var callback = this['_' + key + '_callback'];
167 var callback = this['_' + key + '_callback'];
169 if (callback) {
168 if (callback) {
170 try {
169 try {
171 callback(msg);
170 callback(msg);
172 } catch (e) {
171 } catch (e) {
173 console.log("Exception in Comm callback", e, e.stack, msg);
172 console.log("Exception in Comm callback", e, e.stack, msg);
174 }
173 }
175 }
174 }
176 };
175 };
177
176
178 Comm.prototype.handle_msg = function (msg) {
177 Comm.prototype.handle_msg = function (msg) {
179 this._maybe_callback('msg', msg);
178 this._maybe_callback('msg', msg);
180 };
179 };
181
180
182 Comm.prototype.handle_close = function (msg) {
181 Comm.prototype.handle_close = function (msg) {
183 this._maybe_callback('close', msg);
182 this._maybe_callback('close', msg);
184 };
183 };
185
184
186 // For backwards compatability.
185 // For backwards compatability.
187 IPython.CommManager = CommManager;
186 IPython.CommManager = CommManager;
188 IPython.Comm = Comm;
187 IPython.Comm = Comm;
189
188
190 return {
189 return {
191 'CommManager': CommManager,
190 'CommManager': CommManager,
192 'Comm': Comm
191 'Comm': Comm
193 };
192 };
194 });
193 });
@@ -1,254 +1,253
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 'rsvp',
10 ], function (_, Backbone, $, utils, IPython) {
11 ], function (_, Backbone, $, utils, IPython, rsvp) {
12 "use strict";
11 "use strict";
13 //--------------------------------------------------------------------
12 //--------------------------------------------------------------------
14 // WidgetManager class
13 // WidgetManager class
15 //--------------------------------------------------------------------
14 //--------------------------------------------------------------------
16 var WidgetManager = function (comm_manager, notebook) {
15 var WidgetManager = function (comm_manager, notebook) {
17 // Public constructor
16 // Public constructor
18 WidgetManager._managers.push(this);
17 WidgetManager._managers.push(this);
19
18
20 // Attach a comm manager to the
19 // Attach a comm manager to the
21 this.keyboard_manager = notebook.keyboard_manager;
20 this.keyboard_manager = notebook.keyboard_manager;
22 this.notebook = notebook;
21 this.notebook = notebook;
23 this.comm_manager = comm_manager;
22 this.comm_manager = comm_manager;
24 this._models = {}; /* Dictionary of model ids and model instances */
23 this._models = {}; /* Dictionary of model ids and model instances */
25
24
26 // Register with the comm manager.
25 // Register with the comm manager.
27 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));
28 };
27 };
29
28
30 //--------------------------------------------------------------------
29 //--------------------------------------------------------------------
31 // Class level
30 // Class level
32 //--------------------------------------------------------------------
31 //--------------------------------------------------------------------
33 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. */
34 WidgetManager._view_types = {}; /* Dictionary of view names and view types. */
33 WidgetManager._view_types = {}; /* Dictionary of view names and view types. */
35 WidgetManager._managers = []; /* List of widget managers */
34 WidgetManager._managers = []; /* List of widget managers */
36
35
37 WidgetManager.register_widget_model = function (model_name, model_type) {
36 WidgetManager.register_widget_model = function (model_name, model_type) {
38 // Registers a widget model by name.
37 // Registers a widget model by name.
39 WidgetManager._model_types[model_name] = model_type;
38 WidgetManager._model_types[model_name] = model_type;
40 };
39 };
41
40
42 WidgetManager.register_widget_view = function (view_name, view_type) {
41 WidgetManager.register_widget_view = function (view_name, view_type) {
43 // Registers a widget view by name.
42 // Registers a widget view by name.
44 WidgetManager._view_types[view_name] = view_type;
43 WidgetManager._view_types[view_name] = view_type;
45 };
44 };
46
45
47 //--------------------------------------------------------------------
46 //--------------------------------------------------------------------
48 // Instance level
47 // Instance level
49 //--------------------------------------------------------------------
48 //--------------------------------------------------------------------
50 WidgetManager.prototype.display_view = function(msg, model) {
49 WidgetManager.prototype.display_view = function(msg, model) {
51 // Displays a view for a particular model.
50 // Displays a view for a particular model.
52 var that = this;
51 var that = this;
53 return new rsvp.Promise(function(resolve, reject) {
52 return new Promise(function(resolve, reject) {
54 var cell = that.get_msg_cell(msg.parent_header.msg_id);
53 var cell = that.get_msg_cell(msg.parent_header.msg_id);
55 if (cell === null) {
54 if (cell === null) {
56 reject(new Error("Could not determine where the display" +
55 reject(new Error("Could not determine where the display" +
57 " message was from. Widget will not be displayed"));
56 " message was from. Widget will not be displayed"));
58 } else {
57 } else {
59 var dummy = null;
58 var dummy = null;
60 if (cell.widget_subarea) {
59 if (cell.widget_subarea) {
61 dummy = $('<div />');
60 dummy = $('<div />');
62 cell.widget_subarea.append(dummy);
61 cell.widget_subarea.append(dummy);
63 }
62 }
64
63
65 that.create_view(model, {cell: cell}).then(function(view) {
64 that.create_view(model, {cell: cell}).then(function(view) {
66 that._handle_display_view(view);
65 that._handle_display_view(view);
67 if (dummy) {
66 if (dummy) {
68 dummy.replaceWith(view.$el);
67 dummy.replaceWith(view.$el);
69 }
68 }
70 view.trigger('displayed');
69 view.trigger('displayed');
71 resolve(view);
70 resolve(view);
72 }, function(error) {
71 }, function(error) {
73 reject(new utils.WrappedError('Could not display view', error));
72 reject(new utils.WrappedError('Could not display view', error));
74 });
73 });
75 }
74 }
76 });
75 });
77 };
76 };
78
77
79 WidgetManager.prototype._handle_display_view = function (view) {
78 WidgetManager.prototype._handle_display_view = function (view) {
80 // Have the IPython keyboard manager disable its event
79 // Have the IPython keyboard manager disable its event
81 // handling so the widget can capture keyboard input.
80 // handling so the widget can capture keyboard input.
82 // Note, this is only done on the outer most widgets.
81 // Note, this is only done on the outer most widgets.
83 if (this.keyboard_manager) {
82 if (this.keyboard_manager) {
84 this.keyboard_manager.register_events(view.$el);
83 this.keyboard_manager.register_events(view.$el);
85
84
86 if (view.additional_elements) {
85 if (view.additional_elements) {
87 for (var i = 0; i < view.additional_elements.length; i++) {
86 for (var i = 0; i < view.additional_elements.length; i++) {
88 this.keyboard_manager.register_events(view.additional_elements[i]);
87 this.keyboard_manager.register_events(view.additional_elements[i]);
89 }
88 }
90 }
89 }
91 }
90 }
92 };
91 };
93
92
94 WidgetManager.prototype.create_view = function(model, options) {
93 WidgetManager.prototype.create_view = function(model, options) {
95 // Creates a promise for a view of a given model
94 // Creates a promise for a view of a given model
96
95
97 // Make sure the view creation is not out of order with
96 // Make sure the view creation is not out of order with
98 // any state updates.
97 // any state updates.
99 model.state_change = model.state_change.then(function() {
98 model.state_change = model.state_change.then(function() {
100 try {
99 try {
101 console.log('create_view ' + model.id);
100 console.log('create_view ' + model.id);
102 console.log(' _view_name ' + model.get('_view_name'));
101 console.log(' _view_name ' + model.get('_view_name'));
103 console.log(' _view_module ' + model.get('_view_module'));
102 console.log(' _view_module ' + model.get('_view_module'));
104 } catch (e) { }
103 } catch (e) { }
105
104
106 return utils.load_class(model.get('_view_name'), model.get('_view_module'),
105 return utils.load_class(model.get('_view_name'), model.get('_view_module'),
107 WidgetManager._view_types).then(function(ViewType) {
106 WidgetManager._view_types).then(function(ViewType) {
108
107
109 // If a view is passed into the method, use that view's cell as
108 // If a view is passed into the method, use that view's cell as
110 // the cell for the view that is created.
109 // the cell for the view that is created.
111 options = options || {};
110 options = options || {};
112 if (options.parent !== undefined) {
111 if (options.parent !== undefined) {
113 options.cell = options.parent.options.cell;
112 options.cell = options.parent.options.cell;
114 }
113 }
115 // Create and render the view...
114 // Create and render the view...
116 var parameters = {model: model, options: options};
115 var parameters = {model: model, options: options};
117 var view = new ViewType(parameters);
116 var view = new ViewType(parameters);
118 view.listenTo(model, 'destroy', view.remove);
117 view.listenTo(model, 'destroy', view.remove);
119 view.render();
118 view.render();
120 return view;
119 return view;
121 }, utils.reject("Couldn't create a view for model id '" + String(model.id) + "'"));
120 }, utils.reject("Couldn't create a view for model id '" + String(model.id) + "'"));
122 });
121 });
123 return model.state_change;
122 return model.state_change;
124 };
123 };
125
124
126 WidgetManager.prototype.get_msg_cell = function (msg_id) {
125 WidgetManager.prototype.get_msg_cell = function (msg_id) {
127 var cell = null;
126 var cell = null;
128 // First, check to see if the msg was triggered by cell execution.
127 // First, check to see if the msg was triggered by cell execution.
129 if (this.notebook) {
128 if (this.notebook) {
130 cell = this.notebook.get_msg_cell(msg_id);
129 cell = this.notebook.get_msg_cell(msg_id);
131 }
130 }
132 if (cell !== null) {
131 if (cell !== null) {
133 return cell;
132 return cell;
134 }
133 }
135 // Second, check to see if a get_cell callback was defined
134 // Second, check to see if a get_cell callback was defined
136 // for the message. get_cell callbacks are registered for
135 // for the message. get_cell callbacks are registered for
137 // widget messages, so this block is actually checking to see if the
136 // widget messages, so this block is actually checking to see if the
138 // message was triggered by a widget.
137 // message was triggered by a widget.
139 var kernel = this.comm_manager.kernel;
138 var kernel = this.comm_manager.kernel;
140 if (kernel) {
139 if (kernel) {
141 var callbacks = kernel.get_callbacks_for_msg(msg_id);
140 var callbacks = kernel.get_callbacks_for_msg(msg_id);
142 if (callbacks && callbacks.iopub &&
141 if (callbacks && callbacks.iopub &&
143 callbacks.iopub.get_cell !== undefined) {
142 callbacks.iopub.get_cell !== undefined) {
144 return callbacks.iopub.get_cell();
143 return callbacks.iopub.get_cell();
145 }
144 }
146 }
145 }
147
146
148 // Not triggered by a cell or widget (no get_cell callback
147 // Not triggered by a cell or widget (no get_cell callback
149 // exists).
148 // exists).
150 return null;
149 return null;
151 };
150 };
152
151
153 WidgetManager.prototype.callbacks = function (view) {
152 WidgetManager.prototype.callbacks = function (view) {
154 // callback handlers specific a view
153 // callback handlers specific a view
155 var callbacks = {};
154 var callbacks = {};
156 if (view && view.options.cell) {
155 if (view && view.options.cell) {
157
156
158 // Try to get output handlers
157 // Try to get output handlers
159 var cell = view.options.cell;
158 var cell = view.options.cell;
160 var handle_output = null;
159 var handle_output = null;
161 var handle_clear_output = null;
160 var handle_clear_output = null;
162 if (cell.output_area) {
161 if (cell.output_area) {
163 handle_output = $.proxy(cell.output_area.handle_output, cell.output_area);
162 handle_output = $.proxy(cell.output_area.handle_output, cell.output_area);
164 handle_clear_output = $.proxy(cell.output_area.handle_clear_output, cell.output_area);
163 handle_clear_output = $.proxy(cell.output_area.handle_clear_output, cell.output_area);
165 }
164 }
166
165
167 // Create callback dictionary using what is known
166 // Create callback dictionary using what is known
168 var that = this;
167 var that = this;
169 callbacks = {
168 callbacks = {
170 iopub : {
169 iopub : {
171 output : handle_output,
170 output : handle_output,
172 clear_output : handle_clear_output,
171 clear_output : handle_clear_output,
173
172
174 // Special function only registered by widget messages.
173 // Special function only registered by widget messages.
175 // Allows us to get the cell for a message so we know
174 // Allows us to get the cell for a message so we know
176 // where to add widgets if the code requires it.
175 // where to add widgets if the code requires it.
177 get_cell : function () {
176 get_cell : function () {
178 return cell;
177 return cell;
179 },
178 },
180 },
179 },
181 };
180 };
182 }
181 }
183 return callbacks;
182 return callbacks;
184 };
183 };
185
184
186 WidgetManager.prototype.get_model = function (model_id) {
185 WidgetManager.prototype.get_model = function (model_id) {
187 return this._models[model_id];
186 return this._models[model_id];
188 };
187 };
189
188
190 WidgetManager.prototype._handle_comm_open = function (comm, msg) {
189 WidgetManager.prototype._handle_comm_open = function (comm, msg) {
191 // Handle when a comm is opened.
190 // Handle when a comm is opened.
192 this.create_model({
191 this.create_model({
193 model_name: msg.content.data.model_name,
192 model_name: msg.content.data.model_name,
194 model_module: msg.content.data.model_module,
193 model_module: msg.content.data.model_module,
195 comm: comm}).catch($.proxy(console.error, console));
194 comm: comm}).catch($.proxy(console.error, console));
196 };
195 };
197
196
198 WidgetManager.prototype.create_model = function (options) {
197 WidgetManager.prototype.create_model = function (options) {
199 // Create and return a promise for a new widget model
198 // Create and return a promise for a new widget model
200 //
199 //
201 // Minimally, one must provide the model_name and widget_class
200 // Minimally, one must provide the model_name and widget_class
202 // parameters to create a model from Javascript.
201 // parameters to create a model from Javascript.
203 //
202 //
204 // Example
203 // Example
205 // --------
204 // --------
206 // JS:
205 // JS:
207 // IPython.notebook.kernel.widget_manager.create_model({
206 // IPython.notebook.kernel.widget_manager.create_model({
208 // model_name: 'WidgetModel',
207 // model_name: 'WidgetModel',
209 // widget_class: 'IPython.html.widgets.widget_int.IntSlider'})
208 // widget_class: 'IPython.html.widgets.widget_int.IntSlider'})
210 // .then(function(model) { console.log('Create success!', model); },
209 // .then(function(model) { console.log('Create success!', model); },
211 // $.proxy(console.error, console));
210 // $.proxy(console.error, console));
212 //
211 //
213 // Parameters
212 // Parameters
214 // ----------
213 // ----------
215 // options: dictionary
214 // options: dictionary
216 // Dictionary of options with the following contents:
215 // Dictionary of options with the following contents:
217 // model_name: string
216 // model_name: string
218 // Target name of the widget model to create.
217 // Target name of the widget model to create.
219 // model_module: (optional) string
218 // model_module: (optional) string
220 // Module name of the widget model to create.
219 // Module name of the widget model to create.
221 // widget_class: (optional) string
220 // widget_class: (optional) string
222 // Target name of the widget in the back-end.
221 // Target name of the widget in the back-end.
223 // comm: (optional) Comm
222 // comm: (optional) Comm
224
223
225 // Create a comm if it wasn't provided.
224 // Create a comm if it wasn't provided.
226 var comm = options.comm;
225 var comm = options.comm;
227 if (!comm) {
226 if (!comm) {
228 comm = this.comm_manager.new_comm('ipython.widget', {'widget_class': options.widget_class});
227 comm = this.comm_manager.new_comm('ipython.widget', {'widget_class': options.widget_class});
229 }
228 }
230
229
231 var that = this;
230 var that = this;
232 var model_id = comm.comm_id;
231 var model_id = comm.comm_id;
233 var model_promise = utils.load_class(options.model_name, options.model_module, WidgetManager._model_types)
232 var model_promise = utils.load_class(options.model_name, options.model_module, WidgetManager._model_types)
234 .then(function(ModelType) {
233 .then(function(ModelType) {
235 var widget_model = new ModelType(that, model_id, comm);
234 var widget_model = new ModelType(that, model_id, comm);
236 widget_model.once('comm:close', function () {
235 widget_model.once('comm:close', function () {
237 delete that._models[model_id];
236 delete that._models[model_id];
238 });
237 });
239 return widget_model;
238 return widget_model;
240
239
241 }, function(error) {
240 }, function(error) {
242 delete that._models[model_id];
241 delete that._models[model_id];
243 var wrapped_error = new utils.WrappedError("Couldn't create model", error);
242 var wrapped_error = new utils.WrappedError("Couldn't create model", error);
244 return rsvp.Promise.reject(wrapped_error);
243 return Promise.reject(wrapped_error);
245 });
244 });
246 this._models[model_id] = model_promise;
245 this._models[model_id] = model_promise;
247 return model_promise;
246 return model_promise;
248 };
247 };
249
248
250 // Backwards compatibility.
249 // Backwards compatibility.
251 IPython.WidgetManager = WidgetManager;
250 IPython.WidgetManager = WidgetManager;
252
251
253 return {'WidgetManager': WidgetManager};
252 return {'WidgetManager': WidgetManager};
254 });
253 });
@@ -1,609 +1,608
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(["widgets/js/manager",
4 define(["widgets/js/manager",
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 "rsvp",
10 ], function(widgetmanager, _, Backbone, $, utils, IPython){
11 ], function(widgetmanager, _, Backbone, $, utils, IPython, rsvp){
12
11
13 var WidgetModel = Backbone.Model.extend({
12 var WidgetModel = Backbone.Model.extend({
14 constructor: function (widget_manager, model_id, comm) {
13 constructor: function (widget_manager, model_id, comm) {
15 // Constructor
14 // Constructor
16 //
15 //
17 // Creates a WidgetModel instance.
16 // Creates a WidgetModel instance.
18 //
17 //
19 // Parameters
18 // Parameters
20 // ----------
19 // ----------
21 // widget_manager : WidgetManager instance
20 // widget_manager : WidgetManager instance
22 // model_id : string
21 // model_id : string
23 // An ID unique to this model.
22 // An ID unique to this model.
24 // comm : Comm instance (optional)
23 // comm : Comm instance (optional)
25 this.widget_manager = widget_manager;
24 this.widget_manager = widget_manager;
26 this.state_change = rsvp.Promise.resolve();
25 this.state_change = Promise.resolve();
27 this._buffered_state_diff = {};
26 this._buffered_state_diff = {};
28 this.pending_msgs = 0;
27 this.pending_msgs = 0;
29 this.msg_buffer = null;
28 this.msg_buffer = null;
30 this.state_lock = null;
29 this.state_lock = null;
31 this.id = model_id;
30 this.id = model_id;
32 this.views = {};
31 this.views = {};
33
32
34 if (comm !== undefined) {
33 if (comm !== undefined) {
35 // Remember comm associated with the model.
34 // Remember comm associated with the model.
36 this.comm = comm;
35 this.comm = comm;
37 comm.model = this;
36 comm.model = this;
38
37
39 // Hook comm messages up to model.
38 // Hook comm messages up to model.
40 comm.on_close($.proxy(this._handle_comm_closed, this));
39 comm.on_close($.proxy(this._handle_comm_closed, this));
41 comm.on_msg($.proxy(this._handle_comm_msg, this));
40 comm.on_msg($.proxy(this._handle_comm_msg, this));
42 }
41 }
43 return Backbone.Model.apply(this);
42 return Backbone.Model.apply(this);
44 },
43 },
45
44
46 send: function (content, callbacks) {
45 send: function (content, callbacks) {
47 // Send a custom msg over the comm.
46 // Send a custom msg over the comm.
48 if (this.comm !== undefined) {
47 if (this.comm !== undefined) {
49 var data = {method: 'custom', content: content};
48 var data = {method: 'custom', content: content};
50 this.comm.send(data, callbacks);
49 this.comm.send(data, callbacks);
51 this.pending_msgs++;
50 this.pending_msgs++;
52 }
51 }
53 },
52 },
54
53
55 _handle_comm_closed: function (msg) {
54 _handle_comm_closed: function (msg) {
56 // Handle when a widget is closed.
55 // Handle when a widget is closed.
57 this.trigger('comm:close');
56 this.trigger('comm:close');
58 this.stopListening();
57 this.stopListening();
59 this.trigger('destroy', this);
58 this.trigger('destroy', this);
60 delete this.comm.model; // Delete ref so GC will collect widget model.
59 delete this.comm.model; // Delete ref so GC will collect widget model.
61 delete this.comm;
60 delete this.comm;
62 delete this.model_id; // Delete id from model so widget manager cleans up.
61 delete this.model_id; // Delete id from model so widget manager cleans up.
63 for (var id in this.views) {
62 for (var id in this.views) {
64 if (this.views.hasOwnProperty(id)) {
63 if (this.views.hasOwnProperty(id)) {
65 this.views[id].remove();
64 this.views[id].remove();
66 }
65 }
67 }
66 }
68 },
67 },
69
68
70 _handle_comm_msg: function (msg) {
69 _handle_comm_msg: function (msg) {
71 // Handle incoming comm msg.
70 // Handle incoming comm msg.
72 var method = msg.content.data.method;
71 var method = msg.content.data.method;
73 console.log(method);
72 console.log(method);
74 var that = this;
73 var that = this;
75 switch (method) {
74 switch (method) {
76 case 'update':
75 case 'update':
77 this.state_change = this.state_change.then(function() {
76 this.state_change = this.state_change.then(function() {
78 return that.set_state(msg.content.data.state);
77 return that.set_state(msg.content.data.state);
79 }).catch(utils.reject("Couldn't process update msg for model id '" + String(that.id) + "'", true));
78 }).catch(utils.reject("Couldn't process update msg for model id '" + String(that.id) + "'", true));
80 break;
79 break;
81 case 'custom':
80 case 'custom':
82 this.trigger('msg:custom', msg.content.data.content);
81 this.trigger('msg:custom', msg.content.data.content);
83 break;
82 break;
84 case 'display':
83 case 'display':
85 this.state_change = this.state_change.then(function() {
84 this.state_change = this.state_change.then(function() {
86 return that.widget_manager.display_view(msg, that);
85 return that.widget_manager.display_view(msg, that);
87 }).catch(utils.reject("Couldn't process display msg for model id '" + String(that.id) + "'", true));
86 }).catch(utils.reject("Couldn't process display msg for model id '" + String(that.id) + "'", true));
88 break;
87 break;
89 }
88 }
90 },
89 },
91
90
92 set_state: function (state) {
91 set_state: function (state) {
93 var that = this;
92 var that = this;
94 // Handle when a widget is updated via the python side.
93 // Handle when a widget is updated via the python side.
95 return this._unpack_models(state).then(function(state) {
94 return this._unpack_models(state).then(function(state) {
96 that.state_lock = state;
95 that.state_lock = state;
97 try {
96 try {
98 console.log('set_state ' + that.id);
97 console.log('set_state ' + that.id);
99 console.log(state);
98 console.log(state);
100 WidgetModel.__super__.set.call(that, state);
99 WidgetModel.__super__.set.call(that, state);
101 } finally {
100 } finally {
102 that.state_lock = null;
101 that.state_lock = null;
103 }
102 }
104 return rsvp.Promise.resolve();
103 return Promise.resolve();
105 }, utils.reject("Couldn't set model state", true));
104 }, utils.reject("Couldn't set model state", true));
106 },
105 },
107
106
108 _handle_status: function (msg, callbacks) {
107 _handle_status: function (msg, callbacks) {
109 // Handle status msgs.
108 // Handle status msgs.
110
109
111 // execution_state : ('busy', 'idle', 'starting')
110 // execution_state : ('busy', 'idle', 'starting')
112 if (this.comm !== undefined) {
111 if (this.comm !== undefined) {
113 if (msg.content.execution_state ==='idle') {
112 if (msg.content.execution_state ==='idle') {
114 // Send buffer if this message caused another message to be
113 // Send buffer if this message caused another message to be
115 // throttled.
114 // throttled.
116 if (this.msg_buffer !== null &&
115 if (this.msg_buffer !== null &&
117 (this.get('msg_throttle') || 3) === this.pending_msgs) {
116 (this.get('msg_throttle') || 3) === this.pending_msgs) {
118 var data = {method: 'backbone', sync_method: 'update', sync_data: this.msg_buffer};
117 var data = {method: 'backbone', sync_method: 'update', sync_data: this.msg_buffer};
119 this.comm.send(data, callbacks);
118 this.comm.send(data, callbacks);
120 this.msg_buffer = null;
119 this.msg_buffer = null;
121 } else {
120 } else {
122 --this.pending_msgs;
121 --this.pending_msgs;
123 }
122 }
124 }
123 }
125 }
124 }
126 },
125 },
127
126
128 callbacks: function(view) {
127 callbacks: function(view) {
129 // Create msg callbacks for a comm msg.
128 // Create msg callbacks for a comm msg.
130 var callbacks = this.widget_manager.callbacks(view);
129 var callbacks = this.widget_manager.callbacks(view);
131
130
132 if (callbacks.iopub === undefined) {
131 if (callbacks.iopub === undefined) {
133 callbacks.iopub = {};
132 callbacks.iopub = {};
134 }
133 }
135
134
136 var that = this;
135 var that = this;
137 callbacks.iopub.status = function (msg) {
136 callbacks.iopub.status = function (msg) {
138 that._handle_status(msg, callbacks);
137 that._handle_status(msg, callbacks);
139 };
138 };
140 return callbacks;
139 return callbacks;
141 },
140 },
142
141
143 set: function(key, val, options) {
142 set: function(key, val, options) {
144 // Set a value.
143 // Set a value.
145 var return_value = WidgetModel.__super__.set.apply(this, arguments);
144 var return_value = WidgetModel.__super__.set.apply(this, arguments);
146
145
147 // Backbone only remembers the diff of the most recent set()
146 // Backbone only remembers the diff of the most recent set()
148 // operation. Calling set multiple times in a row results in a
147 // operation. Calling set multiple times in a row results in a
149 // loss of diff information. Here we keep our own running diff.
148 // loss of diff information. Here we keep our own running diff.
150 this._buffered_state_diff = $.extend(this._buffered_state_diff, this.changedAttributes() || {});
149 this._buffered_state_diff = $.extend(this._buffered_state_diff, this.changedAttributes() || {});
151 return return_value;
150 return return_value;
152 },
151 },
153
152
154 sync: function (method, model, options) {
153 sync: function (method, model, options) {
155 // Handle sync to the back-end. Called when a model.save() is called.
154 // Handle sync to the back-end. Called when a model.save() is called.
156
155
157 // Make sure a comm exists.
156 // Make sure a comm exists.
158 var error = options.error || function() {
157 var error = options.error || function() {
159 console.error('Backbone sync error:', arguments);
158 console.error('Backbone sync error:', arguments);
160 };
159 };
161 if (this.comm === undefined) {
160 if (this.comm === undefined) {
162 error();
161 error();
163 return false;
162 return false;
164 }
163 }
165
164
166 // Delete any key value pairs that the back-end already knows about.
165 // Delete any key value pairs that the back-end already knows about.
167 var attrs = (method === 'patch') ? options.attrs : model.toJSON(options);
166 var attrs = (method === 'patch') ? options.attrs : model.toJSON(options);
168 if (this.state_lock !== null) {
167 if (this.state_lock !== null) {
169 var keys = Object.keys(this.state_lock);
168 var keys = Object.keys(this.state_lock);
170 for (var i=0; i<keys.length; i++) {
169 for (var i=0; i<keys.length; i++) {
171 var key = keys[i];
170 var key = keys[i];
172 if (attrs[key] === this.state_lock[key]) {
171 if (attrs[key] === this.state_lock[key]) {
173 delete attrs[key];
172 delete attrs[key];
174 }
173 }
175 }
174 }
176 }
175 }
177
176
178 // Only sync if there are attributes to send to the back-end.
177 // Only sync if there are attributes to send to the back-end.
179 attrs = this._pack_models(attrs);
178 attrs = this._pack_models(attrs);
180 if (_.size(attrs) > 0) {
179 if (_.size(attrs) > 0) {
181
180
182 // If this message was sent via backbone itself, it will not
181 // If this message was sent via backbone itself, it will not
183 // have any callbacks. It's important that we create callbacks
182 // have any callbacks. It's important that we create callbacks
184 // so we can listen for status messages, etc...
183 // so we can listen for status messages, etc...
185 var callbacks = options.callbacks || this.callbacks();
184 var callbacks = options.callbacks || this.callbacks();
186
185
187 // Check throttle.
186 // Check throttle.
188 if (this.pending_msgs >= (this.get('msg_throttle') || 3)) {
187 if (this.pending_msgs >= (this.get('msg_throttle') || 3)) {
189 // The throttle has been exceeded, buffer the current msg so
188 // The throttle has been exceeded, buffer the current msg so
190 // it can be sent once the kernel has finished processing
189 // it can be sent once the kernel has finished processing
191 // some of the existing messages.
190 // some of the existing messages.
192
191
193 // Combine updates if it is a 'patch' sync, otherwise replace updates
192 // Combine updates if it is a 'patch' sync, otherwise replace updates
194 switch (method) {
193 switch (method) {
195 case 'patch':
194 case 'patch':
196 this.msg_buffer = $.extend(this.msg_buffer || {}, attrs);
195 this.msg_buffer = $.extend(this.msg_buffer || {}, attrs);
197 break;
196 break;
198 case 'update':
197 case 'update':
199 case 'create':
198 case 'create':
200 this.msg_buffer = attrs;
199 this.msg_buffer = attrs;
201 break;
200 break;
202 default:
201 default:
203 error();
202 error();
204 return false;
203 return false;
205 }
204 }
206 this.msg_buffer_callbacks = callbacks;
205 this.msg_buffer_callbacks = callbacks;
207
206
208 } else {
207 } else {
209 // We haven't exceeded the throttle, send the message like
208 // We haven't exceeded the throttle, send the message like
210 // normal.
209 // normal.
211 var data = {method: 'backbone', sync_data: attrs};
210 var data = {method: 'backbone', sync_data: attrs};
212 this.comm.send(data, callbacks);
211 this.comm.send(data, callbacks);
213 this.pending_msgs++;
212 this.pending_msgs++;
214 }
213 }
215 }
214 }
216 // Since the comm is a one-way communication, assume the message
215 // Since the comm is a one-way communication, assume the message
217 // arrived. Don't call success since we don't have a model back from the server
216 // arrived. Don't call success since we don't have a model back from the server
218 // this means we miss out on the 'sync' event.
217 // this means we miss out on the 'sync' event.
219 this._buffered_state_diff = {};
218 this._buffered_state_diff = {};
220 },
219 },
221
220
222 save_changes: function(callbacks) {
221 save_changes: function(callbacks) {
223 // Push this model's state to the back-end
222 // Push this model's state to the back-end
224 //
223 //
225 // This invokes a Backbone.Sync.
224 // This invokes a Backbone.Sync.
226 this.save(this._buffered_state_diff, {patch: true, callbacks: callbacks});
225 this.save(this._buffered_state_diff, {patch: true, callbacks: callbacks});
227 },
226 },
228
227
229 _pack_models: function(value) {
228 _pack_models: function(value) {
230 // Replace models with model ids recursively.
229 // Replace models with model ids recursively.
231 var that = this;
230 var that = this;
232 var packed;
231 var packed;
233 if (value instanceof Backbone.Model) {
232 if (value instanceof Backbone.Model) {
234 return "IPY_MODEL_" + value.id;
233 return "IPY_MODEL_" + value.id;
235
234
236 } else if ($.isArray(value)) {
235 } else if ($.isArray(value)) {
237 packed = [];
236 packed = [];
238 _.each(value, function(sub_value, key) {
237 _.each(value, function(sub_value, key) {
239 packed.push(that._pack_models(sub_value));
238 packed.push(that._pack_models(sub_value));
240 });
239 });
241 return packed;
240 return packed;
242 } else if (value instanceof Date || value instanceof String) {
241 } else if (value instanceof Date || value instanceof String) {
243 return value;
242 return value;
244 } else if (value instanceof Object) {
243 } else if (value instanceof Object) {
245 packed = {};
244 packed = {};
246 _.each(value, function(sub_value, key) {
245 _.each(value, function(sub_value, key) {
247 packed[key] = that._pack_models(sub_value);
246 packed[key] = that._pack_models(sub_value);
248 });
247 });
249 return packed;
248 return packed;
250
249
251 } else {
250 } else {
252 return value;
251 return value;
253 }
252 }
254 },
253 },
255
254
256 _unpack_models: function(value) {
255 _unpack_models: function(value) {
257 // Replace model ids with models recursively.
256 // Replace model ids with models recursively.
258 var that = this;
257 var that = this;
259 var unpacked;
258 var unpacked;
260 if ($.isArray(value)) {
259 if ($.isArray(value)) {
261 unpacked = [];
260 unpacked = [];
262 _.each(value, function(sub_value, key) {
261 _.each(value, function(sub_value, key) {
263 unpacked.push(that._unpack_models(sub_value));
262 unpacked.push(that._unpack_models(sub_value));
264 });
263 });
265 return rsvp.Promise.all(unpacked);
264 return Promise.all(unpacked);
266 } else if (value instanceof Object) {
265 } else if (value instanceof Object) {
267 unpacked = {};
266 unpacked = {};
268 _.each(value, function(sub_value, key) {
267 _.each(value, function(sub_value, key) {
269 unpacked[key] = that._unpack_models(sub_value);
268 unpacked[key] = that._unpack_models(sub_value);
270 });
269 });
271 return utils.resolve_dict(unpacked);
270 return utils.resolve_dict(unpacked);
272 } else if (typeof value === 'string' && value.slice(0,10) === "IPY_MODEL_") {
271 } else if (typeof value === 'string' && value.slice(0,10) === "IPY_MODEL_") {
273 // get_model returns a promise already
272 // get_model returns a promise already
274 return this.widget_manager.get_model(value.slice(10, value.length));
273 return this.widget_manager.get_model(value.slice(10, value.length));
275 } else {
274 } else {
276 return rsvp.Promise.resolve(value);
275 return Promise.resolve(value);
277 }
276 }
278 },
277 },
279
278
280 on_some_change: function(keys, callback, context) {
279 on_some_change: function(keys, callback, context) {
281 // on_some_change(["key1", "key2"], foo, context) differs from
280 // on_some_change(["key1", "key2"], foo, context) differs from
282 // on("change:key1 change:key2", foo, context).
281 // on("change:key1 change:key2", foo, context).
283 // If the widget attributes key1 and key2 are both modified,
282 // If the widget attributes key1 and key2 are both modified,
284 // the second form will result in foo being called twice
283 // the second form will result in foo being called twice
285 // while the first will call foo only once.
284 // while the first will call foo only once.
286 this.on('change', function() {
285 this.on('change', function() {
287 if (keys.some(this.hasChanged, this)) {
286 if (keys.some(this.hasChanged, this)) {
288 callback.apply(context);
287 callback.apply(context);
289 }
288 }
290 }, this);
289 }, this);
291
290
292 },
291 },
293 });
292 });
294 widgetmanager.WidgetManager.register_widget_model('WidgetModel', WidgetModel);
293 widgetmanager.WidgetManager.register_widget_model('WidgetModel', WidgetModel);
295
294
296
295
297 var WidgetView = Backbone.View.extend({
296 var WidgetView = Backbone.View.extend({
298 initialize: function(parameters) {
297 initialize: function(parameters) {
299 // Public constructor.
298 // Public constructor.
300 this.model.on('change',this.update,this);
299 this.model.on('change',this.update,this);
301 this.options = parameters.options;
300 this.options = parameters.options;
302 this.child_model_views = {};
301 this.child_model_views = {};
303 this.child_views = {};
302 this.child_views = {};
304 this.id = this.id || utils.uuid();
303 this.id = this.id || utils.uuid();
305 this.model.views[this.id] = this;
304 this.model.views[this.id] = this;
306 this.on('displayed', function() {
305 this.on('displayed', function() {
307 this.is_displayed = true;
306 this.is_displayed = true;
308 }, this);
307 }, this);
309 },
308 },
310
309
311 update: function(){
310 update: function(){
312 // Triggered on model change.
311 // Triggered on model change.
313 //
312 //
314 // Update view to be consistent with this.model
313 // Update view to be consistent with this.model
315 },
314 },
316
315
317 create_child_view: function(child_model, options) {
316 create_child_view: function(child_model, options) {
318 // Create and promise that resolves to a child view of a given model
317 // Create and promise that resolves to a child view of a given model
319 var that = this;
318 var that = this;
320 options = $.extend({ parent: this }, options || {});
319 options = $.extend({ parent: this }, options || {});
321 return this.model.widget_manager.create_view(child_model, options).then(function(child_view) {
320 return this.model.widget_manager.create_view(child_model, options).then(function(child_view) {
322 // Associate the view id with the model id.
321 // Associate the view id with the model id.
323 if (that.child_model_views[child_model.id] === undefined) {
322 if (that.child_model_views[child_model.id] === undefined) {
324 that.child_model_views[child_model.id] = [];
323 that.child_model_views[child_model.id] = [];
325 }
324 }
326 that.child_model_views[child_model.id].push(child_view.id);
325 that.child_model_views[child_model.id].push(child_view.id);
327 // Remember the view by id.
326 // Remember the view by id.
328 that.child_views[child_view.id] = child_view;
327 that.child_views[child_view.id] = child_view;
329 return child_view;
328 return child_view;
330 }, utils.reject("Couldn't create child view"));
329 }, utils.reject("Couldn't create child view"));
331 },
330 },
332
331
333 pop_child_view: function(child_model) {
332 pop_child_view: function(child_model) {
334 // Delete a child view that was previously created using create_child_view.
333 // Delete a child view that was previously created using create_child_view.
335 var view_ids = this.child_model_views[child_model.id];
334 var view_ids = this.child_model_views[child_model.id];
336 if (view_ids !== undefined) {
335 if (view_ids !== undefined) {
337
336
338 // Only delete the first view in the list.
337 // Only delete the first view in the list.
339 var view_id = view_ids[0];
338 var view_id = view_ids[0];
340 var view = this.child_views[view_id];
339 var view = this.child_views[view_id];
341 delete this.child_views[view_id];
340 delete this.child_views[view_id];
342 view_ids.splice(0,1);
341 view_ids.splice(0,1);
343 delete child_model.views[view_id];
342 delete child_model.views[view_id];
344
343
345 // Remove the view list specific to this model if it is empty.
344 // Remove the view list specific to this model if it is empty.
346 if (view_ids.length === 0) {
345 if (view_ids.length === 0) {
347 delete this.child_model_views[child_model.id];
346 delete this.child_model_views[child_model.id];
348 }
347 }
349 return view;
348 return view;
350 }
349 }
351 return null;
350 return null;
352 },
351 },
353
352
354 do_diff: function(old_list, new_list, removed_callback, added_callback) {
353 do_diff: function(old_list, new_list, removed_callback, added_callback) {
355 // Difference a changed list and call remove and add callbacks for
354 // Difference a changed list and call remove and add callbacks for
356 // each removed and added item in the new list.
355 // each removed and added item in the new list.
357 //
356 //
358 // Parameters
357 // Parameters
359 // ----------
358 // ----------
360 // old_list : array
359 // old_list : array
361 // new_list : array
360 // new_list : array
362 // removed_callback : Callback(item)
361 // removed_callback : Callback(item)
363 // Callback that is called for each item removed.
362 // Callback that is called for each item removed.
364 // added_callback : Callback(item)
363 // added_callback : Callback(item)
365 // Callback that is called for each item added.
364 // Callback that is called for each item added.
366
365
367 // Walk the lists until an unequal entry is found.
366 // Walk the lists until an unequal entry is found.
368 var i;
367 var i;
369 for (i = 0; i < new_list.length; i++) {
368 for (i = 0; i < new_list.length; i++) {
370 if (i >= old_list.length || new_list[i] !== old_list[i]) {
369 if (i >= old_list.length || new_list[i] !== old_list[i]) {
371 break;
370 break;
372 }
371 }
373 }
372 }
374
373
375 // Remove the non-matching items from the old list.
374 // Remove the non-matching items from the old list.
376 for (var j = i; j < old_list.length; j++) {
375 for (var j = i; j < old_list.length; j++) {
377 removed_callback(old_list[j]);
376 removed_callback(old_list[j]);
378 }
377 }
379
378
380 // Add the rest of the new list items.
379 // Add the rest of the new list items.
381 for (; i < new_list.length; i++) {
380 for (; i < new_list.length; i++) {
382 added_callback(new_list[i]);
381 added_callback(new_list[i]);
383 }
382 }
384 },
383 },
385
384
386 callbacks: function(){
385 callbacks: function(){
387 // Create msg callbacks for a comm msg.
386 // Create msg callbacks for a comm msg.
388 return this.model.callbacks(this);
387 return this.model.callbacks(this);
389 },
388 },
390
389
391 render: function(){
390 render: function(){
392 // Render the view.
391 // Render the view.
393 //
392 //
394 // By default, this is only called the first time the view is created
393 // By default, this is only called the first time the view is created
395 },
394 },
396
395
397 show: function(){
396 show: function(){
398 // Show the widget-area
397 // Show the widget-area
399 if (this.options && this.options.cell &&
398 if (this.options && this.options.cell &&
400 this.options.cell.widget_area !== undefined) {
399 this.options.cell.widget_area !== undefined) {
401 this.options.cell.widget_area.show();
400 this.options.cell.widget_area.show();
402 }
401 }
403 },
402 },
404
403
405 send: function (content) {
404 send: function (content) {
406 // Send a custom msg associated with this view.
405 // Send a custom msg associated with this view.
407 this.model.send(content, this.callbacks());
406 this.model.send(content, this.callbacks());
408 },
407 },
409
408
410 touch: function () {
409 touch: function () {
411 this.model.save_changes(this.callbacks());
410 this.model.save_changes(this.callbacks());
412 },
411 },
413
412
414 after_displayed: function (callback, context) {
413 after_displayed: function (callback, context) {
415 // Calls the callback right away is the view is already displayed
414 // Calls the callback right away is the view is already displayed
416 // otherwise, register the callback to the 'displayed' event.
415 // otherwise, register the callback to the 'displayed' event.
417 if (this.is_displayed) {
416 if (this.is_displayed) {
418 callback.apply(context);
417 callback.apply(context);
419 } else {
418 } else {
420 this.on('displayed', callback, context);
419 this.on('displayed', callback, context);
421 }
420 }
422 },
421 },
423 });
422 });
424
423
425
424
426 var DOMWidgetView = WidgetView.extend({
425 var DOMWidgetView = WidgetView.extend({
427 initialize: function (parameters) {
426 initialize: function (parameters) {
428 // Public constructor
427 // Public constructor
429 DOMWidgetView.__super__.initialize.apply(this, [parameters]);
428 DOMWidgetView.__super__.initialize.apply(this, [parameters]);
430 this.on('displayed', this.show, this);
429 this.on('displayed', this.show, this);
431 this.model.on('change:visible', this.update_visible, this);
430 this.model.on('change:visible', this.update_visible, this);
432 this.model.on('change:_css', this.update_css, this);
431 this.model.on('change:_css', this.update_css, this);
433
432
434 this.model.on('change:_dom_classes', function(model, new_classes) {
433 this.model.on('change:_dom_classes', function(model, new_classes) {
435 var old_classes = model.previous('_dom_classes');
434 var old_classes = model.previous('_dom_classes');
436 this.update_classes(old_classes, new_classes);
435 this.update_classes(old_classes, new_classes);
437 }, this);
436 }, this);
438
437
439 this.model.on('change:color', function (model, value) {
438 this.model.on('change:color', function (model, value) {
440 this.update_attr('color', value); }, this);
439 this.update_attr('color', value); }, this);
441
440
442 this.model.on('change:background_color', function (model, value) {
441 this.model.on('change:background_color', function (model, value) {
443 this.update_attr('background', value); }, this);
442 this.update_attr('background', value); }, this);
444
443
445 this.model.on('change:width', function (model, value) {
444 this.model.on('change:width', function (model, value) {
446 this.update_attr('width', value); }, this);
445 this.update_attr('width', value); }, this);
447
446
448 this.model.on('change:height', function (model, value) {
447 this.model.on('change:height', function (model, value) {
449 this.update_attr('height', value); }, this);
448 this.update_attr('height', value); }, this);
450
449
451 this.model.on('change:border_color', function (model, value) {
450 this.model.on('change:border_color', function (model, value) {
452 this.update_attr('border-color', value); }, this);
451 this.update_attr('border-color', value); }, this);
453
452
454 this.model.on('change:border_width', function (model, value) {
453 this.model.on('change:border_width', function (model, value) {
455 this.update_attr('border-width', value); }, this);
454 this.update_attr('border-width', value); }, this);
456
455
457 this.model.on('change:border_style', function (model, value) {
456 this.model.on('change:border_style', function (model, value) {
458 this.update_attr('border-style', value); }, this);
457 this.update_attr('border-style', value); }, this);
459
458
460 this.model.on('change:font_style', function (model, value) {
459 this.model.on('change:font_style', function (model, value) {
461 this.update_attr('font-style', value); }, this);
460 this.update_attr('font-style', value); }, this);
462
461
463 this.model.on('change:font_weight', function (model, value) {
462 this.model.on('change:font_weight', function (model, value) {
464 this.update_attr('font-weight', value); }, this);
463 this.update_attr('font-weight', value); }, this);
465
464
466 this.model.on('change:font_size', function (model, value) {
465 this.model.on('change:font_size', function (model, value) {
467 this.update_attr('font-size', this._default_px(value)); }, this);
466 this.update_attr('font-size', this._default_px(value)); }, this);
468
467
469 this.model.on('change:font_family', function (model, value) {
468 this.model.on('change:font_family', function (model, value) {
470 this.update_attr('font-family', value); }, this);
469 this.update_attr('font-family', value); }, this);
471
470
472 this.model.on('change:padding', function (model, value) {
471 this.model.on('change:padding', function (model, value) {
473 this.update_attr('padding', value); }, this);
472 this.update_attr('padding', value); }, this);
474
473
475 this.model.on('change:margin', function (model, value) {
474 this.model.on('change:margin', function (model, value) {
476 this.update_attr('margin', this._default_px(value)); }, this);
475 this.update_attr('margin', this._default_px(value)); }, this);
477
476
478 this.model.on('change:border_radius', function (model, value) {
477 this.model.on('change:border_radius', function (model, value) {
479 this.update_attr('border-radius', this._default_px(value)); }, this);
478 this.update_attr('border-radius', this._default_px(value)); }, this);
480
479
481 this.after_displayed(function() {
480 this.after_displayed(function() {
482 this.update_visible(this.model, this.model.get("visible"));
481 this.update_visible(this.model, this.model.get("visible"));
483 this.update_classes([], this.model.get('_dom_classes'));
482 this.update_classes([], this.model.get('_dom_classes'));
484
483
485 this.update_attr('color', this.model.get('color'));
484 this.update_attr('color', this.model.get('color'));
486 this.update_attr('background', this.model.get('background_color'));
485 this.update_attr('background', this.model.get('background_color'));
487 this.update_attr('width', this.model.get('width'));
486 this.update_attr('width', this.model.get('width'));
488 this.update_attr('height', this.model.get('height'));
487 this.update_attr('height', this.model.get('height'));
489 this.update_attr('border-color', this.model.get('border_color'));
488 this.update_attr('border-color', this.model.get('border_color'));
490 this.update_attr('border-width', this.model.get('border_width'));
489 this.update_attr('border-width', this.model.get('border_width'));
491 this.update_attr('border-style', this.model.get('border_style'));
490 this.update_attr('border-style', this.model.get('border_style'));
492 this.update_attr('font-style', this.model.get('font_style'));
491 this.update_attr('font-style', this.model.get('font_style'));
493 this.update_attr('font-weight', this.model.get('font_weight'));
492 this.update_attr('font-weight', this.model.get('font_weight'));
494 this.update_attr('font-size', this.model.get('font_size'));
493 this.update_attr('font-size', this.model.get('font_size'));
495 this.update_attr('font-family', this.model.get('font_family'));
494 this.update_attr('font-family', this.model.get('font_family'));
496 this.update_attr('padding', this.model.get('padding'));
495 this.update_attr('padding', this.model.get('padding'));
497 this.update_attr('margin', this.model.get('margin'));
496 this.update_attr('margin', this.model.get('margin'));
498 this.update_attr('border-radius', this.model.get('border_radius'));
497 this.update_attr('border-radius', this.model.get('border_radius'));
499
498
500 this.update_css(this.model, this.model.get("_css"));
499 this.update_css(this.model, this.model.get("_css"));
501 }, this);
500 }, this);
502 },
501 },
503
502
504 _default_px: function(value) {
503 _default_px: function(value) {
505 // Makes browser interpret a numerical string as a pixel value.
504 // Makes browser interpret a numerical string as a pixel value.
506 if (/^\d+\.?(\d+)?$/.test(value.trim())) {
505 if (/^\d+\.?(\d+)?$/.test(value.trim())) {
507 return value.trim() + 'px';
506 return value.trim() + 'px';
508 }
507 }
509 return value;
508 return value;
510 },
509 },
511
510
512 update_attr: function(name, value) {
511 update_attr: function(name, value) {
513 // Set a css attr of the widget view.
512 // Set a css attr of the widget view.
514 this.$el.css(name, value);
513 this.$el.css(name, value);
515 },
514 },
516
515
517 update_visible: function(model, value) {
516 update_visible: function(model, value) {
518 // Update visibility
517 // Update visibility
519 this.$el.toggle(value);
518 this.$el.toggle(value);
520 },
519 },
521
520
522 update_css: function (model, css) {
521 update_css: function (model, css) {
523 // Update the css styling of this view.
522 // Update the css styling of this view.
524 var e = this.$el;
523 var e = this.$el;
525 if (css === undefined) {return;}
524 if (css === undefined) {return;}
526 for (var i = 0; i < css.length; i++) {
525 for (var i = 0; i < css.length; i++) {
527 // Apply the css traits to all elements that match the selector.
526 // Apply the css traits to all elements that match the selector.
528 var selector = css[i][0];
527 var selector = css[i][0];
529 var elements = this._get_selector_element(selector);
528 var elements = this._get_selector_element(selector);
530 if (elements.length > 0) {
529 if (elements.length > 0) {
531 var trait_key = css[i][1];
530 var trait_key = css[i][1];
532 var trait_value = css[i][2];
531 var trait_value = css[i][2];
533 elements.css(trait_key ,trait_value);
532 elements.css(trait_key ,trait_value);
534 }
533 }
535 }
534 }
536 },
535 },
537
536
538 update_classes: function (old_classes, new_classes, $el) {
537 update_classes: function (old_classes, new_classes, $el) {
539 // Update the DOM classes applied to an element, default to this.$el.
538 // Update the DOM classes applied to an element, default to this.$el.
540 if ($el===undefined) {
539 if ($el===undefined) {
541 $el = this.$el;
540 $el = this.$el;
542 }
541 }
543 this.do_diff(old_classes, new_classes, function(removed) {
542 this.do_diff(old_classes, new_classes, function(removed) {
544 $el.removeClass(removed);
543 $el.removeClass(removed);
545 }, function(added) {
544 }, function(added) {
546 $el.addClass(added);
545 $el.addClass(added);
547 });
546 });
548 },
547 },
549
548
550 update_mapped_classes: function(class_map, trait_name, previous_trait_value, $el) {
549 update_mapped_classes: function(class_map, trait_name, previous_trait_value, $el) {
551 // Update the DOM classes applied to the widget based on a single
550 // Update the DOM classes applied to the widget based on a single
552 // trait's value.
551 // trait's value.
553 //
552 //
554 // Given a trait value classes map, this function automatically
553 // Given a trait value classes map, this function automatically
555 // handles applying the appropriate classes to the widget element
554 // handles applying the appropriate classes to the widget element
556 // and removing classes that are no longer valid.
555 // and removing classes that are no longer valid.
557 //
556 //
558 // Parameters
557 // Parameters
559 // ----------
558 // ----------
560 // class_map: dictionary
559 // class_map: dictionary
561 // Dictionary of trait values to class lists.
560 // Dictionary of trait values to class lists.
562 // Example:
561 // Example:
563 // {
562 // {
564 // success: ['alert', 'alert-success'],
563 // success: ['alert', 'alert-success'],
565 // info: ['alert', 'alert-info'],
564 // info: ['alert', 'alert-info'],
566 // warning: ['alert', 'alert-warning'],
565 // warning: ['alert', 'alert-warning'],
567 // danger: ['alert', 'alert-danger']
566 // danger: ['alert', 'alert-danger']
568 // };
567 // };
569 // trait_name: string
568 // trait_name: string
570 // Name of the trait to check the value of.
569 // Name of the trait to check the value of.
571 // previous_trait_value: optional string, default ''
570 // previous_trait_value: optional string, default ''
572 // Last trait value
571 // Last trait value
573 // $el: optional jQuery element handle, defaults to this.$el
572 // $el: optional jQuery element handle, defaults to this.$el
574 // Element that the classes are applied to.
573 // Element that the classes are applied to.
575 var key = previous_trait_value;
574 var key = previous_trait_value;
576 if (key === undefined) {
575 if (key === undefined) {
577 key = this.model.previous(trait_name);
576 key = this.model.previous(trait_name);
578 }
577 }
579 var old_classes = class_map[key] ? class_map[key] : [];
578 var old_classes = class_map[key] ? class_map[key] : [];
580 key = this.model.get(trait_name);
579 key = this.model.get(trait_name);
581 var new_classes = class_map[key] ? class_map[key] : [];
580 var new_classes = class_map[key] ? class_map[key] : [];
582
581
583 this.update_classes(old_classes, new_classes, $el || this.$el);
582 this.update_classes(old_classes, new_classes, $el || this.$el);
584 },
583 },
585
584
586 _get_selector_element: function (selector) {
585 _get_selector_element: function (selector) {
587 // Get the elements via the css selector.
586 // Get the elements via the css selector.
588 var elements;
587 var elements;
589 if (!selector) {
588 if (!selector) {
590 elements = this.$el;
589 elements = this.$el;
591 } else {
590 } else {
592 elements = this.$el.find(selector).addBack(selector);
591 elements = this.$el.find(selector).addBack(selector);
593 }
592 }
594 return elements;
593 return elements;
595 },
594 },
596 });
595 });
597
596
598
597
599 var widget = {
598 var widget = {
600 'WidgetModel': WidgetModel,
599 'WidgetModel': WidgetModel,
601 'WidgetView': WidgetView,
600 'WidgetView': WidgetView,
602 'DOMWidgetView': DOMWidgetView,
601 'DOMWidgetView': DOMWidgetView,
603 };
602 };
604
603
605 // For backwards compatability.
604 // For backwards compatability.
606 $.extend(IPython, widget);
605 $.extend(IPython, widget);
607
606
608 return widget;
607 return widget;
609 });
608 });
@@ -1,105 +1,104
1 <!DOCTYPE HTML>
1 <!DOCTYPE HTML>
2 <html>
2 <html>
3
3
4 <head>
4 <head>
5 <meta charset="utf-8">
5 <meta charset="utf-8">
6
6
7 <title>{% block title %}IPython Notebook{% endblock %}</title>
7 <title>{% block title %}IPython Notebook{% endblock %}</title>
8 <link rel="shortcut icon" type="image/x-icon" href="{{static_url("base/images/favicon.ico") }}">
8 <link rel="shortcut icon" type="image/x-icon" href="{{static_url("base/images/favicon.ico") }}">
9 <meta http-equiv="X-UA-Compatible" content="chrome=1">
9 <meta http-equiv="X-UA-Compatible" content="chrome=1">
10 <link rel="stylesheet" href="{{static_url("components/jquery-ui/themes/smoothness/jquery-ui.min.css") }}" type="text/css" />
10 <link rel="stylesheet" href="{{static_url("components/jquery-ui/themes/smoothness/jquery-ui.min.css") }}" type="text/css" />
11 <meta name="viewport" content="width=device-width, initial-scale=1.0">
11 <meta name="viewport" content="width=device-width, initial-scale=1.0">
12
12
13 {% block stylesheet %}
13 {% block stylesheet %}
14 <link rel="stylesheet" href="{{ static_url("style/style.min.css") }}" type="text/css"/>
14 <link rel="stylesheet" href="{{ static_url("style/style.min.css") }}" type="text/css"/>
15 {% endblock %}
15 {% endblock %}
16 <link rel="stylesheet" href="{{ static_url("custom/custom.css") }}" type="text/css" />
16 <link rel="stylesheet" href="{{ static_url("custom/custom.css") }}" type="text/css" />
17 <script src="{{static_url("components/es6-promise/promise.min.js")}}" type="text/javascript" charset="utf-8"></script>
17 <script src="{{static_url("components/es6-promise/promise.min.js")}}" type="text/javascript" charset="utf-8"></script>
18 <script src="{{static_url("components/requirejs/require.js") }}" type="text/javascript" charset="utf-8"></script>
18 <script src="{{static_url("components/requirejs/require.js") }}" type="text/javascript" charset="utf-8"></script>
19 <script>
19 <script>
20 require.config({
20 require.config({
21 baseUrl: '{{static_url("", include_version=False)}}',
21 baseUrl: '{{static_url("", include_version=False)}}',
22 paths: {
22 paths: {
23 nbextensions : '{{ base_url }}nbextensions',
23 nbextensions : '{{ base_url }}nbextensions',
24 underscore : 'components/underscore/underscore-min',
24 underscore : 'components/underscore/underscore-min',
25 backbone : 'components/backbone/backbone-min',
25 backbone : 'components/backbone/backbone-min',
26 jquery: 'components/jquery/jquery.min',
26 jquery: 'components/jquery/jquery.min',
27 bootstrap: 'components/bootstrap/js/bootstrap.min',
27 bootstrap: 'components/bootstrap/js/bootstrap.min',
28 bootstraptour: 'components/bootstrap-tour/build/js/bootstrap-tour.min',
28 bootstraptour: 'components/bootstrap-tour/build/js/bootstrap-tour.min',
29 jqueryui: 'components/jquery-ui/ui/minified/jquery-ui.min',
29 jqueryui: 'components/jquery-ui/ui/minified/jquery-ui.min',
30 moment: "components/moment/moment",
30 moment: "components/moment/moment",
31 codemirror: 'components/codemirror',
31 codemirror: 'components/codemirror',
32 termjs: "components/term.js/src/term",
32 termjs: "components/term.js/src/term",
33 contents: '{{ contents_js_source }}',
33 contents: '{{ contents_js_source }}',
34 rsvp: "components/rsvp/rsvp",
35 },
34 },
36 shim: {
35 shim: {
37 underscore: {
36 underscore: {
38 exports: '_'
37 exports: '_'
39 },
38 },
40 backbone: {
39 backbone: {
41 deps: ["underscore", "jquery"],
40 deps: ["underscore", "jquery"],
42 exports: "Backbone"
41 exports: "Backbone"
43 },
42 },
44 bootstrap: {
43 bootstrap: {
45 deps: ["jquery"],
44 deps: ["jquery"],
46 exports: "bootstrap"
45 exports: "bootstrap"
47 },
46 },
48 bootstraptour: {
47 bootstraptour: {
49 deps: ["bootstrap"],
48 deps: ["bootstrap"],
50 exports: "Tour"
49 exports: "Tour"
51 },
50 },
52 jqueryui: {
51 jqueryui: {
53 deps: ["jquery"],
52 deps: ["jquery"],
54 exports: "$"
53 exports: "$"
55 }
54 }
56 }
55 }
57 });
56 });
58 </script>
57 </script>
59
58
60 {% block meta %}
59 {% block meta %}
61 {% endblock %}
60 {% endblock %}
62
61
63 </head>
62 </head>
64
63
65 <body {% block params %}{% endblock %}>
64 <body {% block params %}{% endblock %}>
66
65
67 <noscript>
66 <noscript>
68 <div id='noscript'>
67 <div id='noscript'>
69 IPython Notebook requires JavaScript.<br>
68 IPython Notebook requires JavaScript.<br>
70 Please enable it to proceed.
69 Please enable it to proceed.
71 </div>
70 </div>
72 </noscript>
71 </noscript>
73
72
74 <div id="header" class="navbar navbar-static-top">
73 <div id="header" class="navbar navbar-static-top">
75 <div class="container">
74 <div class="container">
76 <div id="ipython_notebook" class="nav navbar-brand pull-left"><a href="{{base_url}}tree" alt='dashboard'><img src='{{static_url("base/images/ipynblogo.png") }}' alt='IPython Notebook'/></a></div>
75 <div id="ipython_notebook" class="nav navbar-brand pull-left"><a href="{{base_url}}tree" alt='dashboard'><img src='{{static_url("base/images/ipynblogo.png") }}' alt='IPython Notebook'/></a></div>
77
76
78 {% block login_widget %}
77 {% block login_widget %}
79
78
80 <span id="login_widget">
79 <span id="login_widget">
81 {% if logged_in %}
80 {% if logged_in %}
82 <button id="logout">Logout</button>
81 <button id="logout">Logout</button>
83 {% elif login_available and not logged_in %}
82 {% elif login_available and not logged_in %}
84 <button id="login">Login</button>
83 <button id="login">Login</button>
85 {% endif %}
84 {% endif %}
86 </span>
85 </span>
87
86
88 {% endblock %}
87 {% endblock %}
89
88
90 {% block header %}
89 {% block header %}
91 {% endblock %}
90 {% endblock %}
92 </div>
91 </div>
93 </div>
92 </div>
94
93
95 <div id="site">
94 <div id="site">
96 {% block site %}
95 {% block site %}
97 {% endblock %}
96 {% endblock %}
98 </div>
97 </div>
99
98
100 {% block script %}
99 {% block script %}
101 {% endblock %}
100 {% endblock %}
102
101
103 </body>
102 </body>
104
103
105 </html>
104 </html>
General Comments 0
You need to be logged in to leave comments. Login now