##// END OF EJS Templates
fix typos
Paul Ivanov -
Show More
@@ -1,465 +1,465 b''
1 """Base Widget class. Allows user to create widgets in the backend that render
1 """Base Widget class. Allows user to create widgets in the backend that render
2 in the IPython notebook frontend.
2 in the IPython notebook frontend.
3 """
3 """
4 #-----------------------------------------------------------------------------
4 #-----------------------------------------------------------------------------
5 # Copyright (c) 2013, the IPython Development Team.
5 # Copyright (c) 2013, the IPython Development Team.
6 #
6 #
7 # Distributed under the terms of the Modified BSD License.
7 # Distributed under the terms of the Modified BSD License.
8 #
8 #
9 # The full license is in the file COPYING.txt, distributed with this software.
9 # The full license is in the file COPYING.txt, distributed with this software.
10 #-----------------------------------------------------------------------------
10 #-----------------------------------------------------------------------------
11
11
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13 # Imports
13 # Imports
14 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15 from contextlib import contextmanager
15 from contextlib import contextmanager
16 import inspect
16 import inspect
17 import types
17 import types
18
18
19 from IPython.kernel.comm import Comm
19 from IPython.kernel.comm import Comm
20 from IPython.config import LoggingConfigurable
20 from IPython.config import LoggingConfigurable
21 from IPython.utils.traitlets import Unicode, Dict, Instance, Bool
21 from IPython.utils.traitlets import Unicode, Dict, Instance, Bool
22 from IPython.utils.py3compat import string_types
22 from IPython.utils.py3compat import string_types
23
23
24 #-----------------------------------------------------------------------------
24 #-----------------------------------------------------------------------------
25 # Classes
25 # Classes
26 #-----------------------------------------------------------------------------
26 #-----------------------------------------------------------------------------
27 @contextmanager
27 @contextmanager
28 def PropertyLock(instance, key, value):
28 def PropertyLock(instance, key, value):
29 instance._property_lock = (key, value)
29 instance._property_lock = (key, value)
30 try:
30 try:
31 yield
31 yield
32 finally:
32 finally:
33 del instance._property_lock
33 del instance._property_lock
34
34
35 def should_send_property(instance, key, value):
35 def should_send_property(instance, key, value):
36 return not hasattr(instance, '_property_lock') or \
36 return not hasattr(instance, '_property_lock') or \
37 key != instance._property_lock[0] or \
37 key != instance._property_lock[0] or \
38 value != instance._property_lock[1]
38 value != instance._property_lock[1]
39
39
40
40
41 class Widget(LoggingConfigurable):
41 class Widget(LoggingConfigurable):
42
42
43 # Shared declarations (Class level)
43 # Shared declarations (Class level)
44 widget_construction_callback = None
44 widget_construction_callback = None
45 widgets = []
45 widgets = []
46
46
47 keys = ['view_name'] # TODO: Sync = True
47 keys = ['view_name'] # TODO: Sync = True
48
48
49 def on_widget_constructed(callback):
49 def on_widget_constructed(callback):
50 """Class method, registers a callback to be called when a widget is
50 """Class method, registers a callback to be called when a widget is
51 constructed. The callback must have the following signature:
51 constructed. The callback must have the following signature:
52 callback(widget)"""
52 callback(widget)"""
53 Widget.widget_construction_callback = callback
53 Widget.widget_construction_callback = callback
54
54
55 def _call_widget_constructed(widget):
55 def _call_widget_constructed(widget):
56 """Class method, called when a widget is constructed."""
56 """Class method, called when a widget is constructed."""
57 if Widget.widget_construction_callback is not None and callable(Widget.widget_construction_callback):
57 if Widget.widget_construction_callback is not None and callable(Widget.widget_construction_callback):
58 Widget.widget_construction_callback(widget)
58 Widget.widget_construction_callback(widget)
59
59
60
60
61
61
62 # Public declarations (Instance level)
62 # Public declarations (Instance level)
63 target_name = Unicode('widget', help="""Name of the backbone model
63 target_name = Unicode('widget', help="""Name of the backbone model
64 registered in the frontend to create and sync this widget with.""")
64 registered in the frontend to create and sync this widget with.""")
65 # model_name
65 # model_name
66 view_name = Unicode(help="""Default view registered in the frontend
66 view_name = Unicode(help="""Default view registered in the frontend
67 to use to represent the widget.""")
67 to use to represent the widget.""")
68
68
69 # Private/protected declarations
69 # Private/protected declarations
70 _comm = Instance('IPython.kernel.comm.Comm')
70 _comm = Instance('IPython.kernel.comm.Comm')
71
71
72 def __init__(self, **kwargs):
72 def __init__(self, **kwargs):
73 """Public constructor
73 """Public constructor
74 """
74 """
75 self.closed = False
75 self.closed = False
76 self._display_callbacks = []
76 self._display_callbacks = []
77 self._msg_callbacks = []
77 self._msg_callbacks = []
78 super(Widget, self).__init__(**kwargs)
78 super(Widget, self).__init__(**kwargs)
79
79
80 self.on_trait_change(self._handle_property_changed, self.keys)
80 self.on_trait_change(self._handle_property_changed, self.keys)
81 Widget.widgets.append(self)
81 Widget.widgets.append(self)
82 Widget._call_widget_constructed(self)
82 Widget._call_widget_constructed(self)
83
83
84 def __del__(self):
84 def __del__(self):
85 """Object disposal"""
85 """Object disposal"""
86 self.close()
86 self.close()
87
87
88 def close(self):
88 def close(self):
89 """Close method. Closes the widget which closes the underlying comm.
89 """Close method. Closes the widget which closes the underlying comm.
90 When the comm is closed, all of the widget views are automatically
90 When the comm is closed, all of the widget views are automatically
91 removed from the frontend."""
91 removed from the frontend."""
92 if not self.closed:
92 if not self.closed:
93 self.closed = True
93 self.closed = True
94 self._close_communication()
94 self._close_communication()
95 Widget.widgets.remove(self)
95 Widget.widgets.remove(self)
96
96
97 @property
97 @property
98 def comm(self):
98 def comm(self):
99 if self._comm is None:
99 if self._comm is None:
100 self._open_communication()
100 self._open_communication()
101 return self._comm
101 return self._comm
102
102
103 @property
103 @property
104 def model_id(self):
104 def model_id(self):
105 return self.comm.comm_id
105 return self.comm.comm_id
106
106
107 # Event handlers
107 # Event handlers
108 def _handle_msg(self, msg):
108 def _handle_msg(self, msg):
109 """Called when a msg is recieved from the frontend"""
109 """Called when a msg is received from the frontend"""
110 data = msg['content']['data']
110 data = msg['content']['data']
111 method = data['method']
111 method = data['method']
112
112
113 # TODO: Log unrecog.
113 # TODO: Log unrecog.
114
114
115 # Handle backbone sync methods CREATE, PATCH, and UPDATE all in one.
115 # Handle backbone sync methods CREATE, PATCH, and UPDATE all in one.
116 if method == 'backbone' and 'sync_data' in data:
116 if method == 'backbone' and 'sync_data' in data:
117 sync_data = data['sync_data']
117 sync_data = data['sync_data']
118 self._handle_recieve_state(sync_data) # handles all methods
118 self._handle_receive_state(sync_data) # handles all methods
119
119
120 # Handle a custom msg from the front-end
120 # Handle a custom msg from the front-end
121 elif method == 'custom':
121 elif method == 'custom':
122 if 'custom_content' in data:
122 if 'custom_content' in data:
123 self._handle_custom_msg(data['custom_content'])
123 self._handle_custom_msg(data['custom_content'])
124
124
125
125
126 def _handle_recieve_state(self, sync_data):
126 def _handle_receive_state(self, sync_data):
127 """Called when a state is recieved from the frontend."""
127 """Called when a state is received from the frontend."""
128 for name in self.keys:
128 for name in self.keys:
129 if name in sync_data:
129 if name in sync_data:
130 value = self._unpack_widgets(sync_data[name])
130 value = self._unpack_widgets(sync_data[name])
131 with PropertyLock(self, name, value):
131 with PropertyLock(self, name, value):
132 setattr(self, name, value)
132 setattr(self, name, value)
133
133
134
134
135 def _handle_custom_msg(self, content):
135 def _handle_custom_msg(self, content):
136 """Called when a custom msg is recieved."""
136 """Called when a custom msg is received."""
137 for handler in self._msg_callbacks:
137 for handler in self._msg_callbacks:
138 if callable(handler):
138 if callable(handler):
139 argspec = inspect.getargspec(handler)
139 argspec = inspect.getargspec(handler)
140 nargs = len(argspec[0])
140 nargs = len(argspec[0])
141
141
142 # Bound methods have an additional 'self' argument
142 # Bound methods have an additional 'self' argument
143 if isinstance(handler, types.MethodType):
143 if isinstance(handler, types.MethodType):
144 nargs -= 1
144 nargs -= 1
145
145
146 # Call the callback
146 # Call the callback
147 if nargs == 1:
147 if nargs == 1:
148 handler(content)
148 handler(content)
149 elif nargs == 2:
149 elif nargs == 2:
150 handler(self, content)
150 handler(self, content)
151 else:
151 else:
152 raise TypeError('Widget msg callback must ' \
152 raise TypeError('Widget msg callback must ' \
153 'accept 1 or 2 arguments, not %d.' % nargs)
153 'accept 1 or 2 arguments, not %d.' % nargs)
154
154
155
155
156 def _handle_property_changed(self, name, old, new):
156 def _handle_property_changed(self, name, old, new):
157 """Called when a property has been changed."""
157 """Called when a property has been changed."""
158 # Make sure this isn't information that the front-end just sent us.
158 # Make sure this isn't information that the front-end just sent us.
159 if should_send_property(self, name, new):
159 if should_send_property(self, name, new):
160 # Send new state to frontend
160 # Send new state to frontend
161 self.send_state(key=name)
161 self.send_state(key=name)
162
162
163 def _handle_displayed(self, **kwargs):
163 def _handle_displayed(self, **kwargs):
164 """Called when a view has been displayed for this widget instance"""
164 """Called when a view has been displayed for this widget instance"""
165 for handler in self._display_callbacks:
165 for handler in self._display_callbacks:
166 if callable(handler):
166 if callable(handler):
167 argspec = inspect.getargspec(handler)
167 argspec = inspect.getargspec(handler)
168 nargs = len(argspec[0])
168 nargs = len(argspec[0])
169
169
170 # Bound methods have an additional 'self' argument
170 # Bound methods have an additional 'self' argument
171 if isinstance(handler, types.MethodType):
171 if isinstance(handler, types.MethodType):
172 nargs -= 1
172 nargs -= 1
173
173
174 # Call the callback
174 # Call the callback
175 if nargs == 0:
175 if nargs == 0:
176 handler()
176 handler()
177 elif nargs == 1:
177 elif nargs == 1:
178 handler(self)
178 handler(self)
179 else:
179 else:
180 handler(self, **kwargs)
180 handler(self, **kwargs)
181
181
182 # Public methods
182 # Public methods
183 def send_state(self, key=None):
183 def send_state(self, key=None):
184 """Sends the widget state, or a piece of it, to the frontend.
184 """Sends the widget state, or a piece of it, to the frontend.
185
185
186 Parameters
186 Parameters
187 ----------
187 ----------
188 key : unicode (optional)
188 key : unicode (optional)
189 A single property's name to sync with the frontend.
189 A single property's name to sync with the frontend.
190 """
190 """
191 self._send({"method": "update",
191 self._send({"method": "update",
192 "state": self.get_state()})
192 "state": self.get_state()})
193
193
194 def get_state(self, key=None):
194 def get_state(self, key=None):
195 """Gets the widget state, or a piece of it.
195 """Gets the widget state, or a piece of it.
196
196
197 Parameters
197 Parameters
198 ----------
198 ----------
199 key : unicode (optional)
199 key : unicode (optional)
200 A single property's name to get.
200 A single property's name to get.
201 """
201 """
202 state = {}
202 state = {}
203
203
204 # If a key is provided, just send the state of that key.
204 # If a key is provided, just send the state of that key.
205 if key is None:
205 if key is None:
206 keys = self.keys[:]
206 keys = self.keys[:]
207 else:
207 else:
208 keys = [key]
208 keys = [key]
209 for k in keys:
209 for k in keys:
210 state[k] = self._pack_widgets(getattr(self, k))
210 state[k] = self._pack_widgets(getattr(self, k))
211 return state
211 return state
212
212
213
213
214 def _pack_widgets(self, values):
214 def _pack_widgets(self, values):
215 """This function recursively converts all widget instances to model id
215 """This function recursively converts all widget instances to model id
216 strings.
216 strings.
217
217
218 Children widgets will be stored and transmitted to the front-end by
218 Children widgets will be stored and transmitted to the front-end by
219 their model ids."""
219 their model ids."""
220 if isinstance(values, dict):
220 if isinstance(values, dict):
221 new_dict = {}
221 new_dict = {}
222 for key in values.keys():
222 for key in values.keys():
223 new_dict[key] = self._pack_widgets(values[key])
223 new_dict[key] = self._pack_widgets(values[key])
224 return new_dict
224 return new_dict
225 elif isinstance(values, list):
225 elif isinstance(values, list):
226 new_list = []
226 new_list = []
227 for value in values:
227 for value in values:
228 new_list.append(self._pack_widgets(value))
228 new_list.append(self._pack_widgets(value))
229 return new_list
229 return new_list
230 elif isinstance(values, Widget):
230 elif isinstance(values, Widget):
231 return values.model_id
231 return values.model_id
232 else:
232 else:
233 return values
233 return values
234
234
235
235
236 def _unpack_widgets(self, values):
236 def _unpack_widgets(self, values):
237 """This function recursively converts all model id strings to widget
237 """This function recursively converts all model id strings to widget
238 instances.
238 instances.
239
239
240 Children widgets will be stored and transmitted to the front-end by
240 Children widgets will be stored and transmitted to the front-end by
241 their model ids."""
241 their model ids."""
242 if isinstance(values, dict):
242 if isinstance(values, dict):
243 new_dict = {}
243 new_dict = {}
244 for key in values.keys():
244 for key in values.keys():
245 new_dict[key] = self._unpack_widgets(values[key])
245 new_dict[key] = self._unpack_widgets(values[key])
246 return new_dict
246 return new_dict
247 elif isinstance(values, list):
247 elif isinstance(values, list):
248 new_list = []
248 new_list = []
249 for value in values:
249 for value in values:
250 new_list.append(self._unpack_widgets(value))
250 new_list.append(self._unpack_widgets(value))
251 return new_list
251 return new_list
252 elif isinstance(values, string_types):
252 elif isinstance(values, string_types):
253 for widget in Widget.widgets:
253 for widget in Widget.widgets:
254 if widget.model_id == values:
254 if widget.model_id == values:
255 return widget
255 return widget
256 return values
256 return values
257 else:
257 else:
258 return values
258 return values
259
259
260
260
261 def send(self, content):
261 def send(self, content):
262 """Sends a custom msg to the widget model in the front-end.
262 """Sends a custom msg to the widget model in the front-end.
263
263
264 Parameters
264 Parameters
265 ----------
265 ----------
266 content : dict
266 content : dict
267 Content of the message to send.
267 Content of the message to send.
268 """
268 """
269 self._send({"method": "custom",
269 self._send({"method": "custom",
270 "custom_content": content})
270 "custom_content": content})
271
271
272
272
273 def on_msg(self, callback, remove=False): # TODO: Use lambdas and inspect here
273 def on_msg(self, callback, remove=False): # TODO: Use lambdas and inspect here
274 """Register or unregister a callback for when a custom msg is recieved
274 """Register or unregister a callback for when a custom msg is received
275 from the front-end.
275 from the front-end.
276
276
277 Parameters
277 Parameters
278 ----------
278 ----------
279 callback: method handler
279 callback: method handler
280 Can have a signature of:
280 Can have a signature of:
281 - callback(content)
281 - callback(content)
282 - callback(sender, content)
282 - callback(sender, content)
283 remove: bool
283 remove: bool
284 True if the callback should be unregistered."""
284 True if the callback should be unregistered."""
285 if remove and callback in self._msg_callbacks:
285 if remove and callback in self._msg_callbacks:
286 self._msg_callbacks.remove(callback)
286 self._msg_callbacks.remove(callback)
287 elif not remove and not callback in self._msg_callbacks:
287 elif not remove and not callback in self._msg_callbacks:
288 self._msg_callbacks.append(callback)
288 self._msg_callbacks.append(callback)
289
289
290
290
291 def on_displayed(self, callback, remove=False):
291 def on_displayed(self, callback, remove=False):
292 """Register or unregister a callback to be called when the widget has
292 """Register or unregister a callback to be called when the widget has
293 been displayed.
293 been displayed.
294
294
295 Parameters
295 Parameters
296 ----------
296 ----------
297 callback: method handler
297 callback: method handler
298 Can have a signature of:
298 Can have a signature of:
299 - callback()
299 - callback()
300 - callback(sender)
300 - callback(sender)
301 - callback(sender, **kwargs)
301 - callback(sender, **kwargs)
302 kwargs from display call passed through without modification.
302 kwargs from display call passed through without modification.
303 remove: bool
303 remove: bool
304 True if the callback should be unregistered."""
304 True if the callback should be unregistered."""
305 if remove and callback in self._display_callbacks:
305 if remove and callback in self._display_callbacks:
306 self._display_callbacks.remove(callback)
306 self._display_callbacks.remove(callback)
307 elif not remove and not callback in self._display_callbacks:
307 elif not remove and not callback in self._display_callbacks:
308 self._display_callbacks.append(callback)
308 self._display_callbacks.append(callback)
309
309
310
310
311 # Support methods
311 # Support methods
312 def _repr_widget_(self, **kwargs):
312 def _repr_widget_(self, **kwargs):
313 """Function that is called when `IPython.display.display` is called on
313 """Function that is called when `IPython.display.display` is called on
314 the widget."""
314 the widget."""
315
315
316 # Show view. By sending a display message, the comm is opened and the
316 # Show view. By sending a display message, the comm is opened and the
317 # initial state is sent.
317 # initial state is sent.
318 self._send({"method": "display"})
318 self._send({"method": "display"})
319 self._handle_displayed(**kwargs)
319 self._handle_displayed(**kwargs)
320
320
321
321
322 def _open_communication(self):
322 def _open_communication(self):
323 """Opens a communication with the front-end."""
323 """Opens a communication with the front-end."""
324 # Create a comm.
324 # Create a comm.
325 self._comm = Comm(target_name=self.target_name)
325 self._comm = Comm(target_name=self.target_name)
326 self._comm.on_msg(self._handle_msg)
326 self._comm.on_msg(self._handle_msg)
327 self._comm.on_close(self._close_communication)
327 self._comm.on_close(self._close_communication)
328
328
329 # first update
329 # first update
330 self.send_state()
330 self.send_state()
331
331
332
332
333 def _close_communication(self):
333 def _close_communication(self):
334 """Closes a communication with the front-end."""
334 """Closes a communication with the front-end."""
335 if self._comm is not None:
335 if self._comm is not None:
336 try:
336 try:
337 self._comm.close() # TODO: Check
337 self._comm.close() # TODO: Check
338 finally:
338 finally:
339 self._comm = None
339 self._comm = None
340
340
341
341
342 def _send(self, msg):
342 def _send(self, msg):
343 """Sends a message to the model in the front-end"""
343 """Sends a message to the model in the front-end"""
344 self.comm.send(msg)
344 self.comm.send(msg)
345
345
346
346
347 class DOMWidget(Widget):
347 class DOMWidget(Widget):
348 visible = Bool(True, help="Whether or not the widget is visible.")
348 visible = Bool(True, help="Whether or not the widget is visible.")
349
349
350 # Private/protected declarations
350 # Private/protected declarations
351 _css = Dict() # Internal CSS property dict
351 _css = Dict() # Internal CSS property dict
352
352
353 keys = ['visible', '_css'] + Widget.keys
353 keys = ['visible', '_css'] + Widget.keys
354
354
355 def get_css(self, key, selector=""):
355 def get_css(self, key, selector=""):
356 """Get a CSS property of the widget.
356 """Get a CSS property of the widget.
357
357
358 Note: This function does not actually request the CSS from the
358 Note: This function does not actually request the CSS from the
359 front-end; Only properties that have been set with set_css can be read.
359 front-end; Only properties that have been set with set_css can be read.
360
360
361 Parameters
361 Parameters
362 ----------
362 ----------
363 key: unicode
363 key: unicode
364 CSS key
364 CSS key
365 selector: unicode (optional)
365 selector: unicode (optional)
366 JQuery selector used when the CSS key/value was set.
366 JQuery selector used when the CSS key/value was set.
367 """
367 """
368 if selector in self._css and key in self._css[selector]:
368 if selector in self._css and key in self._css[selector]:
369 return self._css[selector][key]
369 return self._css[selector][key]
370 else:
370 else:
371 return None
371 return None
372
372
373
373
374 def set_css(self, *args, **kwargs):
374 def set_css(self, *args, **kwargs):
375 """Set one or more CSS properties of the widget.
375 """Set one or more CSS properties of the widget.
376
376
377 This function has two signatures:
377 This function has two signatures:
378 - set_css(css_dict, selector='')
378 - set_css(css_dict, selector='')
379 - set_css(key, value, selector='')
379 - set_css(key, value, selector='')
380
380
381 Parameters
381 Parameters
382 ----------
382 ----------
383 css_dict : dict
383 css_dict : dict
384 CSS key/value pairs to apply
384 CSS key/value pairs to apply
385 key: unicode
385 key: unicode
386 CSS key
386 CSS key
387 value
387 value
388 CSS value
388 CSS value
389 selector: unicode (optional)
389 selector: unicode (optional)
390 JQuery selector to use to apply the CSS key/value. If no selector
390 JQuery selector to use to apply the CSS key/value. If no selector
391 is provided, an empty selector is used. An empty selector makes the
391 is provided, an empty selector is used. An empty selector makes the
392 front-end try to apply the css to a default element. The default
392 front-end try to apply the css to a default element. The default
393 element is an attribute unique to each view, which is a DOM element
393 element is an attribute unique to each view, which is a DOM element
394 of the view that should be styled with common CSS (see
394 of the view that should be styled with common CSS (see
395 `$el_to_style` in the Javascript code).
395 `$el_to_style` in the Javascript code).
396 """
396 """
397 selector = kwargs.get('selector', '')
397 selector = kwargs.get('selector', '')
398
398
399 # Signature 1: set_css(css_dict, selector='')
399 # Signature 1: set_css(css_dict, selector='')
400 if len(args) == 1:
400 if len(args) == 1:
401 if isinstance(args[0], dict):
401 if isinstance(args[0], dict):
402 for (key, value) in args[0].items():
402 for (key, value) in args[0].items():
403 if not (key in self._css[selector] and value == self._css[selector][key]):
403 if not (key in self._css[selector] and value == self._css[selector][key]):
404 self._css[selector][key] = value
404 self._css[selector][key] = value
405 self.send_state('_css')
405 self.send_state('_css')
406 else:
406 else:
407 raise Exception('css_dict must be a dict.')
407 raise Exception('css_dict must be a dict.')
408
408
409 # Signature 2: set_css(key, value, selector='')
409 # Signature 2: set_css(key, value, selector='')
410 elif len(args) == 2 or len(args) == 3:
410 elif len(args) == 2 or len(args) == 3:
411
411
412 # Selector can be a positional arg if it's the 3rd value
412 # Selector can be a positional arg if it's the 3rd value
413 if len(args) == 3:
413 if len(args) == 3:
414 selector = args[2]
414 selector = args[2]
415 if selector not in self._css:
415 if selector not in self._css:
416 self._css[selector] = {}
416 self._css[selector] = {}
417
417
418 # Only update the property if it has changed.
418 # Only update the property if it has changed.
419 key = args[0]
419 key = args[0]
420 value = args[1]
420 value = args[1]
421 if not (key in self._css[selector] and value == self._css[selector][key]):
421 if not (key in self._css[selector] and value == self._css[selector][key]):
422 self._css[selector][key] = value
422 self._css[selector][key] = value
423 self.send_state('_css') # Send new state to client.
423 self.send_state('_css') # Send new state to client.
424 else:
424 else:
425 raise Exception('set_css only accepts 1-3 arguments')
425 raise Exception('set_css only accepts 1-3 arguments')
426
426
427
427
428 def add_class(self, class_names, selector=""):
428 def add_class(self, class_names, selector=""):
429 """Add class[es] to a DOM element
429 """Add class[es] to a DOM element
430
430
431 Parameters
431 Parameters
432 ----------
432 ----------
433 class_names: unicode or list
433 class_names: unicode or list
434 Class name(s) to add to the DOM element(s).
434 Class name(s) to add to the DOM element(s).
435 selector: unicode (optional)
435 selector: unicode (optional)
436 JQuery selector to select the DOM element(s) that the class(es) will
436 JQuery selector to select the DOM element(s) that the class(es) will
437 be added to.
437 be added to.
438 """
438 """
439 class_list = class_names
439 class_list = class_names
440 if isinstance(class_list, list):
440 if isinstance(class_list, list):
441 class_list = ' '.join(class_list)
441 class_list = ' '.join(class_list)
442
442
443 self.send({"msg_type": "add_class",
443 self.send({"msg_type": "add_class",
444 "class_list": class_list,
444 "class_list": class_list,
445 "selector": selector})
445 "selector": selector})
446
446
447
447
448 def remove_class(self, class_names, selector=""):
448 def remove_class(self, class_names, selector=""):
449 """Remove class[es] from a DOM element
449 """Remove class[es] from a DOM element
450
450
451 Parameters
451 Parameters
452 ----------
452 ----------
453 class_names: unicode or list
453 class_names: unicode or list
454 Class name(s) to remove from the DOM element(s).
454 Class name(s) to remove from the DOM element(s).
455 selector: unicode (optional)
455 selector: unicode (optional)
456 JQuery selector to select the DOM element(s) that the class(es) will
456 JQuery selector to select the DOM element(s) that the class(es) will
457 be removed from.
457 be removed from.
458 """
458 """
459 class_list = class_names
459 class_list = class_names
460 if isinstance(class_list, list):
460 if isinstance(class_list, list):
461 class_list = ' '.join(class_list)
461 class_list = ' '.join(class_list)
462
462
463 self.send({"msg_type": "remove_class",
463 self.send({"msg_type": "remove_class",
464 "class_list": class_list,
464 "class_list": class_list,
465 "selector": selector})
465 "selector": selector})
General Comments 0
You need to be logged in to leave comments. Login now