@@ -27,6 +27,34 @@ export type RenderBundle = {
27
27
div : string
28
28
}
29
29
30
+ export interface DocumentChanged {
31
+ event : "jsevent"
32
+ kind : string
33
+ }
34
+
35
+ export interface ModelChanged extends DocumentChanged {
36
+ event : "jsevent"
37
+ kind : "ModelChanged"
38
+ id : string
39
+ new : unknown
40
+ attr : string
41
+ old : unknown
42
+ hint : unknown
43
+ }
44
+
45
+ export interface MessageSent extends DocumentChanged {
46
+ event : "jsevent"
47
+ kind : "MessageSent"
48
+ msg_data : {
49
+ event_name : string ,
50
+ event_values : {
51
+ model : { id : string } ,
52
+ [ other : string ] : any
53
+ }
54
+ }
55
+ msg_type : string
56
+ }
57
+
30
58
export class BokehModel extends DOMWidgetModel {
31
59
defaults ( ) {
32
60
return {
@@ -40,6 +68,7 @@ export class BokehModel extends DOMWidgetModel {
40
68
_view_module : name ,
41
69
_view_module_version : version_range ,
42
70
71
+ combine_events : false ,
43
72
render_bundle : { } ,
44
73
}
45
74
}
@@ -53,17 +82,46 @@ export class BokehView extends DOMWidgetView {
53
82
private _document : Document | null
54
83
private _receiver : Receiver
55
84
private _blocked : boolean
85
+ private _msgs : any [ ]
86
+ private _idle : boolean
87
+ private _combine : boolean
56
88
57
89
constructor ( options ?: any ) {
58
90
super ( options )
59
91
this . _document = null
60
92
this . _blocked = false
93
+ this . _idle = true
94
+ this . _combine = true
95
+ this . _msgs = [ ]
61
96
const { Receiver} = bk_require ( "protocol/receiver" )
62
97
this . _receiver = new Receiver ( )
63
98
this . model . on ( "change:render_bundle" , ( ) => this . render ( ) )
99
+ if ( ( window as any ) . Jupyter != null && ( window as any ) . Jupyter . notebook != null ) {
100
+ // Handle classic Jupyter notebook
101
+ const events = ( window as any ) . require ( 'base/js/events' )
102
+ events . on ( 'kernel_idle.Kernel' , ( ) => this . _process_msg ( ) )
103
+ } else if ( ( this . model . widget_manager as any ) . context != null ) {
104
+ // Handle JupyterLab
105
+ ( this . model . widget_manager as any ) . context . sessionContext . statusChanged . connect ( ( _ : any , status : any ) => {
106
+ if ( status === "idle" )
107
+ this . _process_msg ( )
108
+ } )
109
+ } else {
110
+ if ( this . model . get ( "combine_events" ) )
111
+ console . warn ( "BokehView cannot combine events because Kernel idle status cannot be determined." )
112
+ this . _combine = false
113
+ }
64
114
this . listenTo ( this . model , "msg:custom" , ( content , buffers ) => this . _consume_patch ( content , buffers ) )
65
115
}
66
116
117
+ protected _process_msg ( ) : void {
118
+ if ( this . _msgs . length == 0 ) {
119
+ this . _idle = true
120
+ return
121
+ }
122
+ this . send ( this . _msgs . shift ( ) )
123
+ }
124
+
67
125
render ( ) : void {
68
126
const bundle = JSON . parse ( this . model . get ( "render_bundle" ) )
69
127
const { docs_json, render_items, div} = bundle as RenderBundle
@@ -82,10 +140,77 @@ export class BokehView extends DOMWidgetView {
82
140
this . _document . on_change ( ( event : any ) => this . _change_event ( event ) )
83
141
}
84
142
143
+ _combine_events ( new_msg : ( ModelChanged | MessageSent ) ) : ( ModelChanged | MessageSent ) [ ] {
144
+ const new_msgs = [ ]
145
+ for ( const msg of this . _msgs ) {
146
+ if ( new_msg . kind != msg . kind )
147
+ new_msgs . push ( msg )
148
+ else if ( msg . kind == "ModelChanged" && new_msg . kind == "ModelChanged" ) {
149
+ if ( msg . id != new_msg . id || msg . attr != new_msg . attr )
150
+ new_msgs . push ( msg )
151
+ } else if ( msg . kind == "MessageSent" && new_msg . kind == "MessageSent" ) {
152
+ if (
153
+ msg . msg_data . event_values . model . id != new_msg . msg_data . event_values . model . id ||
154
+ msg . msg_data . event_name != new_msg . msg_data . event_name
155
+ )
156
+ new_msgs . push ( msg )
157
+ }
158
+ }
159
+ new_msgs . push ( new_msg )
160
+ return new_msgs
161
+ }
162
+
163
+ _send ( msg : ( ModelChanged | MessageSent ) ) : void {
164
+ if ( ! this . _idle && this . _combine && this . model . get ( "combine_events" ) )
165
+ // Queue event and drop previous events on same model attribute
166
+ this . _msgs = this . _combine_events ( msg )
167
+ else {
168
+ this . _idle = false
169
+ this . send ( msg )
170
+ }
171
+ }
172
+
85
173
protected _change_event ( event : DocumentChangedEvent ) : void {
86
- const { ModelChangedEvent} = bk_require ( "document/events" )
87
- if ( ! this . _blocked && event instanceof ModelChangedEvent )
88
- this . send ( { event : "jsevent" , id : event . model . id , new : event . new_ , attr : event . attr , old : event . old } )
174
+ if ( this . _blocked ) { return }
175
+ const { ModelChangedEvent, MessageSentEvent} = bk_require ( "document/events" )
176
+ if ( event instanceof ModelChangedEvent ) {
177
+ const js_msg : ModelChanged = {
178
+ event : "jsevent" ,
179
+ kind : "ModelChanged" ,
180
+ id : event . model . id ,
181
+ attr : event . attr ,
182
+ new : event . new_ ,
183
+ old : event . old ,
184
+ hint : null ,
185
+ }
186
+ if ( event . hint != null ) {
187
+ if ( event . hint . patches != null ) {
188
+ js_msg [ "hint" ] = {
189
+ column_source : event . hint . column_source ,
190
+ patches : event . hint . patches ,
191
+ }
192
+ } else if ( event . hint . data != null ) {
193
+ js_msg [ "hint" ] = {
194
+ column_source : event . hint . column_source ,
195
+ data : event . hint . data ,
196
+ rollover : event . hint . rollover ,
197
+ }
198
+ }
199
+ }
200
+ this . _send ( js_msg )
201
+ } else if ( ( event instanceof MessageSentEvent ) && ( event . msg_type == "bokeh_event" ) ) {
202
+ const msg_data = { ...event . msg_data }
203
+ const event_values = { ...msg_data . event_values }
204
+ event_values [ "model" ] = { id : event_values . model . id }
205
+ msg_data [ "event_values" ] = event_values
206
+ const js_msg : MessageSent = {
207
+ event : "jsevent" ,
208
+ kind : "MessageSent" ,
209
+ msg_type : event . msg_type ,
210
+ msg_data : msg_data ,
211
+ }
212
+ this . _send ( js_msg )
213
+ }
89
214
}
90
215
91
216
protected _consume_patch ( content : { msg : "patch" , payload ?: Fragment } , buffers : DataView [ ] ) : void {
0 commit comments