7
7
December 6, 2017 @jcb91: use bootstrap 'hidden' class to play nicely with collapsible_headings
8
8
December 30, 2015: update to 4.1
9
9
Update december 22, 2015:
10
- Added the metadata solution_first to mark the beginning of an exercise. It is now possible to have several consecutive exercises.
11
- Update october 21-27,2015:
10
+ Added the metadata solution_first to mark the beginning of an exercise. It is now possible to have several consecutive exercises.
11
+ Update october 21-27,2015:
12
12
1- the extension now works with the multicell API, that is
13
13
- several cells can be selected either via the rubberband extension
14
14
- or via Shift-J (select next) or Shift-K (select previous) keyboard shortcuts
15
- (probably Shit-up and down will work in a near future)
15
+ (probably Shit-up and down will work in a near future)
16
16
Note: previously, the extension required the selected cells to be marked with a "selected" key in metadata. This is no more necessary with the new API.
17
17
Then clicking on the toolbar button transforms these cells into a "solution" which is hidden by default
18
- ** Do not forget to keep the Shift key pressed down while clicking on the menu button
18
+ ** Do not forget to keep the Shift key pressed down while clicking on the menu button
19
19
(otherwise selected cells will be lost)**
20
20
2- the "state" of solutions, hidden or shown, is saved and restored at reload/restart. We use the "solution" metadata to store the current state.
21
21
3- A small issue (infinite loop when a solution was defined at the bottom edge of the notebook have been corrected)
@@ -27,181 +27,123 @@ define([
27
27
'jquery' ,
28
28
'require' ,
29
29
'base/js/events' ,
30
- 'nbextensions/rubberband/main'
31
- ] , function ( IPython , $ , requirejs , events , rubberband ) {
30
+ ] , function ( IPython , $ , requirejs , events ) {
32
31
"use strict" ;
33
32
34
33
/**
35
34
* handle click event
36
35
*
37
36
* @method click_solution_lock
38
- * @param ev {Event} jquery event
37
+ * @param evt {Event} jquery event
39
38
*/
40
- function click_solution_lock ( ev ) {
41
- var cell = IPython . notebook . get_selected_cell ( ) ;
42
- var cell_index = IPython . notebook . get_selected_index ( ) ;
43
- var ncells = IPython . notebook . ncells ( ) ;
44
- var is_locked = cell . element . find ( '#lock' ) . hasClass ( 'fa-plus-square-o' ) ;
45
- if ( is_locked == true ) {
46
- cell . element . find ( '#lock' ) . removeClass ( 'fa-plus-square-o' ) ;
47
- cell . element . find ( '#lock' ) . addClass ( 'fa-minus-square-o' ) ;
48
- cell . metadata . solution = "shown" ;
49
- IPython . notebook . select_next ( ) ;
50
- cell = IPython . notebook . get_selected_cell ( ) ;
51
- while ( cell_index ++ < ncells & cell . metadata . solution != undefined & cell . metadata . solution_first != true ) {
52
- cell . element . removeClass ( 'hidden' ) ;
53
- cell . metadata . solution = "shown" ;
54
- IPython . notebook . select_next ( ) ;
55
- cell = IPython . notebook . get_selected_cell ( ) ;
56
- }
57
- } else {
58
- cell . element . find ( '#lock' ) . removeClass ( 'fa-minus-square-o' ) ;
59
- cell . element . find ( '#lock' ) . addClass ( 'fa-plus-square-o' ) ;
60
- cell . metadata . solution = "hidden"
61
- IPython . notebook . select_next ( ) ;
62
- cell = IPython . notebook . get_selected_cell ( ) ;
63
- while ( cell_index ++ < ncells & cell . metadata . solution != undefined & cell . metadata . solution_first != true ) {
64
- cell . element . addClass ( 'hidden' ) ;
65
- cell . metadata . solution = "hidden"
66
- IPython . notebook . select_next ( ) ;
67
- cell = IPython . notebook . get_selected_cell ( ) ;
68
- }
39
+ function click_solution_lock ( evt ) {
40
+ var cell = IPython . notebook . get_selected_cell ( ) ;
41
+ var is_locked = cell . metadata . solution === 'hidden' ;
42
+ cell . metadata . solution = is_locked ? 'shown' : 'hidden' ;
43
+ element_set_locked ( cell , ! is_locked ) ;
44
+ cell = IPython . notebook . get_next_cell ( cell ) ;
45
+ while ( cell !== null && cell . metadata . solution !== undefined && ! cell . metadata . solution_first ) {
46
+ cell . element . toggleClass ( 'hidden' , ! is_locked ) ;
47
+ cell . metadata . solution = is_locked ? 'shown' : 'hidden' ;
48
+ cell = IPython . notebook . get_next_cell ( cell ) ;
69
49
}
70
- }
50
+ }
71
51
72
52
/**
73
- * Hide solutions
53
+ * Create or Remove an exercise in selected cells
74
54
*
75
- * @method hide_solutions
55
+ * @method create_remove_exercise
76
56
*
77
57
*/
78
- function hide_solutions ( ) {
79
- // first check if lock symbol is already present in selected cell, if yes, remove it
80
- var lcells = IPython . notebook . get_selected_cells ( ) ; //list of selected cells
81
- if ( typeof IPython . notebook . get_selected_indices == "undefined" ) { //noteboox 4.1.x
82
- var icells = IPython . notebook . get_selected_cells_indices ( ) ; // corresponding indices 4.1.x version
83
- }
84
- else { //notebook 4.0.x
85
- var icells = IPython . notebook . get_selected_indices ( ) ; // corresponding indices
86
- }
58
+ function create_remove_exercise ( ) {
59
+ var lcells = IPython . notebook . get_selected_cells ( ) ;
87
60
// It is possible that no cell is selected
88
- if ( lcells . length == 0 ) { alert ( "Exercise extension: \nPlease select some cells..." ) ; return } ;
61
+ if ( lcells . length < 1 ) {
62
+ alert ( "Exercise extension: \nPlease select some cells..." ) ;
63
+ return ;
64
+ }
89
65
90
- var cell = lcells [ 0 ] ;
91
- var has_lock = cell . element . find ( '#lock' ) . is ( 'div' ) ;
92
- if ( has_lock === true ) {
93
- cell . element . find ( '#lock' ) . remove ( ) ;
66
+ var cell = lcells [ 0 ] ;
67
+ if ( cell . metadata . solution_first ) {
68
+ remove_element ( cell ) ;
94
69
delete cell . metadata . solution_first ;
95
- while ( cell . metadata . solution != undefined & cell . metadata . solution_first != true ) {
70
+ while ( cell !== null && cell . metadata . solution !== undefined && ! cell . metadata . solution_first ) {
96
71
delete cell . metadata . solution ;
97
72
cell . element . removeClass ( 'hidden' ) ;
98
- IPython . notebook . select_next ( ) ;
99
- cell = IPython . notebook . get_selected_cell ( )
100
- }
101
- } else {
102
- /*(jfb) --- I do not understand this part... --- It looks for the first selected cell, but we already have the list of selected cells lcells
103
- // find first cell with solution
104
- var start_cell_i; // = undefined
105
- var cells = IPython.notebook.get_cells();
106
- for(var i in cells){
107
- var cell = cells[i];
108
- if (typeof cell.metadata.selected != undefined && cell.metadata.selected === true) {
109
- start_cell_i = i;
110
- console.log("selected start cell:", i);
111
- break
73
+ cell = IPython . notebook . get_next_cell ( cell ) ;
112
74
}
113
75
}
114
- IPython.notebook.select(start_cell_i);
115
- */
116
- // if (cell.metadata.selected == true) { // (jfb) no metadata "selected"
117
- var el = $ ( '<div id="lock" class="fa fa-plus-square-o">' ) ;
118
- cell . element . prepend ( el ) ;
119
- cell . metadata . solution_first = true ;
120
- cell . metadata . solution = "hidden" ;
121
- cell . element . css ( { "background-color" : "#ffffff" } ) ;
122
- el . click ( click_solution_lock ) ;
123
- for ( var k = 1 ; k < lcells . length ; k ++ ) {
124
- cell = lcells [ k ] ;
125
- //console.log("new cell:", icells[k]);
126
- cell . element . css ( { "background-color" : "#ffffff" } ) ;
127
- cell . element . addClass ( 'hidden' ) ;
128
- cell . metadata . solution = "hidden" ;
129
- }
76
+ else {
77
+ cell . metadata . solution_first = true ;
78
+ cell . metadata . solution = 'hidden' ;
79
+ add_element ( cell ) ;
80
+ for ( var k = 1 ; k < lcells . length ; k ++ ) {
81
+ cell = lcells [ k ] ;
82
+ cell . element . addClass ( 'hidden' ) ;
83
+ cell . metadata . solution = 'hidden' ;
130
84
}
131
- IPython . notebook . select ( icells [ 0 ] ) ; //select first cell in the list
132
85
}
133
-
134
- function load_ipython_extension ( ) {
135
- IPython . toolbar . add_buttons_group ( [
136
- IPython . keyboard_manager . actions . register ( {
137
- help : 'Exercise: Create/Remove solutions' ,
138
- icon : 'fa-mortar-board' ,
139
- handler : function ( ) {
140
- //console.log(IPython.notebook.get_selected_cells())
141
- hide_solutions ( ) ;
142
- }
143
- } , 'hide_solutions' , 'exercise' )
144
- ] ) ;
86
+ }
145
87
146
- /**
147
- * load css file and append to document
148
- *
149
- * @method load_css
150
- * @param name {String} filenaame of CSS file
151
- *
88
+ /**
89
+ * Add a lock control to the given cell
152
90
*/
153
- var load_css = function ( name ) {
154
- var link = document . createElement ( "link" ) ;
155
- link . type = "text/css" ;
156
- link . rel = "stylesheet" ;
157
- link . href = requirejs . toUrl ( name ) ;
158
- document . getElementsByTagName ( "head" ) [ 0 ] . appendChild ( link ) ;
159
- } ;
91
+ function add_element ( cell ) {
92
+ var ctrl = cell . element . find ( '.exercise' ) ;
93
+ if ( ctrl . length > 0 ) return ctrl ;
94
+ var locked = cell . metadata . solution === 'hidden' ;
95
+ ctrl = $ ( '<div class="exercise fa">' )
96
+ . prependTo ( cell . element )
97
+ . on ( 'click' , click_solution_lock ) ;
98
+ element_set_locked ( cell , locked ) ;
99
+ return ctrl ;
100
+ }
160
101
161
- load_css ( './main.css' ) ;
162
- var exercise_wrapper = $ ( '<div id="dragmask" class="highlight-drag"></div>' ) ;
163
- $ ( "#header" ) . append ( exercise_wrapper ) ;
102
+ function remove_element ( cell ) {
103
+ cell . element . find ( '.exercise' ) . remove ( ) ;
104
+ }
164
105
165
- // ***************** Keyboard shortcuts ******************************
166
- var add_cmd_shortcuts = {
167
- 'Alt-S' : {
168
- help : 'Define Solution (Exercise)' ,
169
- help_index : 'ht' ,
170
- handler : function ( event ) {
171
- hide_solutions ( ) ;
172
- return false ;
173
- }
174
- }
106
+ function element_set_locked ( cell , locked ) {
107
+ return cell . element . find ( '.exercise' )
108
+ . toggleClass ( 'fa-plus-square-o' , locked )
109
+ . toggleClass ( 'fa-minus-square-o' , ! locked ) ;
175
110
}
176
- IPython . keyboard_manager . command_shortcuts . add_shortcuts ( add_cmd_shortcuts ) ;
177
-
178
- /**
179
- * Display existing solutions at startup
180
- *
181
- */
182
- var cells = IPython . notebook . get_cells ( ) ;
183
- var found_solution = false ;
184
- for ( var i in cells ) {
185
- var cell = cells [ i ] ;
186
- if ( found_solution == true && typeof cell . metadata . solution != "undefined" && cell . metadata . solution_first != true ) {
187
- cell . element . toggleClass ( 'hidden' , cell . metadata . solution === 'hidden' ) ;
188
- } else {
189
- found_solution = false
190
- }
191
111
192
- if ( found_solution == false && typeof cell . metadata . solution != "undefined" ) {
193
- if ( cell . metadata . solution == "hidden" ) var el = $ ( '<div id="lock" class="fa fa-plus-square-o">' ) ;
194
- else var el = $ ( '<div id="lock" class="fa fa-minus-square-o">' ) ;
195
- cell . element . prepend ( el ) ;
196
- el . click ( click_solution_lock ) ;
197
- found_solution = true ;
198
- }
112
+ function refresh_exercises ( ) {
113
+ var in_exercise = false ;
114
+ IPython . notebook . get_cells ( ) . forEach ( function ( cell ) {
115
+ if ( in_exercise && cell . metadata . solution !== undefined && ! cell . metadata . solution_first ) {
116
+ cell . element . toggleClass ( 'hidden' , cell . metadata . solution === 'hidden' ) ;
117
+ } else {
118
+ in_exercise = false ;
119
+ }
120
+ if ( ! in_exercise && cell . metadata . solution !== undefined ) {
121
+ in_exercise = true ;
122
+ add_element ( cell ) ;
123
+ }
124
+ } ) ;
199
125
}
200
126
201
- console . log ( "Executing rubberband load_ipython" )
202
- rubberband . load_ipython_extension ( ) ;
203
- }
127
+ function load_ipython_extension ( ) {
128
+ // add css
129
+ $ ( '<link rel="stylesheet" type="text/css">' )
130
+ . attr ( 'href' , requirejs . toUrl ( './main.css' ) )
131
+ . appendTo ( 'head' ) ;
132
+
133
+ // Hide/display existing solutions at startup
134
+ events . on ( 'notebook_loaded.Notebook' , refresh_exercises ) ;
135
+ if ( IPython . notebook . _fully_loaded ) refresh_exercises ( ) ;
204
136
137
+ var action_name = IPython . keyboard_manager . actions . register ( {
138
+ help : 'Exercise: Create/Remove exercise' ,
139
+ help_index : 'ht' ,
140
+ icon : 'fa-mortar-board' ,
141
+ handler : create_remove_exercise
142
+ } , 'create_remove_exercise' , 'exercise' ) ;
143
+
144
+ IPython . toolbar . add_buttons_group ( [ action_name ] ) ;
145
+ IPython . keyboard_manager . command_shortcuts . add_shortcuts ( { 'Alt-S' : action_name } } ) ;
146
+ }
205
147
206
148
return {
207
149
load_ipython_extension : load_ipython_extension ,
0 commit comments