updated search
authorveltr
Wed, 21 Nov 2012 16:33:51 +0100
changeset 983 97fef7a4b189
parent 982 cfcbac34d020
child 984 e034099276f6
updated search
src/js/model.js
src/js/serializers/ldt.js
src/widgets/Annotation.css
src/widgets/Annotation.js
src/widgets/AnnotationsList.css
src/widgets/AnnotationsList.js
src/widgets/Controller.js
src/widgets/KnowledgeConcierge.css
src/widgets/KnowledgeConcierge.js
src/widgets/MashupPlayer.js
src/widgets/MediaList.js
src/widgets/Polemic.js
src/widgets/Renkan.js
src/widgets/Segments.js
src/widgets/Tagcloud.js
--- a/src/js/model.js	Fri Nov 16 17:36:56 2012 +0100
+++ b/src/js/model.js	Wed Nov 21 16:33:51 2012 +0100
@@ -1,7 +1,6 @@
 /* TODO: Separate Project-specific data from Source */
 
 /* model.js is where data is stored in a standard form, whatever the serializer */
-
 IriSP.Model = (function (ns) {
     
     function pad(n, x, b) {
@@ -18,7 +17,21 @@
     }
     
     var uidbase = rand16(8) + "-" + rand16(4) + "-", uidincrement = Math.floor(Math.random()*0x10000);
-
+    
+    var charsub = [
+        [ 'a', 'á', 'à', 'â', 'ä' ],
+        [ 'c', 'ç' ],
+        [ 'e', 'é', 'è', 'ê', 'ë' ],
+        [ 'i', 'í', 'ì', 'î', 'ï' ],
+        [ 'o', 'ó', 'ò', 'ô', 'ö' ]
+    ];
+    
+    var removeChars = [
+        String.fromCharCode(768), String.fromCharCode(769), String.fromCharCode(770), String.fromCharCode(771), String.fromCharCode(807),
+        "{", "}", "(", ")", "[", "]", "【", "】", "、", "・", "‥", "。", "「", "」", "『", "』", "〜", ":", "!", "?", " ",
+        ",", " ", ";", "(", ")", ".", "*", "+", "\\", "?", "|", "{", "}", "[", "]", "^", "#", "/"
+    ]
+    
 var Model = {
     _SOURCE_STATUS_EMPTY : 0,
     _SOURCE_STATUS_WAITING : 1,
@@ -26,6 +39,13 @@
     getUID : function() {
         return uidbase + pad(4, (++uidincrement % 0x10000), 16) + "-" + rand16(4) + "-" + rand16(6) + rand16(6);
     },
+    isLocalURL : function(url) {
+        var matches = url.match(/^(\w+:)\/\/([^/]+)/);
+        if (matches) {
+            return(matches[1] === document.location.protocol && matches[2] === document.location.host)
+        }
+        return true;
+    },
     regexpFromTextOrArray : function(_textOrArray, _testOnly, _iexact) {
         var _testOnly = _testOnly || false,
             _iexact = _iexact || false;
@@ -46,6 +66,30 @@
         }
         return new RegExp( _source, _flags);
     },
+    fullTextRegexps: function(_text) {
+        var remsrc = "[\\" + removeChars.join("\\") + "]",
+            remrx = new RegExp(remsrc,"gm"),
+            txt = _text.toLowerCase().replace(remrx,"")
+            res = [],
+            charsrc = ns._(charsub).map(function(c) {
+                return "(" + c.join("|") + ")";
+            }),
+            charsrx = ns._(charsrc).map(function(c) {
+                return new RegExp(c);
+            }),
+            src = "";
+        for (var j = 0; j < txt.length; j++) {
+            if (j) {
+                src += remsrc + "*";
+            }
+            var l = txt[j];
+            ns._(charsrc).each(function(v, k) {
+                l = l.replace(charsrx[k], v);
+            });
+            src += l;
+        }
+        return "(" + src + ")";
+    },
     isoToDate : function(_str) {
         // http://delete.me.uk/2005/03/iso8601.html
         var regexp = "([0-9]{4})(-([0-9]{2})(-([0-9]{2})(T([0-9]{2}):([0-9]{2})(:([0-9]{2})(\.([0-9]+))?)?(Z|(([-+])([0-9]{2}):([0-9]{2})))?)?)?)?";
@@ -71,7 +115,8 @@
         _res.setTime(Number(time));
         return _res;
     },
-    dateToIso : function(d) {
+    dateToIso : function(_d) {
+        var d = _d ? new Date(_d) : new Date();
         return d.getUTCFullYear()+'-'  
             + pad(2, d.getUTCMonth()+1)+'-'  
             + pad(2, d.getUTCDate())+'T'  
@@ -93,6 +138,15 @@
         console.trace();
         throw "Error : new Model.List(directory): directory is undefined";
     }
+    var _this =  this;
+    this.on("clear-search", function() {
+        _this.searching = false;
+        _this.regexp = undefined;
+        _this.forEach(function(_element) {
+            _element.found = undefined;
+        });
+        _this.trigger("search-cleared");
+    })
 }
 
 Model.List.prototype = new Array();
@@ -191,6 +245,28 @@
     });
 }
 
+Model.List.prototype.search = function(_text) {
+    if (!_text) {
+        this.trigger("clear-search");
+        return this;
+    }
+    this.searching = true;
+    this.trigger("search", _text);
+    var rxsource = Model.fullTextRegexps(_text)
+        rgxp = new RegExp(rxsource,"im"),
+        this.regexp = new RegExp(rxsource,"gim");
+    var res = this.filter(function(_element, _k) {
+        var titlematch = rgxp.test(_element.title),
+            descmatch = rgxp.test(_element.description),
+            _isfound = !!(titlematch || descmatch);
+        _element.found = _isfound;
+        _element.trigger(_isfound ? "found" : "not-found");
+        return _isfound;
+    });
+    this.trigger(res.length ? "found" : "not-found",res);
+    return res;
+}
+
 Model.List.prototype.getTitles = function() {
     return this.map(function(_el) {
         return _el.title;
@@ -297,16 +373,16 @@
 }
 
 Model.Time.prototype.setMilliseconds = function(_milliseconds) {
-    var _ante = _milliseconds;
+    var _ante = this.milliseconds;
     switch(typeof _milliseconds) {
         case "string":
-            this.milliseconds = parseFloat(_milliseconds);
+            this.milliseconds = parseInt(_milliseconds);
             break;
         case "number":
-            this.milliseconds = _milliseconds;
+            this.milliseconds = Math.floor(_milliseconds);
             break;
         case "object":
-            this.milliseconds = parseFloat(_milliseconds.valueOf());
+            this.milliseconds = parseInt(_milliseconds.valueOf());
             break;
         default:
             this.milliseconds = 0;
@@ -329,7 +405,8 @@
     return {
         hours : Math.floor(_totalSeconds / 3600),
         minutes : (Math.floor(_totalSeconds / 60) % 60),
-        seconds : _totalSeconds % 60
+        seconds : _totalSeconds % 60,
+        milliseconds: this.milliseconds % 1000
     } 
 }
 
@@ -341,13 +418,16 @@
     return this.milliseconds;
 }
 
-Model.Time.prototype.toString = function() {
+Model.Time.prototype.toString = function(showCs) {
     var _hms = this.getHMS(),
         _res = '';
     if (_hms.hours) {
         _res += _hms.hours + ':'
     }
     _res += pad(2, _hms.minutes) + ':' + pad(2, _hms.seconds);
+    if (showCs) {
+        _res += "." + Math.round(_hms.milliseconds / 100)
+    }
     return _res;
 }
 
@@ -394,18 +474,20 @@
 
 Model.Element = function(_id, _source) {
     this.elementType = 'element';
+    this.title = "";
+    this.description = "";
+    this.__events = {}
     if (typeof _source === "undefined") {
         return;
     }
     if (typeof _id === "undefined" || !_id) {
         _id = Model.getUID();
     }
+    this.id = _id;
     this.source = _source;
-    this.id = _id;
-    this.title = "";
-    this.description = "";
-    this.__events = {}
-    this.source.directory.addElement(this);
+    if (_source !== this) {
+        this.source.directory.addElement(this);
+    }
 }
 
 Model.Element.prototype.toString = function() {
@@ -474,6 +556,20 @@
     });
     this.on("timeupdate", function(_time) {
         _this.currentTime = _time;
+        _this.getAnnotations().filter(function(_a) {
+            return (_a.end <= _time || _a.begin > _time) && _a.playing
+        }).forEach(function(_a) {
+            _a.playing = false;
+            _a.trigger("leave");
+            _this.trigger("leave-annotation",_a);
+        });
+        _this.getAnnotations().filter(function(_a) {
+            return _a.begin <= _time && _a.end > _time && !_a.playing
+        }).forEach(function(_a) {
+            _a.playing = true;
+            _a.trigger("enter");
+            _this.trigger("enter-annotation",_a);
+        });
     });
 }
 
@@ -515,6 +611,9 @@
     this.trigger("setpause");
 }
 
