##// END OF EJS Templates
slightly generalize utils generator
Matthias BUSSONNIER -
Show More
@@ -1,353 +1,349 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 // CellToolbar
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 CellToolbar
18 18 */
19 19 var IPython = (function (IPython) {
20 20 "use strict";
21 21
22 22
23 23 /**
24 24 * @constructor
25 25 * @class CellToolbar
26 26 * @param {The cell to attach the metadata UI to} cell
27 27 */
28 28 var CellToolbar = function (cell) {
29 29 CellToolbar._instances.push(this);
30 30 this.inner_element = $('<div/>');
31 31 this.cell = cell;
32 32 this.element = $('<div/>').addClass('celltoolbar')
33 33 .append(this.inner_element)
34 34 this.rebuild();
35 35 return this;
36 36 };
37 37
38 38 CellToolbar.dropdown_preset_element = $('<select/>')
39 39 .addClass('ui-widget ui-widget-content')
40 .attr('id','celltoolbar_selector')
41 .append($('<option/>').attr('value','').text('-'))
40 .attr('id', 'celltoolbar_selector')
41 .append($('<option/>').attr('value', '').text('-'))
42 42
43 43 CellToolbar.dropdown_preset_element.change(function(){
44 44 var val = CellToolbar.dropdown_preset_element.val()
45 45 if(val ==''){
46 46 $('body').removeClass('celltoolbar-on')
47 47 } else {
48 48 $('body').addClass('celltoolbar-on')
49 49 CellToolbar.set_preset(val)
50 50 }
51 51 })
52 52
53 53
54 54
55 55 /**
56 56 * Class variable that should contain a dict of all availlable callback
57 57 * we need to think of wether or not we allow nested namespace
58 58 * @property _callback_dict
59 59 * @private
60 60 */
61 61 CellToolbar._callback_dict = {};
62 62
63 63 /**
64 64 * Class variable that should contain the reverse order list of the button
65 65 * to add to the toolbar of each cell
66 66 * @property _ui_controls_list
67 67 * @private
68 68 */
69 69 CellToolbar._ui_controls_list = [];
70 70
71 71 /**
72 72 * keep a list of all instances to
73 73 * be able to llop over them...
74 74 * but how to 'destroy' them ?
75 75 * have to think about it...
76 76 * or loop over the cells, and find their CellToolbar instances.
77 77 * @private
78 78 * @property _instances
79 79 */
80 80 CellToolbar._instances =[]
81 81
82 82 /**
83 83 * keep a list of all the availlabel presets for the toolbar
84 84 * @private
85 85 * @property _presets
86 86 */
87 87 CellToolbar._presets ={}
88 88
89 89 // this is by design not a prototype.
90 90 /**
91 91 * Register a callback to create an UI element in a cell toolbar.
92 92 * @method register_callback
93 93 * @param name {String} name to use to refer to the callback. It is advised to use a prefix with the name
94 94 * for easier sorting and avoid collision
95 95 * @param callback {function(div, cell)} callback that will be called to generate the ui element
96 96 *
97 97 *
98 98 * The callback will receive the following element :
99 99 *
100 100 * * a div in which to add element.
101 101 * * the cell it is responsable from
102 102 *
103 103 * @example
104 104 *
105 105 * Example that create callback for a button that toggle between `true` and `false` label,
106 106 * with the metadata under the key 'foo' to reflect the status of the button.
107 107 *
108 108 * // first param reference to a DOM div
109 109 * // second param reference to the cell.
110 110 * var toggle = function(div, cell) {
111 111 * var button_container = $(div)
112 112 *
113 113 * // let's create a button that show the current value of the metadata
114 114 * var button = $('<div/>').button({label:String(cell.metadata.foo)});
115 115 *
116 116 * // On click, change the metadata value and update the button label
117 117 * button.click(function(){
118 118 * var v = cell.metadata.foo;
119 119 * cell.metadata.foo = !v;
120 * button.button("option","label",String(!v));
120 * button.button("option", "label", String(!v));
121 121 * })
122 122 *
123 123 * // add the button to the DOM div.
124 124 * button_container.append(button);
125 125 * }
126 126 *
127 127 * // now we register the callback under the name `foo` to give the
128 128 * // user the ability to use it later
129 * CellToolbar.register_callback('foo',toggle);
129 * CellToolbar.register_callback('foo', toggle);
130 130 */
131 131 CellToolbar.register_callback = function(name, callback){
132 132 // what do we do if name already exist ?
133 133 CellToolbar._callback_dict[name] = callback;
134 134 };
135 135
136 136 /**
137 137 * Register a preset of UI element in a cell toolbar.
138 138 * Not supported Yet.
139 139 * @method register_preset
140 140 * @param name {String} name to use to refer to the preset. It is advised to use a prefix with the name
141 141 * for easier sorting and avoid collision
142 142 * @param preset_list {List of String} reverse order of the button in the toolbar. Each String of the list
143 143 * should correspond to a name of a registerd callback.
144 144 *
145 145 * @private
146 146 * @example
147 147 *
148 * CellToolbar.register_callback('foo.c1',function(div,cell){...});
149 * CellToolbar.register_callback('foo.c2',function(div,cell){...});
150 * CellToolbar.register_callback('foo.c3',function(div,cell){...});
151 * CellToolbar.register_callback('foo.c4',function(div,cell){...});
152 * CellToolbar.register_callback('foo.c5',function(div,cell){...});
148 * CellToolbar.register_callback('foo.c1', function(div, cell){...});
149 * CellToolbar.register_callback('foo.c2', function(div, cell){...});
150 * CellToolbar.register_callback('foo.c3', function(div, cell){...});
151 * CellToolbar.register_callback('foo.c4', function(div, cell){...});
152 * CellToolbar.register_callback('foo.c5', function(div, cell){...});
153 153 *
154 * CellToolbar.register_preset('foo.foo_preset1',['foo.c1','foo.c2','foo.c5'])
155 * CellToolbar.register_preset('foo.foo_preset2',['foo.c4','foo.c5'])
154 * CellToolbar.register_preset('foo.foo_preset1', ['foo.c1', 'foo.c2', 'foo.c5'])
155 * CellToolbar.register_preset('foo.foo_preset2', ['foo.c4', 'foo.c5'])
156 156 */
157 157 CellToolbar.register_preset = function(name, preset_list){
158 158 CellToolbar._presets[name] = preset_list
159 159 CellToolbar.dropdown_preset_element.append(
160 $('<option/>').attr('value',name).text(name)
160 $('<option/>').attr('value', name).text(name)
161 161 )
162 162 }
163 163 /**
164 164 * set an UI preset from `register_preset`
165 165 * @method set_preset
166 166 * @param preset_name {String} string corresponding to the preset name
167 167 *
168 168 * @static
169 169 * @private
170 170 * @example
171 171 *
172 172 * CellToolbar.set_preset('foo.foo_preset1');
173 173 */
174 174 CellToolbar.set_preset= function(preset_name){
175 175 var preset = CellToolbar._presets[preset_name];
176 176
177 177 if(preset != undefined){
178 178 CellToolbar._ui_controls_list = preset;
179 179 CellToolbar.rebuild_all();
180 180 }
181 181 }
182 182
183 183
184 184 // this is by design not a prototype.
185 185 /**
186 186 * This should be called on the class and not on a instance as it will trigger
187 187 * rebuild of all the instances.
188 188 * @method rebuild_all
189 189 * @static
190 190 *
191 191 */
192 192 CellToolbar.rebuild_all = function(){
193 193 for(var i in CellToolbar._instances){
194 194 CellToolbar._instances[i].rebuild();
195 195 }
196 196 }
197 197
198 198 /**
199 199 * Rebuild all the button on the toolbar to update it's state.
200 200 * @method rebuild
201 201 */
202 202 CellToolbar.prototype.rebuild = function(){
203 203 // strip evrything from the div
204 204 // which is probabli metainner.
205 205 // or this.element.
206 206 this.inner_element.empty();
207 207
208 208 var cdict = CellToolbar._callback_dict;
209 209 var preset = CellToolbar._ui_controls_list;
210 210 // Yes we iterate on the class varaible, not the instance one.
211 211 for ( var index in CellToolbar._ui_controls_list){
212 212 var local_div = $('<div/>').addClass('button_container');
213 213 // Note,
214 214 // do this the other way, wrap in try/catch and don't append if any errors.
215 215 this.inner_element.append(local_div)
216 cdict[preset[index]](local_div,this.cell)
216 cdict[preset[index]](local_div, this.cell)
217 217 }
218 218
219 219 }
220 220
221
222
223
224 221
225 222 /**
226 223 */
227 224 CellToolbar.utils = {};
228 225
229 226 /**
230 * A utility function to generate bindings between a checkbox and metadata
227 * A utility function to generate bindings between a checkbox and cell/metadata
231 228 * @method utils.checkbox_ui_generator
232 229 * @static
233 230 *
234 231 * @param name {string} Label in front of the checkbox
235 * @param setter {function( metadata_dict, newValue )}
236 * A setter method to set the newValue of the metadata dictionnary
237 * @param getter {function( metadata )}
238 * A getter methods which return the current value of the metadata.
232 * @param setter {function( cell, newValue )}
233 * A setter method to set the newValue
234 * @param getter {function( cell )}
235 * A getter methods which return the current value.
239 236 *
240 237 * @return callback {function( div, cell )} Callback to be passed to `register_callback`
241 238 *
242 239 * @example
243 240 *
244 241 * An exmple that bind the subkey `slideshow.isSectionStart` to a checkbox with a `New Slide` label
245 242 *
246 243 * var newSlide = CellToolbar.utils.checkbox_ui_generator('New Slide',
247 244 * // setter
248 * function(metadata,value){
245 * function(cell, value){
249 246 * // we check that the slideshow namespace exist and create it if needed
250 * if (metadata.slideshow == undefined){metadata.slideshow = {}}
247 * if (cell.metadata.slideshow == undefined){cell.metadata.slideshow = {}}
251 248 * // set the value
252 * metadata.slideshow.isSectionStart = value
249 * cell.metadata.slideshow.isSectionStart = value
253 250 * },
254 251 * //geter
255 * function(metadata){ var ns = metadata.slideshow;
252 * function(cell){ var ns = cell.metadata.slideshow;
256 253 * // if the slideshow namespace does not exist return `undefined`
257 254 * // (will be interpreted as `false` by checkbox) otherwise
258 255 * // return the value
259 256 * return (ns == undefined)? undefined: ns.isSectionStart
260 257 * }
261 258 * );
262 259 *
263 260 * CellToolbar.register_callback('newSlide', newSlide);
264 261 *
265 262 */
266 CellToolbar.utils.checkbox_ui_generator = function(name,setter,getter){
263 CellToolbar.utils.checkbox_ui_generator = function(name, setter, getter){
267 264 return function(div, cell) {
268 265 var button_container = $(div)
269 266
270 var chkb = $('<input/>').attr('type','checkbox');
271 var lbl = $('<label/>').append($('<span/>').text(name).css('font-size','77%'));
267 var chkb = $('<input/>').attr('type', 'checkbox');
268 var lbl = $('<label/>').append($('<span/>').text(name).css('font-size', '77%'));
272 269 lbl.append(chkb);
273 chkb.attr("checked",getter(cell.metadata));
270 chkb.attr("checked", getter(cell));
274 271
275 272 chkb.click(function(){
276 var v = getter(cell.metadata);
277 setter(cell.metadata,!v);
278 chkb.attr("checked",!v);
273 var v = getter(cell);
274 setter(cell, !v);
275 chkb.attr("checked", !v);
279 276 })
280 277 button_container.append($('<div/>').append(lbl));
281 278
282 279 }
283 280 }
284 281
285 282 /**
286 * A utility function to generate bindings between a dropdown list and metadata
283 * A utility function to generate bindings between a dropdown list cell
287 284 * @method utils.select_ui_generator
288 285 * @static
289 286 *
290 287 * @param list_list {list of sublist} List of sublist of metadata value and name in the dropdown list.
291 288 * subslit shoud contain 2 element each, first a string that woul be displayed in the dropdown list,
292 * and second the corresponding value for the metadata to be passed to setter/return by getter.
293 * @param setter {function( metadata_dict, newValue )}
294 * A setter method to set the newValue of the metadata dictionnary
295 * @param getter {function( metadata )}
289 * and second the corresponding value to be passed to setter/return by getter.
290 * @param setter {function( cell, newValue )}
291 * A setter method to set the newValue
292 * @param getter {function( cell )}
296 293 * A getter methods which return the current value of the metadata.
297 294 * @param [label=""] {String} optionnal label for the dropdown menu
298 295 *
299 296 * @return callback {function( div, cell )} Callback to be passed to `register_callback`
300 297 *
301 298 * @example
302 299 *
303 300 * var select_type = CellToolbar.utils.select_ui_generator([
304 * ["-" ,undefined ],
305 * ["Header Slide" ,"header_slide" ],
306 * ["Slide" ,"slide" ],
307 * ["Fragment" ,"fragment" ],
308 * ["Skip" ,"skip" ],
301 * ["-" , undefined ],
302 * ["Header Slide" , "header_slide" ],
303 * ["Slide" , "slide" ],
304 * ["Fragment" , "fragment" ],
305 * ["Skip" , "skip" ],
309 306 * ],
310 307 * // setter
311 * function(metadata,value){
308 * function(cell, value){
312 309 * // we check that the slideshow namespace exist and create it if needed
313 * if (metadata.slideshow == undefined){metadata.slideshow = {}}
310 * if (cell.metadata.slideshow == undefined){cell.metadata.slideshow = {}}
314 311 * // set the value
315 * metadata.slideshow.slide_type = value
312 * cell.metadata.slideshow.slide_type = value
316 313 * },
317 314 * //geter
318 * function(metadata){ var ns = metadata.slideshow;
315 * function(cell){ var ns = cell.metadata.slideshow;
319 316 * // if the slideshow namespace does not exist return `undefined`
320 317 * // (will be interpreted as `false` by checkbox) otherwise
321 318 * // return the value
322 319 * return (ns == undefined)? undefined: ns.slide_type
323 320 * }
324 * CellToolbar.register_callback('slideshow.select',select_type);
321 * CellToolbar.register_callback('slideshow.select', select_type);
325 322 *
326 323 */
327 CellToolbar.utils.select_ui_generator = function(list_list,setter, getter, label){
324 CellToolbar.utils.select_ui_generator = function(list_list, setter, getter, label){
328 325 label= label? label: "";
329 326 return function(div, cell) {
330 327 var button_container = $(div)
331 var lbl = $("<label/>").append($('<span/>').text(label).css('font-size','77%'));
328 var lbl = $("<label/>").append($('<span/>').text(label).css('font-size', '77%'));
332 329 var select = $('<select/>');
333 330 for(var itemn in list_list){
334 331 var opt = $('<option/>');
335 opt.attr('value',list_list[itemn][1])
332 opt.attr('value', list_list[itemn][1])
336 333 opt.text(list_list[itemn][0])
337 334 select.append(opt);
338 335 }
339 select.val(getter(cell.metadata));
340
336 select.val(getter(cell));
341 337 select.change(function(){
342 setter(cell.metadata,select.val());
338 setter(cell, select.val());
343 339 });
344 340 button_container.append($('<div/>').append(lbl).append(select));
345 341
346 342 }
347 343 };
348 344
349 345
350 346 IPython.CellToolbar = CellToolbar;
351 347
352 348 return IPython;
353 349 }(IPython));
@@ -1,217 +1,224 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 // CellToolbar Example
10 10 //============================================================================
11 11
12 12 /**
13 13 * Example Use for the CellToolbar library
14 14 * add the following to your custom.js to load
15 15 * Celltoolbar UI for slideshow
16 16 *
17 17 * ```
18 18 * $.getScript('/static/js/examples/celltoolbar.example.js');
19 19 * ```
20 20 */
21 21 // IIFE without asignement, we don't modifiy the IPython namespace
22 22 (function (IPython) {
23 23 "use strict";
24 24
25 25 var CellToolbar = IPython.CellToolbar;
26 26
27 27
28 28 var raw_edit = function(cell){
29 29
30 30 var md = cell.metadata
31 31 var error_div = $('<div/>').css('color','red')
32 32
33 33 var textarea = $('<textarea/>')
34 34 .attr('rows','13')
35 35 .attr('cols','75')
36 36 .attr('name','metadata')
37 37 .text(JSON.stringify(md, null,4)||'');
38 38 var dialogform = $('<div/>').attr('title','Edit the metadata')
39 39 .append(
40 40 $('<form/>').append(
41 41 $('<fieldset/>').append(
42 42 $('<label/>')
43 43 .attr('for','metadata')
44 44 .text("Metadata (I know what I'm dooing and I won't complain if it breaks my notebook)")
45 45 )
46 46 .append(error_div)
47 47 .append($('<br/>'))
48 48 .append(
49 49 textarea
50 50 )
51 51 )
52 52 );
53 53 var editor = CodeMirror.fromTextArea(textarea[0], {
54 54 lineNumbers: true,
55 55 matchBrackets: true,
56 56 });
57 57 $(dialogform).dialog({
58 58 autoOpen: true,
59 59 height: 300,
60 60 width: 650,
61 61 modal: true,
62 62 buttons: {
63 63 "Ok": function() {
64 64 //validate json and set it
65 65 try {
66 66 var json = JSON.parse(editor.getValue());
67 67 cell.metadata = json;
68 68 $( this ).dialog( "close" );
69 69 }
70 70 catch(e)
71 71 {
72 72 error_div.text('Warning, invalid json, not saved');
73 73 }
74 74 },
75 75 Cancel: function() {
76 76 $( this ).dialog( "close" );
77 77 }
78 78 },
79 79 close: function() {
80 80 //cleanup on close
81 81 $(this).remove();
82 82 }
83 83 });
84 84 editor.refresh();
85 85 }
86 86
87 87 var add_raw_edit_button = function(div, cell) {
88 88 var button_container = div
89 89 var button = $('<div/>').button({label:'Raw Edit'})
90 90 .click(function(){raw_edit(cell); return false;})
91 91 button_container.append(button);
92 92 }
93 93
94 94 CellToolbar.register_callback('example.rawedit',add_raw_edit_button);
95 95 var example_preset = []
96 96 example_preset.push('example.rawedit');
97 97
98 98
99 99 var simple_button = function(div, cell) {
100 100 var button_container = $(div);
101 101 var button = $('<div/>').button({icons:{primary:'ui-icon-locked'}});
102 102 var fun = function(value){
103 103 try{
104 104 if(value){
105 105 cell.code_mirror.setOption('readOnly','nocursor')
106 106 button.button('option','icons',{primary:'ui-icon-locked'})
107 107 } else {
108 108 cell.code_mirror.setOption('readOnly','false')
109 109 button.button('option','icons',{primary:'ui-icon-unlocked'})
110 110 }
111 111 } catch(e){}
112 112
113 113 }
114 114 fun(cell.metadata.ro)
115 115 button.click(function(){
116 116 var v = cell.metadata.ro;
117 117 var locked = !v;
118 118 cell.metadata.ro = locked;
119 119 fun(locked)
120 120 })
121 121 .css('height','16px')
122 122 .css('width','35px');
123 123 button_container.append(button);
124 124 }
125 125
126 126 CellToolbar.register_callback('example.lock',simple_button);
127 127 example_preset.push('example.lock');
128 128
129 129 var toggle_test = function(div, cell) {
130 130 var button_container = $(div)
131 131 var button = $('<div/>')
132 132 .button({label:String(cell.metadata.foo)}).
133 133 css('width','65px');
134 134 button.click(function(){
135 135 var v = cell.metadata.foo;
136 136 cell.metadata.foo = !v;
137 137 button.button("option","label",String(!v));
138 138 })
139 139 button_container.append(button);
140 140 }
141 141
142 142 CellToolbar.register_callback('example.toggle',toggle_test);
143 143 example_preset.push('example.toggle');
144 144
145 var checkbox_test = function(div, cell) {
146 var button_container = $(div)
145 var checkbox_test = CellToolbar.utils.checkbox_ui_generator('Yes/No',
146 // setter
147 function(cell, value){
148 // we check that the slideshow namespace exist and create it if needed
149 if (cell.metadata.yn_test == undefined){cell.metadata.yn_test = {}}
150 // set the value
151 cell.metadata.yn_test.value = value
152 },
153 //geter
154 function(cell){ var ns = cell.metadata.yn_test;
155 // if the slideshow namespace does not exist return `undefined`
156 // (will be interpreted as `false` by checkbox) otherwise
157 // return the value
158 return (ns == undefined)? undefined: ns.value
159 }
160 );
147 161
148 var chkb = $('<input/>').attr('type','checkbox');
149 var lbl = $('<label/>').append($('<span/>').text('bar :').css('font-size','77%'));
150 lbl.append(chkb);
151 chkb.attr("checked",cell.metadata.bar);
152 chkb.click(function(){
153 var v = cell.metadata.bar;
154 cell.metadata.bar = !v;
155 chkb.attr("checked",!v);
156 })
157 button_container.append($('<div/>').append(lbl));
158
159 }
160 162
161 163 CellToolbar.register_callback('example.checkbox',checkbox_test);
162 164 example_preset.push('example.checkbox');
163 165
164 var select_test = function(div, cell) {
165 var button_container = $(div)
166
167 var select = $('<select/>');
168
169 select.append($('<option/>').attr('value','foo').text('foo'));
170 select.append($('<option/>').attr('value','bar').text('bar'));
171 select.append($('<option/>').attr('value','qux').text('qux'));
172 select.append($('<option/>').attr('value','zip').text('zip'));
173 select.val(cell.metadata.option);
174 select.change(function(){
175 cell.metadata.option = select.val();
166 var select_test = CellToolbar.utils.select_ui_generator([
167 ["-" ,undefined ],
168 ["Header Slide" ,"header_slide" ],
169 ["Slide" ,"slide" ],
170 ["Fragment" ,"fragment" ],
171 ["Skip" ,"skip" ],
172 ],
173 // setter
174 function(cell,value){
175 // we check that the slideshow namespace exist and create it if needed
176 if (cell.metadata.test == undefined){cell.metadata.test = {}}
177 // set the value
178 cell.metadata.test.slide_type = value
179 },
180 //geter
181 function(cell){ var ns = cell.metadata.test;
182 // if the slideshow namespace does not exist return `undefined`
183 // (will be interpreted as `false` by checkbox) otherwise
184 // return the value
185 return (ns == undefined)? undefined: ns.slide_type
176 186 });
177 button_container.append($('<div/>').append(select));
178
179 }
180 187
181 188 CellToolbar.register_callback('example.select',select_test);
182 189 example_preset.push('example.select');
183 190
184 191 var simple_dialog = function(title,text){
185 192 var dlg = $('<div/>').attr('title',title)
186 193 .append($('<p/>').text(text))
187 194 $(dlg).dialog({
188 195 autoOpen: true,
189 196 height: 300,
190 197 width: 650,
191 198 modal: true,
192 199 close: function() {
193 200 //cleanup on close
194 201 $(this).remove();
195 202 }
196 203 });
197 204 }
198 205
199 206 var add_simple_dialog_button = function(div, cell) {
200 207 var help_text = ["This is the Metadata editting UI.",
201 208 "It heavily rely on plugin to work ",
202 209 "and is still under developpement. You shouldn't wait too long before",
203 210 " seeing some customisable buttons in those toolbar."
204 211 ].join('\n')
205 212 var button_container = $(div)
206 213 var button = $('<div/>').button({label:'?'})
207 214 .click(function(){simple_dialog('help',help_text); return false;})
208 215 button_container.append(button);
209 216 }
210 217
211 218 CellToolbar.register_callback('example.help',add_simple_dialog_button)
212 219 example_preset.push('example.help')
213 220
214 221 CellToolbar.register_preset('example',example_preset);
215 222 console.log('Example extension for metadata editting loaded.');
216 223
217 224 }(IPython));
General Comments 0
You need to be logged in to leave comments. Login now