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)
@@ -34,169 +34,116 @@ define([
34
34
* handle click event
35
35
*
36
36
* @method click_solution_lock
37
- * @param ev {Event} jquery event
37
+ * @param evt {Event} jquery event
38
38
*/
39
- function click_solution_lock ( ev ) {
40
- var cell = IPython . notebook . get_selected_cell ( ) ;
41
- var cell_index = IPython . notebook . get_selected_index ( ) ;
42
- var ncells = IPython . notebook . ncells ( ) ;
43
- var is_locked = cell . element . find ( '#lock' ) . hasClass ( 'fa-plus-square-o' ) ;
44
- if ( is_locked == true ) {
45
- cell . element . find ( '#lock' ) . removeClass ( 'fa-plus-square-o' ) ;
46
- cell . element . find ( '#lock' ) . addClass ( 'fa-minus-square-o' ) ;
47
- cell . metadata . solution = "shown" ;
48
- IPython . notebook . select_next ( ) ;
49
- cell = IPython . notebook . get_selected_cell ( ) ;
50
- while ( cell_index ++ < ncells & cell . metadata . solution != undefined & cell . metadata . solution_first != true ) {
51
- cell . element . removeClass ( 'hidden' ) ;
52
- cell . metadata . solution = "shown" ;
53
- IPython . notebook . select_next ( ) ;
54
- cell = IPython . notebook . get_selected_cell ( ) ;
55
- }
56
- } else {
57
- cell . element . find ( '#lock' ) . removeClass ( 'fa-minus-square-o' ) ;
58
- cell . element . find ( '#lock' ) . addClass ( 'fa-plus-square-o' ) ;
59
- cell . metadata . solution = "hidden"
60
- IPython . notebook . select_next ( ) ;
61
- cell = IPython . notebook . get_selected_cell ( ) ;
62
- while ( cell_index ++ < ncells & cell . metadata . solution != undefined & cell . metadata . solution_first != true ) {
63
- cell . element . addClass ( 'hidden' ) ;
64
- cell . metadata . solution = "hidden"
65
- IPython . notebook . select_next ( ) ;
66
- cell = IPython . notebook . get_selected_cell ( ) ;
67
- }
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 ) ;
68
49
}
69
- }
50
+ }
70
51
71
52
/**
72
- * Hide solutions
53
+ * Create or Remove an exercise in selected cells
73
54
*
74
- * @method hide_solutions
55
+ * @method create_remove_exercise
75
56
*
76
57
*/
77
- function hide_solutions ( ) {
78
- // first check if lock symbol is already present in selected cell, if yes, remove it
79
- var lcells = IPython . notebook . get_selected_cells ( ) ; //list of selected cells
80
- if ( typeof IPython . notebook . get_selected_indices == "undefined" ) { //noteboox 4.1.x
81
- var icells = IPython . notebook . get_selected_cells_indices ( ) ; // corresponding indices 4.1.x version
82
- }
83
- else { //notebook 4.0.x
84
- var icells = IPython . notebook . get_selected_indices ( ) ; // corresponding indices
85
- }
58
+ function create_remove_exercise ( ) {
59
+ var lcells = IPython . notebook . get_selected_cells ( ) ;
86
60
// It is possible that no cell is selected
87
- 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
+ }
88
65
89
- var cell = lcells [ 0 ] ;
90
- var has_lock = cell . element . find ( '#lock' ) . is ( 'div' ) ;
91
- if ( has_lock === true ) {
92
- cell . element . find ( '#lock' ) . remove ( ) ;
66
+ var cell = lcells [ 0 ] ;
67
+ if ( cell . metadata . solution_first ) {
68
+ remove_element ( cell ) ;
93
69
delete cell . metadata . solution_first ;
94
- while ( cell . metadata . solution != undefined & cell . metadata . solution_first != true ) {
70
+ while ( cell !== null && cell . metadata . solution !== undefined && ! cell . metadata . solution_first ) {
95
71
delete cell . metadata . solution ;
96
72
cell . element . removeClass ( 'hidden' ) ;
97
- IPython . notebook . select_next ( ) ;
98
- cell = IPython . notebook . get_selected_cell ( )
99
- }
100
- } else {
101
- /*(jfb) --- I do not understand this part... --- It looks for the first selected cell, but we already have the list of selected cells lcells
102
- // find first cell with solution
103
- var start_cell_i; // = undefined
104
- var cells = IPython.notebook.get_cells();
105
- for(var i in cells){
106
- var cell = cells[i];
107
- if (typeof cell.metadata.selected != undefined && cell.metadata.selected === true) {
108
- start_cell_i = i;
109
- console.log("selected start cell:", i);
110
- break
73
+ cell = IPython . notebook . get_next_cell ( cell ) ;
111
74
}
112
75
}
113
- IPython.notebook.select(start_cell_i);
114
- */
115
- // if (cell.metadata.selected == true) { // (jfb) no metadata "selected"
116
- var el = $ ( '<div id="lock" class="fa fa-plus-square-o">' ) ;
117
- cell . element . prepend ( el ) ;
118
- cell . metadata . solution_first = true ;
119
- cell . metadata . solution = "hidden" ;
120
- cell . element . css ( { "background-color" : "#ffffff" } ) ;
121
- el . click ( click_solution_lock ) ;
122
- for ( var k = 1 ; k < lcells . length ; k ++ ) {
123
- cell = lcells [ k ] ;
124
- //console.log("new cell:", icells[k]);
125
- cell . element . css ( { "background-color" : "#ffffff" } ) ;
126
- cell . element . addClass ( 'hidden' ) ;
127
- cell . metadata . solution = "hidden" ;
128
- }
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' ;
129
84
}
130
- IPython . notebook . select ( icells [ 0 ] ) ; //select first cell in the list
131
85
}
132
-
133
- function load_ipython_extension ( ) {
134
- IPython . toolbar . add_buttons_group ( [
135
- IPython . keyboard_manager . actions . register ( {
136
- help : 'Exercise: Create/Remove solutions' ,
137
- icon : 'fa-mortar-board' ,
138
- handler : function ( ) {
139
- //console.log(IPython.notebook.get_selected_cells())
140
- hide_solutions ( ) ;
141
- }
142
- } , 'hide_solutions' , 'exercise' )
143
- ] ) ;
86
+ }
144
87
145
- /**
146
- * load css file and append to document
147
- *
148
- * @method load_css
149
- * @param name {String} filenaame of CSS file
150
- *
88
+ /**
89
+ * Add a lock control to the given cell
151
90
*/
152
- var load_css = function ( name ) {
153
- var link = document . createElement ( "link" ) ;
154
- link . type = "text/css" ;
155
- link . rel = "stylesheet" ;
156
- link . href = requirejs . toUrl ( name ) ;
157
- document . getElementsByTagName ( "head" ) [ 0 ] . appendChild ( link ) ;
158
- } ;
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
+ }
159
101
160
- load_css ( './main.css' ) ;
102
+ function remove_element ( cell ) {
103
+ cell . element . find ( '.exercise' ) . remove ( ) ;
104
+ }
161
105
162
- // ***************** Keyboard shortcuts ******************************
163
- var add_cmd_shortcuts = {
164
- 'Alt-S' : {
165
- help : 'Define Solution (Exercise)' ,
166
- help_index : 'ht' ,
167
- handler : function ( event ) {
168
- hide_solutions ( ) ;
169
- return false ;
170
- }
171
- }
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 ) ;
172
110
}
173
- IPython . keyboard_manager . command_shortcuts . add_shortcuts ( add_cmd_shortcuts ) ;
174
-
175
- /**
176
- * Display existing solutions at startup
177
- *
178
- */
179
- var cells = IPython . notebook . get_cells ( ) ;
180
- var found_solution = false ;
181
- for ( var i in cells ) {
182
- var cell = cells [ i ] ;
183
- if ( found_solution == true && typeof cell . metadata . solution != "undefined" && cell . metadata . solution_first != true ) {
184
- cell . element . toggleClass ( 'hidden' , cell . metadata . solution === 'hidden' ) ;
185
- } else {
186
- found_solution = false
187
- }
188
111
189
- if ( found_solution == false && typeof cell . metadata . solution != "undefined" ) {
190
- if ( cell . metadata . solution == "hidden" ) var el = $ ( '<div id="lock" class="fa fa-plus-square-o">' ) ;
191
- else var el = $ ( '<div id="lock" class="fa fa-minus-square-o">' ) ;
192
- cell . element . prepend ( el ) ;
193
- el . click ( click_solution_lock ) ;
194
- found_solution = true ;
195
- }
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
+ } ) ;
196
125
}
197
126
198
- }
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 ( ) ;
199
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
+ }
200
147
201
148
return {
202
149
load_ipython_extension : load_ipython_extension ,
0 commit comments