forked from plotly/plotly.py
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgraphWidget.js
166 lines (136 loc) · 6.71 KB
/
graphWidget.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
window.genUID = function() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
return v.toString(16);
});
};
var IPYTHON_VERSION = '3';
require(["widgets/js/widget", "widgets/js/manager"], function (widget, manager) {
if (!('DOMWidgetView' in widget)) {
// we're in IPython2, things moved a bit from 2 --> 3.
// construct the expected IPython3 widget API
IPYTHON_VERSION = '2';
manager = {WidgetManager: widget};
widget = {DOMWidgetView: IPython.DOMWidgetView};
}
var GraphView = widget.DOMWidgetView.extend({
render: function(){
var that = this;
var graphId = window.genUID();
var loadingId = 'loading-'+graphId;
var _graph_url = that.model.get('_graph_url');
// variable plotlyDomain in the case of enterprise
var url_parts = _graph_url.split('/');
var plotlyDomain = url_parts[0] + '//' + url_parts[2];
if(!('plotlyDomains' in window)){
window.plotlyDomains = {};
}
window.plotlyDomains[graphId] = plotlyDomain;
// Place IFrame in output cell div `$el`
that.$el.css('width', '100%');
that.$graph = $(['<iframe id="'+graphId+'"',
'src="'+_graph_url+'.embed"',
'seamless',
'style="border: none;"',
'width="100%"',
'height="600">',
'</iframe>'].join(' '));
that.$graph.appendTo(that.$el);
that.$loading = $('<div id="'+loadingId+'">Initializing...</div>')
.appendTo(that.$el);
// for some reason the 'width' is being changed in IPython 3.0.0
// for the containing `div` element. There's a flicker here, but
// I was unable to fix it otherwise.
setTimeout(function () {
if (IPYTHON_VERSION === '3') {
$('#' + graphId)[0].parentElement.style.width = '100%';
}
}, 500);
// initialize communication with the iframe
if(!('pingers' in window)){
window.pingers = {};
}
window.pingers[graphId] = setInterval(function() {
that.graphContentWindow = $('#'+graphId)[0].contentWindow;
that.graphContentWindow.postMessage({task: 'ping'}, plotlyDomain);
}, 200);
// Assign a message listener to the 'message' events
// from iframe's postMessage protocol.
// Filter the messages by iframe src so that the right message
// gets passed to the right widget
if(!('messageListeners' in window)){
window.messageListeners = {};
}
window.messageListeners[graphId] = function(e) {
if(_graph_url.indexOf(e.origin)>-1) {
var frame = document.getElementById(graphId);
if(frame === null){
// frame doesn't exist in the dom anymore, clean up it's old event listener
window.removeEventListener('message', window.messageListeners[graphId]);
clearInterval(window.pingers[graphId]);
} else if(frame.contentWindow === e.source) {
// TODO: Stop event propagation, so each frame doesn't listen and filter
var frameContentWindow = $('#'+graphId)[0].contentWindow;
var message = e.data;
if('pong' in message && message.pong) {
$('#loading-'+graphId).hide();
clearInterval(window.pingers[graphId]);
that.send({event: 'pong', graphId: graphId});
} else if (message.type==='hover' ||
message.type==='zoom' ||
message.type==='click' ||
message.type==='unhover') {
// click and hover events contain all of the data in the traces,
// which can be a very large object and may take a ton of time
// to pass to the python backend. Strip out the data, and require
// the user to call get_figure if they need trace information
if(message.type !== 'zoom') {
for(var i in message.points) {
delete message.points[i].data;
delete message.points[i].fullData;
}
}
that.send({event: message.type, message: message, graphId: graphId});
} else if (message.task === 'getAttributes') {
that.send({event: 'getAttributes', response: message.response});
}
}
}
};
window.removeEventListener('message', window.messageListeners[graphId]);
window.addEventListener('message', window.messageListeners[graphId]);
},
update: function() {
// Listen for messages from the graph widget in python
var jmessage = this.model.get('_message');
var message = JSON.parse(jmessage);
// check for duplicate messages
if(!('messageIds' in window)){
window.messageIds = {};
}
if(!(message.uid in window.messageIds)){
// message hasn't been received yet, do stuff
window.messageIds[message.uid] = true;
if (message.fadeTo) {
this.fadeTo(message);
} else {
var plot = $('#' + message.graphId)[0].contentWindow;
plot.postMessage(message, window.plotlyDomains[message.graphId]);
}
}
return GraphView.__super__.update.apply(this);
},
/**
* Wrapper for jquery's `fadeTo` function.
*
* @param message Contains the id we need to find the element.
*/
fadeTo: function (message) {
var plot = $('#' + message.graphId);
plot.fadeTo(message.duration, message.opacity);
}
});
// Register the GraphView with the widget manager.
manager.WidgetManager.register_widget_view('GraphView', GraphView);
});
//@ sourceURL=graphWidget.js