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