src/js/widgets/polemicWidget.js
branchnew-model
changeset 874 38b65761a7d5
parent 842 4ae2247a59f4
equal deleted inserted replaced
872:d777d05a16e4 874:38b65761a7d5
     1 /* 
     1 IriSP.PolemicWidget = function(player, config) {
     2  *   
     2     IriSP.Widget.call(this, player, config);
     3  *  Copyright 2010 Institut de recherche et d'innovation 
     3     this.bindPopcorn("IriSP.search", "searchHandler");
     4  *  contributor(s) : Samuel Huron 
     4     this.bindPopcorn("IriSP.search.closed", "searchHandler");
     5  *   
     5     this.bindPopcorn("IriSP.search.cleared", "searchHandler");
     6  *  contact@iri.centrepompidou.fr
     6     this.bindPopcorn("timeupdate", "onTimeupdate");
     7  *  http://www.iri.centrepompidou.fr 
     7     this.sliceCount = Math.floor( this.width / this.element_width );
     8  *   
     8     this.$zone = IriSP.jQuery('<div>');
     9  *  This software is a computer program whose purpose is to show and add annotations on a video .
     9     this.$.append(this.$zone);
    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 /** the polemic widget */
       
    22 IriSP.PolemicWidget = function(Popcorn, config, Serializer) {
       
    23   IriSP.Widget.call(this, Popcorn, config, Serializer);
       
    24  
       
    25   this.userPol    = new Array();
       
    26   this.userNoPol  = new Array();
       
    27   this.userst      = new Array();
       
    28   this.numberOfTweet = 0;
       
    29   this.Users;
       
    30   this.TweetPolemic;
       
    31   this.yMax        = this.height; 
       
    32   this.PaperSlider;
       
    33   this.heightOfChart;
       
    34   this.tweets  = new Array();
       
    35   this.svgElements = {};
       
    36   
       
    37   this.oldSearchMatches = [];
       
    38 };
    10 };
    39 
    11 
    40 IriSP.PolemicWidget.prototype = new IriSP.Widget();
    12 IriSP.PolemicWidget.prototype = new IriSP.Widget();
    41   
    13 
       
    14 IriSP.PolemicWidget.prototype.searchHandler = function(searchString) {
       
    15     this.searchString = typeof searchString !== "undefined" ? searchString : '';
       
    16     var _found = 0,
       
    17         _re = IriSP.Model.regexpFromTextOrArray(searchString)
       
    18         _this = this;
       
    19     this.$tweets.each(function() {
       
    20         var _el = IriSP.jQuery(this);
       
    21         if (_this.searchString) {
       
    22             if (_re.test(_el.attr("tweet-title"))) {
       
    23                 _el.css({
       
    24                     "background" : _this.foundcolor,
       
    25                     "opacity" : 1
       
    26                 });
       
    27                 _found++;
       
    28             } else {
       
    29                 _el.css({
       
    30                     "background" : _el.attr("polemic-color"),
       
    31                     "opacity" : .5
       
    32                 });
       
    33             }
       
    34         } else {
       
    35             _el.css({
       
    36                 "background" : _el.attr("polemic-color"),
       
    37                 "opacity" : 1
       
    38             });
       
    39         }
       
    40     });
       
    41     if (this.searchString) {
       
    42         if (_found) {
       
    43             this.player.popcorn.trigger("IriSP.search.matchFound");
       
    44         } else {
       
    45             this.player.popcorn.trigger("IriSP.search.noMatchFound");
       
    46         }
       
    47     }
       
    48 }
       
    49 
    42 IriSP.PolemicWidget.prototype.draw = function() {
    50 IriSP.PolemicWidget.prototype.draw = function() {
    43   
    51     var _slices = [],
    44     // variable 
    52         _duration = this.source.getDuration(),
    45     // yMax
    53         _max = 0,
       
    54         _list = this.annotation_type ? this.source.getAnnotationsByTypeTitle(this.annotation_type) : this.source.getAnnotations();
    46     
    55     
    47     var self = this;
    56     for (var _i = 0; _i < this.sliceCount; _i++) {
    48     var yCoef        = 2;             // coef for height of 1 tweet 
    57         var _begin = new IriSP.Model.Time(_i*_duration/this.sliceCount),
    49     var frameSize     = 5;             // frame size 
    58             _end = new IriSP.Model.Time((_i+1)*_duration/this.sliceCount),
    50     var margin         = 1;            // marge between frame
    59             _count = 0,
    51     var lineSize      = this.width;        // timeline pixel width 
    60             _res = {
    52     var nbrframes     = lineSize/frameSize;     // frame numbers
    61                 annotations : _list.filter(function(_annotation) {
    53     var numberOfTweet   = 0;            // number of tweet overide later 
    62                     return _annotation.begin >= _begin && _annotation.end < _end;
    54     var duration      = this.getDuration();      // timescale width 
    63                 }),
    55     var frameLength   = lineSize / frameSize;    // frame timescale  
    64                 polemicStacks : []
    56     var timeline;
    65             }
    57     var colors  = new Array("","#1D973D","#036AAE","#CE0A15","#C5A62D","#585858");
    66             
       
    67         for (var _j = 0; _j < this.tags.length; _j++) {
       
    68             var _polemic = _res.annotations.searchByDescription(this.tags[_j].keywords);
       
    69             _count += _polemic.length;
       
    70             _res.polemicStacks.push(_polemic);
       
    71         }
       
    72         for (var _j = 0; _j < this.tags.length; _j++) {
       
    73             _res.annotations.removeElements(_res.polemicStacks[_j]);
       
    74         }
       
    75         _count += _res.annotations.length;
       
    76         _max = Math.max(_max, _count);
       
    77         _slices.push(_res);
       
    78     }
       
    79     this.height = (_max ? (_max + 2) * this.element_height : 0);
       
    80     this.$zone.css({
       
    81         width: this.width + "px",
       
    82         height: this.height + "px",
       
    83         position: "relative"
       
    84     });
    58     
    85     
    59     // array 
    86     this.$elapsed = IriSP.jQuery('<div>')
    60     //var tweets  = new Array();
    87         .css({
    61     var element = new Array();
    88             background: '#cccccc',
    62     var cluster = new Array();
    89             position: "absolute",
    63     var frames  = new Array(frameLength);
    90             top: 0,
    64     var slices  = new Array();
    91             left: 0,
       
    92             width: 0,
       
    93             height: "100%"
       
    94         });
       
    95         
       
    96     this.$zone.append(this.$elapsed);
    65     
    97     
       
    98     var _x = 0,
       
    99         _this = this;
    66     
   100     
    67     // Classes =======================================================================
   101     function displayElement(_x, _y, _color, _id, _title) {
    68     var Frames = function(){
   102         var _el = IriSP.jQuery('<div>')
    69       
   103             .attr({
    70       var Myclusters;
   104                 "tweet-title" : _title,
    71       var x;
   105                 "pos-x" : Math.floor(_x + (_this.element_width - 1) / 2),
    72       var y;
   106                 "pos-y" : _y,
    73       var width;
   107                 "polemic-color" : _color,
    74       var height;
   108                 "annotation-id" : _id
    75     };
   109             })
    76     Frames = function(json){
   110             .css({
    77       // make my clusters
   111                 position: "absolute",
    78       // ou Frame vide 
   112                 width: (_this.element_width-1) + "px",
    79     };
   113                 height: _this.element_height + "px",
    80     Frames.prototype.draw = function(){
   114                 left: _x + "px",
    81     };
   115                 top: _y + "px",
    82     Frames.prototype.zoom = function(){
   116                 background: _color
    83     };
   117             })
    84     Frames.prototype.inside = function(){
   118             .addClass("Ldt-Polemic-TweetDiv");
    85     };
   119         _this.$zone.append(_el);
    86     var Clusters = function(){
   120         return _el;
    87       var Object;
       
    88       var yDist;
       
    89       var x;
       
    90       var y;
       
    91       var width;
       
    92       var height;
       
    93     };
       
    94     Clusters = function(json){
       
    95       // make my object
       
    96     };
       
    97     var Tweet = function(){
       
    98     };
       
    99     // Classes =======================================================================
       
   100 
       
   101     // Refactoring (parametere) ************************************************************
       
   102     // color translastion
       
   103     var qTweet_0  =0;
       
   104     var qTweet_Q  =0;
       
   105     var qTweet_REF=0;
       
   106     var qTweet_OK =0;
       
   107     var qTweet_KO =0;
       
   108     function colorTranslation(value){
       
   109       if(value == "Q"){
       
   110         qTweet_Q+=1;
       
   111         return 2;
       
   112       }else if(value =="REF"){
       
   113         qTweet_REF+=1;
       
   114         return 4;
       
   115       }else if(value =="OK"){
       
   116         qTweet_OK+=1;
       
   117         return 1;
       
   118       }else if(value =="KO"){
       
   119         qTweet_KO+=1;
       
   120         return 3;
       
   121       }else if(value ==""){
       
   122         qTweet_0+=1;
       
   123         return 5;
       
   124       }
       
   125     }
   121     }
   126     
   122     
   127 
   123     IriSP._(_slices).forEach(function(_slice) {
   128       this._serializer.sync(function(data) { loaded_callback.call(self, data); return; });
   124         var _y = _this.height;
   129       
   125         _slice.annotations.forEach(function(_annotation) {
   130       function loaded_callback (json) {
   126             _y -= _this.element_height;
   131       var view_type = this._serializer.getTweets();
   127             displayElement(_x, _y, _this.defaultcolor, _annotation.namespacedId.name, _annotation.title);
   132 
   128         });
   133       
   129         IriSP._(_slice.polemicStacks).forEach(function(_annotations, _j) {
   134       if (typeof(view_type) === "undefined") {
   130             var _color = _this.tags[_j].color;
   135         var view_type = this._serializer.getTweetIds()[0];
   131             _annotations.forEach(function(_annotation) {
   136         if (typeof(view_type) === "undefined") {
   132                 _y -= _this.element_height;
   137           // default to guessing if nothing else works.
   133                 displayElement(_x, _y, _color, _annotation.namespacedId.name, _annotation.title);
   138           var view = json.views[0];
   134             });
   139           
   135         });
   140           if(typeof(view.annotation_types) !== "undefined") {
   136         _x += _this.element_width;
   141             /* we need to be backward compatible with the old files which used to
   137     });
   142                feature only two lines : Chapitrage and Tweets. We've added a
   138     
   143                "Contributions" line so we need to discriminate against that */
   139     this.$tweets = this.$.find(".Ldt-Polemic-TweetDiv");
   144             if (view.annotation_types.length === 2 && typeof(this._serializer.getContributions()) === "undefined") {
   140     
   145               var view_type = view.annotation_types[1];
   141     this.$position = IriSP.jQuery('<div>')
   146             } else {
   142         .css({
   147               console.log("PolemicWidget: invalid file - minimizing");
   143             background: '#fc00ff',
   148               return;
   144             position: "absolute",
   149             }
   145             top: 0,
   150           }      
   146             left: "-1px",
   151         }
   147             width: "2px",
   152       }
   148             height: "100%"
   153       
   149         });
   154       // Make and define the Raphael area
       
   155       this.paper = Raphael(document.getElementById(this._id), this._config.width, this._config.height);
       
   156       
       
   157       // event handlers
       
   158       this._Popcorn.listen("IriSP.search", IriSP.wrap(this, function(searchString) { this.searchHandler(searchString); }));
       
   159       this._Popcorn.listen("IriSP.search.closed", IriSP.wrap(this, this.searchFieldClosedHandler));
       
   160       this._Popcorn.listen("IriSP.search.cleared", IriSP.wrap(this, this.searchFieldClearedHandler));
       
   161       this.selector.mouseleave(IriSP.wrap(this, function() { self.TooltipWidget.hide.call(self.TooltipWidget); }));
       
   162       this._Popcorn.listen("timeupdate", IriSP.wrap(this, this.sliderUpdater));
       
   163       this._Popcorn.listen("IriSP.Mediafragment.showAnnotation", IriSP.wrap(this, this.showAnnotation));
       
   164       
       
   165       for(var i = 0; i < json.annotations.length; i++) {
       
   166         var item = json.annotations[i];        
       
   167         var MyTime  = Math.floor(item.begin/duration*lineSize);
       
   168         var Myframe = Math.floor(MyTime/lineSize*frameLength);
       
   169 
       
   170         if (typeof(item.meta) !== "undefined" 
       
   171           && typeof(item.meta["id-ref"]) !== "undefined"
       
   172           && item.meta["id-ref"] === view_type) {
       
   173             
       
   174             var MyTJson = {},
       
   175                 _source = IriSP.get_aliased(item.meta, ['dc:source', 'source']);
       
   176             if (_source !== null) {
       
   177               var MyTJson = JSON.parse(_source['content']);
       
   178             }
       
   179             
       
   180             if (item.content['polemics'] != undefined 
       
   181             && item.content['polemics'][0] != null) {
       
   182             
       
   183               // a tweet can have many polemics at the same time.
       
   184               for(var j=0; j<item.content['polemics'].length; j++){
       
   185                   
       
   186                   this.tweets[numberOfTweet] = {
       
   187                         id:i,
       
   188                         qualification:colorTranslation(item.content['polemics'][j]),
       
   189                         yIndicator:MyTime,
       
   190                         yframe:Myframe,
       
   191                         title:item.content['title'],
       
   192                         timeframe:item.begin,
       
   193                         userId: MyTJson.id,
       
   194                         userScreenName: MyTJson.screen_name,
       
   195                         tsource:MyTJson,
       
   196                         cinecast_id: item.id
       
   197                         };
       
   198                   numberOfTweet+=1;
       
   199                   
       
   200               }
       
   201           }
       
   202           else {
       
   203             this.tweets[numberOfTweet] = {
       
   204                   id:i,
       
   205                   qualification:colorTranslation(""),
       
   206                   yIndicator:MyTime,
       
   207                   yframe:Myframe,
       
   208                   title:item.content['title'],
       
   209                   timeframe:item.begin,
       
   210                   userId: MyTJson.id,
       
   211                   userScreenName: MyTJson.screen_name,
       
   212                   tsource:MyTJson,
       
   213                   cinecast_id: item.id
       
   214             };
       
   215             numberOfTweet+=1;
       
   216           }
       
   217           
       
   218         } 
       
   219       };  
       
   220       
       
   221        DrawTweets.call (this); // FIXME: ugly.
       
   222        
       
   223       };      
       
   224 
       
   225     // tweet Drawing (in raphael) 
       
   226     function DrawTweets (){
       
   227     // GROUPES TWEET ============================================
       
   228     // Count nbr of cluster and tweet in a frame an save int in "frames"
       
   229       numberOfTweet = this.tweets.length;
       
   230       for(var i=0; i<nbrframes; i++) {  
       
   231         for(var j=0; j<numberOfTweet; j++) {  
       
   232         
   150         
   233           if (i==this.tweets[j].yframe){
   151     this.$zone.append(this.$position);
   234             
       
   235             var k = this.tweets[j].qualification;
       
   236             
       
   237             // make array for frame cluster
       
   238             if(frames[i]==undefined){
       
   239               frames[i] = {id:i,
       
   240                      qualifVol:new Array(),
       
   241                      mytweetsID:new Array()
       
   242                     };
       
   243             }
       
   244             // add my tweet to frame
       
   245             frames[i].mytweetsID.push(this.tweets[j]);
       
   246             
       
   247             // count opinion by frame
       
   248             if( frames[i].qualifVol[k] == undefined){
       
   249               frames[i].qualifVol[k] = 1;
       
   250             }else{
       
   251               frames[i].qualifVol[k] += 1;
       
   252             }
       
   253             
       
   254           }
       
   255         }
       
   256       }
       
   257     
   152     
   258     // GROUPES TWEET ============================================    
   153     this.$tweets
   259     // max of tweet by Frame 
   154         .mouseover(function() {
   260       var max = 0; 
   155             var _el = IriSP.jQuery(this);
   261       for(var i = 0; i < nbrframes; i++) {
   156             _this.TooltipWidget.show(_el.attr("pos-x"), _el.attr("pos-y"), _el.attr("tweet-title"), _el.attr("polemic-color"));
   262         var moy  = 0;
   157         })
   263         for (var j = 0; j < 6; j++) {    
   158         .mouseout(function() {
   264           if (frames[i] != undefined) {
   159             _this.TooltipWidget.hide();
   265             if (frames[i].qualifVol[j] != undefined) {
   160         });
   266               moy += frames[i].qualifVol[j];
       
   267             }
       
   268           }
       
   269         }
       
   270         
       
   271         if (moy > max) {
       
   272           max = moy;
       
   273         }
       
   274       }
       
   275     
   161     
   276       var tweetDrawed = new Array();
   162     //TODO: Display Tweet in Tweet Widget on click
   277       var TweetHeight = 5;
       
   278       var newHeight = TweetHeight * max + 10;
       
   279 
       
   280       
       
   281       if (newHeight > this.height) {
       
   282         this.paper.setSize(this.width, newHeight);
       
   283         this.height = newHeight;
       
   284         console.log("resizeing");
       
   285       }
       
   286       
       
   287   
       
   288       // DRAW  TWEETS ============================================
       
   289       for(var i = 0; i < nbrframes; i++) {
       
   290         var addEheight = 5;
       
   291         if (frames[i] != undefined){                
       
   292           // by type 
       
   293           
       
   294           for (var j = 6; j > -1; j--) {
       
   295             if (frames[i].qualifVol[j] != undefined) {
       
   296               // show tweet by type 
       
   297               for (var k = 0; k < frames[i].mytweetsID.length; k++) {
       
   298               
       
   299                 if (frames[i].mytweetsID[k].qualification == j) {                
       
   300                   var x = i * frameSize;
       
   301                   var y = this.height - addEheight;
       
   302                   
       
   303                   if (this.yMax > y) {
       
   304                     this.yMax = y;
       
   305                   }
       
   306                   
       
   307                   /* some tweets seem to be duplicated - so we make a check before
       
   308                      creating a new rect */
       
   309                   if (this.svgElements.hasOwnProperty(frames[i].mytweetsID[k].cinecast_id))
       
   310                     continue;
       
   311                   
       
   312                   var e = this.paper.rect(x, y, frameSize - margin, TweetHeight /* height */)
       
   313                                     .attr({stroke:"#00","stroke-width":0.1,  fill: colors[j]});  
       
   314                   
       
   315                   addEheight += TweetHeight;
       
   316                   
       
   317                   /* stick a lot of things into e because that's the easiest way
       
   318                      to do it */
       
   319                   e.color = colors[j];
       
   320                   e.time = frames[i].mytweetsID[k].timeframe;
       
   321                   e.title = frames[i].mytweetsID[k].title;
       
   322                   e.id = frames[i].mytweetsID[k].cinecast_id;
       
   323                   this.svgElements[e.id] = e;
       
   324                   
       
   325                   IriSP.jQuery(e.node).mouseenter(function(element) { return function (_e) {                    
       
   326                         self.TooltipWidget.show.call(self.TooltipWidget, element.title, element.attr("fill"), element.attrs.x + element.attrs.width / 2, element.attrs.y - 2);
       
   327                         element.displayed = true;
       
   328                         self._Popcorn.trigger("IriSP.TraceWidget.MouseEvents", {
       
   329                             "widget" : "StackGraphWidget",
       
   330                             "type": "mousemove",
       
   331                             "x": _e.pageX,
       
   332                             "y": _e.pageY,
       
   333                             "annotation_id": element.id
       
   334                         });
       
   335                   }}(e)).mousedown(function(element) { return function () {                    
       
   336                     self._Popcorn.currentTime(element.time/1000);
       
   337                     self._Popcorn.trigger("IriSP.PolemicTweet.click", element.id); 
       
   338                     }
       
   339                   }(e));                  
       
   340                   
       
   341                   IriSP.jQuery(e.node).attr('id', 't' + k + '');
       
   342                   IriSP.jQuery(e.node).attr('title', frames[i].mytweetsID[k].title);
       
   343                   IriSP.jQuery(e.node).attr('begin',  frames[i].mytweetsID[k].timeframe);                  
       
   344                 }
       
   345               }
       
   346             }
       
   347           }
       
   348         }
       
   349 
       
   350       }    
       
   351       // DRAW UI :: resize border and bgd      
       
   352       this.paperBackground = this.paper.rect(0, 0, this.width, this.height).attr({fill:"#F8F8F8","stroke-width":0.1,opacity: 1});  
       
   353 
       
   354       // outer borders
       
   355       this.outerBorders   = [];
       
   356       this.outerBorders.push(this.paper.rect(0, this.height - 1, this.width, 1).attr({fill:"#ababab",stroke: "none",opacity: 1}));  
       
   357       this.outerBorders.push(this.paper.rect(0, 0, this.width, 1).attr({fill:"#ababab",stroke: "none",opacity: 1}));  
       
   358 
       
   359       // inner borders
       
   360       this.innerBorders   = [];
       
   361       this.innerBorders.push(this.paper.rect(1, this.height - 2, this.width, 1).attr({fill:"#efefef",stroke: "none",opacity: 1}));  
       
   362       this.innerBorders.push(this.paper.rect(1, 1, this.width, 1).attr({fill:"#efefef",stroke: "none",opacity: 1}));  
       
   363       this.innerBorders.push(this.paper.rect(1, 1, 1, this.height - 2).attr({fill:"#d0d1d1",stroke: "none",opacity: 0.8}));  
       
   364       this.innerBorders.push(this.paper.rect(this.width - 2, 1, 1, this.height - 2).attr({fill:"#efefef",stroke: "none",opacity: 1}));  
       
   365 
       
   366 
       
   367 
       
   368       this.paperSlider   = this.paper.rect(0, 0, 0, this.height).attr({fill:"#D4D5D5", stroke: "none", opacity: 1});
       
   369       
       
   370       // the small white line displayed over the slider.
       
   371       this.sliderTip = this.paper.rect(0, 0, 1, this.height).attr({fill:"#fc00ff", stroke: "none", opacity: 1});
       
   372       // decalage 
       
   373       // tweetSelection = this.paper.rect(-100,-100,5,5).attr({fill:"#fff",stroke: "none",opacity: 1});  
       
   374       
       
   375       
       
   376       this.paperSlider.toBack();
       
   377       this.paperBackground.toBack();
       
   378       this.sliderTip.toFront();
       
   379     }
       
   380     
   163     
   381 
   164     this.$zone.click(function(_e) {
       
   165         var _x = _e.pageX - _this.$zone.offset().left;
       
   166         _this.player.popcorn.currentTime(_this.source.getDuration().getSeconds() * _x / _this.width);
       
   167     });
   382 }
   168 }
   383 
   169 
   384 /** update the positionMarker as time passes */
   170 IriSP.PolemicWidget.prototype.onTimeupdate = function() {
   385 IriSP.PolemicWidget.prototype.sliderUpdater = function() {
   171     var _x = Math.floor( this.width * this.player.popcorn.currentTime() / this.source.getDuration().getSeconds());
   386 
   172     this.$elapsed.css({
   387     var time = +this._Popcorn.currentTime();
   173         width:  _x + "px"
   388     var duration = this.getDuration();
   174     });
   389     
   175     this.$position.css({
   390     this.paperSlider.attr("width", time * (this.width / (duration / 1000)));
   176         left: (_x - 1) + "px"
   391         
   177     })
   392     this.sliderTip.attr("x", time * (this.width / (duration / 1000)));
   178 }
   393 };
       
   394 
       
   395 /** reacts to IriSP.search events */    
       
   396 IriSP.PolemicWidget.prototype.searchHandler = function(searchString) {
       
   397   if (searchString == "")
       
   398     return;
       
   399 
       
   400   var matches = this._serializer.searchTweetsOccurences(searchString);
       
   401 
       
   402   if (IriSP.countProperties(matches) > 0) {
       
   403     this._Popcorn.trigger("IriSP.search.matchFound");
       
   404   } else {
       
   405     this._Popcorn.trigger("IriSP.search.noMatchFound");
       
   406   }
       
   407 
       
   408   
       
   409   // decrease the opacity of the other elements.
       
   410   for (var id in this.svgElements) {
       
   411     var e = this.svgElements[id];
       
   412     e.attr({fill: e.color, opacity: 0.4});   
       
   413   }
       
   414   
       
   415 
       
   416   for (var id in matches) {    
       
   417     if (this.svgElements.hasOwnProperty(id)) {
       
   418       var e = this.svgElements[id];
       
   419       this.svgElements[id].attr({fill: "#fc00ff", opacity: 1});
       
   420     }
       
   421   }
       
   422 
       
   423   this.oldSearchMatches = matches;
       
   424 };
       
   425 
       
   426 /** reacts to IriSP.search.cleared messages */
       
   427 IriSP.PolemicWidget.prototype.searchFieldClearedHandler = function() {
       
   428   for (var id in this.svgElements) {
       
   429     var e = this.svgElements[id];
       
   430     e.attr({fill: e.color, opacity: 1});
       
   431   }
       
   432 };
       
   433 
       
   434 /** reacts to IriSP.search.closed messages by clearing the highlighted elements */
       
   435 IriSP.PolemicWidget.prototype.searchFieldClosedHandler = function() {
       
   436   for (var id in this.svgElements) {
       
   437     var e = this.svgElements[id];
       
   438     e.attr({fill: e.color, opacity: 1});
       
   439   }
       
   440  
       
   441 };
       
   442    
       
   443 IriSP.PolemicWidget.prototype.showAnnotation = function(id) {
       
   444   if (this.svgElements.hasOwnProperty(id)) {
       
   445     var e = this.svgElements[id];
       
   446     this.TooltipWidget.show(e.title, e.attr("fill"), e.x - 103, e.y - 160);
       
   447   }
       
   448 };