1 IriSP.StackGraphWidget = function(Popcorn, config, Serializer) { |
|
2 IriSP.Widget.call(this, Popcorn, config, Serializer); |
|
3 } |
|
4 |
|
5 IriSP.StackGraphWidget.prototype = new IriSP.Widget(); |
|
6 |
|
7 IriSP.StackGraphWidget.prototype.draw = function() { |
|
8 var _ = IriSP._; |
|
9 this.height = this._config.height || 50; |
|
10 this.width = this.selector.width(); |
|
11 this.slices = this._config.slices || ~~(this.width/(this.streamgraph ? 20 : 5)); |
|
12 _(this.tags).each(function(_a) { |
|
13 _a.regexp = new RegExp(_(_a.keywords).map(function(_k) { |
|
14 return _k.replace(/([\W])/gm,'\\$1'); |
|
15 }).join("|"),"im") |
|
16 }); |
|
17 this.paper = new Raphael(this.selector[0], this.width, this.height); |
|
18 this.groups = []; |
|
19 this.duration = this.getDuration(); |
|
20 |
|
21 var _annotationType = this._serializer.getTweets(), |
|
22 _sliceDuration = ~~ ( this.duration / this.slices), |
|
23 _annotations = this._serializer._data.annotations, |
|
24 _groupedAnnotations = _(_.range(this.slices)).map(function(_i) { |
|
25 return _(_annotations).filter(function(_a){ |
|
26 return (_a.begin <= (1 + _i) * _sliceDuration) && (_a.end >= _i * _sliceDuration) |
|
27 }); |
|
28 }), |
|
29 _max = IriSP._(_groupedAnnotations).max(function(_g) { |
|
30 return _g.length |
|
31 }).length, |
|
32 _scale = this.height / _max, |
|
33 _width = this.width / this.slices, |
|
34 _showTitle = !this._config.excludeTitle, |
|
35 _showDescription = !this._config.excludeDescription; |
|
36 |
|
37 |
|
38 var _paths = _(this.tags).map(function() { |
|
39 return []; |
|
40 }); |
|
41 _paths.push([]); |
|
42 |
|
43 for (var i = 0; i < this.slices; i++) { |
|
44 var _group = _groupedAnnotations[i]; |
|
45 if (_group) { |
|
46 var _vol = _(this.tags).map(function() { |
|
47 return 0; |
|
48 }); |
|
49 for (var j = 0; j < _group.length; j++){ |
|
50 var _txt = (_showTitle ? _group[j].content.title : '') + ' ' + (_showDescription ? _group[j].content.description : '') |
|
51 var _tags = _(this.tags).map(function(_tag) { |
|
52 return (_txt.search(_tag.regexp) == -1 ? 0 : 1) |
|
53 }), |
|
54 _nbtags = _(_tags).reduce(function(_a,_b) { |
|
55 return _a + _b; |
|
56 }, 0); |
|
57 if (_nbtags) { |
|
58 IriSP._(_tags).each(function(_v, _k) { |
|
59 _vol[_k] += (_v / _nbtags); |
|
60 }); |
|
61 } |
|
62 } |
|
63 var _nbtags = _(_vol).reduce(function(_a,_b) { |
|
64 return _a + _b; |
|
65 }, 0), |
|
66 _nbneutre = _group.length - _nbtags, |
|
67 _h = _nbneutre * _scale, |
|
68 _base = this.height - _h; |
|
69 if (!this.streamgraph) { |
|
70 this.paper.rect(i * _width, _base, _width - 1, _h ).attr({ |
|
71 "stroke" : "none", |
|
72 "fill" : this.defaultcolor |
|
73 }); |
|
74 } |
|
75 _paths[0].push(_base); |
|
76 for (var j = 0; j < this.tags.length; j++) { |
|
77 _h = _vol[j] * _scale; |
|
78 _base = _base - _h; |
|
79 if (!this.streamgraph) { |
|
80 this.paper.rect(i * _width, _base, _width - 1, _h ).attr({ |
|
81 "stroke" : "none", |
|
82 "fill" : this.tags[j].color |
|
83 }); |
|
84 } |
|
85 _paths[j+1].push(_base); |
|
86 } |
|
87 this.groups.push(_(_vol).map(function(_v) { |
|
88 return _v / _group.length; |
|
89 })) |
|
90 } else { |
|
91 for (var j = 0; j < _paths.length; j++) { |
|
92 _paths[j].push(this.height); |
|
93 } |
|
94 this.groups.push(_(this.tags).map(function() { |
|
95 return 0; |
|
96 })); |
|
97 } |
|
98 } |
|
99 |
|
100 if (this.streamgraph) { |
|
101 for (var j = _paths.length - 1; j >= 0; j--) { |
|
102 var _d = _(_paths[j]).reduce(function(_memo, _v, _k) { |
|
103 return _memo + ( _k |
|
104 ? 'C' + (_k * _width) + ' ' + _paths[j][_k - 1] + ' ' + (_k * _width) + ' ' + _v + ' ' + ((_k + .5) * _width) + ' ' + _v |
|
105 : 'M0 ' + _v + 'L' + (.5*_width) + ' ' + _v ) |
|
106 },'') + 'L' + this.width + ' ' + _paths[j][_paths[j].length - 1] + 'L' + this.width + ' ' + this.height + 'L0 ' + this.height; |
|
107 this.paper.path(_d).attr({ |
|
108 "stroke" : "none", |
|
109 "fill" : (j ? this.tags[j-1].color : this.defaultcolor) |
|
110 }); |
|
111 } |
|
112 } |
|
113 this.rectangleFocus = this.paper.rect(0,0,_width,this.height) |
|
114 .attr({ |
|
115 "stroke" : "none", |
|
116 "fill" : "#ff00ff", |
|
117 "opacity" : 0 |
|
118 }) |
|
119 this.rectangleProgress = this.paper.rect(0,0,0,this.height) |
|
120 .attr({ |
|
121 "stroke" : "none", |
|
122 "fill" : "#808080", |
|
123 "opacity" : .3 |
|
124 }); |
|
125 this.ligneProgress = this.paper.path("M0 0L0 "+this.height).attr({"stroke":"#ff00ff", "line-width" : 2}) |
|
126 |
|
127 this._Popcorn.listen("timeupdate", IriSP.wrap(this, this.timeUpdateHandler)); |
|
128 var _this = this; |
|
129 this.selector |
|
130 .click(IriSP.wrap(this, this.clickHandler)) |
|
131 .mousemove(function(_e) { |
|
132 _this.updateTooltip(_e); |
|
133 // Trace |
|
134 var relX = _e.pageX - _this.selector.offset().left; |
|
135 var _duration = _this.getDuration(); |
|
136 var _time = parseInt((relX / _this.width) * _duration); |
|
137 _this._Popcorn.trigger("IriSP.TraceWidget.MouseEvents", { |
|
138 "widget" : "StackGraphWidget", |
|
139 "type": "mousemove", |
|
140 "x": _e.pageX, |
|
141 "y": _e.pageY, |
|
142 "time": _time |
|
143 }); |
|
144 |
|
145 }) |
|
146 .mouseout(function() { |
|
147 _this.TooltipWidget.hide(); |
|
148 _this.rectangleFocus.attr({ |
|
149 "opacity" : 0 |
|
150 }) |
|
151 }) |
|
152 } |
|
153 |
|
154 IriSP.StackGraphWidget.prototype.timeUpdateHandler = function() { |
|
155 var _currentTime = this._Popcorn.currentTime(), |
|
156 _x = (1000 * _currentTime / this.duration) * this.width; |
|
157 this.rectangleProgress.attr({ |
|
158 "width" : _x |
|
159 }); |
|
160 this.ligneProgress.attr({ |
|
161 "path" : "M" + _x + " 0L" + _x + " " + this.height |
|
162 }) |
|
163 } |
|
164 |
|
165 IriSP.StackGraphWidget.prototype.clickHandler = function(event) { |
|
166 /* Ctrl-C Ctrl-V'ed from another widget |
|
167 */ |
|
168 |
|
169 var relX = event.pageX - this.selector.offset().left; |
|
170 var newTime = ((relX / this.width) * this.duration/1000).toFixed(2); |
|
171 this._Popcorn.trigger("IriSP.StackGraphWidget.clicked", newTime); |
|
172 this._Popcorn.currentTime(newTime); |
|
173 }; |
|
174 |
|
175 IriSP.StackGraphWidget.prototype.updateTooltip = function(event) { |
|
176 var _segment = Math.max(0,Math.min(this.groups.length - 1, Math.floor(this.slices * (event.pageX - this.selector.offset().left)/this.width))), |
|
177 _valeurs = this.groups[_segment], |
|
178 _width = this.width / this.slices, |
|
179 _html = '<ul style="list-style: none; margin: 0; padding: 0;">' + IriSP._(this.tags).map(function(_tag, _i) { |
|
180 return '<li style="clear: both;"><span style="float: left; width: 10px; height: 10px; margin: 2px; background: ' |
|
181 + _tag.color |
|
182 + ';"></span>' |
|
183 + ~~(100 * _valeurs[_i]) |
|
184 + '% de ' |
|
185 + _tag.description |
|
186 + '</li>'; |
|
187 }).join('') + '</ul>'; |
|
188 this.TooltipWidget._shown = false; // Vraiment, on ne peut pas ouvrir le widget s'il n'est pas encore ouvert ? |
|
189 this.TooltipWidget.show('','',(_segment + .5)* this.width / this.slices, 0); |
|
190 this.TooltipWidget.selector.find(".tip").html(_html); |
|
191 this.rectangleFocus.attr({ |
|
192 "x" : _segment * _width, |
|
193 "opacity" : .4 |
|
194 }) |
|
195 } |
|
196 |
|