+Model.Playable.prototype.show = function() {}
+
+Model.Playable.prototype.hide = function() {}
 
 /* */
 
@@ -523,22 +622,7 @@
     this.elementType = 'media';
     this.duration = new Model.Time();
     this.video = '';
-    
     var _this = this;
-    this.on("timeupdate", function(_time) {
-        _this.getAnnotations().filter(function(_a) {
-            return (_a.end <= _time || _a.begin > _time) && _a.playing
-        }).forEach(function(_a) {
-            _a.playing = false;
-            _a.trigger("leave");
-        });
-        _this.getAnnotations().filter(function(_a) {
-            return _a.begin <= _time && _a.end > _time && !_a.playing
-        }).forEach(function(_a) {
-            _a.playing = true;
-            _a.trigger("enter");
-        });
-    });
 }
 
 Model.Media.prototype = new Model.Playable();
@@ -610,11 +694,17 @@
 Model.Annotation.prototype.setBegin = function(_beginMs) {
     this.begin.setMilliseconds(Math.max(0,_beginMs));
     this.trigger("change-begin");
+    if (this.end < this.begin) {
+        this.setEnd(this.begin);
+    }
 }
 
 Model.Annotation.prototype.setEnd = function(_endMs) {
-    this.end.setMilliseconds(Math.min(_endMs));
+    this.end.setMilliseconds(Math.min(_endMs, this.getMedia().duration.milliseconds));
     this.trigger("change-end");
+    if (this.end < this.begin) {
+        this.setBegin(this.end);
+    }
 }
 
 Model.Annotation.prototype.setDuration = function(_durMs) {
@@ -659,8 +749,9 @@
     Model.Element.call(this, _mashup.id + "_" + _annotation.id, _annotation.source);
     this.elementType = 'mashedAnnotation';
     this.annotation = _annotation;
-    this.begin = new Model.Time(_mashup.duration);
-    this.end = new Model.Time(_mashup.duration + _annotation.getDuration());
+    this.begin = new Model.Time();
+    this.end = new Model.Time();
+    this.duration = new Model.Time();
     this.title = this.annotation.title;
     this.description = this.annotation.description;
     this.color = this.annotation.color;
@@ -668,6 +759,12 @@
     this.on("click", function() {
         _mashup.setCurrentTime(_this.begin);
     });
+    this.on("enter", function() {
+        _this.annotation.trigger("enter");
+    });
+    this.on("leave", function() {
+        _this.annotation.trigger("leave");
+    });
 }
 
 Model.MashedAnnotation.prototype = new Model.Element(null);
@@ -692,6 +789,12 @@
     return this.annotation.getDuration();
 }
 
+Model.MashedAnnotation.prototype.setBegin = function(_begin) {
+    this.begin.setMilliseconds(_begin);
+    this.duration.setMilliseconds(this.annotation.getDuration());
+    this.end.setMilliseconds(_begin + this.duration);
+}
+
 /* */
 
 Model.Mashup = function(_id, _source) {
@@ -699,55 +802,145 @@
     this.elementType = 'mashup';
     this.duration = new Model.Time();
     this.segments = new Model.List(_source.directory);
-    this.medias = new Model.List(_source.directory);
-    var _currentMedia = null;
+    this.loaded = false;
     var _this = this;
-    this.on("timeupdate", function(_time) {
-        _this.getAnnotations().filter(function(_a) {
-            return (_a.end <= _time || _a.begin > _time) && _a.playing
-        }).forEach(function(_a) {
-            _a.playing = false;
-            _a.trigger("leave");
-        });
-        _this.getAnnotations().filter(function(_a) {
-            return _a.begin <= _time && _a.end > _time && !_a.playing
-        }).forEach(function(_a) {
-            _a.playing = true;
-            _a.trigger("enter");
-            var _m = _a.getMedia();
-            if (_m !== _currentMedia) {
-                if (_currentMedia) {
-                    _currentMedia.trigger("leave");
-                }
-                _m.trigger("enter");
-                _currentMedia = _m;
-            }
-        });
-    });
+    this._updateTimes = function() {
+        _this.updateTimes();
+        _this.trigger("change");
+    }
+    this.on("add", this._updateTimes);
+    this.on("remove", this._updateTimes);
 }
 
 Model.Mashup.prototype = new Model.Playable();
 
