##// END OF EJS Templates
Merge pull request #6127 from minrk/fix-edit-md...
Matthias Bussonnier -
r17287:c3e6f16b merge
parent child Browse files
Show More
@@ -1,163 +1,163 b''
1 // Copyright (c) IPython Development Team.
1 // Copyright (c) IPython Development Team.
2 // Distributed under the terms of the Modified BSD License.
2 // Distributed under the terms of the Modified BSD License.
3
3
4 define([
4 define([
5 'base/js/namespace',
5 'base/js/namespace',
6 'jquery',
6 'jquery',
7 ], function(IPython, $) {
7 ], function(IPython, $) {
8 "use strict";
8 "use strict";
9
9
10 var modal = function (options) {
10 var modal = function (options) {
11
11
12 var modal = $("<div/>")
12 var modal = $("<div/>")
13 .addClass("modal")
13 .addClass("modal")
14 .addClass("fade")
14 .addClass("fade")
15 .attr("role", "dialog");
15 .attr("role", "dialog");
16 var dialog = $("<div/>")
16 var dialog = $("<div/>")
17 .addClass("modal-dialog")
17 .addClass("modal-dialog")
18 .appendTo(modal);
18 .appendTo(modal);
19 var dialog_content = $("<div/>")
19 var dialog_content = $("<div/>")
20 .addClass("modal-content")
20 .addClass("modal-content")
21 .appendTo(dialog);
21 .appendTo(dialog);
22 dialog_content.append(
22 dialog_content.append(
23 $("<div/>")
23 $("<div/>")
24 .addClass("modal-header")
24 .addClass("modal-header")
25 .append($("<button>")
25 .append($("<button>")
26 .attr("type", "button")
26 .attr("type", "button")
27 .addClass("close")
27 .addClass("close")
28 .attr("data-dismiss", "modal")
28 .attr("data-dismiss", "modal")
29 .attr("aria-hidden", "true")
29 .attr("aria-hidden", "true")
30 .html("&times;")
30 .html("&times;")
31 ).append(
31 ).append(
32 $("<h4/>")
32 $("<h4/>")
33 .addClass('modal-title')
33 .addClass('modal-title')
34 .text(options.title || "")
34 .text(options.title || "")
35 )
35 )
36 ).append(
36 ).append(
37 $("<div/>").addClass("modal-body").append(
37 $("<div/>").addClass("modal-body").append(
38 options.body || $("<p/>")
38 options.body || $("<p/>")
39 )
39 )
40 );
40 );
41
41
42 var footer = $("<div/>").addClass("modal-footer");
42 var footer = $("<div/>").addClass("modal-footer");
43
43
44 for (var label in options.buttons) {
44 for (var label in options.buttons) {
45 var btn_opts = options.buttons[label];
45 var btn_opts = options.buttons[label];
46 var button = $("<button/>")
46 var button = $("<button/>")
47 .addClass("btn btn-default btn-sm")
47 .addClass("btn btn-default btn-sm")
48 .attr("data-dismiss", "modal")
48 .attr("data-dismiss", "modal")
49 .text(label);
49 .text(label);
50 if (btn_opts.click) {
50 if (btn_opts.click) {
51 button.click($.proxy(btn_opts.click, dialog_content));
51 button.click($.proxy(btn_opts.click, dialog_content));
52 }
52 }
53 if (btn_opts.class) {
53 if (btn_opts.class) {
54 button.addClass(btn_opts.class);
54 button.addClass(btn_opts.class);
55 }
55 }
56 footer.append(button);
56 footer.append(button);
57 }
57 }
58 dialog_content.append(footer);
58 dialog_content.append(footer);
59 // hook up on-open event
59 // hook up on-open event
60 modal.on("shown.bs.modal", function() {
60 modal.on("shown.bs.modal", function() {
61 setTimeout(function() {
61 setTimeout(function() {
62 footer.find("button").last().focus();
62 footer.find("button").last().focus();
63 if (options.open) {
63 if (options.open) {
64 $.proxy(options.open, modal)();
64 $.proxy(options.open, modal)();
65 }
65 }
66 }, 0);
66 }, 0);
67 });
67 });
68
68
69 // destroy modal on hide, unless explicitly asked not to
69 // destroy modal on hide, unless explicitly asked not to
70 if (options.destroy === undefined || options.destroy) {
70 if (options.destroy === undefined || options.destroy) {
71 modal.on("hidden.bs.modal", function () {
71 modal.on("hidden.bs.modal", function () {
72 modal.remove();
72 modal.remove();
73 });
73 });
74 }
74 }
75 modal.on("hidden.bs.modal", function () {
75 modal.on("hidden.bs.modal", function () {
76 if (options.notebook) {
76 if (options.notebook) {
77 var cell = options.notebook.get_selected_cell();
77 var cell = options.notebook.get_selected_cell();
78 if (cell) cell.select();
78 if (cell) cell.select();
79 }
79 }
80 if (options.keyboard_manager) {
80 if (options.keyboard_manager) {
81 options.keyboard_manager.enable();
81 options.keyboard_manager.enable();
82 options.keyboard_manager.command_mode();
82 options.keyboard_manager.command_mode();
83 }
83 }
84 });
84 });
85
85
86 if (options.keyboard_manager) {
86 if (options.keyboard_manager) {
87 options.keyboard_manager.disable();
87 options.keyboard_manager.disable();
88 }
88 }
89
89
90 return modal.modal(options);
90 return modal.modal(options);
91 };
91 };
92
92
93 var edit_metadata = function (options) {
93 var edit_metadata = function (options) {
94 options.name = options.name || "Cell";
94 options.name = options.name || "Cell";
95 var error_div = $('<div/>').css('color', 'red');
95 var error_div = $('<div/>').css('color', 'red');
96 var message =
96 var message =
97 "Manually edit the JSON below to manipulate the metadata for this " + options.name + "." +
97 "Manually edit the JSON below to manipulate the metadata for this " + options.name + "." +
98 " We recommend putting custom metadata attributes in an appropriately named sub-structure," +
98 " We recommend putting custom metadata attributes in an appropriately named sub-structure," +
99 " so they don't conflict with those of others.";
99 " so they don't conflict with those of others.";
100
100
101 var textarea = $('<textarea/>')
101 var textarea = $('<textarea/>')
102 .attr('rows', '13')
102 .attr('rows', '13')
103 .attr('cols', '80')
103 .attr('cols', '80')
104 .attr('name', 'metadata')
104 .attr('name', 'metadata')
105 .text(JSON.stringify(options.md || {}, null, 2));
105 .text(JSON.stringify(options.md || {}, null, 2));
106
106
107 var dialogform = $('<div/>').attr('title', 'Edit the metadata')
107 var dialogform = $('<div/>').attr('title', 'Edit the metadata')
108 .append(
108 .append(
109 $('<form/>').append(
109 $('<form/>').append(
110 $('<fieldset/>').append(
110 $('<fieldset/>').append(
111 $('<label/>')
111 $('<label/>')
112 .attr('for','metadata')
112 .attr('for','metadata')
113 .text(message)
113 .text(message)
114 )
114 )
115 .append(error_div)
115 .append(error_div)
116 .append($('<br/>'))
116 .append($('<br/>'))
117 .append(textarea)
117 .append(textarea)
118 )
118 )
119 );
119 );
120 var editor = CodeMirror.fromTextArea(textarea[0], {
120 var editor = CodeMirror.fromTextArea(textarea[0], {
121 lineNumbers: true,
121 lineNumbers: true,
122 matchBrackets: true,
122 matchBrackets: true,
123 indentUnit: 2,
123 indentUnit: 2,
124 autoIndent: true,
124 autoIndent: true,
125 mode: 'application/json',
125 mode: 'application/json',
126 });
126 });
127 var modal = modal({
127 var modal_obj = modal({
128 title: "Edit " + options.name + " Metadata",
128 title: "Edit " + options.name + " Metadata",
129 body: dialogform,
129 body: dialogform,
130 buttons: {
130 buttons: {
131 OK: { class : "btn-primary",
131 OK: { class : "btn-primary",
132 click: function() {
132 click: function() {
133 // validate json and set it
133 // validate json and set it
134 var new_md;
134 var new_md;
135 try {
135 try {
136 new_md = JSON.parse(editor.getValue());
136 new_md = JSON.parse(editor.getValue());
137 } catch(e) {
137 } catch(e) {
138 console.log(e);
138 console.log(e);
139 error_div.text('WARNING: Could not save invalid JSON.');
139 error_div.text('WARNING: Could not save invalid JSON.');
140 return false;
140 return false;
141 }
141 }
142 options.callback(new_md);
142 options.callback(new_md);
143 }
143 }
144 },
144 },
145 Cancel: {}
145 Cancel: {}
146 },
146 },
147 notebook: options.notebook,
147 notebook: options.notebook,
148 keyboard_manager: options.keyboard_manager,
148 keyboard_manager: options.keyboard_manager,
149 });
149 });
150
150
151 modal.on('shown.bs.modal', function(){ editor.refresh(); });
151 modal_obj.on('shown.bs.modal', function(){ editor.refresh(); });
152 };
152 };
153
153
154 var dialog = {
154 var dialog = {
155 modal : modal,
155 modal : modal,
156 edit_metadata : edit_metadata,
156 edit_metadata : edit_metadata,
157 };
157 };
158
158
159 // Backwards compatability.
159 // Backwards compatability.
160 IPython.dialog = dialog;
160 IPython.dialog = dialog;
161
161
162 return dialog;
162 return dialog;
163 });
163 });
@@ -1,417 +1,417 b''
1 // Copyright (c) IPython Development Team.
1 // Copyright (c) IPython Development Team.
2 // Distributed under the terms of the Modified BSD License.
2 // Distributed under the terms of the Modified BSD License.
3
3
4 define([
4 define([
5 'base/js/namespace',
5 'base/js/namespace',
6 'jquery',
6 'jquery',
7 ], function(IPython, $) {
7 ], function(IPython, $) {
8 "use strict";
8 "use strict";
9
9
10 var CellToolbar = function (options) {
10 var CellToolbar = function (options) {
11 // Constructor
11 // Constructor
12 //
12 //
13 // Parameters:
13 // Parameters:
14 // options: dictionary
14 // options: dictionary
15 // Dictionary of keyword arguments.
15 // Dictionary of keyword arguments.
16 // events: $(Events) instance
16 // events: $(Events) instance
17 // cell: Cell instance
17 // cell: Cell instance
18 // notebook: Notebook instance
18 // notebook: Notebook instance
19 CellToolbar._instances.push(this);
19 CellToolbar._instances.push(this);
20 this.notebook = options.notebook;
20 this.notebook = options.notebook;
21 this.events = options.events;
21 this.events = options.events;
22 this.cell = options.cell;
22 this.cell = options.cell;
23 this.create_element();
23 this.create_element();
24 this.rebuild();
24 this.rebuild();
25 return this;
25 return this;
26 };
26 };
27
27
28
28
29 CellToolbar.prototype.create_element = function () {
29 CellToolbar.prototype.create_element = function () {
30 this.inner_element = $('<div/>').addClass('celltoolbar');
30 this.inner_element = $('<div/>').addClass('celltoolbar');
31 this.element = $('<div/>').addClass('ctb_hideshow')
31 this.element = $('<div/>').addClass('ctb_hideshow')
32 .append(this.inner_element);
32 .append(this.inner_element);
33 };
33 };
34
34
35
35
36 // The default css style for the outer celltoolbar div
36 // The default css style for the outer celltoolbar div
37 // (ctb_hideshow) is display: none.
37 // (ctb_hideshow) is display: none.
38 // To show the cell toolbar, *both* of the following conditions must be met:
38 // To show the cell toolbar, *both* of the following conditions must be met:
39 // - A parent container has class `ctb_global_show`
39 // - A parent container has class `ctb_global_show`
40 // - The celltoolbar has the class `ctb_show`
40 // - The celltoolbar has the class `ctb_show`
41 // This allows global show/hide, as well as per-cell show/hide.
41 // This allows global show/hide, as well as per-cell show/hide.
42
42
43 CellToolbar.global_hide = function () {
43 CellToolbar.global_hide = function () {
44 $('body').removeClass('ctb_global_show');
44 $('body').removeClass('ctb_global_show');
45 };
45 };
46
46
47
47
48 CellToolbar.global_show = function () {
48 CellToolbar.global_show = function () {
49 $('body').addClass('ctb_global_show');
49 $('body').addClass('ctb_global_show');
50 };
50 };
51
51
52
52
53 CellToolbar.prototype.hide = function () {
53 CellToolbar.prototype.hide = function () {
54 this.element.removeClass('ctb_show');
54 this.element.removeClass('ctb_show');
55 };
55 };
56
56
57
57
58 CellToolbar.prototype.show = function () {
58 CellToolbar.prototype.show = function () {
59 this.element.addClass('ctb_show');
59 this.element.addClass('ctb_show');
60 };
60 };
61
61
62
62
63 /**
63 /**
64 * Class variable that should contain a dict of all available callback
64 * Class variable that should contain a dict of all available callback
65 * we need to think of wether or not we allow nested namespace
65 * we need to think of wether or not we allow nested namespace
66 * @property _callback_dict
66 * @property _callback_dict
67 * @private
67 * @private
68 * @static
68 * @static
69 * @type Dict
69 * @type Dict
70 */
70 */
71 CellToolbar._callback_dict = {};
71 CellToolbar._callback_dict = {};
72
72
73
73
74 /**
74 /**
75 * Class variable that should contain the reverse order list of the button
75 * Class variable that should contain the reverse order list of the button
76 * to add to the toolbar of each cell
76 * to add to the toolbar of each cell
77 * @property _ui_controls_list
77 * @property _ui_controls_list
78 * @private
78 * @private
79 * @static
79 * @static
80 * @type List
80 * @type List
81 */
81 */
82 CellToolbar._ui_controls_list = [];
82 CellToolbar._ui_controls_list = [];
83
83
84
84
85 /**
85 /**
86 * Class variable that should contain the CellToolbar instances for each
86 * Class variable that should contain the CellToolbar instances for each
87 * cell of the notebook
87 * cell of the notebook
88 *
88 *
89 * @private
89 * @private
90 * @property _instances
90 * @property _instances
91 * @static
91 * @static
92 * @type List
92 * @type List
93 */
93 */
94 CellToolbar._instances = [];
94 CellToolbar._instances = [];
95
95
96
96
97 /**
97 /**
98 * keep a list of all the available presets for the toolbar
98 * keep a list of all the available presets for the toolbar
99 * @private
99 * @private
100 * @property _presets
100 * @property _presets
101 * @static
101 * @static
102 * @type Dict
102 * @type Dict
103 */
103 */
104 CellToolbar._presets = {};
104 CellToolbar._presets = {};
105
105
106
106
107 // this is by design not a prototype.
107 // this is by design not a prototype.
108 /**
108 /**
109 * Register a callback to create an UI element in a cell toolbar.
109 * Register a callback to create an UI element in a cell toolbar.
110 * @method register_callback
110 * @method register_callback
111 * @param name {String} name to use to refer to the callback. It is advised to use a prefix with the name
111 * @param name {String} name to use to refer to the callback. It is advised to use a prefix with the name
112 * for easier sorting and avoid collision
112 * for easier sorting and avoid collision
113 * @param callback {function(div, cell)} callback that will be called to generate the ui element
113 * @param callback {function(div, cell)} callback that will be called to generate the ui element
114 * @param [cell_types] {List of String|undefined} optional list of cell types. If present the UI element
114 * @param [cell_types] {List of String|undefined} optional list of cell types. If present the UI element
115 * will be added only to cells of types in the list.
115 * will be added only to cells of types in the list.
116 *
116 *
117 *
117 *
118 * The callback will receive the following element :
118 * The callback will receive the following element :
119 *
119 *
120 * * a div in which to add element.
120 * * a div in which to add element.
121 * * the cell it is responsible from
121 * * the cell it is responsible from
122 *
122 *
123 * @example
123 * @example
124 *
124 *
125 * Example that create callback for a button that toggle between `true` and `false` label,
125 * Example that create callback for a button that toggle between `true` and `false` label,
126 * with the metadata under the key 'foo' to reflect the status of the button.
126 * with the metadata under the key 'foo' to reflect the status of the button.
127 *
127 *
128 * // first param reference to a DOM div
128 * // first param reference to a DOM div
129 * // second param reference to the cell.
129 * // second param reference to the cell.
130 * var toggle = function(div, cell) {
130 * var toggle = function(div, cell) {
131 * var button_container = $(div)
131 * var button_container = $(div)
132 *
132 *
133 * // let's create a button that show the current value of the metadata
133 * // let's create a button that show the current value of the metadata
134 * var button = $('<div/>').button({label:String(cell.metadata.foo)});
134 * var button = $('<div/>').button({label:String(cell.metadata.foo)});
135 *
135 *
136 * // On click, change the metadata value and update the button label
136 * // On click, change the metadata value and update the button label
137 * button.click(function(){
137 * button.click(function(){
138 * var v = cell.metadata.foo;
138 * var v = cell.metadata.foo;
139 * cell.metadata.foo = !v;
139 * cell.metadata.foo = !v;
140 * button.button("option", "label", String(!v));
140 * button.button("option", "label", String(!v));
141 * })
141 * })
142 *
142 *
143 * // add the button to the DOM div.
143 * // add the button to the DOM div.
144 * button_container.append(button);
144 * button_container.append(button);
145 * }
145 * }
146 *
146 *
147 * // now we register the callback under the name `foo` to give the
147 * // now we register the callback under the name `foo` to give the
148 * // user the ability to use it later
148 * // user the ability to use it later
149 * CellToolbar.register_callback('foo', toggle);
149 * CellToolbar.register_callback('foo', toggle);
150 */
150 */
151 CellToolbar.register_callback = function(name, callback, cell_types) {
151 CellToolbar.register_callback = function(name, callback, cell_types) {
152 // Overwrite if it already exists.
152 // Overwrite if it already exists.
153 CellToolbar._callback_dict[name] = cell_types ? {callback: callback, cell_types: cell_types} : callback;
153 CellToolbar._callback_dict[name] = cell_types ? {callback: callback, cell_types: cell_types} : callback;
154 };
154 };
155
155
156
156
157 /**
157 /**
158 * Register a preset of UI element in a cell toolbar.
158 * Register a preset of UI element in a cell toolbar.
159 * Not supported Yet.
159 * Not supported Yet.
160 * @method register_preset
160 * @method register_preset
161 * @param name {String} name to use to refer to the preset. It is advised to use a prefix with the name
161 * @param name {String} name to use to refer to the preset. It is advised to use a prefix with the name
162 * for easier sorting and avoid collision
162 * for easier sorting and avoid collision
163 * @param preset_list {List of String} reverse order of the button in the toolbar. Each String of the list
163 * @param preset_list {List of String} reverse order of the button in the toolbar. Each String of the list
164 * should correspond to a name of a registerd callback.
164 * should correspond to a name of a registerd callback.
165 *
165 *
166 * @private
166 * @private
167 * @example
167 * @example
168 *
168 *
169 * CellToolbar.register_callback('foo.c1', function(div, cell){...});
169 * CellToolbar.register_callback('foo.c1', function(div, cell){...});
170 * CellToolbar.register_callback('foo.c2', function(div, cell){...});
170 * CellToolbar.register_callback('foo.c2', function(div, cell){...});
171 * CellToolbar.register_callback('foo.c3', function(div, cell){...});
171 * CellToolbar.register_callback('foo.c3', function(div, cell){...});
172 * CellToolbar.register_callback('foo.c4', function(div, cell){...});
172 * CellToolbar.register_callback('foo.c4', function(div, cell){...});
173 * CellToolbar.register_callback('foo.c5', function(div, cell){...});
173 * CellToolbar.register_callback('foo.c5', function(div, cell){...});
174 *
174 *
175 * CellToolbar.register_preset('foo.foo_preset1', ['foo.c1', 'foo.c2', 'foo.c5'])
175 * CellToolbar.register_preset('foo.foo_preset1', ['foo.c1', 'foo.c2', 'foo.c5'])
176 * CellToolbar.register_preset('foo.foo_preset2', ['foo.c4', 'foo.c5'])
176 * CellToolbar.register_preset('foo.foo_preset2', ['foo.c4', 'foo.c5'])
177 */
177 */
178 CellToolbar.register_preset = function(name, preset_list, notebook, events) {
178 CellToolbar.register_preset = function(name, preset_list, notebook, events) {
179 CellToolbar._presets[name] = preset_list;
179 CellToolbar._presets[name] = preset_list;
180 events.trigger('preset_added.CellToolbar', {name: name});
180 events.trigger('preset_added.CellToolbar', {name: name});
181 // When "register_callback" is called by a custom extension, it may be executed after notebook is loaded.
181 // When "register_callback" is called by a custom extension, it may be executed after notebook is loaded.
182 // In that case, activate the preset if needed.
182 // In that case, activate the preset if needed.
183 if (notebook && notebook.metadata && notebook.metadata.celltoolbar === name)
183 if (notebook && notebook.metadata && notebook.metadata.celltoolbar === name)
184 CellToolbar.activate_preset(name, events);
184 CellToolbar.activate_preset(name, events);
185 };
185 };
186
186
187
187
188 /**
188 /**
189 * List the names of the presets that are currently registered.
189 * List the names of the presets that are currently registered.
190 *
190 *
191 * @method list_presets
191 * @method list_presets
192 * @static
192 * @static
193 */
193 */
194 CellToolbar.list_presets = function() {
194 CellToolbar.list_presets = function() {
195 var keys = [];
195 var keys = [];
196 for (var k in CellToolbar._presets) {
196 for (var k in CellToolbar._presets) {
197 keys.push(k);
197 keys.push(k);
198 }
198 }
199 return keys;
199 return keys;
200 };
200 };
201
201
202
202
203 /**
203 /**
204 * Activate an UI preset from `register_preset`
204 * Activate an UI preset from `register_preset`
205 *
205 *
206 * This does not update the selection UI.
206 * This does not update the selection UI.
207 *
207 *
208 * @method activate_preset
208 * @method activate_preset
209 * @param preset_name {String} string corresponding to the preset name
209 * @param preset_name {String} string corresponding to the preset name
210 *
210 *
211 * @static
211 * @static
212 * @private
212 * @private
213 * @example
213 * @example
214 *
214 *
215 * CellToolbar.activate_preset('foo.foo_preset1');
215 * CellToolbar.activate_preset('foo.foo_preset1');
216 */
216 */
217 CellToolbar.activate_preset = function(preset_name, events){
217 CellToolbar.activate_preset = function(preset_name, events){
218 var preset = CellToolbar._presets[preset_name];
218 var preset = CellToolbar._presets[preset_name];
219
219
220 if(preset !== undefined){
220 if(preset !== undefined){
221 CellToolbar._ui_controls_list = preset;
221 CellToolbar._ui_controls_list = preset;
222 CellToolbar.rebuild_all();
222 CellToolbar.rebuild_all();
223 }
223 }
224
224
225 if (events) {
225 if (events) {
226 events.trigger('preset_activated.CellToolbar', {name: preset_name});
226 events.trigger('preset_activated.CellToolbar', {name: preset_name});
227 }
227 }
228 };
228 };
229
229
230
230
231 /**
231 /**
232 * This should be called on the class and not on a instance as it will trigger
232 * This should be called on the class and not on a instance as it will trigger
233 * rebuild of all the instances.
233 * rebuild of all the instances.
234 * @method rebuild_all
234 * @method rebuild_all
235 * @static
235 * @static
236 *
236 *
237 */
237 */
238 CellToolbar.rebuild_all = function(){
238 CellToolbar.rebuild_all = function(){
239 for(var i=0; i < CellToolbar._instances.length; i++){
239 for(var i=0; i < CellToolbar._instances.length; i++){
240 CellToolbar._instances[i].rebuild();
240 CellToolbar._instances[i].rebuild();
241 }
241 }
242 };
242 };
243
243
244 /**
244 /**
245 * Rebuild all the button on the toolbar to update its state.
245 * Rebuild all the button on the toolbar to update its state.
246 * @method rebuild
246 * @method rebuild
247 */
247 */
248 CellToolbar.prototype.rebuild = function(){
248 CellToolbar.prototype.rebuild = function(){
249 // strip evrything from the div
249 // strip evrything from the div
250 // which is probably inner_element
250 // which is probably inner_element
251 // or this.element.
251 // or this.element.
252 this.inner_element.empty();
252 this.inner_element.empty();
253 this.ui_controls_list = [];
253 this.ui_controls_list = [];
254
254
255 var callbacks = CellToolbar._callback_dict;
255 var callbacks = CellToolbar._callback_dict;
256 var preset = CellToolbar._ui_controls_list;
256 var preset = CellToolbar._ui_controls_list;
257 // Yes we iterate on the class variable, not the instance one.
257 // Yes we iterate on the class variable, not the instance one.
258 for (var i=0; i < preset.length; i++) {
258 for (var i=0; i < preset.length; i++) {
259 var key = preset[i];
259 var key = preset[i];
260 var callback = callbacks[key];
260 var callback = callbacks[key];
261 if (!callback) continue;
261 if (!callback) continue;
262
262
263 if (typeof callback === 'object') {
263 if (typeof callback === 'object') {
264 if (callback.cell_types.indexOf(this.cell.cell_type) === -1) continue;
264 if (callback.cell_types.indexOf(this.cell.cell_type) === -1) continue;
265 callback = callback.callback;
265 callback = callback.callback;
266 }
266 }
267
267
268 var local_div = $('<div/>').addClass('button_container');
268 var local_div = $('<div/>').addClass('button_container');
269 try {
269 try {
270 callback(local_div, this.cell, this);
270 callback(local_div, this.cell, this);
271 this.ui_controls_list.push(key);
271 this.ui_controls_list.push(key);
272 } catch (e) {
272 } catch (e) {
273 console.log("Error in cell toolbar callback " + key, e);
273 console.log("Error in cell toolbar callback " + key, e);
274 continue;
274 continue;
275 }
275 }
276 // only append if callback succeeded.
276 // only append if callback succeeded.
277 this.inner_element.append(local_div);
277 this.inner_element.append(local_div);
278 }
278 }
279
279
280 // If there are no controls or the cell is a rendered TextCell hide the toolbar.
280 // If there are no controls or the cell is a rendered TextCell hide the toolbar.
281 if (!this.ui_controls_list.length || (this.cell_type != 'code' && this.cell.rendered)) {
281 if (!this.ui_controls_list.length || (this.cell.cell_type != 'code' && this.cell.rendered)) {
282 this.hide();
282 this.hide();
283 } else {
283 } else {
284 this.show();
284 this.show();
285 }
285 }
286 };
286 };
287
287
288
288
289 /**
289 /**
290 */
290 */
291 CellToolbar.utils = {};
291 CellToolbar.utils = {};
292
292
293
293
294 /**
294 /**
295 * A utility function to generate bindings between a checkbox and cell/metadata
295 * A utility function to generate bindings between a checkbox and cell/metadata
296 * @method utils.checkbox_ui_generator
296 * @method utils.checkbox_ui_generator
297 * @static
297 * @static
298 *
298 *
299 * @param name {string} Label in front of the checkbox
299 * @param name {string} Label in front of the checkbox
300 * @param setter {function( cell, newValue )}
300 * @param setter {function( cell, newValue )}
301 * A setter method to set the newValue
301 * A setter method to set the newValue
302 * @param getter {function( cell )}
302 * @param getter {function( cell )}
303 * A getter methods which return the current value.
303 * A getter methods which return the current value.
304 *
304 *
305 * @return callback {function( div, cell )} Callback to be passed to `register_callback`
305 * @return callback {function( div, cell )} Callback to be passed to `register_callback`
306 *
306 *
307 * @example
307 * @example
308 *
308 *
309 * An exmple that bind the subkey `slideshow.isSectionStart` to a checkbox with a `New Slide` label
309 * An exmple that bind the subkey `slideshow.isSectionStart` to a checkbox with a `New Slide` label
310 *
310 *
311 * var newSlide = CellToolbar.utils.checkbox_ui_generator('New Slide',
311 * var newSlide = CellToolbar.utils.checkbox_ui_generator('New Slide',
312 * // setter
312 * // setter
313 * function(cell, value){
313 * function(cell, value){
314 * // we check that the slideshow namespace exist and create it if needed
314 * // we check that the slideshow namespace exist and create it if needed
315 * if (cell.metadata.slideshow == undefined){cell.metadata.slideshow = {}}
315 * if (cell.metadata.slideshow == undefined){cell.metadata.slideshow = {}}
316 * // set the value
316 * // set the value
317 * cell.metadata.slideshow.isSectionStart = value
317 * cell.metadata.slideshow.isSectionStart = value
318 * },
318 * },
319 * //geter
319 * //geter
320 * function(cell){ var ns = cell.metadata.slideshow;
320 * function(cell){ var ns = cell.metadata.slideshow;
321 * // if the slideshow namespace does not exist return `undefined`
321 * // if the slideshow namespace does not exist return `undefined`
322 * // (will be interpreted as `false` by checkbox) otherwise
322 * // (will be interpreted as `false` by checkbox) otherwise
323 * // return the value
323 * // return the value
324 * return (ns == undefined)? undefined: ns.isSectionStart
324 * return (ns == undefined)? undefined: ns.isSectionStart
325 * }
325 * }
326 * );
326 * );
327 *
327 *
328 * CellToolbar.register_callback('newSlide', newSlide);
328 * CellToolbar.register_callback('newSlide', newSlide);
329 *
329 *
330 */
330 */
331 CellToolbar.utils.checkbox_ui_generator = function(name, setter, getter){
331 CellToolbar.utils.checkbox_ui_generator = function(name, setter, getter){
332 return function(div, cell, celltoolbar) {
332 return function(div, cell, celltoolbar) {
333 var button_container = $(div);
333 var button_container = $(div);
334
334
335 var chkb = $('<input/>').attr('type', 'checkbox');
335 var chkb = $('<input/>').attr('type', 'checkbox');
336 var lbl = $('<label/>').append($('<span/>').text(name));
336 var lbl = $('<label/>').append($('<span/>').text(name));
337 lbl.append(chkb);
337 lbl.append(chkb);
338 chkb.attr("checked", getter(cell));
338 chkb.attr("checked", getter(cell));
339
339
340 chkb.click(function(){
340 chkb.click(function(){
341 var v = getter(cell);
341 var v = getter(cell);
342 setter(cell, !v);
342 setter(cell, !v);
343 chkb.attr("checked", !v);
343 chkb.attr("checked", !v);
344 });
344 });
345 button_container.append($('<div/>').append(lbl));
345 button_container.append($('<div/>').append(lbl));
346 };
346 };
347 };
347 };
348
348
349
349
350 /**
350 /**
351 * A utility function to generate bindings between a dropdown list cell
351 * A utility function to generate bindings between a dropdown list cell
352 * @method utils.select_ui_generator
352 * @method utils.select_ui_generator
353 * @static
353 * @static
354 *
354 *
355 * @param list_list {list of sublist} List of sublist of metadata value and name in the dropdown list.
355 * @param list_list {list of sublist} List of sublist of metadata value and name in the dropdown list.
356 * subslit shoud contain 2 element each, first a string that woul be displayed in the dropdown list,
356 * subslit shoud contain 2 element each, first a string that woul be displayed in the dropdown list,
357 * and second the corresponding value to be passed to setter/return by getter. the corresponding value
357 * and second the corresponding value to be passed to setter/return by getter. the corresponding value
358 * should not be "undefined" or behavior can be unexpected.
358 * should not be "undefined" or behavior can be unexpected.
359 * @param setter {function( cell, newValue )}
359 * @param setter {function( cell, newValue )}
360 * A setter method to set the newValue
360 * A setter method to set the newValue
361 * @param getter {function( cell )}
361 * @param getter {function( cell )}
362 * A getter methods which return the current value of the metadata.
362 * A getter methods which return the current value of the metadata.
363 * @param [label=""] {String} optionnal label for the dropdown menu
363 * @param [label=""] {String} optionnal label for the dropdown menu
364 *
364 *
365 * @return callback {function( div, cell )} Callback to be passed to `register_callback`
365 * @return callback {function( div, cell )} Callback to be passed to `register_callback`
366 *
366 *
367 * @example
367 * @example
368 *
368 *
369 * var select_type = CellToolbar.utils.select_ui_generator([
369 * var select_type = CellToolbar.utils.select_ui_generator([
370 * ["<None>" , "None" ],
370 * ["<None>" , "None" ],
371 * ["Header Slide" , "header_slide" ],
371 * ["Header Slide" , "header_slide" ],
372 * ["Slide" , "slide" ],
372 * ["Slide" , "slide" ],
373 * ["Fragment" , "fragment" ],
373 * ["Fragment" , "fragment" ],
374 * ["Skip" , "skip" ],
374 * ["Skip" , "skip" ],
375 * ],
375 * ],
376 * // setter
376 * // setter
377 * function(cell, value){
377 * function(cell, value){
378 * // we check that the slideshow namespace exist and create it if needed
378 * // we check that the slideshow namespace exist and create it if needed
379 * if (cell.metadata.slideshow == undefined){cell.metadata.slideshow = {}}
379 * if (cell.metadata.slideshow == undefined){cell.metadata.slideshow = {}}
380 * // set the value
380 * // set the value
381 * cell.metadata.slideshow.slide_type = value
381 * cell.metadata.slideshow.slide_type = value
382 * },
382 * },
383 * //geter
383 * //geter
384 * function(cell){ var ns = cell.metadata.slideshow;
384 * function(cell){ var ns = cell.metadata.slideshow;
385 * // if the slideshow namespace does not exist return `undefined`
385 * // if the slideshow namespace does not exist return `undefined`
386 * // (will be interpreted as `false` by checkbox) otherwise
386 * // (will be interpreted as `false` by checkbox) otherwise
387 * // return the value
387 * // return the value
388 * return (ns == undefined)? undefined: ns.slide_type
388 * return (ns == undefined)? undefined: ns.slide_type
389 * }
389 * }
390 * CellToolbar.register_callback('slideshow.select', select_type);
390 * CellToolbar.register_callback('slideshow.select', select_type);
391 *
391 *
392 */
392 */
393 CellToolbar.utils.select_ui_generator = function(list_list, setter, getter, label) {
393 CellToolbar.utils.select_ui_generator = function(list_list, setter, getter, label) {
394 label = label || "";
394 label = label || "";
395 return function(div, cell, celltoolbar) {
395 return function(div, cell, celltoolbar) {
396 var button_container = $(div);
396 var button_container = $(div);
397 var lbl = $("<label/>").append($('<span/>').text(label));
397 var lbl = $("<label/>").append($('<span/>').text(label));
398 var select = $('<select/>').addClass('ui-widget ui-widget-content');
398 var select = $('<select/>').addClass('ui-widget ui-widget-content');
399 for(var i=0; i < list_list.length; i++){
399 for(var i=0; i < list_list.length; i++){
400 var opt = $('<option/>')
400 var opt = $('<option/>')
401 .attr('value', list_list[i][1])
401 .attr('value', list_list[i][1])
402 .text(list_list[i][0]);
402 .text(list_list[i][0]);
403 select.append(opt);
403 select.append(opt);
404 }
404 }
405 select.val(getter(cell));
405 select.val(getter(cell));
406 select.change(function(){
406 select.change(function(){
407 setter(cell, select.val());
407 setter(cell, select.val());
408 });
408 });
409 button_container.append($('<div/>').append(lbl).append(select));
409 button_container.append($('<div/>').append(lbl).append(select));
410 };
410 };
411 };
411 };
412
412
413 // Backwards compatability.
413 // Backwards compatability.
414 IPython.CellToolbar = CellToolbar;
414 IPython.CellToolbar = CellToolbar;
415
415
416 return {'CellToolbar': CellToolbar};
416 return {'CellToolbar': CellToolbar};
417 });
417 });
@@ -1,40 +1,51 b''
1 // Copyright (c) IPython Development Team.
1 // Copyright (c) IPython Development Team.
2 // Distributed under the terms of the Modified BSD License.
2 // Distributed under the terms of the Modified BSD License.
3
3
4 define([
4 define([
5 'jquery',
5 'jquery',
6 'notebook/js/celltoolbar',
6 'notebook/js/celltoolbar',
7 'base/js/dialog',
7 'base/js/dialog',
8 ], function($, celltoolbar, dialog) {
8 ], function($, celltoolbar, dialog) {
9 "use strict";
9 "use strict";
10
10
11 var CellToolbar = celltoolbar.CellToolbar;
11 var CellToolbar = celltoolbar.CellToolbar;
12
12
13 var raw_edit = function(cell){
13 var raw_edit = function (cell) {
14 dialog.edit_metadata(cell.metadata, function (md) {
14 dialog.edit_metadata({
15 cell.metadata = md;
15 md: cell.metadata,
16 callback: function (md) {
17 cell.metadata = md;
18 },
19 name: 'Cell',
20 notebook: this.notebook,
21 keyboard_manager: this.keyboard_manager
16 });
22 });
17 };
23 };
18
24
19 var add_raw_edit_button = function(div, cell) {
25 var add_raw_edit_button = function(div, cell) {
20 var button_container = $(div);
26 var button_container = $(div);
21 var button = $('<button/>')
27 var button = $('<button/>')
22 .addClass("btn btn-default btn-xs")
28 .addClass("btn btn-default btn-xs")
23 .text("Edit Metadata")
29 .text("Edit Metadata")
24 .click( function () {
30 .click( function () {
25 raw_edit(cell);
31 raw_edit(cell);
26 return false;
32 return false;
27 });
33 });
28 button_container.append(button);
34 button_container.append(button);
29 };
35 };
30
36
31 var register = function (notebook, events) {
37 var register = function (notebook, events) {
32 CellToolbar.register_callback('default.rawedit', add_raw_edit_button);
38 CellToolbar.register_callback('default.rawedit', add_raw_edit_button);
39 raw_edit = $.proxy(raw_edit, {
40 notebook: notebook,
41 keyboard_manager: notebook.keyboard_manager
42 });
43
33 var example_preset = [];
44 var example_preset = [];
34 example_preset.push('default.rawedit');
45 example_preset.push('default.rawedit');
35
46
36 CellToolbar.register_preset('Edit Metadata', example_preset, notebook, events);
47 CellToolbar.register_preset('Edit Metadata', example_preset, notebook, events);
37 console.log('Default extension for cell metadata editing loaded.');
48 console.log('Default extension for cell metadata editing loaded.');
38 };
49 };
39 return {'register': register};
50 return {'register': register};
40 });
51 });
General Comments 0
You need to be logged in to leave comments. Login now