##// END OF EJS Templates
add a small default metaui-toolbar...
Matthias BUSSONNIER -
Show More
@@ -1,443 +1,476 b''
1 1 //----------------------------------------------------------------------------
2 2 // Copyright (C) 2012 The IPython Development Team
3 3 //
4 4 // Distributed under the terms of the BSD License. The full license is in
5 5 // the file COPYING, distributed as part of this software.
6 6 //----------------------------------------------------------------------------
7 7
8 8 //============================================================================
9 9 // MetaUI
10 10 //============================================================================
11 11
12 12
13 13 /**
14 14 * A Module to control the per-cell toolbar.
15 15 * @module IPython
16 16 * @namespace IPython
17 17 * @submodule MetaUI
18 18 */
19 19 var IPython = (function (IPython) {
20 20 "use strict";
21 21
22 22
23 23 /**
24 24 * @constructor
25 25 * @class MetaUI
26 26 * @param {The cell to attach the metadata UI to} cell
27 27 */
28 28 var MetaUI = function (cell) {
29 29 MetaUI._instances.push(this);
30 30 this.metainner = $('<div/>');
31 31 this.cell = cell;
32 32 this.element = $('<div/>').addClass('metaedit')
33 33 .append(this.metainner)
34 34 this.rebuild();
35 35 return this;
36 36 };
37 37
38 38 /**
39 39 * Class variable that should contain a dict of all availlable callback
40 40 * we need to think of wether or not we allow nested namespace
41 41 * @property _callback_dict
42 42 * @private
43 43 */
44 44 MetaUI._callback_dict = {};
45 45
46 46 /**
47 47 * Class variable that should contain the reverse order list of the button
48 48 * to add to the toolbar of each cell
49 49 * @property _button_list
50 50 * @private
51 51 */
52 52 MetaUI._button_list = [];
53 53
54 54 /**
55 55 * keep a list of all instances to
56 56 * be able to llop over them...
57 57 * but how to 'destroy' them ?
58 58 * have to think about it...
59 59 * or loop over the cells, and find their MetaUI instances.
60 60 * @private
61 61 * @property _instances
62 62 */
63 63 MetaUI._instances =[]
64 64
65 65 /**
66 66 * keep a list of all the availlabel presets for the toolbar
67 67 * @private
68 68 * @property _presets
69 69 */
70 70 MetaUI._presets ={}
71 71
72 72 // this is by design not a prototype.
73 73 /**
74 74 * Register a callback to create an UI element in a cell toolbar.
75 75 * @method register_callback
76 76 * @param name {String} name to use to refer to the callback. It is advised to use a prefix with the name
77 77 * for easier sorting and avoid collision
78 78 * @param callback {function(div, cell)} callback that will be called to generate the ui element
79 79 *
80 80 *
81 81 * The callback will receive the following element :
82 82 *
83 83 * * a div in which to add element.
84 84 * * the cell it is responsable from
85 85 *
86 86 * @example
87 87 *
88 88 * Example that create callback for a button that toggle between `true` and `false` label,
89 89 * with the metadata under the key 'foo' to reflect the status of the button.
90 90 *
91 91 * // first param reference to a DOM div
92 92 * // second param reference to the cell.
93 93 * var toggle = function(div, cell) {
94 94 * var button_container = $(div)
95 95 *
96 96 * // let's create a button that show the current value of the metadata
97 97 * var button = $('<div/>').button({label:String(cell.metadata.foo)});
98 98 *
99 99 * // On click, change the metadata value and update the button label
100 100 * button.click(function(){
101 101 * var v = cell.metadata.foo;
102 102 * cell.metadata.foo = !v;
103 103 * button.button("option","label",String(!v));
104 104 * })
105 105 *
106 106 * // add the button to the DOM div.
107 107 * button_container.append(button);
108 108 * }
109 109 *
110 110 * // now we register the callback under the name `foo` to give the
111 111 * // user the ability to use it later
112 112 * MetaUI.register_callback('foo',toggle);
113 113 */
114 114 MetaUI.register_callback = function(name, callback){
115 115 // what do we do if name already exist ?
116 116 MetaUI._callback_dict[name] = callback;
117 117 };
118 118
119 119 /**
120 120 * Register a preset of UI element in a cell toolbar.
121 121 * Not supported Yet.
122 122 * @method register_preset
123 123 * @param name {String} name to use to refer to the preset. It is advised to use a prefix with the name
124 124 * for easier sorting and avoid collision
125 125 * @param preset_list {List of String} reverse order of the button in the toolbar. Each String of the list
126 126 * should correspond to a name of a registerd callback.
127 127 *
128 128 * @private
129 129 * @example
130 130 *
131 131 * MetaUI.register_callback('foo.c1',function(div,cell){...});
132 132 * MetaUI.register_callback('foo.c2',function(div,cell){...});
133 133 * MetaUI.register_callback('foo.c3',function(div,cell){...});
134 134 * MetaUI.register_callback('foo.c4',function(div,cell){...});
135 135 * MetaUI.register_callback('foo.c5',function(div,cell){...});
136 136 *
137 137 * MetaUI.register_preset('foo.foo_preset1',['foo.c1','foo.c2','foo.c5'])
138 138 * MetaUI.register_preset('foo.foo_preset2',['foo.c4','foo.c5'])
139 139 */
140 140 MetaUI.register_preset = function(name, preset_list){
141 141 MetaUI._presets[name] = preset_list
142 142 }
143 143 /**
144 144 * set an UI preset from `register_preset`
145 145 * @method set_preset
146 146 * @param preset_name {String} string corresponding to the preset name
147 147 *
148 148 * @static
149 149 * @private
150 150 * @example
151 151 *
152 152 * MetaUI.set_preset('foo.foo_preset1');
153 153 */
154 154 MetaUI.set_preset= function(preset_name){
155 155 var preset = MetaUI._presets[preset_name];
156 156
157 157 if(preset != undefined){
158 158 MetaUI._button_list = preset;
159 159 MetaUI.rebuild_all();
160 160 }
161 161 }
162 162
163 163 // this is by design not a prototype.
164 164 /**
165 165 * This should be called on the class and not on a instance as it will trigger
166 166 * rebuild of all the instances.
167 167 * @method rebuild_all
168 168 * @static
169 169 *
170 170 */
171 171 MetaUI.rebuild_all = function(){
172 172 for(var i in MetaUI._instances){
173 173 MetaUI._instances[i].rebuild();
174 174 }
175 175 }
176 176
177 177 /**
178 178 * Rebuild all the button on the toolbar to update it's state.
179 179 * @method rebuild
180 180 */
181 181 MetaUI.prototype.rebuild = function(){
182 182 // strip evrything from the div
183 183 // which is probabli metainner.
184 184 // or this.element.
185 185 this.metainner.empty();
186 186 //this.add_raw_edit_button()
187 187
188 188
189 189 var cdict = MetaUI._callback_dict;
190 190 var preset = MetaUI._button_list;
191 191 // Yes we iterate on the class varaible, not the instance one.
192 192 for ( var index in MetaUI._button_list){
193 193 var local_div = $('<div/>').addClass('button_container');
194 194 // Note,
195 195 // do this the other way, wrap in try/catch and don't append if any errors.
196 196 this.metainner.append(local_div)
197 197 cdict[preset[index]](local_div,this.cell)
198 198 }
199 199
200 200 }
201 201
202 202 var raw_edit = function(cell){
203 203
204 204 var md = cell.metadata
205 205
206 206 var textarea = $('<textarea/>')
207 207 .attr('rows','13')
208 208 .attr('cols','75')
209 209 .attr('name','metadata')
210 210 .text(JSON.stringify(md, null,4)||'');
211 211 var dialogform = $('<div/>').attr('title','Edit the metadata')
212 212 .append(
213 213 $('<form/>').append(
214 214 $('<fieldset/>').append(
215 215 $('<label/>')
216 216 .attr('for','metadata')
217 217 .text("Metadata (I know what I'm dooing and I won't complain if it breaks my notebook)")
218 218 )
219 219 .append($('<br/>'))
220 220 .append(
221 221 textarea
222 222 )
223 223 )
224 224 );
225 225 var editor = CodeMirror.fromTextArea(textarea[0], {
226 226 lineNumbers: true,
227 227 matchBrackets: true,
228 228 });
229 229 $(dialogform).dialog({
230 230 autoOpen: true,
231 231 height: 300,
232 232 width: 650,
233 233 modal: true,
234 234 buttons: {
235 235 "Ok": function() {
236 236 //validate json and set it
237 237 try {
238 238 var json = JSON.parse(editor.getValue());
239 239 cell.metadata = json;
240 240 $( this ).dialog( "close" );
241 241 }
242 242 catch(e)
243 243 {
244 244 alert('invalid json');
245 245 }
246 246 },
247 247 Cancel: function() {
248 248 $( this ).dialog( "close" );
249 249 }
250 250 },
251 251 close: function() {
252 252 //cleanup on close
253 253 $(this).remove();
254 254 }
255 255 });
256 256 editor.refresh();
257 257 }
258 258
259
259 260 var add_raw_edit_button = function(div, cell) {
260 261 var button_container = $(div)
261 262 var button = $('<div/>').button({label:'Raw Edit'})
262 263 .click(function(){raw_edit(cell); return false;})
263 264 button_container.append(button);
264 265 }
265 266
266 267 MetaUI.register_callback('example.rawedit',add_raw_edit_button);
267 268 var example_preset = []
268 269 example_preset.push('example.rawedit');
269 270
271 var simple_dialog = function(title,text){
272 var dlg = $('<div/>').attr('title',title)
273 .append($('<p/>').text(text))
274 $(dlg).dialog({
275 autoOpen: true,
276 height: 300,
277 width: 650,
278 modal: true,
279 close: function() {
280 //cleanup on close
281 $(this).remove();
282 }
283 });
284 }
285
286 var add_simple_dialog_button = function(div, cell) {
287 var help_text = ["This is the Metadata editting UI.",
288 "It heavily rely on plugin to work ",
289 "and is still under developpement. You shouldn't wait too long before",
290 " seeing some customisable buttons in those toolbar."
291 ].join('\n')
292 var button_container = $(div)
293 var button = $('<div/>').button({label:'?'})
294 .click(function(){simple_dialog('help',help_text); return false;})
295 button_container.append(button);
296 }
297
298 MetaUI.register_callback('default.help',add_simple_dialog_button)
299 var default_preset = []
300 default_preset.push('default.help')
301 MetaUI.register_preset('default',default_preset)
302 MetaUI.set_preset('default')
270 303
271 304 var simple_button = function(div, cell) {
272 305 var button_container = $(div);
273 306 var button = $('<div/>').button({icons:{primary:'ui-icon-locked'}});
274 307 var fun = function(value){
275 308 try{
276 309 if(value){
277 310 cell.code_mirror.setOption('readOnly','nocursor')
278 311 button.button('option','icons',{primary:'ui-icon-locked'})
279 312 } else {
280 313 cell.code_mirror.setOption('readOnly','false')
281 314 button.button('option','icons',{primary:'ui-icon-unlocked'})
282 315 }
283 316 } catch(e){}
284 317
285 318 }
286 319 fun(cell.metadata.ro)
287 320 button.click(function(){
288 321 var v = cell.metadata.ro;
289 322 var locked = !v;
290 323 cell.metadata.ro = locked;
291 324 fun(locked)
292 325 })
293 326 .css('height','16px')
294 327 .css('width','35px');
295 328 button_container.append(button);
296 329 }
297 330
298 331 MetaUI.register_callback('example.lock',simple_button);
299 332 example_preset.push('example.lock');
300 333
301 334 var toggle_test = function(div, cell) {
302 335 var button_container = $(div)
303 336 var button = $('<div/>').button({label:String(cell.metadata.foo)});
304 337 button.click(function(){
305 338 var v = cell.metadata.foo;
306 339 cell.metadata.foo = !v;
307 340 button.button("option","label",String(!v));
308 341 })
309 342 button_container.append(button);
310 343 }
311 344
312 345 MetaUI.register_callback('example.toggle',toggle_test);
313 346 example_preset.push('example.toggle');
314 347
315 348 /**
316 349 */
317 350 MetaUI.utils = {};
318 351
319 352 /**
320 353 * A utility function to generate bindings between a checkbox and metadata
321 354 * @method utils.checkbox_ui_generator
322 355 * @static
323 356 *
324 357 * @param name {string} Label in front of the checkbox
325 358 * @param setter {function( metadata_dict, newValue )}
326 359 * A setter method to set the newValue of the metadata dictionnary
327 360 * @param getter {function( metadata )}
328 361 * A getter methods which return the current value of the metadata.
329 362 *
330 363 * @return callback {function( div, cell )} Callback to be passed to `register_callback`
331 364 *
332 365 * @example
333 366 *
334 367 * An exmple that bind the subkey `slideshow.isSectionStart` to a checkbox with a `New Slide` label
335 368 *
336 369 * var newSlide = MetaUI.utils.checkbox_ui_generator('New Slide',
337 370 * // setter
338 371 * function(metadata,value){
339 372 * // we check that the slideshow namespace exist and create it if needed
340 373 * if (metadata.slideshow == undefined){metadata.slideshow = {}}
341 374 * // set the value
342 375 * metadata.slideshow.isSectionStart = value
343 376 * },
344 377 * //geter
345 378 * function(metadata){ var ns = metadata.slideshow;
346 379 * // if the slideshow namespace does not exist return `undefined`
347 380 * // (will be interpreted as `false` by checkbox) otherwise
348 381 * // return the value
349 382 * return (ns == undefined)? undefined: ns.isSectionStart
350 383 * }
351 384 * );
352 385 *
353 386 * MetaUI.register_callback('newSlide', newSlide);
354 387 *
355 388 */
356 389 MetaUI.utils.checkbox_ui_generator = function(name,setter,getter){
357 390 return function(div, cell) {
358 391 var button_container = $(div)
359 392
360 393 var chkb = $('<input/>').attr('type','checkbox');
361 394 var lbl = $('<label/>').append($('<span/>').text(name).css('font-size','77%'));
362 395 lbl.append(chkb);
363 396 chkb.attr("checked",getter(cell.metadata));
364 397
365 398 chkb.click(function(){
366 399 var v = getter(cell.metadata);
367 400 setter(cell.metadata,!v);
368 401 chkb.attr("checked",!v);
369 402 })
370 403 button_container.append($('<div/>').append(lbl));
371 404
372 405 }
373 406 }
374 407
375 408 /**
376 409 * A utility function to generate bindings between a dropdown list and metadata
377 410 * @method utils.select_ui_generator
378 411 * @static
379 412 *
380 413 * @param list_list {list of sublist} List of sublist of metadata value and name in the dropdown list.
381 414 * subslit shoud contain 2 element each, first a string that woul be displayed in the dropdown list,
382 415 * and second the corresponding value for the metadata to be passed to setter/return by getter.
383 416 * @param setter {function( metadata_dict, newValue )}
384 417 * A setter method to set the newValue of the metadata dictionnary
385 418 * @param getter {function( metadata )}
386 419 * A getter methods which return the current value of the metadata.
387 420 * @param [label=""] {String} optionnal label for the dropdown menu
388 421 *
389 422 * @return callback {function( div, cell )} Callback to be passed to `register_callback`
390 423 *
391 424 * @example
392 425 *
393 426 * var select_type = MetaUI.utils.select_ui_generator([
394 427 * ["-" ,undefined ],
395 428 * ["Header Slide" ,"header_slide" ],
396 429 * ["Slide" ,"slide" ],
397 430 * ["Fragment" ,"fragment" ],
398 431 * ["Skip" ,"skip" ],
399 432 * ],
400 433 * // setter
401 434 * function(metadata,value){
402 435 * // we check that the slideshow namespace exist and create it if needed
403 436 * if (metadata.slideshow == undefined){metadata.slideshow = {}}
404 437 * // set the value
405 438 * metadata.slideshow.slide_type = value
406 439 * },
407 440 * //geter
408 441 * function(metadata){ var ns = metadata.slideshow;
409 442 * // if the slideshow namespace does not exist return `undefined`
410 443 * // (will be interpreted as `false` by checkbox) otherwise
411 444 * // return the value
412 445 * return (ns == undefined)? undefined: ns.slide_type
413 446 * }
414 447 * MetaUI.register_callback('slideshow.select',select_type);
415 448 *
416 449 */
417 450 MetaUI.utils.select_ui_generator = function(list_list,setter, getter, label){
418 451 label= label? label: "";
419 452 return function(div, cell) {
420 453 var button_container = $(div)
421 454 var lbl = $("<label/>").append($('<span/>').text(label).css('font-size','77%'));
422 455 var select = $('<select/>');
423 456 for(var itemn in list_list){
424 457 var opt = $('<option/>');
425 458 opt.attr('value',list_list[itemn][1])
426 459 opt.text(list_list[itemn][0])
427 460 select.append(opt);
428 461 }
429 462 select.val(getter(cell.metadata));
430 463
431 464 select.change(function(){
432 465 setter(cell.metadata,select.val());
433 466 });
434 467 button_container.append($('<div/>').append(lbl).append(select));
435 468
436 469 }
437 470 };
438 471
439 472
440 473 IPython.MetaUI = MetaUI;
441 474
442 475 return IPython;
443 476 }(IPython));
General Comments 0
You need to be logged in to leave comments. Login now