|
1 /* |
|
2 * |
|
3 * Copyright 2010 Institut de recherche et d'innovation |
|
4 * contributor(s) : Samuel Huron |
|
5 * |
|
6 * contact@iri.centrepompidou.fr |
|
7 * http://www.iri.centrepompidou.fr |
|
8 * |
|
9 * This software is a computer program whose purpose is to show and add annotations on a video . |
|
10 * This software is governed by the CeCILL-C license under French law and |
|
11 * abiding by the rules of distribution of free software. You can use, |
|
12 * modify and/ or redistribute the software under the terms of the CeCILL-C |
|
13 * license as circulated by CEA, CNRS and INRIA at the following URL |
|
14 * "http://www.cecill.info". |
|
15 * |
|
16 * The fact that you are presently reading this means that you have had |
|
17 * knowledge of the CeCILL-C license and that you accept its terms. |
|
18 */ |
|
19 // CHART TIMELINE / VERSION PROTOTYPE :: |
|
20 |
|
21 IriSP.PolemicViewer = function(Popcorn, config, Serializer) { |
|
22 IriSP.Widget.call(this, Popcorn, config, Serializer); |
|
23 |
|
24 this.userPol = new Array(); |
|
25 this.userNoPol = new Array(); |
|
26 this.userst = new Array(); |
|
27 this.numberOfTweet = 0; |
|
28 this.Users; |
|
29 this.TweetPolemic; |
|
30 this.yMax = this.height; |
|
31 this.PaperSlider; |
|
32 this.heightOfChart; |
|
33 this.tweets = new Array(); |
|
34 this.svgElements = {}; |
|
35 |
|
36 // Make and define the Raphael area |
|
37 this.paper = Raphael(document.getElementById(this._id), config.width, config.height); |
|
38 |
|
39 this.oldSearchMatches = []; |
|
40 |
|
41 // event handlers |
|
42 this._Popcorn.listen("IriSP.search", IriSP.wrap(this, function(searchString) { this.searchHandler(searchString); })); |
|
43 this._Popcorn.listen("IriSP.search.closed", IriSP.wrap(this, this.searchFieldClosedHandler)); |
|
44 this._Popcorn.listen("IriSP.search.cleared", IriSP.wrap(this, this.searchFieldClearedHandler)); |
|
45 |
|
46 }; |
|
47 |
|
48 IriSP.PolemicViewer.prototype = new IriSP.Widget(); |
|
49 |
|
50 IriSP.PolemicViewer.prototype.draw = function() { |
|
51 |
|
52 // variable |
|
53 // yMax |
|
54 |
|
55 var self = this; |
|
56 var yCoef = 2; // coef for height of 1 tweet |
|
57 var frameSize = 5; // frame size |
|
58 var margin = 1; // marge between frame |
|
59 var lineSize = this.width; // timeline pixel width |
|
60 var nbrframes = lineSize/frameSize; // frame numbers |
|
61 var numberOfTweet = 0; // number of tweet overide later |
|
62 var duration = +this._serializer.currentMedia().meta["dc:duration"]; // timescale width |
|
63 var frameLength = lineSize / frameSize; // frame timescale |
|
64 var timeline; |
|
65 var colors = new Array("","#1D973D","#C5A62D","#CE0A15","#036AAE","#585858"); |
|
66 |
|
67 // array |
|
68 //var tweets = new Array(); |
|
69 var element = new Array(); |
|
70 var cluster = new Array(); |
|
71 var frames = new Array(frameLength); |
|
72 var slices = new Array(); |
|
73 |
|
74 |
|
75 // Classes ======================================================================= |
|
76 var Frames = function(){ |
|
77 |
|
78 var Myclusters; |
|
79 var x; |
|
80 var y; |
|
81 var width; |
|
82 var height; |
|
83 }; |
|
84 Frames = function(json){ |
|
85 // make my clusters |
|
86 // ou Frame vide |
|
87 }; |
|
88 Frames.prototype.draw = function(){ |
|
89 }; |
|
90 Frames.prototype.zoom = function(){ |
|
91 }; |
|
92 Frames.prototype.inside = function(){ |
|
93 }; |
|
94 var Clusters = function(){ |
|
95 var Object; |
|
96 var yDist; |
|
97 var x; |
|
98 var y; |
|
99 var width; |
|
100 var height; |
|
101 }; |
|
102 Clusters = function(json){ |
|
103 // make my object |
|
104 }; |
|
105 var Tweet = function(){ |
|
106 }; |
|
107 // Classes ======================================================================= |
|
108 |
|
109 // Refactoring (parametere) ************************************************************ |
|
110 // color translastion |
|
111 var qTweet_0 =0; |
|
112 var qTweet_Q =0; |
|
113 var qTweet_REF=0; |
|
114 var qTweet_OK =0; |
|
115 var qTweet_KO =0; |
|
116 function colorTranslation(value){ |
|
117 if(value.indexOf("??") !== -1){ |
|
118 qTweet_Q+=1; |
|
119 return 2; |
|
120 }else if(value.indexOf("==") !== -1){ |
|
121 qTweet_REF+=1; |
|
122 return 4; |
|
123 }else if(value.indexOf("++") !== -1){ |
|
124 qTweet_OK+=1; |
|
125 return 1; |
|
126 }else if(value.indexOf("--") !== -1){ |
|
127 qTweet_KO+=1; |
|
128 return 3; |
|
129 }else { |
|
130 qTweet_0+=1; |
|
131 return 5; |
|
132 } |
|
133 } |
|
134 |
|
135 |
|
136 this._serializer.sync(function(data) { loaded_callback.call(self, data) }); |
|
137 |
|
138 function loaded_callback (json) { |
|
139 |
|
140 // get current view (the first ???) |
|
141 view = json.views[0]; |
|
142 |
|
143 // the tweets are by definition of the second annotation type FIXME ? |
|
144 tweet_annot_type = null; |
|
145 if(typeof(view.annotation_types) !== "undefined" && view.annotation_types.length > 1) { |
|
146 tweet_annot_type = view.annotation_types[1]; |
|
147 } |
|
148 |
|
149 for(var i = 0; i < json.annotations.length; i++) { |
|
150 var item = json.annotations[i]; |
|
151 var MyTime = Math.floor(item.begin/duration*lineSize); |
|
152 var Myframe = Math.floor(MyTime/lineSize*frameLength); |
|
153 /* |
|
154 if (typeof(item.meta) !== "undefined" |
|
155 && typeof(item.meta["id-ref"]) !== "undefined" |
|
156 && item.meta["id-ref"] === tweet_annot_type) { |
|
157 |
|
158 var MyTJson = JSON.parse(item.meta['dc:source']['content']); |
|
159 |
|
160 if (item.content['polemics'] != undefined |
|
161 && item.content['polemics'][0] != null) { |
|
162 |
|
163 // a tweet can have many polemics at the same time. |
|
164 for(var j=0; j<item.content['polemics'].length; j++){ |
|
165 |
|
166 this.tweets[numberOfTweet] = { |
|
167 id:i, |
|
168 qualification:colorTranslation(item.content['polemics'][j]), |
|
169 yIndicator:MyTime, |
|
170 yframe:Myframe, |
|
171 title:item.content['title'], |
|
172 timeframe:item.begin, |
|
173 userId: MyTJson.id, |
|
174 userScreenName: MyTJson.screen_name, |
|
175 tsource:MyTJson, |
|
176 cinecast_id: item.id |
|
177 }; |
|
178 numberOfTweet+=1; |
|
179 |
|
180 } |
|
181 } |
|
182 else { |
|
183 */ |
|
184 this.tweets[numberOfTweet] = { |
|
185 id:i, |
|
186 qualification:colorTranslation(item.content['description']), |
|
187 description:item.content['description'], |
|
188 yIndicator:MyTime, |
|
189 yframe:Myframe, |
|
190 title:item.content['title'], |
|
191 timeframe:item.begin, |
|
192 userId: "nobody", |
|
193 userScreenName: "nobody", |
|
194 tsource: "iri ldt platform", |
|
195 cinecast_id: item.id |
|
196 }; |
|
197 numberOfTweet+=1; |
|
198 |
|
199 /* } |
|
200 |
|
201 } */ |
|
202 }; |
|
203 |
|
204 DrawTweets.call (this); // FIXME: ugly. |
|
205 |
|
206 }; |
|
207 |
|
208 // tweet Drawing (in raphael) |
|
209 function DrawTweets (){ |
|
210 // GROUPES TWEET ============================================ |
|
211 // Count nbr of cluster and tweet in a frame an save int in "frames" |
|
212 numberOfTweet = this.tweets.length; |
|
213 for(var i=0; i<nbrframes; i++) { |
|
214 for(var j=0; j<numberOfTweet; j++) { |
|
215 |
|
216 if (i==this.tweets[j].yframe){ |
|
217 |
|
218 var k = this.tweets[j].qualification; |
|
219 |
|
220 // make array for frame cluster |
|
221 if(frames[i]==undefined){ |
|
222 frames[i] = {id:i, |
|
223 qualifVol:new Array(), |
|
224 mytweetsID:new Array() |
|
225 }; |
|
226 } |
|
227 // add my tweet to frame |
|
228 frames[i].mytweetsID.push(this.tweets[j]); |
|
229 |
|
230 // count opinion by frame |
|
231 if( frames[i].qualifVol[k] == undefined){ |
|
232 frames[i].qualifVol[k] = 1; |
|
233 }else{ |
|
234 frames[i].qualifVol[k] += 1; |
|
235 } |
|
236 |
|
237 } |
|
238 } |
|
239 } |
|
240 |
|
241 // GROUPES TWEET ============================================ |
|
242 // max of tweet by Frame |
|
243 var max = 0; |
|
244 for(var i = 0; i < nbrframes; i++) { |
|
245 var moy = 0; |
|
246 for (var j = 0; j < 6; j++) { |
|
247 if (frames[i] != undefined) { |
|
248 if (frames[i].qualifVol[j] != undefined) { |
|
249 moy += frames[i].qualifVol[j]; |
|
250 } |
|
251 } |
|
252 } |
|
253 |
|
254 if (moy > max) { |
|
255 max = moy; |
|
256 } |
|
257 } |
|
258 |
|
259 var tweetDrawed = new Array(); |
|
260 var TweetHeight = 5; |
|
261 |
|
262 // DRAW TWEETS ============================================ |
|
263 for(var i = 0; i < nbrframes; i++) { |
|
264 var addEheight = 5; |
|
265 if (frames[i] != undefined){ |
|
266 // by type |
|
267 |
|
268 for (var j = 6; j > -1; j--) { |
|
269 if (frames[i].qualifVol[j] != undefined) { |
|
270 // show tweet by type |
|
271 for (var k = 0; k < frames[i].mytweetsID.length; k++) { |
|
272 |
|
273 if (frames[i].mytweetsID[k].qualification == j) { |
|
274 var x = i * frameSize; |
|
275 var y = this.heightmax - addEheight; |
|
276 |
|
277 if (this.yMax > y) { |
|
278 this.yMax = y; |
|
279 } |
|
280 |
|
281 var e = this.paper.rect(x, y, frameSize - margin, TweetHeight /* height */) |
|
282 .attr({stroke:"#00","stroke-width":0.1, fill: colors[j]}); |
|
283 |
|
284 addEheight += TweetHeight; |
|
285 |
|
286 e.color = colors[j]; |
|
287 e.time = frames[i].mytweetsID[k].timeframe; |
|
288 e.title = frames[i].mytweetsID[k].title; |
|
289 e.description = frames[i].mytweetsID[k].description; |
|
290 e.id = frames[i].mytweetsID[k].cinecast_id; |
|
291 |
|
292 this.svgElements[e.id] = e; |
|
293 |
|
294 e.mouseover(function(element) { return function (event) { |
|
295 // event.clientX and event.clientY are to raphael what event.pageX and pageY are to jquery. |
|
296 self.TooltipWidget.show.call(self.TooltipWidget, element.description, element.attr("fill"), event.clientX - 106, event.clientY - 160); |
|
297 element.displayed = true; |
|
298 }}(e)).mouseout(function(element) { return function () { |
|
299 self.TooltipWidget.hide.call(self.TooltipWidget); |
|
300 }}(e)).mousedown(function () { |
|
301 self._Popcorn.currentTime(this.time/1000); |
|
302 self._Popcorn.trigger("IriSP.PolemicTweet.click", this.id); |
|
303 }); |
|
304 |
|
305 IriSP.jQuery(e.node).attr('id', 't' + k + ''); |
|
306 IriSP.jQuery(e.node).attr('title', frames[i].mytweetsID[k].title); |
|
307 IriSP.jQuery(e.node).attr('begin', frames[i].mytweetsID[k].timeframe); |
|
308 } |
|
309 } |
|
310 } |
|
311 } |
|
312 } |
|
313 |
|
314 } |
|
315 // DRAW UI :: resize border and bgd |
|
316 this.paperBackground = this.paper.rect(0, 0, this.width, this.heightmax).attr({fill:"#F8F8F8","stroke-width":0.1,opacity: 1}); |
|
317 |
|
318 // outer borders |
|
319 this.outerBorders = []; |
|
320 this.outerBorders.push(this.paper.rect(0, this.height - 1, this.width, 1).attr({fill:"#ababab",stroke: "none",opacity: 1})); |
|
321 this.outerBorders.push(this.paper.rect(0, 0, this.width, 1).attr({fill:"#ababab",stroke: "none",opacity: 1})); |
|
322 |
|
323 // inner borders |
|
324 this.innerBorders = []; |
|
325 this.innerBorders.push(this.paper.rect(1, this.height - 2, this.width, 1).attr({fill:"#efefef",stroke: "none",opacity: 1})); |
|
326 this.innerBorders.push(this.paper.rect(1, 1, this.width, 1).attr({fill:"#efefef",stroke: "none",opacity: 1})); |
|
327 this.innerBorders.push(this.paper.rect(1, 1, 1, this.height - 2).attr({fill:"#d0d1d1",stroke: "none",opacity: 0.8})); |
|
328 this.innerBorders.push(this.paper.rect(this.width - 2, 1, 1, this.height - 2).attr({fill:"#efefef",stroke: "none",opacity: 1})); |
|
329 |
|
330 |
|
331 |
|
332 this.paperSlider = this.paper.rect(0, 0, 0, this.heightmax).attr({fill:"#D4D5D5", stroke: "none", opacity: 1}); |
|
333 |
|
334 // the small white line displayed over the slider. |
|
335 this.sliderTip = this.paper.rect(0, 0, 1, this.heightmax).attr({fill:"#fc00ff", stroke: "none", opacity: 1}); |
|
336 // decalage |
|
337 // tweetSelection = this.paper.rect(-100,-100,5,5).attr({fill:"#fff",stroke: "none",opacity: 1}); |
|
338 |
|
339 |
|
340 this.paperSlider.toBack(); |
|
341 this.paperBackground.toBack(); |
|
342 this.sliderTip.toFront(); |
|
343 } |
|
344 |
|
345 this._Popcorn.listen("timeupdate", IriSP.wrap(this, this.sliderUpdater)); |
|
346 } |
|
347 |
|
348 IriSP.PolemicViewer.prototype.sliderUpdater = function() { |
|
349 |
|
350 var time = +this._Popcorn.currentTime(); |
|
351 var duration = +this._serializer.currentMedia().meta["dc:duration"]; |
|
352 |
|
353 this.paperSlider.attr("width", time * (this.width / (duration / 1000))); |
|
354 |
|
355 this.sliderTip.attr("x", time * (this.width / (duration / 1000))); |
|
356 }; |
|
357 |
|
358 IriSP.PolemicViewer.prototype.searchHandler = function(searchString) { |
|
359 |
|
360 if (searchString == "") |
|
361 return; |
|
362 |
|
363 var matches = this._serializer.searchTweetsOccurences(searchString); |
|
364 |
|
365 if (IriSP.countProperties(matches) > 0) { |
|
366 this._Popcorn.trigger("IriSP.search.matchFound"); |
|
367 } else { |
|
368 this._Popcorn.trigger("IriSP.search.noMatchFound"); |
|
369 } |
|
370 |
|
371 for (var id in matches) { |
|
372 var factor = 0.5 + matches[id] * 0.2; |
|
373 if (this.svgElements.hasOwnProperty(id)) { |
|
374 this.svgElements[id].attr({fill: "#fc00ff"}); |
|
375 } |
|
376 } |
|
377 |
|
378 // clean up the blocks that were in the previous search |
|
379 // but who aren't in the current one. |
|
380 for (var id in this.oldSearchMatches) { |
|
381 if (!matches.hasOwnProperty(id)) { |
|
382 var e = this.svgElements[id]; |
|
383 e.attr({fill: e.color}); |
|
384 } |
|
385 } |
|
386 |
|
387 this.oldSearchMatches = matches; |
|
388 }; |
|
389 |
|
390 IriSP.PolemicViewer.prototype.searchFieldClearedHandler = function() { |
|
391 // clean up the blocks that were in the previous search |
|
392 // but who aren't in the current one. |
|
393 for (var id in this.oldSearchMatches) { |
|
394 var e = this.svgElements[id]; |
|
395 e.attr({fill: e.color}); |
|
396 } |
|
397 |
|
398 }; |
|
399 |
|
400 IriSP.PolemicViewer.prototype.searchFieldClosedHandler = function() { |
|
401 // clean up the blocks that were in the previous search |
|
402 // but who aren't in the current one. |
|
403 for (var id in this.oldSearchMatches) { |
|
404 var e = this.svgElements[id]; |
|
405 e.attr({fill: e.color}); |
|
406 } |
|
407 |
|
408 }; |
|
409 |