-Model.Mashup.prototype.addSegment = function(_annotation) {
-    var _mashedAnnotation = new Model.MashedAnnotation(this, _annotation);
-    this.duration.setMilliseconds(_mashedAnnotation.end);
+Model.Mashup.prototype.checkLoaded = function() {
+    var loaded = !!this.segments.length;
+    this.getMedias().forEach(function(_m) {
+        loaded = loaded && _m.loaded;
+    });
+    this.loaded = loaded;
+    if (loaded) {
+        this.trigger("loadedmetadata");
+    }
+}
+
+Model.Mashup.prototype.updateTimes = function() {
+    var _time = 0;
+    this.segments.forEach(function(_segment) {
+        _segment.setBegin(_time);
+        _time = _segment.end;
+    });
+    this.duration.setMilliseconds(_time);
+}
+
+Model.Mashup.prototype.addAnnotation = function(_annotation, _defer) {
+    var _mashedAnnotation = new Model.MashedAnnotation(this, _annotation),
+        _defer = _defer || false;
     this.segments.push(_mashedAnnotation);
-    this.medias.push(_annotation.getMedia());
+    _annotation.on("change-begin", this._updateTimes);
+    _annotation.on("change-end", this._updateTimes);
+    if (!_defer) {
+        this.trigger("add");
+    }
+}
+
+Model.Mashup.prototype.addAnnotationById = function(_elId, _defer) {
+    var _annotation = this.source.getElement(_elId),
+        _defer = _defer || false;
+    if (typeof _annotation !== "undefined") {
+        this.addAnnotation(_annotation, _defer);
+    }
+}
+
+Model.Mashup.prototype.addAnnotations = function(_segments) {
+    var _this = this;
+    ns._(_segments).forEach(function(_segment) {
+        _this.addAnnotation(_segment, true);
+    });
+    this.trigger("add");
 }
 
-Model.Mashup.prototype.addSegmentById = function(_elId) {
-    var _annotation = this.source.getElement(_elId);
-    if (typeof _annotation !== "undefined") {
-        this.addSegment(_annotation);
+Model.Mashup.prototype.addAnnotationsById = function(_segments) {
+    var _this = this;
+    ns._(_segments).forEach(function(_segment) {
+        _this.addAnnotationById(_segment, true);
+    });
+    this.trigger("add");
+}
+
+Model.Mashup.prototype.removeAnnotation = function(_annotation, _defer) {
+    var _defer = _defer || false;
+    _annotation.off("change-begin", this._updateTimes);
+    _annotation.off("change-end", this._updateTimes);
+    this.segments.removeId(this.id + "_" + _annotation.id);
+    if (!_defer) {
+        this.trigger("remove");
+    }
+}
+
+Model.Mashup.prototype.removeAnnotationById = function(_annId, _defer) {
+    var _defer = _defer || false;
+    var _annotation = this.source.getElement(_annId);
+
+    if (_annotation) {
+        this.removeAnnotation(_annotation, _defer);
+    }
+    if (!_defer) {
+        this.trigger("remove");
     }
 }
 
+Model.Mashup.prototype.setAnnotations = function(_segments) {
+    while (this.segments.length) {
+        this.removeAnnotation(this.segments[0].annotation, true);
+    }
+    this.addAnnotations(_segments);
+}
+
+Model.Mashup.prototype.setAnnotationsById = function(_segments) {
+    while (this.segments.length) {
+        this.removeAnnotation(this.segments[0].annotation, true);
+    }
+    this.addAnnotationsById(_segments);
+}
+
+Model.Mashup.prototype.hasAnnotation = function(_annotation) {
+    return !!ns._(this.segments).find(function(_s) {
+        return _s.annotation === _annotation
+    });
+}
+
+Model.Mashup.prototype.getAnnotation = function(_annotation) {
+    return ns._(this.segments).find(function(_s) {
+        return _s.annotation === _annotation
+    });
+}
+
+Model.Mashup.prototype.getAnnotationById = function(_id) {
+    return ns._(this.segments).find(function(_s) {
+        return _s.annotation.id === _id
+    });
+}
+
 Model.Mashup.prototype.getAnnotations = function() {
     return this.segments;
 }
 
+Model.Mashup.prototype.getOriginalAnnotations = function() {
+    var annotations = new Model.List(this.source.directory);
+    this.segments.forEach(function(_s) {
+        annotations.push(_s.annotation);
+    });
+    return annotations;
+}
+
 Model.Mashup.prototype.getMedias = function() {
-    return this.medias;
+    var medias = new Model.List(this.source.directory);
+    this.segments.forEach(function(_annotation) {
+        medias.push(_annotation.getMedia())
+    })
+    return medias;
 }
 
 Model.Mashup.prototype.getAnnotationsByTypeTitle = function(_title) {
@@ -784,6 +977,7 @@
 /* */
 
 Model.Source = function(_config) {
+    Model.Element.call(this, false, this);
     this.status = Model._SOURCE_STATUS_EMPTY;
     this.elementType = "source";
     if (typeof _config !== "undefined") {
@@ -939,10 +1133,18 @@
 
 Model.RemoteSource.prototype.get = function() {
     this.status = Model._SOURCE_STATUS_WAITING;
-    var _this = this;
-    this.serializer.loadData(this.url, function(_result) {
-        _this.deSerialize(_result);
-        _this.handleCallbacks();
+    var _this = this,
+        urlparams = this.url_params || {},
+        dataType = (Model.isLocalURL(this.url) ? "json" : "jsonp");
+    urlparams.format = dataType;
+    ns.jQuery.ajax({
+        url: this.url,
+        dataType: dataType,
+        data: urlparams,
+        success: function(_result) {
+            _this.deSerialize(_result);
+            _this.handleCallbacks();
+        }
     });
 }
 
@@ -958,10 +1160,12 @@
         throw "Error : Model.Directory.remoteSource(configuration): configuration.url is undefined";
     }
     var _config = ns._({ directory: this }).extend(_properties);
-    if (typeof this.remoteSources[_properties.url] === "undefined") {
-        this.remoteSources[_properties.url] = new Model.RemoteSource(_config);
+    _config.url_params = _config.url_params || {};
+    var _hash = _config.url + "?" + ns.jQuery.param(_config.url_params);
+    if (typeof this.remoteSources[_hash] === "undefined") {
+        this.remoteSources[_hash] = new Model.RemoteSource(_config);
     }
-    return this.remoteSources[_properties.url];
+    return this.remoteSources[_hash];
 }
 
 Model.Directory.prototype.newLocalSource = function(_properties) {
--- a/src/js/serializers/ldt.js	Fri Nov 16 17:36:56 2012 +0100
+++ b/src/js/serializers/ldt.js	Wed Nov 21 16:33:51 2012 +0100
@@ -31,16 +31,42 @@
                 }
                 return _res;        
             },
-            serializer : function(_data, _source) {
-                return {
+            serializer : function(_data, _source, _dest) {
+                var _res = {
                     id : _data.id,
                     url : _data.video,
                     meta : {
-                        "dc:title" : _data.title,
-                        "dc:description" : _data.description,
+                        "dc:title": _data.title || "",
+                        "dc:description": _data.description || "",
+                        "dc:created" : IriSP.Model.dateToIso(_data.created || _source.created),
+                        "dc:modified" : IriSP.Model.dateToIso(_data.modified || _source.modified),
+                        "dc:creator" : _data.creator || _source.creator,
+                        "dc:contributor" : _data.contributor || _source.contributor || _data.creator || _source.creator,
                         "dc:duration" : _data.duration.milliseconds
                     }
                 }
+                _dest.medias.push(_res);
+                var _list = {
+                    id: IriSP.Model.getUID(),
+                    meta : {
+                        "dc:title": _data.title || "",
+                        "dc:description": _data.description || "",
+                        "dc:created" : IriSP.Model.dateToIso(_data.created || _source.created),
+                        "dc:modified" : IriSP.Model.dateToIso(_data.modified || _source.modified),
+                        "dc:creator" : _data.creator || _source.creator,
+                        "dc:contributor" : _data.contributor || _source.contributor || _data.creator || _source.creator,
+                        "id-ref": _data.id
+                    },
+                    items: _source.getAnnotationTypes().filter(function(_at) {
+                        return _at.media === _data;
+                    }).map(function(_at) {
+                        return {
+                            "id-ref": _at.id
+                        }
+                    })
+                }
+                _dest.lists.push(_list);
+                _dest.views[0].contents.push(_data.id);
             }
         },
         tag : {
@@ -51,13 +77,19 @@
                 _res.title = _data.meta["dc:title"];
                 return _res;        
             },
-            serializer : function(_data, _source) {
-                return {
+            serializer : function(_data, _source, _dest) {
+                var _res = {
                     id : _data.id,
                     meta : {
-                        "dc:title" : _data.title
+                        "dc:title": _data.title || "",
+                        "dc:description": _data.description || "",
+                        "dc:created" : IriSP.Model.dateToIso(_data.created || _source.created),
+                        "dc:modified" : IriSP.Model.dateToIso(_data.modified || _source.modified),
+                        "dc:creator" : _data.creator || _source.creator,
+                        "dc:contributor" : _data.contributor || _source.contributor || _data.creator || _source.creator,
                     }
                 }
+                _dest.tags.push(_res);
             }
         },
         annotationType : {
@@ -68,12 +100,18 @@
                 _res.description = _data["dc:description"];
                 return _res;        
             },
-            serializer : function(_data, _source) {
-                return {
+            serializer : function(_data, _source, _dest) {
+                var _res = {
                     id : _data.id,
-                    "dc:title" : _data.title,
-                    "dc:description" : _data.description
+                    "dc:title": _data.title || "",
+                    "dc:description": _data.description || "",
+                    "dc:created" : IriSP.Model.dateToIso(_data.created || _source.created),
+                    "dc:modified" : IriSP.Model.dateToIso(_data.modified || _source.modified),
+                    "dc:creator" : _data.creator || _source.creator,
+                    "dc:contributor" : _data.contributor || _source.contributor || _data.creator || _source.creator,
                 }
+                _dest["annotation-types"].push(_res);
+                _dest.views[0].annotation_types.push(_data.id);
             }
         },
         annotation : {
@@ -96,6 +134,7 @@
                 _res.setMedia(_data.media);
                 _res.setAnnotationType(_data.meta["id-ref"]);
                 _res.setTags(IriSP._(_data.tags).pluck("id-ref"));
+                _res.keywords = _res.getTagTexts();
                 _res.setBegin(_data.begin);
                 _res.setEnd(_data.end);
                 _res.creator = _data.meta["dc:creator"] || "";
@@ -108,22 +147,29 @@
                 }
                 return _res;
             },
-            serializer : function(_data, _source) {
-                return {
+            serializer : function(_data, _source, _dest) {
+                var _color = parseInt(_data.color.replace(/^#/,''),16).toString();
+                var _res = {
                     id : _data.id,
                     begin : _data.begin.milliseconds,
                     end : _data.end.milliseconds,
                     content : {
-                        title : _data.title,
-                        description : _data.description,
-                        audio : _data.audio
+                        title : _data.title || "",
+                        description : _data.description || "",
+                        audio : _data.audio,
+                        img: {
+                            src: _data.thumbnail
+                        }
                     },
+                    color: _color,
                     media : _data.media.id,
                     meta : {
-                        "id-ref" : _data.annotationType.id,
-                        "dc:created" : IriSP.Model.dateToIso(_data.created),
-                        "dc:creator" : _data.creator,
-                        project : _source.projectId
+                        "id-ref" : _data.getAnnotationType().id,
+                        "dc:created" : IriSP.Model.dateToIso(_data.created || _source.created),
+                        "dc:modified" : IriSP.Model.dateToIso(_data.modified || _source.modified),
+                        "dc:creator" : _data.creator || _source.creator,
+                        "dc:contributor" : _data.contributor || _source.contributor || _data.creator || _source.creator,
+//                        project : _source.projectId
                     },
                     tags : IriSP._(_data.tag.id).map(function(_id) {
                        return {
@@ -131,6 +177,7 @@
                        } 
                     })
                 }
+                _dest.annotations.push(_res);
             }
         },
         mashup : {
@@ -142,41 +189,64 @@
                 var _res = new IriSP.Model.Mashup(_data.id, _source);
                 _res.title = _data.meta["dc:title"];
                 _res.description = _data.meta["dc:description"];
-                for (var _i = 0; _i < _data.items.length; _i++) {
-                    _res.addSegmentById(_data.items[_i]);
-                }
+                _res.creator = _data.meta["dc:creator"];
+                _res.setAnnotationsById(_data.items);
                 return _res;        
             },
-            serializer : function(_data, _source) {
-                return {
+            serializer : function(_data, _source, _dest) {
+                var _res = {
                     meta : {
-                        "dc:title": _data.title,
-                        "dc:description": _data.description,
+                        "dc:title": _data.title || "",
+                        "dc:description": _data.description || "",
+                        "dc:created" : IriSP.Model.dateToIso(_data.created || _source.created),
+                        "dc:modified" : IriSP.Model.dateToIso(_data.modified || _source.modified),
+                        "dc:creator" : _data.creator || _source.creator,
+                        "dc:contributor" : _data.contributor || _source.contributor || _data.creator || _source.creator,
                         listtype: "mashup"
                     },
                     items: _data.segments.map(function(_annotation) {
-                        return _id;
+                        return _annotation.annotation.id;
                     }),
                     id: _data.id
                 }
+                _dest.lists.push(_res);
             }
         }
     },
     serialize : function(_source) {
-        var _res = {},
+        var _res = {
+                meta: {
+                    "dc:creator": _source.creator,
+                    "dc:contributor" : _source.contributor || _source.creator,
+                    "dc:created": IriSP.Model.dateToIso(_source.created),
+                    "dc:modified" : IriSP.Model.dateToIso(_source.modified),
+                    "dc:title": _source.title || "",
+                    "dc:description": _source.description || "",
+                    id: _source.projectId || _source.id
+                },
+                views: [
+                    {
+                        id: IriSP.Model.getUID(),
+                        contents: [],
+                        annotation_types: []
+                    }
+                ],
+                lists: [],
+                "annotation-types": [],
+                medias: [],
+                tags: [],
+                annotations: []
+            },
             _this = this;
         _source.forEach(function(_list, _typename) {
             if (typeof _this.types[_typename] !== "undefined") {
-                _res[_this.types[_typename].serialized_name] = _list.map(function(_el) {
-                    return _this.types[_typename].serializer(_el, _source);
+                _list.forEach(function(_el) {
+                    _this.types[_typename].serializer(_el, _source, _res);
                 });
             }
         });
         return JSON.stringify(_res);
     },
-    loadData : function(_url, _callback) {
-        IriSP.jQuery.getJSON(_url, _callback)
-    },
     deSerialize : function(_data, _source) {
         if (typeof _data !== "object" || _data === null) {
             return;
--- a/src/widgets/Annotation.css	Fri Nov 16 17:36:56 2012 +0100
+++ b/src/widgets/Annotation.css	Wed Nov 21 16:33:51 2012 +0100
@@ -6,6 +6,10 @@
     margin: 0;
 }
 
+.Ldt-Annotation-Highlight {
+    background: #ffa0fc;
+}
+
 .Ldt-Annotation-Widget.Ldt-Annotation-ShowTop {
     border-top-style: solid;
     padding-top: 1px;
@@ -15,6 +19,8 @@
     background: url(img/pinstripe.png);
     padding: 5px;
     margin: 0;
+    max-height: 300px;
+    overflow: auto;
 }
 
 .Ldt-Annotation-Inner h3 {
@@ -66,6 +72,7 @@
 
 .Ldt-Annotation-Inner p {
     font-size: 12px;
+    line-height: 16px;
 }
 
 .Ldt-Annotation-Label {
--- a/src/widgets/Annotation.js	Fri Nov 16 17:36:56 2012 +0100
+++ b/src/widgets/Annotation.js	Wed Nov 21 16:33:51 2012 +0100
@@ -56,7 +56,8 @@
 
 IriSP.Widgets.Annotation.prototype.draw = function() {
     
-    var _this = this;
+    var _this = this,
+        currentAnnotation;
     
     function timeupdate(_time) {
         var _list = _this.getWidgetAnnotationsAtTime();
@@ -70,7 +71,29 @@
         }
     }
     
+    function highlightTitleAndDescription() {
+        if (!currentAnnotation) {
+            return;
+        }
+        var title = currentAnnotation.title,
+            description = currentAnnotation.description.replace(/(^\s+|\s+$)/g,'');
+        if (currentAnnotation.found) {
+            var rgxp = _this.source.getAnnotations().regexp || /^$/,
+                repl = '<span class="Ldt-Annotation-Highlight">$1</span>';
+            title = title.replace(rgxp,repl);
+            description = description.replace(rgxp,repl);
+        }
+        _this.$.find(".Ldt-Annotation-Title").html(title || "(" + _this.l10n.untitled + ")");
+        if (description) {
+            _this.$.find(".Ldt-Annotation-Description-Block").removeClass("Ldt-Annotation-EmptyBlock");
+            _this.$.find(".Ldt-Annotation-Description").html(description);
+        } else {
+            _this.$.find(".Ldt-Annotation-Description-Block").addClass("Ldt-Annotation-EmptyBlock");
+        }
+    }
+    
     function drawAnnotation(_annotation) {
+        currentAnnotation = _annotation;
         var _url = (typeof _annotation.url !== "undefined" 
                 ? _annotation.url
                 : (document.location.href.replace(/#.*$/,'') + '#id='  + _annotation.id)),
@@ -85,7 +108,7 @@
                     var _el = IriSP.jQuery('<li class="Ldt-Annotation-TagLabel"></li>').append(IriSP.jQuery('<span>').text(_trimmedTitle));
                     _el.click(function() {
                         if (_this.search_on_tag_click) {
-                            _this.player.trigger("search.triggeredSearch",_trimmedTitle);
+                            _this.source.getAnnotations().search(_trimmedTitle);
                         }
                         _tag.trigger("click");
                     });
@@ -95,14 +118,7 @@
         } else {
             _this.$.find(".Ldt-Annotation-Tags-Block").addClass("Ldt-Annotation-EmptyBlock");
         }
-        _this.$.find(".Ldt-Annotation-Title").text(_annotation.title || "(" + _this.l10n.untitled + ")");
-        var _desc = _annotation.description.replace(/(^\s+|\s+$)/g,'');
-        if (_desc) {
-            _this.$.find(".Ldt-Annotation-Description-Block").removeClass("Ldt-Annotation-EmptyBlock");
-            _this.$.find(".Ldt-Annotation-Description").html(_desc);
-        } else {
-            _this.$.find(".Ldt-Annotation-Description-Block").addClass("Ldt-Annotation-EmptyBlock");
-        }
+        highlightTitleAndDescription();
         if (_this.show_annotation_type) {
             _this.$.find(".Ldt-Annotation-Type").text(_annotation.getAnnotationType().title)
         }
@@ -135,7 +151,7 @@
     
     this.renderTemplate();
     
-    if (_this.show_social) {
+    if (this.show_social) {
         this.insertSubwidget(this.$.find(".Ldt-Annotation-Social"), { type: "Social" }, "socialWidget");
     }
     
@@ -152,6 +168,9 @@
             drawAnnotation(_a)
         });
     });
+    this.source.getAnnotations().on("found", highlightTitleAndDescription);
+    this.source.getAnnotations().on("not-found", highlightTitleAndDescription);
+    this.source.getAnnotations().on("search-cleared", highlightTitleAndDescription);
 }
 
 IriSP.Widgets.Annotation.prototype.sendBounds = function() {
--- a/src/widgets/AnnotationsList.css	Fri Nov 16 17:36:56 2012 +0100
+++ b/src/widgets/AnnotationsList.css	Wed Nov 21 16:33:51 2012 +0100
@@ -29,8 +29,7 @@
     background-image: url(img/pinstripe-grey.png);
 }
 .Ldt-AnnotationsList-highlight {
-    background: #F7268E;
-    color: #ffffff;
+    background: #FFA0FC;
 }
 .Ldt-AnnotationsList-ThumbContainer {
     float: left;
--- a/src/widgets/AnnotationsList.js	Fri Nov 16 17:36:56 2012 +0100
+++ b/src/widgets/AnnotationsList.js	Wed Nov 21 16:33:51 2012 +0100
@@ -1,11 +1,12 @@
 IriSP.Widgets.AnnotationsList = function(player, config) {
     IriSP.Widgets.Widget.call(this, player, config);
-    this.searchString = false;
     this.lastIds = [];
     var _this = this;
     this.throttledRefresh = IriSP._.throttle(function() {
         _this.refresh(false);
-    }, 1500);
+    }, 800);
+    this.searchString = false;
+    this.lastSearch = false;
 };
 
 IriSP.Widgets.AnnotationsList.prototype = new IriSP.Widgets.Widget();
@@ -87,18 +88,6 @@
     + '{{#audio}}<div class="Ldt-AnnotationsList-Play" data-audio={{audio}}>{{l10n.voice_annotation}}</div>{{/audio}}'
     + '</li>';
 
-IriSP.Widgets.AnnotationsList.prototype.onSearch = function(searchString) {
-    this.searchString = typeof searchString !== "undefined" ? searchString : '';
-    var _n = this.refresh(true);
-    if (this.searchString) {
-        if (_n) {
-            this.player.trigger("search.matchFound");
-        } else {
-            this.player.trigger("search.noMatchFound");
-        }
-    }
-}
-
 //obj.url = this.project_url + "/" + media + "/" + annotations[i].meta.project + "/" + annotations[i].meta["id-ref"] + '#id=' + annotations[i].id;
 
 IriSP.Widgets.AnnotationsList.prototype.ajaxSource = function() {
@@ -150,9 +139,9 @@
             });
         }
     }
-    if (this.searchString) {
-        _list = _list.searchByTextFields(this.searchString);
-    }
+    _list = _list.filter(function(_annotation) {
+        return _annotation.found !== false;
+    });
     if (this.limit_count) {
         /* Get the n annotations closest to current timecode */
         _list = _list.sortBy(function(_annotation) {
@@ -171,8 +160,9 @@
     
     var _ids = _list.idIndex;
     
-    if (_forceRedraw || !IriSP._.isEqual(_ids, this.lastIds)) {
+    if (_forceRedraw || !IriSP._.isEqual(_ids, this.lastIds) || this.searchString !== this.lastSearch) {
         /* This part only gets executed if the list needs updating */
+        this.lastSearch = this.searchString;
         this.lastIds = _ids;
         this.list_$.html("");
         _list.forEach(function(_annotation) {
@@ -258,7 +248,7 @@
         });
     
         this.$.find('.Ldt-AnnotationsList-Tag-Li').click(function() {
-            _this.player.trigger("search.triggeredSearch", IriSP.jQuery(this).text().replace(/(^\s+|\s+$)/g,''));
+            _this.source.getAnnotations().search(IriSP.jQuery(this).text().replace(/(^\s+|\s+$)/g,''));
         });
         
         this.$.find(".Ldt-AnnotationsList-Play").click(function() {
@@ -274,11 +264,10 @@
             _this.jw_paused_media = true;
         });
         
-        if(this.searchString) {
-            var _searchRe = IriSP.Model.regexpFromTextOrArray(this.searchString);
+        if (this.source.getAnnotations().searching) {
             this.$.find(".Ldt-AnnotationsList-Title a, .Ldt-AnnotationsList-Description").each(function() {
                 var _$ = IriSP.jQuery(this);
-                _$.html(_$.text().replace(/(^\s+|\s+$)/g,'').replace(_searchRe, '<span class="Ldt-AnnotationsList-highlight">$1</span>'))
+                _$.html(_$.text().replace(/(^\s+|\s+$)/g,'').replace(_this.source.getAnnotations().regexp, '<span class="Ldt-AnnotationsList-highlight">$1</span>'))
             })
         }
     }
@@ -328,9 +317,29 @@
     this.list_$ = this.$.find(".Ldt-AnnotationsList-ul");
     
     
-    this.onMdpEvent("search", "onSearch");
-    this.onMdpEvent("search.closed", "onSearch");
-    this.onMdpEvent("search.cleared", "onSearch");
+    this.source.getAnnotations().on("search", function(_text) {
+        _this.searchString = _text;
+        if (_this.source !== _this.currentSource) {
+            _this.currentSource.getAnnotations().search(_text);
+            _this.throttledRefresh();
+        }
+    });
+    this.source.getAnnotations().on("found", function() {
+        _this.throttledRefresh();
+    });
+    this.source.getAnnotations().on("not-found", function() {
+        _this.throttledRefresh();
+    });
+    this.source.getAnnotations().on("clear-search", function() {
+        _this.searchString = false;
+        if (_this.source !== _this.currentSource) {
+            _this.currentSource.getAnnotations().trigger("clear-search");
+        }
+    });
+    this.source.getAnnotations().on("search-cleared", function() {
+        _this.throttledRefresh();
+    });
+    
     this.onMdpEvent("AnnotationsList.refresh", function() {
         if (_this.ajax_url) {
             if (_this.mashupMode) {
--- a/src/widgets/Controller.js	Fri Nov 16 17:36:56 2012 +0100
+++ b/src/widgets/Controller.js	Wed Nov 21 16:33:51 2012 +0100
@@ -92,10 +92,6 @@
     this.onMediaEvent("volumechange","volumeUpdater");
     this.onMediaEvent("timeupdate","timeDisplayUpdater");
     this.onMediaEvent("loadedmetadata","volumeUpdater");
-    this.onMdpEvent("search.matchFound","searchMatch");
-    this.onMdpEvent("search.noMatchFound","searchNoMatch");
-    this.onMdpEvent("search.triggeredSearch","triggeredSearch");
-    this.onMdpEvent("search.cleared","hideSearchBlock");
     
     // handle clicks
     this.$playButton.click(this.functionWrapper("playHandler"));
@@ -157,6 +153,21 @@
         });
     
     this.timeDisplayUpdater(new IriSP.Model.Time(0));
+    
+    var annotations = this.source.getAnnotations();
+    annotations.on("search", function(_text) {
+        _this.$searchInput.val(_text);
+        _this.showSearchBlock();
+    });
+    annotations.on("found", function(_text) {
+        _this.$searchInput.css('background-color','#e1ffe1');
+    });
+    annotations.on("not-found", function(_text) {
+        _this.$searchInput.css('background-color', "#d62e3a");
+    });
+    annotations.on("search-cleared", function() {
+        _this.hideSearchBlock();
+    });
    
 };
 
@@ -222,23 +233,11 @@
 IriSP.Widgets.Controller.prototype.showSearchBlock = function() {
     this.$searchBlock.animate({ width:"160px" }, 200);
     this.$searchInput.css('background-color','#fff');
-   
     this.$searchInput.focus();
-    
-    // we need this variable because some widgets can find a match in
-    // their data while at the same time others don't. As we want the
-    // search field to become green when there's a match, we need a 
-    // variable to remember that we had one.
-    this._positiveMatch = false;
-
-    // tell the world the field is open
-    this.player.trigger("search.open");
 };
 
 IriSP.Widgets.Controller.prototype.hideSearchBlock = function() {
     this.$searchBlock.animate( { width: 0 }, 200);
-    this._positiveMatch = false;
-    this.player.trigger("search.closed");
 };
 
 /** react to clicks on the search button */
@@ -247,7 +246,7 @@
         this.showSearchBlock();
         var _val = this.$searchInput.val();
         if (_val) {
-            this.player.trigger("search", _val); // trigger the search to make it more natural.
+            this.source.getAnnotations().search(_val);
         }
 	} else {
         this.hideSearchBlock();
@@ -267,37 +266,12 @@
     // do nothing if the search field is empty, instead of highlighting everything.
     if (_val !== this.lastSearchValue) {
         if (_val) {
-            this.player.trigger("search", _val);
+            this.source.getAnnotations().search(_val);
         } else {
-            this.player.trigger("search.cleared");
+            this.source.getAnnotations().trigger("clear-search");
             this.$searchInput.css('background-color','');
         }
     }
     this.lastSearchValue = _val;
 };
 
-/**
-  handler for the IriSP.search.found message, which is sent by some views when they
-  highlight a match.
-*/
-IriSP.Widgets.Controller.prototype.searchMatch = function() {
-    this._positiveMatch = true;
-    this.$searchInput.css('background-color','#e1ffe1');
-};
-
-/** the same, except that no value could be found */
-IriSP.Widgets.Controller.prototype.searchNoMatch = function() {
-    if (this._positiveMatch !== true) {
-        this.$searchInput.css('background-color', "#d62e3a");
-    }
-};
-
-/** react to an IriSP.Player.triggeredSearch - that is, when
-    a widget ask the.Player to do a search on his behalf */
-IriSP.Widgets.Controller.prototype.triggeredSearch = function(searchString) {
-    this.showSearchBlock();
-    this.$searchInput.attr('value', searchString);      
-    this.player.trigger("search", searchString); // trigger the search to make it more natural.
-};
-
-
--- a/src/widgets/KnowledgeConcierge.css	Fri Nov 16 17:36:56 2012 +0100
+++ b/src/widgets/KnowledgeConcierge.css	Wed Nov 21 16:33:51 2012 +0100
@@ -10,6 +10,27 @@
     display: none;
 }
 
+.Ldt-Kc-Related h2 {
+    border: none;
+    color: #330099;
+    font-size: 18px;
+    margin: 8px 0 2px;
+    padding: 0 5px;
+}
+
+h3.Ldt-Kc-For-Keywords {
+    border-bottom: 1px solid #666666;
+    color: #000000;
+    font-size: 12px;
+    margin: 2px 0 5px;
+    padding: 0 5px 5px;
+    text-align: right;
+}
+
+.Ldt-Kc-Keywords {
+    color: #d000c0; font-weight: bold;
+}
+
 .Ldt-Kc-Related-Item {
     width: 235px; float: left; margin: 4px 0; padding: 4px 0;
 }
--- a/src/widgets/KnowledgeConcierge.js	Fri Nov 16 17:36:56 2012 +0100
+++ b/src/widgets/KnowledgeConcierge.js	Wed Nov 21 16:33:51 2012 +0100
@@ -13,17 +13,19 @@
     related_api_endpoint: "",
     use_word_boundaries: false,
     related_data_type: 'json', // SET TO "jsonp" FOR CROSS-DOMAIN OPERATION
-    related_count: 8
+    related_count: 8,
 }
 
 IriSP.Widgets.KnowledgeConcierge.prototype.messages = {
     "fr": {
         related_videos: "Vidéos liées",
-        duration_: "Durée&nbsp;:"
+        duration_: "Durée&nbsp;:",
+        for_keywords_: "pour le(s) mots-clé(s)&nbsp;:"
     },
     "en": {
         related_videos: "Related Videos",
-        duration_: "Duration:"
+        duration_: "Duration:",
+        for_keywords_: "for keyword(s):"
     }
 }
 
@@ -31,6 +33,7 @@
     '<div class="Ldt-Kc-Slider"></div><canvas class="Ldt-Kc-Canvas" />'
     + '<div class="Ldt-Kc-Waiting"></div>'
     + '<div class="Ldt-Kc-Related"><h2>{{ l10n.related_videos }}</h2>'
+    + '<h3 class="Ldt-Kc-For-Keywords">{{l10n.for_keywords_}} <span class="Ldt-Kc-Keywords"></span></h3>'
     + '<div class="Ldt-Kc-Related-List"></div></div>';
 
 IriSP.Widgets.KnowledgeConcierge.prototype.draw = function() {
@@ -54,6 +57,7 @@
         _selectedText = "",
         currentNodesList = "",
         relatedCache = {},
+        relatedRequests = {},
         relatedTemplate = '<div class="Ldt-Kc-Related-Item"><a href="{{ widget.video_url_base }}{{ media.iri_id }}"><img src="{{ media.image }}"></a>'
             + '<h3><a href="{{ widget.video_url_base }}{{ media.iri_id }}">{{ media.title }}</a></h3><p>{{ description }}</p>'
             + '<p>{{ widget.l10n.duration_ }} <span class="Ldt-Kc-Item-Duration">{{ duration }}</span></p>'
@@ -61,9 +65,14 @@
             
     Processing.loadSketchFromSources(_canvas[0],_pjsfiles);
     
-    function renderRelated(keywords) {
+    function renderRelated() {
+        var keywords = currentNodesList;
+        if (typeof relatedCache[keywords] === "undefined") {
+            return;
+        }
         _this.$.find(".Ldt-Kc-Waiting").hide();
         if (relatedCache[keywords].length) {
+            _this.$.find(".Ldt-Kc-Keywords").html(keywords.replace(/\,/g,", "));
             var _html = '<div class="Ldt-Kc-Row">';
             IriSP._(relatedCache[keywords]).each(function(media, i) {
                 var _tmpldata = {
@@ -87,9 +96,8 @@
 
     function triggerSearch(text) {
         if (_selectedText !== text) {
-            //console.log("Trigger search for '" + text + "'");
             _selectedText = text;
-            _this.player.trigger("search.triggeredSearch", text);
+            _this.source.getAnnotations().search(text);
         }
     }
     
@@ -119,45 +127,43 @@
                             node.position(Math.floor(radius*Math.sin(2 * Math.PI * i / l)),Math.floor(radius*Math.cos(2 * Math.PI * i / l)));
                         }
                     }
-                } else {
-                    console.log("No match found");
                 }
             }
         );
     }
     
-    function listNodes() {
-        var nodes = _pjs.getNodes().values().toArray(),
-            nodetexts = IriSP._(nodes).chain().pluck("name").sortBy().value().join(",");
-        if (currentNodesList !== nodetexts) {
-            if (typeof relatedCache[nodetexts] === "undefined") {
-                _this.$.find(".Ldt-Kc-Waiting").show();
-                _this.$.find(".Ldt-Kc-Related").hide();
-                IriSP.jQuery.ajax({
-                    url: _this.related_api_endpoint,
-                    data: {
-                        format: _this.related_data_type,
-                        keywords: nodetexts
-                    },
-                    dataType: _this.related_data_type,
-                    success: function(data) {
-                        relatedCache[nodetexts] = IriSP._(data.objects)
-                            .chain()
-                            .filter(function(o) {
-                                return o.iri_id !== _this.media.id;
-                            })
-                            .sortBy(function(o) {
-                                return - o.score;
-                            })
-                            .first(_this.related_count)
-                            .value();
-                        renderRelated(nodetexts);
-                    }
-                })
-            } else {
-                renderRelated(nodetexts);
+    function showRelated(nodetexts) {
+        currentNodesList = nodetexts;
+        if (typeof relatedCache[nodetexts] === "undefined") {
+            _this.$.find(".Ldt-Kc-Waiting").show();
+            _this.$.find(".Ldt-Kc-Related").hide();
+            if (relatedRequests[nodetexts]) {
+                return;
             }
-            currentNodesList = nodetexts;
+            relatedRequests[nodetexts] = true;
+            IriSP.jQuery.ajax({
+                url: _this.related_api_endpoint,
+                data: {
+                    format: _this.related_data_type,
+                    keywords: nodetexts
+                },
+                dataType: _this.related_data_type,
+                success: function(data) {
+                    relatedCache[nodetexts] = IriSP._(data.objects)
+                        .chain()
+                        .filter(function(o) {
+                            return o.iri_id !== _this.media.id;
+                        })
+                        .sortBy(function(o) {
+                            return - o.score;
+                        })
+                        .first(_this.related_count)
+                        .value();
+                    renderRelated();
+                }
+            })
+        } else {
+            renderRelated();
         }
     }
     
@@ -173,10 +179,9 @@
                     item = response.items[0];
                     _pjs.initNode(item.id, item.name, item.grp, item.uid, item.proj);
                     _fns.countassoc(item.id, item.proj);
-                } else {
-                    console.debug('No such topic.');
                 }
-        });
+            }
+        );
     }
     
     function bindJavascript() {
@@ -189,9 +194,6 @@
                     _teamMode = true;
                 _pjs.saveMode("en",false,_teamMode,false,"both",_edit);
                 rootNode(_this.topic_id, _this.project_id);
-                _canvas.click(function() {
-                    triggerSearch("")
-                });
                 _slider.slider({
                     min: -20,
                     max: 20,
@@ -206,10 +208,9 @@
             setTimeout(bindJavascript, 1000); 
         }
     }
-    
+    var currentSelection = null, lockMode = false;
     var _fns = {
         adjacentnodes: function(id, proj, adj, both) {
-            //console.log("Function adjacentnodes called with", arguments);
             jQuery.ajax({
                 url: _this.kc_api_root + "associations-bd.jsp",
                 cache: false,
@@ -236,7 +237,6 @@
                                 _pjs.setNodeAssoc(item.to_id, item.to_proj, item.to_assoc);
                             }
                         }
-                        listNodes();
                         return response;
                     } else {
                         //.debug('No such topic.');
@@ -246,11 +246,9 @@
             });
         },
         setscale: function(scl){
-            //console.log("Function setscale called with", arguments);
             _slider.slider("value", 10*Math.log(scl));
         },
         countassoc: function(id, proj) {
-            //console.log("Fonction countassoc called with", arguments);
             jQuery.ajax({
                 url: _this.kc_api_root + "count-assoc.jsp",
                 data: {
@@ -265,23 +263,38 @@
                             if (item.assoc!=null) _pjs.setNodeAssoc(item.id, item.proj, item.assoc);
                             if (item.mass!=null) _pjs.setNodeMass( item.id, item.proj, item.mass);
                         }
-                    } else {
-                        console.debug('No such topic.');
                     }
                 }
             });
         },
-        new_select: function(id, proj) {
-            var node = _pjs.findNode(id, proj);
-            triggerSearch(node.name);
-            //console.log("Mouse over node named '" + node.name + "'");
+        username: function() {
+            var nodes = _pjs.getNodes().values().toArray(),
+                nodetexts = IriSP._(nodes).chain().pluck("name").sortBy().value().join(",");
+            showRelated(nodetexts);
         },
-        username: listNodes
+        mousemove: function(selection) {
+            if (selection !== currentSelection) {
+                if (selection && !lockMode) {
+                    triggerSearch(selection.name);
+                }
+                currentSelection = selection;
+            }
+        },
+        click: function(selection) {
+            if (selection) {
+                lockMode = true;
+                triggerSearch(selection.name);
+                showRelated(selection.name);
+            } else {
+                lockMode = false;
+                triggerSearch()
+            }
+        }
     }
     var uselessfuncts = [
         "selectnode", "selectedge", "topicnode","group_shapes",
         "allbackup", "allretrieve", "new_topic", "pedia", "set_mode",
-        "new_relation", "startexpand", "endexpand" //, "username"
+        "new_relation", "startexpand", "endexpand", "new_select" //, "mouseover" //, "username"
     ];
     
     IriSP._(uselessfuncts).each(function(funcname) {
--- a/src/widgets/MashupPlayer.js	Fri Nov 16 17:36:56 2012 +0100
+++ b/src/widgets/MashupPlayer.js	Wed Nov 21 16:33:51 2012 +0100
@@ -24,7 +24,8 @@
         _timecode = 0,
         _seeking = false,
         _seekdiv,
-        _timedelta;
+        _timedelta,
+        medialist = _mashup.getMedias();
     
     _mashup.paused = (!this.autostart && !this.autoplay)
     
@@ -54,14 +55,14 @@
             _timedelta = _segmentBegin - _currentAnnotation.begin.milliseconds;
             _currentMedia = _currentAnnotation.getMedia();
             
-            for (var _i = 0; _i < _mashup.medias.length; _i++) {
-                if (_mashup.medias[_i].id !== _currentMedia.id) {
+            for (var _i = 0; _i < medialist.length; _i++) {
+                if (medialist[_i].id !== _currentMedia.id) {
                     if (!_this.split_screen) {
-                        _mashup.medias[_i].hide();
+                        medialist[_i].hide();
                     }
-                    _mashup.medias[_i].pause();
+                    medialist[_i].pause();
                 } else {
-                    _mashup.medias[_i].show();
+                    medialist[_i].show();
                 }
             }
             
@@ -113,11 +114,11 @@
         background: this.background
     });
     
-    var _grid = Math.ceil(Math.sqrt(_mashup.medias.length)),
+    var _grid = Math.ceil(Math.sqrt(medialist.length)),
         _width = (this.split_screen ? this.width / _grid : this.width),
-        _height = (this.split_screen ? this.height / _grid : this.height)
+        _height = (this.split_screen ? this.height / _grid : this.height);
 
-    IriSP._(_mashup.medias).each(function(_media, _key) {
+    IriSP._(medialist).each(function(_media, _key) {
         var _el = IriSP.jQuery('<div class="Ldt-MashupPlayer-Media"><div class="Ldt-MashupPlayer-Subwidget"></div></div>');
         _el.css({
             top: (_this.split_screen ? _height * Math.floor(_key / _grid) : 0),
@@ -149,8 +150,8 @@
         _media.on("loadedmetadata", function() {
             _media.loadedMetadata = true;
             var _allLoaded = true;
-            for (var _i = 0; _i < _mashup.medias.length; _i++) {
-                _allLoaded = _allLoaded && _mashup.medias[_i].loadedMetadata;
+            for (var _i = 0; _i < medialist.length; _i++) {
+                _allLoaded = _allLoaded && medialist[_i].loadedMetadata;
             }
             if (_allLoaded) {
                 _seekdiv.fadeOut();
@@ -227,15 +228,15 @@
     });
     
     _mashup.on("setvolume", function(_vol) {
-        for (var _i = 0; _i < _mashup.medias.length; _i++) {
-            _mashup.medias[_i].setVolume(_vol);
+        for (var _i = 0; _i < medialist.length; _i++) {
+            medialist[_i].setVolume(_vol);
         }
         _mashup.volume = _vol;
     });
     
     _mashup.on("setmuted", function(_muted) {
-        for (var _i = 0; _i < _mashup.medias.length; _i++) {
-            _mashup.medias[_i].setMuted(_muted);
+        for (var _i = 0; _i < medialist.length; _i++) {
+            medialist[_i].setMuted(_muted);
         }
         _mashup.muted = _muted;
     });
--- a/src/widgets/MediaList.js	Fri Nov 16 17:36:56 2012 +0100
+++ b/src/widgets/MediaList.js	Wed Nov 21 16:33:51 2012 +0100
@@ -61,10 +61,8 @@
     this.renderTemplate();
     var _this = this;
     if (typeof this.media.getMedias === "function") {
-        this.media.getMedias().forEach(function(_m) {
-            _m.on("enter", function() {
-                _this.redraw(_m);
-            });
+        this.media.on("enter-annotation", function(_a) {
+            _this.redraw(_a.getMedia());
         })
     }
     this.redraw();
--- a/src/widgets/Polemic.js	Fri Nov 16 17:36:56 2012 +0100
+++ b/src/widgets/Polemic.js	Wed Nov 21 16:33:51 2012 +0100
@@ -47,42 +47,6 @@
     ]
 };
 
-IriSP.Widgets.Polemic.prototype.onSearch = function(searchString) {
-    this.searchString = typeof searchString !== "undefined" ? searchString : '';
-    var _found = 0,
-        _re = IriSP.Model.regexpFromTextOrArray(searchString, true),
-        _this = this;
-    this.$tweets.each(function() {
-        var _el = IriSP.jQuery(this);
-        if (_this.searchString) {
-            if (_re.test(_el.attr("tweet-title"))) {
-                _el.css({
-                    "background" : _this.foundcolor,
-                    "opacity" : 1
-                });
-                _found++;
-            } else {
-                _el.css({
-                    "background" : _el.attr("polemic-color"),
-                    "opacity" : .3
-                });
-            }
-        } else {
-            _el.css({
-                "background" : _el.attr("polemic-color"),
-                "opacity" : 1
-            });
-        }
-    });
-    if (this.searchString) {
-        if (_found) {
-            this.player.trigger("search.matchFound");
-        } else {
-            this.player.trigger("search.noMatchFound");
-        }
-    }
-}
-
 IriSP.Widgets.Polemic.prototype.draw = function() {
     
     this.onMediaEvent("timeupdate", "onTimeupdate");
@@ -189,6 +153,18 @@
                     _this.tooltip.hide();
                     _this.$tweets.css("opacity",1);
                 });
+                _annotation.on("found", function() {
+                    _el.css({
+                        "background" : _this.foundcolor,
+                        "opacity" : 1
+                    });
+                });
+                _annotation.on("not-found", function() {
+                    _el.css({
+                        "background" : _col,
+                        "opacity" : .3
+                    });
+                });
                 _this.$zone.append(_el);
             }
             
@@ -213,9 +189,15 @@
             
             this.$tweets = this.$.find(".Ldt-Polemic-TweetDiv");
             
-            this.onMdpEvent("search", "onSearch");
-            this.onMdpEvent("search.closed", "onSearch");
-            this.onMdpEvent("search.cleared", "onSearch");
+            this.source.getAnnotations().on("search-cleared", function() {
+                _this.$tweets.each(function() {
+                    var _el = IriSP.jQuery(this);
+                    _el.css({
+                        "background" : _el.attr("polemic-color"),
+                        "opacity" : 1
+                    });
+                });
+            });
             
         } else {
             this.$zone.hide();
--- a/src/widgets/Renkan.js	Fri Nov 16 17:36:56 2012 +0100
+++ b/src/widgets/Renkan.js	Wed Nov 21 16:33:51 2012 +0100
@@ -90,10 +90,10 @@
             var _tagmatch = _uri.match(_this.tag_regexp);
             if (_tagmatch) {
                 _node.on("select", function() {
-                    _this.player.trigger("search.triggeredSearch",_tagmatch[1]);
+                    _this.source.getAnnotations().search(_tagmatch[1]);
                 })
                 _node.on("unselect", function() {
-                    _this.player.trigger("search.cleared");
+                    _this.source.getAnnotations().search("");
                 })
             }
         });
--- a/src/widgets/Segments.js	Fri Nov 16 17:36:56 2012 +0100
+++ b/src/widgets/Segments.js	Wed Nov 21 16:33:51 2012 +0100
@@ -11,7 +11,9 @@
     colors: ["#1f77b4","#aec7e8","#ff7f0e","#ffbb78","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5","#8c564b","#c49c94","#e377c2","#f7b6d2","#7f7f7f","#c7c7c7","#bcbd22","#dbdb8d","#17becf","#9edae5"],
     line_height: 8,
     background: "#e0e0e0",
-    overlap: .25
+    overlap: .25,
+    found_color: "#FF00FC",
+    faded_found_color: "#ff80fc"
 };
 
 IriSP.Widgets.Segments.prototype.template =
@@ -25,21 +27,18 @@
 
 
 IriSP.Widgets.Segments.prototype.draw = function() {
-    this.onMdpEvent("search", "onSearch");
-    this.onMdpEvent("search.closed", "onSearch");
-    this.onMdpEvent("search.cleared", "onSearch");
     this.onMediaEvent("timeupdate", "onTimeupdate");
-    
     this.renderTemplate();
     
     var _list = this.getWidgetAnnotations().filter(function(_ann) {
             return _ann.getDuration() > 0;
         }),
         _this = this,
-        _scale = this.width / this.source.getDuration();
-    var list_$ = this.$.find('.Ldt-Segments-List'),
+        _scale = this.width / this.source.getDuration(),
+        list_$ = this.$.find('.Ldt-Segments-List'),
         lines = [],
-        zindex = 1;
+        zindex = 1,
+        searching = false;
     
     function saturate(r, g, b, s) {
         function satcomp(c) {
@@ -52,14 +51,6 @@
         return "#" + res;
     }
     
-    function unselect() {
-        _this.tooltip.hide();
-        _this.$segments.each(function() {
-            var _segment = IriSP.jQuery(this);
-            _segment.css("background", _segment.attr("data-medium-color"));
-        });
-    }
-    
     _list.forEach(function(_annotation, _k) {
         var _left = _annotation.begin * _scale,
             _width = ( _annotation.getDuration() ) * _scale,
@@ -110,16 +101,28 @@
             _this.$segments.each(function() {
                 var _segment = IriSP.jQuery(this);
                 _segment.css({
-                    background: _segment.attr("data-low-color")
+                    background: _segment.hasClass("found") ? _this.faded_found_color : _segment.attr("data-low-color")
                 });
             });
+            _el.css({
+                background: _el.hasClass("found") ? _this.found_color: color,
+                "z-index": ++zindex
+            });
             _this.tooltip.show( _center, _top, _data.text, _data.color );
-            _el.css({
-                background: color,
-                "z-index": ++zindex
+        });
+        _annotation.on("unselect", function() {
+            _this.tooltip.hide();
+            _this.$segments.each(function() {
+                var _segment = IriSP.jQuery(this);
+                _segment.css("background", _segment.hasClass("found") ? _this.found_color : _segment.attr(searching ? "data-low-color" : "data-medium-color"));
             });
         });
-        _annotation.on("unselect", unselect);
+        _annotation.on("found", function() {
+            _el.css("background", _this.found_color).addClass("found");
+        });
+        _annotation.on("not-found", function() {
+            _el.css("background", lowcolor).removeClass("found");
+        });
     });
     
     this.$.css({
@@ -130,33 +133,16 @@
     });
     this.insertSubwidget(this.$.find(".Ldt-Segments-Tooltip"), { type: "Tooltip" }, "tooltip");
     this.$segments = this.$.find('.Ldt-Segments-Segment');
-}
-
-IriSP.Widgets.Segments.prototype.onSearch = function(searchString) {
-    this.searchString = typeof searchString !== "undefined" ? searchString : '';
-    var _found = 0,
-        _re = IriSP.Model.regexpFromTextOrArray(searchString, true);
-    if (this.searchString) {
-        this.$segments.each(function() {
-            var _el = IriSP.jQuery(this);
-            if (_re.test(_el.attr("segment-text"))) {
-                _el.css("background", _el.attr("data-base-color"));
-                _found++;
-            } else {
-                _el.css("background", _el.attr("data-low-color"));
-            }
-        });
-        if (_found) {
-            this.player.trigger("search.matchFound");
-        } else {
-            this.player.trigger("search.noMatchFound");
-        }
-    } else {
+    this.source.getAnnotations().on("search", function() {
+        searching = true;
+    });
+    this.source.getAnnotations().on("search-cleared", function() {
+        searching = false;
         _this.$segments.each(function() {
             var _segment = IriSP.jQuery(this);
-            _segment.css("background", _segment.attr("data-medium-color"));
+            _segment.css("background", _segment.attr("data-medium-color")).removeClass("found");
         });
-    }
+    });
 }
 
 IriSP.Widgets.Segments.prototype.onTimeupdate = function(_time) {
--- a/src/widgets/Tagcloud.js	Fri Nov 16 17:36:56 2012 +0100
+++ b/src/widgets/Tagcloud.js	Wed Nov 21 16:33:51 2012 +0100
@@ -39,9 +39,6 @@
 }
 
 IriSP.Widgets.Tagcloud.prototype.draw = function() {
-    this.onMdpEvent("search", "onSearch");
-    this.onMdpEvent("search.closed", "onSearch");
-    this.onMdpEvent("search.cleared", "onSearch");
     
     if (this.segment_annotation_type) {
         var _this = this;
@@ -108,9 +105,10 @@
     this.$.html(Mustache.to_html(this.template,  {words: _words }));
     this.$.find(".Ldt-Tagcloud-item").click(function() {
         var _txt = IriSP.jQuery(this).attr("content");
-        _this.player.trigger("search.triggeredSearch", _txt);
+        _this.source.getAnnotations().search(_txt);
     });
-    
+    this.source.getAnnotations().on("search", this.functionWrapper("onSearch"));
+    this.source.getAnnotations().on("search-cleared", this.functionWrapper("onSearch"));
 }
 
 IriSP.Widgets.Tagcloud.prototype.onSearch = function(searchString) {