update metedataplayer
authorymh <ymh.work@gmail.com>
Thu, 20 Aug 2015 13:25:40 +0200
changeset 1410 bc9244b671bd
parent 1409 7fdc585c8cb4
child 1411 e0cb5e956d79
update metedataplayer
src/ldt/ldt/static/ldt/metadataplayer/AdaptivePlayer.js
src/ldt/ldt/static/ldt/metadataplayer/Annotation.js
src/ldt/ldt/static/ldt/metadataplayer/AutoPlayer.js
src/ldt/ldt/static/ldt/metadataplayer/Controller.css
src/ldt/ldt/static/ldt/metadataplayer/Controller.js
src/ldt/ldt/static/ldt/metadataplayer/Highlighter.css
src/ldt/ldt/static/ldt/metadataplayer/Highlighter.js
src/ldt/ldt/static/ldt/metadataplayer/HtmlMashupPlayer.js
src/ldt/ldt/static/ldt/metadataplayer/ImageDisplay.css
src/ldt/ldt/static/ldt/metadataplayer/ImageDisplay.js
src/ldt/ldt/static/ldt/metadataplayer/LdtPlayer-core.js
src/ldt/ldt/static/ldt/metadataplayer/MashupPlayer.css
src/ldt/ldt/static/ldt/metadataplayer/MashupPlayer.js
src/ldt/ldt/static/ldt/metadataplayer/MultiSegments.js
src/ldt/ldt/static/ldt/metadataplayer/PlaceholderPlayer.css
src/ldt/ldt/static/ldt/metadataplayer/PlaceholderPlayer.js
src/ldt/ldt/static/ldt/metadataplayer/Polemic.js
src/ldt/ldt/static/ldt/metadataplayer/Renkan.css
src/ldt/ldt/static/ldt/metadataplayer/Renkan.js
src/ldt/ldt/static/ldt/metadataplayer/Segments.js
src/ldt/ldt/static/ldt/metadataplayer/Shortcuts.css
src/ldt/ldt/static/ldt/metadataplayer/Shortcuts.js
src/ldt/ldt/static/ldt/metadataplayer/Slice.js
src/ldt/ldt/static/ldt/metadataplayer/SlideVideoPlayer.css
src/ldt/ldt/static/ldt/metadataplayer/SlideVideoPlayer.js
src/ldt/ldt/static/ldt/metadataplayer/SpelSyncHtml.js
src/ldt/ldt/static/ldt/metadataplayer/Title.js
src/ldt/ldt/static/ldt/metadataplayer/Trace.js
src/ldt/ldt/static/ldt/metadataplayer/Transcript.js
src/ldt/ldt/static/ldt/metadataplayer/img/hand_left.png
src/ldt/ldt/static/ldt/metadataplayer/img/hand_right.png
src/ldt/ldt/static/ldt/metadataplayer/img/reel.gif
--- a/src/ldt/ldt/static/ldt/metadataplayer/AdaptivePlayer.js	Wed Jul 22 15:03:14 2015 +0200
+++ b/src/ldt/ldt/static/ldt/metadataplayer/AdaptivePlayer.js	Thu Aug 20 13:25:40 2015 +0200
@@ -5,7 +5,7 @@
 IriSP.Widgets.AdaptivePlayer.prototype = new IriSP.Widgets.Widget();
 
 IriSP.Widgets.AdaptivePlayer.prototype.defaults = {
-    mime_type: "video/mp4",
+    mime_type: 'video/mp4; codecs="avc1.42E01E"',
     normal_player: "HtmlPlayer",
     fallback_player: "JwpPlayer"
 };
@@ -20,7 +20,7 @@
         _opts = {},
         _canPlayType = document.createElement('video').canPlayType(this.mime_type);
     
-    _opts.type = (_canPlayType == "maybe" || _canPlayType == "probably") ? this.normal_player : this.fallback_player;
+    _opts.type = (_canPlayType !== "no") ? this.normal_player : this.fallback_player;
     
     for (var i = 0; i < _props.length; i++) {
         if (typeof this[_props[i]] !== "undefined") {
--- a/src/ldt/ldt/static/ldt/metadataplayer/Annotation.js	Wed Jul 22 15:03:14 2015 +0200
+++ b/src/ldt/ldt/static/ldt/metadataplayer/Annotation.js	Thu Aug 20 13:25:40 2015 +0200
@@ -41,9 +41,9 @@
     + '<h3 class="Ldt-Annotation-MashupOrigin Ldt-Annotation-HiddenWhenEmpty">{{l10n.excerpt_from}} <span class="Ldt-Annotation-MashupMedia"></span> <span class="Ldt-Annotation-Time Ldt-Annotation-HiddenWhenMinimized">'
     + '(<span class="Ldt-Annotation-MashupBegin"></span> - <span class="Ldt-Annotation-MashupEnd"></span>)</span></h3>'
     + '<div class="Ldt-Annotation-Cleared Ldt-Annotation-HiddenWhenMinimized Ldt-Annotation-HiddenWhenEmpty Ldt-Annotation-Creator-Block"><div class="Ldt-Annotation-Label">{{l10n.creator_}}</div>'
-    + '{{#show_creator}}<p class="Ldt-Annotation-Labelled Ldt-Annotation-Creator"></p></div>'
-    + '<div class="Ldt-Annotation-Cleared Ldt-Annotation-HiddenWhenMinimized Ldt-Annotation-HiddenWhenEmpty Ldt-Annotation-Description-Block"><div class="Ldt-Annotation-Label">{{l10n.description_}}</div>{{/show_creator}}'
-    + '<p class="Ldt-Annotation-Labelled Ldt-Annotation-Description"></p></div>'
+    + '{{#show_creator}}<p class="Ldt-Annotation-Labelled Ldt-Annotation-Creator"></p></div>{{/show_creator}}'
+    + '{{#show_description}}<div class="Ldt-Annotation-Cleared Ldt-Annotation-HiddenWhenMinimized Ldt-Annotation-HiddenWhenEmpty Ldt-Annotation-Description-Block"><div class="Ldt-Annotation-Label">{{l10n.description_}}</div>'
+    + '<p class="Ldt-Annotation-Labelled Ldt-Annotation-Description"></p></div>{{/show_description}}'
     + '<div class="Ldt-Annotation-Tags-Block Ldt-Annotation-HiddenWhenMinimized Ldt-Annotation-HiddenWhenEmpty Ldt-Annotation-Cleared">'
     + '<div class="Ldt-Annotation-Label">{{l10n.tags_}}</div><ul class="Ldt-Annotation-Labelled Ldt-Annotation-Tags"></ul>'
     + '</div></div></div></div>';
@@ -53,6 +53,7 @@
     start_minimized: false,
     show_arrow : true,
     show_creator: true,
+    show_description: true,
     arrow_position: .5,
     site_name : "Lignes de Temps",
     search_on_tag_click: true,
@@ -231,4 +232,4 @@
 IriSP.Widgets.Annotation.prototype.maximize = function() {
     this.minimized = false;
     this.$.find('.Ldt-Annotation-Inner').removeClass("Ldt-Annotation-Minimized");
-};
\ No newline at end of file
+};
--- a/src/ldt/ldt/static/ldt/metadataplayer/AutoPlayer.js	Wed Jul 22 15:03:14 2015 +0200
+++ b/src/ldt/ldt/static/ldt/metadataplayer/AutoPlayer.js	Thu Aug 20 13:25:40 2015 +0200
@@ -56,8 +56,8 @@
     }
     
     if (_opts.type === "AdaptivePlayer") {
-        var _canPlayType = document.createElement('video').canPlayType("video/mp4");
-        _opts.type = (_canPlayType == "maybe" || _canPlayType == "probably") ? "HtmlPlayer" : "JwpPlayer";
+        var _canPlayType = document.createElement('video').canPlayType('video/mp4; codecs="avc1.42E01E"');
+        _opts.type = (_canPlayType !== "no") ? "HtmlPlayer" : "JwpPlayer";
     }
     
     if (_rtmprgx.test(this.video)) {
--- a/src/ldt/ldt/static/ldt/metadataplayer/Controller.css	Wed Jul 22 15:03:14 2015 +0200
+++ b/src/ldt/ldt/static/ldt/metadataplayer/Controller.css	Thu Aug 20 13:25:40 2015 +0200
@@ -86,7 +86,7 @@
 }
 
 input.Ldt-Ctrl-SearchInput {
-    width: 145px; height: 13px; margin: 2px; padding: 3px;
+    width: 145px; height: 20px; margin: 2px; padding: 3px;
     border: 1px solid #8080a0; border-radius: 3px; font-size: 13px;
 }
 
--- a/src/ldt/ldt/static/ldt/metadataplayer/Controller.js	Wed Jul 22 15:03:14 2015 +0200
+++ b/src/ldt/ldt/static/ldt/metadataplayer/Controller.js	Thu Aug 20 13:25:40 2015 +0200
@@ -10,7 +10,8 @@
 IriSP.Widgets.Controller.prototype.defaults = {
     disable_annotate_btn: false,
     disable_search_btn: false,
-    disable_ctrl_f: false
+    disable_ctrl_f: false,
+    always_show_search: false
 };
 
 IriSP.Widgets.Controller.prototype.template =
@@ -27,7 +28,7 @@
     + '<div class="Ldt-Ctrl-spacer"></div>'
     + '{{/disable_search_btn}}'
     + '<div class="Ldt-Ctrl-Search">'
-    + '<input class="Ldt-Ctrl-SearchInput Ldt-TraceMe"></input>'
+    + '<input placeholder="{{ l10n.search }}" type="search" class="Ldt-Ctrl-SearchInput Ldt-TraceMe"></input>'
     + '</div>'
     + '</div>'
     + '<div class="Ldt-Ctrl-Right">'
@@ -102,6 +103,7 @@
     this.$.find(".Ldt-Ctrl-SearchBtn").click(this.functionWrapper("searchButtonHandler"));
     
     this.$searchInput.keyup(this.functionWrapper("searchHandler"));
+    this.$searchInput.on("search", this.functionWrapper("searchHandler"));
   
 	var _volctrl = this.$.find(".Ldt-Ctrl-Volume-Control");
     this.$.find('.Ldt-Ctrl-Sound')
@@ -168,7 +170,9 @@
     annotations.on("search-cleared", function() {
         _this.hideSearchBlock();
     });
-   
+    if (_this.always_show_search) {
+        _this.showSearchBlock();
+    }
 };
 
 /* Update the elasped time div */
@@ -237,7 +241,9 @@
 };
 
 IriSP.Widgets.Controller.prototype.hideSearchBlock = function() {
-    this.$searchBlock.animate( { width: 0 }, 200);
+    if (! this.always_show_search) {
+        this.$searchBlock.animate( { width: 0 }, 200);
+    }
 };
 
 /** react to clicks on the search button */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ldt/ldt/static/ldt/metadataplayer/Highlighter.css	Thu Aug 20 13:25:40 2015 +0200
@@ -0,0 +1,3 @@
+.currentAnnotation  {
+    border: solid 3px red;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ldt/ldt/static/ldt/metadataplayer/Highlighter.js	Thu Aug 20 13:25:40 2015 +0200
@@ -0,0 +1,43 @@
+IriSP.Widgets.Highlighter = function(player, config) {
+    var _this = this;
+    IriSP.Widgets.Widget.call(this, player, config);
+    this.throttledRefresh = IriSP._.throttle(function() {
+        console.log("highlighter Refresh");
+        _this.update();
+    }, 800);
+};
+
+/**
+ * Highlighter widget
+ * This widgets highlights the current annotations by setting the
+ * .activeAnnotation class on appropriate .Ldt-Highlighter-Annotation
+ * elements. These elements *must* have data-begin and data-end properties specifying their bounds (in ms) (and data-media specifying the media-id)
+ */
+IriSP.Widgets.Highlighter.prototype = new IriSP.Widgets.Widget();
+
+IriSP.Widgets.Highlighter.prototype.defaults =  {
+}
+
+IriSP.Widgets.Highlighter.prototype.update = function() {
+    var  _this = this;
+    var _currentTime = _this.media.getCurrentTime();
+    _this.$.find(".Ldt-Highlighter-Annotation", document).toggleClass("currentAnnotation", function () {
+        return (this.dataset.media === _this.media.id && this.dataset.begin <= _currentTime && _currentTime < this.dataset.end);
+    });
+    console.log(_this.$.find(".currentAnnotation"));
+    return false;
+};
+
+IriSP.Widgets.Highlighter.prototype.draw = function() {
+    var  _this = this;
+    
+    var _events = [
+        "timeupdate",
+        "seeked",
+        "loadedmetadata"
+    ];
+    for (var _i = 0; _i < _events.length; _i++) {
+        _this.onMediaEvent(_events[_i], _this.throttledRefresh);
+    }
+    _this.throttledRefresh();
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ldt/ldt/static/ldt/metadataplayer/HtmlMashupPlayer.js	Thu Aug 20 13:25:40 2015 +0200
@@ -0,0 +1,345 @@
+IriSP.Widgets.HtmlMashupPlayer = function(player, config) {
+    IriSP.Widgets.Widget.call(this, player, config);
+};
+
+IriSP.Widgets.HtmlMashupPlayer.prototype = new IriSP.Widgets.Widget();
+
+
+IriSP.Widgets.HtmlMashupPlayer.prototype.defaults = {
+    aspect_ratio: 14/9,
+    background: "#333333"
+};
+
+IriSP.Widgets.HtmlMashupPlayer.prototype.draw = function() {
+    
+    if (!this.height && this.aspect_ratio) {
+        this.height = this.width / this.aspect_ratio;
+        this.$.css("height", this.height);
+    }
+    
+    if (this.background) {
+        this.$.css("background", this.background);
+    }
+    
+    var mashup = this.media,
+        sel = this.$,
+        width = this.width,
+        height = this.height,
+        url_transform = this.url_transform;
+    
+    mashup.currentMedia = null;
+    mashup.currentAnnotation = null;
+    mashup.seeking = false;
+    var mashupSegmentBegin,
+        mashupSegmentEnd,
+        mashupTimecode = 0,
+ //       seekdiv = $(".video-wait"),
+        mashupTimedelta;
+        
+/*    
+    function showSeek() {
+        if (currentMedia.seeking) {
+            seekdiv.show();
+        }
+    }
+*/
+
+    function changeCurrentAnnotation() {
+        if (mashupTimecode >= mashup.duration) {
+            if (!mashup.paused) {
+                mashup.paused = true;
+                mashup.trigger("pause");
+            }
+            mashupTimecode = 0;
+        }
+        var _annotation = mashup.getAnnotationAtTime( mashupTimecode );
+        if (typeof _annotation === "undefined") {
+            if (mashup.currentMedia) {
+                mashup.currentMedia.pause();
+                if (!mashup.paused) {
+                    mashup.paused = true;
+                    mashup.trigger("pause");
+                }
+            }
+            return;
+        }
+        mashup.currentAnnotation = _annotation;
+        mashupSegmentBegin = mashup.currentAnnotation.annotation.begin.milliseconds;
+        mashupSegmentEnd = mashup.currentAnnotation.annotation.end.milliseconds;
+        mashupTimedelta = mashupSegmentBegin - mashup.currentAnnotation.begin.milliseconds;
+        mashup.currentMedia = mashup.currentAnnotation.getMedia();
+        mashup.getMedias().forEach(function(_media) {
+            if (_media !== mashup.currentMedia) {
+                _media.hide();
+                _media.pause();
+            } else {
+                _media.show();
+            }
+        });
+        
+        mashup.currentMedia.setCurrentTime( mashupTimecode + mashupTimedelta);
+        mashup.currentMedia.seeking = true;
+        
+        if (!mashup.paused) {
+            mashup.currentMedia.play();
+            mashup.seeking = true;
+//            setTimeout(showSeek,200);
+        }
+        mashup.trigger("timeupdate", new IriSP.Model.Time(mashupTimecode));
+
+    }
+    
+    mashup.getMedias().forEach(addMedia);
+    changeCurrentAnnotation();
+    mashup.trigger("loadedmetadata");
+    
+    function addMedia(media) {
+        if (media.has_player) {
+            return;
+        }
+        media.has_player = true;
+        var videourl = media.video;
+        if (typeof url_transform === "function") {
+            videourl = url_transform(media.video);
+        }
+        var videoid = "video_" + media.id,
+            videoElement;
+        
+        media.show = function() {
+            
+            if (document.getElementById(videoid)) {
+                return;
+            }
+            
+            media.loaded = false;
+            media.paused = true;
+            var videoSelector = $('<video>');
+            
+            videoSelector.attr({
+                id : videoid,
+                width : width,
+                height : height
+            }).css({
+                width: width,
+                height: height
+            });
+            
+            if (typeof videourl === "string") {
+                videoSelector.attr( "src", videourl );
+            } else {
+                for (var i = 0; i < videourl.length; i++) {
+                    var _srcNode = IriSP.jQuery('<source>');
+                    _srcNode.attr({
+                        src: videourl[i].src,
+                        type: videourl[i].type
+                    });
+                    videoSelector.append(_srcNode);
+                }
+            }
+            
+            sel.append(videoSelector);
+            
+            videoElement = videoSelector[0];
+            
+            // Binding DOM events to media
+            
+            function getVolume() {
+                media.muted = videoElement.muted;
+                media.volume = videoElement.volume;
+            }
+            
+            videoSelector.on("loadedmetadata", function() {
+                getVolume();
+                media.loaded = true;
+                media.trigger("loadedmetadata");
+                media.trigger("volumechange");
+            });
+            
+            videoSelector.on("timeupdate", function() {
+                media.trigger("timeupdate", new IriSP.Model.Time(1000*videoElement.currentTime));
+            });
+            
+            videoSelector.on("volumechange", function() {
+                getVolume();
+                media.trigger("volumechange");
+            });
+            
+            videoSelector.on("play", function() {
+                media.trigger("play");
+            });
+            
+            videoSelector.on("pause", function() {
+                media.trigger("pause");
+            });
+            
+            videoSelector.on("seeking", function() {
+                media.trigger("seeking");
+            });
+            
+            videoSelector.on("seeked", function() {
+                media.trigger("seeked");
+            });
+        };
+        
+        media.hide = function() {
+            videoElement = undefined;
+            sel.find("#" + videoid).remove();
+        };
+        
+        // Binding functions to Media Element Functions
+        
+        var deferredTime = undefined,
+            deferredPlayPause = undefined;
+        
+        media.on("setcurrenttime", function(_milliseconds) {
+            if (videoElement && videoElement.readyState >= videoElement.HAVE_METADATA) {
+                try {
+                    videoElement.currentTime = (_milliseconds / 1000);
+                    deferredTime = undefined;
+                } catch(err) {
+                    deferredTime = _milliseconds;
+                }
+            } else {
+                deferredTime = _milliseconds;
+            }
+        });
+        
+        media.on("setvolume", function(_vol) {
+            if (videoElement && videoElement.readyState >= videoElement.HAVE_METADATA) {
+                media.volume = _vol;
+                videoElement.volume = _vol;
+            }
+        });
+        
+        media.on("setmuted", function(_muted) {
+            if (videoElement && videoElement.readyState >= videoElement.HAVE_METADATA) {
+                media.muted = _muted;
+                videoElement.muted = _muted;
+            }
+        });
+        
+        media.on("setplay", function() {
+            if (videoElement && videoElement.readyState >= videoElement.HAVE_METADATA) {
+                try {
+                    videoElement.play();
+                    deferredPlayPause = undefined;
+                } catch(err) {
+                    deferredPlayPause = true;
+                }
+            } else {
+                deferredPlayPause = true;
+            }
+        });
+
+        media.on("setpause", function() {
+            if (videoElement && videoElement.readyState >= videoElement.HAVE_METADATA) {
+                try {
+                    videoElement.pause();
+                    deferredPlayPause = undefined;
+                } catch(err) {
+                    deferredPlayPause = false;
+                }
+            } else {
+                deferredPlayPause = false;
+            }
+        });
+        
+        media.on("loadedmetadata", function() {
+            if (typeof deferredTime !== "undefined") {
+                media.setCurrentTime(deferredTime);
+            }
+            if (typeof deferredPlayPause !== "undefined") {
+                if (deferredPlayPause) {
+                    media.play();
+                } else {
+                    media.pause();
+                }
+            }
+        });
+        
+        // Binding UI Events and Mashup Playing to Media
+        
+        media.on("play", function() {
+            if (media === mashup.currentMedia) {
+                mashup.trigger("play");
+            }
+        });
+        
+        media.on("pause", function() {
+            if (media === mashup.currentMedia) {
+                mashup.trigger("pause");
+            }
+        });
+        
+        media.on("timeupdate", function(_time) {
+            if (!mashup.paused && media === mashup.currentMedia && !media.seeking) {
+                if ( _time < mashupSegmentEnd ) {
+                    if ( _time >= mashupSegmentBegin ) {
+                        mashupTimecode = _time - mashupTimedelta;
+                    } else {
+                        mashupTimecode = mashupSegmentBegin - mashupTimedelta;
+                        media.setCurrentTime(mashupSegmentBegin);
+                    }
+                } else {
+                    mashupTimecode = mashupSegmentEnd - mashupTimedelta;
+                    media.pause();
+                    changeCurrentAnnotation();
+                }
+                mashup.trigger("timeupdate", new IriSP.Model.Time(mashupTimecode));
+            }
+        });
+        
+        media.on("seeked", function() {
+            media.seeking = false;
+            if (media === mashup.currentMedia && mashup.seeking) {
+                mashup.seeking = false;
+            }
+//            seekdiv.hide();
+        });
+        
+        media.on("volumechange", function() {
+            mashup.muted = media.muted;
+            mashup.volume = media.volume;
+            mashup.trigger("volumechange");
+        });
+        
+    }
+
+    // Mashup Events
+    
+    mashup.on("setcurrenttime", function(_milliseconds) {
+        mashupTimecode = _milliseconds;
+        changeCurrentAnnotation();
+    });
+    
+    mashup.on("setvolume", function(_vol) {
+        mashup.getMedias().forEach(function(_media) {
+            _media.setVolume(_vol);
+        });
+        mashup.volume = _vol;
+    });
+    
+    mashup.on("setmuted", function(_muted) {
+        mashup.getMedias().forEach(function(_media) {
+            _media.setMuted(_muted);
+        });
+        mashup.muted = _muted;
+    });
+    
+    mashup.on("setplay", function() {
+        mashup.paused = false;
+        changeCurrentAnnotation();
+    });
+    
+    mashup.on("setpause", function() {
+        mashup.paused = true;
+        if (mashup.currentMedia) {
+            mashup.currentMedia.pause();
+        }
+    });
+    
+    mashup.on("loadedmetadata", function() {
+        changeCurrentAnnotation();
+    });
+    
+};
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ldt/ldt/static/ldt/metadataplayer/ImageDisplay.css	Thu Aug 20 13:25:40 2015 +0200
@@ -0,0 +1,32 @@
+/* Nothing */
+.Ldt-ImageDisplay-Container {
+    margin: auto;
+}
+
+.Ldt-ImageDisplay-Image {
+    max-width: 100%;
+    max-height: 100%;
+}
+
+.Ldt-ImageDisplay-Overlay {
+    width: 20%;
+    min-width: 20px;
+    height: 100%;
+    opacity: 0.1;
+    position: absolute;
+    top: 0px;
+    bottom: 0px;
+}
+
+.Ldt-ImageDisplay-Overlay:hover {
+}
+
+.Ldt-ImageDisplay-Overlay-Left {
+    left: 0px;
+    cursor: url(img/hand_left.png), pointer;
+}
+
+.Ldt-ImageDisplay-Overlay-Right {
+    right: 0px;
+    cursor: url(img/hand_right.png), pointer;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ldt/ldt/static/ldt/metadataplayer/ImageDisplay.js	Thu Aug 20 13:25:40 2015 +0200
@@ -0,0 +1,42 @@
+/* This widget displays the image associated to the annotation in the given container */
+
+IriSP.Widgets.ImageDisplay = function(player, config) {
+    IriSP.Widgets.Widget.call(this, player, config);
+}
+
+IriSP.Widgets.ImageDisplay.prototype = new IriSP.Widgets.Widget();
+
+IriSP.Widgets.ImageDisplay.prototype.defaults = {
+    annotation_type: "Slides",
+    // container: "imageContainer"
+}
+
+IriSP.Widgets.ImageDisplay.prototype.template = '<div class="Ldt-ImageDisplay-Container"><img class="Ldt-ImageDisplay-Image" title="" alt="Slide Image" src=""/><div class="Ldt-ImageDisplay-Overlay Ldt-ImageDisplay-Overlay-Left"></div><div class="Ldt-ImageDisplay-Overlay Ldt-ImageDisplay-Overlay-Right"></div></div>';
+
+IriSP.Widgets.ImageDisplay.prototype.annotationTemplate = '';
+
+IriSP.Widgets.ImageDisplay.prototype.update = function(annotation) {
+    // Update the widget with data corresponding to the annotation
+    this.image.setAttribute("title", IriSP.textFieldHtml(annotation.title) + " - " + annotation.begin.toString());
+    this.image.setAttribute("src", annotation.thumbnail);
+};
+
+IriSP.Widgets.ImageDisplay.prototype.draw = function() {    
+    var _annotations = this.getWidgetAnnotations().sortBy(function(_annotation) {
+        return _annotation.begin;
+    });
+    var _this = this;
+    _this.renderTemplate();
+    _this.image = _this.$.find("img")[0];
+
+    _this.$.find(".Ldt-ImageDisplay-Overlay-Left").on("click", function () { _this.navigate(-1); });
+    _this.$.find(".Ldt-ImageDisplay-Overlay-Right").on("click", function () { _this.navigate(+1); });
+
+    _annotations.forEach(function(_a) {
+        _a.on("enter", function() {
+            _this.update(_a);
+        });
+    });
+    if (_annotations.length)
+        _this.update(_annotations[0]);
+}
--- a/src/ldt/ldt/static/ldt/metadataplayer/LdtPlayer-core.js	Wed Jul 22 15:03:14 2015 +0200
+++ b/src/ldt/ldt/static/ldt/metadataplayer/LdtPlayer-core.js	Thu Aug 20 13:25:40 2015 +0200
@@ -448,8 +448,7 @@
     var res = this.filter(function(_element, _k) {
         var titlematch = rgxp.test(_element.title),
             descmatch = rgxp.test(_element.description),
-            tagsmatch = rgxp.test(_element.getTagTexts()),
-            _isfound = !!(titlematch || descmatch || tagsmatch);
+            _isfound = !!(titlematch || descmatch);
         _element.found = _isfound;
         _element.trigger(_isfound ? "found" : "not-found");
         return _isfound;
@@ -1658,7 +1657,7 @@
                         "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:contributor" : _data.contributor || _source.contributor || _data.creator || _source.creator
                     }
                 };
                 _dest.tags.push(_res);
@@ -1680,7 +1679,7 @@
                     "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:contributor" : _data.contributor || _source.contributor || _data.creator || _source.creator
                 };
                 _dest["annotation-types"].push(_res);
                 _dest.views[0].annotation_types.push(_data.id);
@@ -1749,7 +1748,7 @@
                         "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:contributor" : _data.contributor || _source.contributor || _data.creator || _source.creator
 //                        project : _source.projectId
                     }
                 };
@@ -1896,7 +1895,8 @@
     }
 };
 
-/* End of LDT Platform Serializer *//* ldt_annotate serializer: Used when Putting annotations on the platform */
+/* End of LDT Platform Serializer */
+/* ldt_annotate serializer: Used when Putting annotations on the platform */
 
 if (typeof IriSP.serializers === "undefined") {
     IriSP.serializers = {};
@@ -1973,7 +1973,86 @@
     }
 };
 
-/* End ldt_annotate serializer *//* START segmentapi-serializer.js */
+/* End ldt_annotate serializer *//* ldt_localstorage serializer: Used to store personal annotations in local storage */
+
+if (typeof IriSP.serializers === "undefined") {
+    IriSP.serializers = {};
+}
+
+IriSP.serializers.ldt_localstorage = {
+    serializeAnnotation : function(_data, _source) {
+        var _annType = _data.getAnnotationType();
+        return {
+            begin: _data.begin.milliseconds,
+            end: _data.end.milliseconds,
+            content: {
+                description: _data.description,
+                title: _data.title,
+                audio: _data.audio
+            },
+            tags: _data.getTagTexts(),
+            media: _data.getMedia().id,
+            type_title: _annType.title,
+            type: ( typeof _annType.dont_send_id !== "undefined" && _annType.dont_send_id ? "" : _annType.id ),
+            meta: {
+                created: _data.created,
+                creator: _data.creator
+            }
+        };
+    },
+    deserializeAnnotation : function(_anndata, _source) {
+        var _ann = new IriSP.Model.Annotation(_anndata.id, _source);
+        _ann.description = _anndata.content.description || "";
+        _ann.title = _anndata.content.title || "";
+        _ann.creator = _anndata.meta.creator || "";
+        _ann.created = new Date(_anndata.meta.created);
+        _ann.setMedia(_anndata.media, _source);
+        var _anntype = _source.getElement(_anndata.type);
+        if (!_anntype) {
+            _anntype = new IriSP.Model.AnnotationType(_anndata.type, _source);
+            _anntype.title = _anndata.type_title;
+            _source.getAnnotationTypes().push(_anntype);
+        }
+        _ann.setAnnotationType(_anntype.id);
+        var _tagIds = IriSP._(_anndata.tags).map(function(_title) {
+            var _tags = _source.getTags(true).searchByTitle(_title, true);
+            if (_tags.length) {
+                var _tag = _tags[0];
+            }
+            else {
+                _tag = new IriSP.Model.Tag(_title.replace(/\W/g,'_'),_source);
+                _tag.title = _title;
+                _source.getTags().push(_tag);
+            }
+            return _tag.id;
+        });
+        _ann.setTags(_tagIds);
+        _ann.setBegin(_anndata.begin);
+        _ann.setEnd(_anndata.end);
+        if (typeof _anndata.content.audio !== "undefined" && _anndata.content.audio.href) {
+            _ann.audio = _anndata.content.audio;
+        }
+        _source.getAnnotations().push(_ann);
+    },
+    serialize : function(_source) {
+        var _this = this;
+        return JSON.stringify(_source.getAnnotations().map(function (a) { return _this.serializeAnnotation(a, _source); }));
+    },
+    deSerialize : function(_data, _source) {
+        var _this = this;
+        if (typeof _data == "string") {
+            _data = JSON.parse(_data);
+        }
+
+        _source.addList('tag', new IriSP.Model.List(_source.directory));
+        _source.addList('annotationType', new IriSP.Model.List(_source.directory));
+        _source.addList('annotation', new IriSP.Model.List(_source.directory));
+        _data.map( function (a) { _this.deserializeAnnotation(a, _source); });
+    }
+};
+
+/* End ldt_localstorage serializer */
+/* START segmentapi-serializer.js */
 
 if (typeof IriSP.serializers === "undefined") {
     IriSP.serializers = {};
@@ -1994,7 +2073,7 @@
             }
             _ann.setMedia(_s.iri_id);
             _ann.title = _s.title;
-            _ann.description = _s.abstract;
+            _ann.description = _s['abstract'];
             _ann.begin = new IriSP.Model.Time(_s.start_ts);
             _ann.end = new IriSP.Model.Time(_s.start_ts + _s.duration);
             _ann.keywords = (_s.tags ? _s.tags.split(",") : []);
@@ -2037,9 +2116,13 @@
         backboneRelational: "backbone-relational.js",
         paper: "paper.js",
         jqueryMousewheel: "jquery.mousewheel.min.js",
+        splitter: "jquery.splitter.js",
+        cssSplitter: "jquery.splitter.css",
         renkanPublish: "renkan.js",
         processing: "processing-1.3.6.min.js",
-        recordMicSwf: "record_mic.swf"
+        recordMicSwf: "record_mic.swf",
+        mousetrap: "mousetrap.min.js",
+        mousetrapGlobal: "mousetrap-global-bind.js"
     },
     locations : {
         // use to define locations outside default_dir
@@ -2114,6 +2197,12 @@
     },
     MultiSegments: {
         noCss: true
+    },
+    SlideVideoPlayer: {
+        requires: [ "jQuery", "jQueryUI", "splitter" ]
+    },
+    Shortcuts: {
+        requires: [ "mousetrap", "mousetrapGlobal" ]
     }
 };
 
@@ -2487,13 +2576,19 @@
 };
 
 IriSP.Widgets.Widget.prototype.getWidgetAnnotations = function() {
+    var result = null;
     if (typeof this.annotation_type === "undefined") {
-        return this.media.getAnnotations();
+        result = this.media.getAnnotations();
+    } else if (this.annotation_type.elementType === "annotationType") {
+        result = this.annotation_type.getAnnotations();
+    } else {
+        result = this.media.getAnnotationsByTypeTitle(this.annotation_type);
     }
-    if (this.annotation_type.elementType === "annotationType") {
-        return this.annotation_type.getAnnotations();
+    if (typeof this.annotation_filter !== "undefined") {
+        return this.annotation_filter(result);
+    } else {
+        return result;
     }
-    return this.media.getAnnotationsByTypeTitle(this.annotation_type);
 };
 
 IriSP.Widgets.Widget.prototype.getWidgetAnnotationsAtTime = function() {
@@ -2537,9 +2632,32 @@
     });
 };
 
+/*
+ * Position the player to the next/previous annotations based on current player position
+ *
+ * Parameter: offset: -1 for previous annotation, +1 for next annotation
+ */
+IriSP.Widgets.Widget.prototype.navigate = function(offset) {
+    // offset is normally either -1 (previous slide) or +1 (next slide)
+    var _this = this;
+    var currentTime = _this.media.getCurrentTime();
+    var annotations = _this.source.getAnnotations().sortBy(function(_annotation) {
+        return _annotation.begin;
+    });
+    for (var i = 0; i < annotations.length; i++) {
+        if (annotations[i].begin <= currentTime && currentTime < annotations[i].end) {
+            // Found a current annotation - clamp i+offset value to [0, length - 1]
+            i = Math.min(annotations.length - 1, Math.max(0, i + offset));
+            _this.media.setCurrentTime(annotations[i].begin);
+            break;
+        }
+    };
+};
+
+
 /**
  * This method responsible of drawing a widget on screen.
  */
 IriSP.Widgets.Widget.prototype.draw = function() {
     /* implemented by "sub-classes" */
-};
\ No newline at end of file
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ldt/ldt/static/ldt/metadataplayer/MashupPlayer.css	Thu Aug 20 13:25:40 2015 +0200
@@ -0,0 +1,11 @@
+.Ldt-MashupPlayer-Media {
+    position: absolute;
+}
+
+.Ldt-MashupPlayer-Waiting {
+    position: absolute; left: 0; top: 0; width: 100%; height: 100%;
+    background-color: rgba(120, 120, 140, .6);
+    background-image: url(img/reel.gif);
+    background-position: center;
+    background-repeat: no-repeat;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ldt/ldt/static/ldt/metadataplayer/MashupPlayer.js	Thu Aug 20 13:25:40 2015 +0200
@@ -0,0 +1,256 @@
+IriSP.Widgets.MashupPlayer = function(player, config) {
+    IriSP.Widgets.Widget.call(this, player, config);
+    this.is_mashup = true;
+};
+
+IriSP.Widgets.MashupPlayer.prototype = new IriSP.Widgets.Widget();
+
+IriSP.Widgets.MashupPlayer.prototype.defaults = {
+    aspect_ratio: 14/9,
+    split_screen: false,
+    player_type: "PopcornPlayer",
+    background: "#000000"
+};
+
+IriSP.Widgets.MashupPlayer.prototype.draw = function() {
+    var _this = this,
+        _mashup = this.media,
+        _currentMedia = null,
+        _currentAnnotation = null,
+        _segmentBegin,
+        _segmentEnd,
+        _timecode = 0,
+        _seeking = false,
+        _seekdiv,
+        _timedelta,
+        medialist = _mashup.getMedias();
+    
+    _mashup.paused = (!this.autostart && !this.autoplay);
+    
+    function changeCurrentAnnotation() {
+        if (_timecode >= _mashup.duration) {
+            if (!_mashup.paused) {
+                _mashup.paused = true;
+                _mashup.trigger("pause");
+            }
+            _timecode = 0;
+        }
+        var _annotation = _mashup.getAnnotationAtTime( _timecode );
+        if (typeof _annotation === "undefined") {
+            if (_currentMedia) {
+                _currentMedia.pause();
+                if (!_mashup.paused) {
+                    _mashup.paused = true;
+                    _mashup.trigger("pause");
+                }
+            }
+            return;
+        }
+        if (_annotation !== _currentAnnotation) {
+            _currentAnnotation = _annotation;
+            _segmentBegin = _currentAnnotation.annotation.begin.milliseconds;
+            _segmentEnd = _currentAnnotation.annotation.end.milliseconds;
+            _timedelta = _segmentBegin - _currentAnnotation.begin.milliseconds;
+            _currentMedia = _currentAnnotation.getMedia();
+            
+            for (var _i = 0; _i < medialist.length; _i++) {
+                if (medialist[_i].id !== _currentMedia.id) {
+                    if (!_this.split_screen) {
+                        medialist[_i].hide();
+                    }
+                    medialist[_i].pause();
+                } else {
+                    medialist[_i].show();
+                }
+            }
+            
+    /* PRELOADING */
+            var _preloadedMedias = [],
+                _toPreload = _mashup.getAnnotations().filter(function(_a) {
+                return (_a.begin >= _currentAnnotation.end && _a.getMedia() !== _currentMedia);
+            });
+            IriSP._(_toPreload).each(function(_a) {
+                var _media = _a.getMedia();
+                if (IriSP._(_preloadedMedias).indexOf(_media.id) === -1) {
+                    _preloadedMedias.push(_media.id);
+                    _media.setCurrentTime(_a.annotation.begin.getSeconds());
+                    _media.seeking = true;
+/*
+                    console.log("Preloading ", _media.id, " at t=", _a.annotation.begin.getSeconds());
+*/
+                }
+            });
+            
+    //        console.log("Changed segment: media="+ this.currentMedia.id + ", from=" + this.segmentBegin + " to=" + this.segmentEnd +", timedelta = ", this.timedelta)
+    //    } else {
+    //        console.log("changeCurrentAnnotation called, but segment hasn't changed");
+        }
+        
+        _currentMedia.setCurrentTime( _timecode + _timedelta);
+        _currentMedia.seeking = true;
+        
+        if (!_mashup.paused) {
+            _currentMedia.play();
+            _seeking = true;
+            _seekdiv.show();
+        }
+/*
+        console.log("Setting time of media", _currentMedia.id, "to", _timecode + _timedelta)     
+*/
+        _mashup.trigger("timeupdate", new IriSP.Model.Time(_timecode));
+
+    }
+    
+    if (!this.height) {
+        this.height = Math.floor(this.width/this.aspect_ratio);
+        this.$.css({
+            height: this.height
+        });
+    }
+    
+    this.$.css({
+        background: this.background
+    });
+    
+    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);
+
+    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),
+            left: (_this.split_screen ? _width * (_key % _grid) : 0),
+            height: _height,
+            width: _width,
+            display: (_this.split_screen ? "block" : "none")
+        });
+        _this.$.append(_el);
+        
+        _this.insertSubwidget(
+            _el.find(".Ldt-MashupPlayer-Subwidget"),
+            IriSP._({
+                type: _this.player_type,
+                media_id: _media.id,
+                height: _height,
+                width: _width,
+                url_transform: _this.url_transform
+            }).extend(_this.player_options)
+        );
+        
+        _media.loadedMetadata = false;
+        _media.show = function() {
+            _el.show();
+        };
+        _media.hide = function() {
+            _el.hide();
+        };
+        _media.on("loadedmetadata", function() {
+            _media.loadedMetadata = true;
+            var _allLoaded = true;
+            for (var _i = 0; _i < medialist.length; _i++) {
+                _allLoaded = _allLoaded && medialist[_i].loadedMetadata;
+            }
+            if (_allLoaded) {
+                _seekdiv.fadeOut();
+                changeCurrentAnnotation();
+                _mashup.trigger("loadedmetadata");
+            }
+        });
+        _media.on("timeupdate", function(_time) {
+            if (!_mashup.paused && _media === _currentMedia && !_media.seeking) {
+/*
+                var _status = "Timeupdate from " + _media.id + " at time " + _time;
+*/
+                if ( _time < _segmentEnd ) {
+                    if ( _time >= _segmentBegin ) {
+                        _timecode = _time - _timedelta;
+/*
+                        _status += " within segment";
+*/
+                    } else {
+                        _timecode = _segmentBegin - _timedelta;
+                        _media.setCurrentTime(_segmentBegin);
+/*
+                        _status += " before segment";
+*/
+                    }
+                } else {
+                    _timecode = _segmentEnd - _timedelta;
+                    _media.pause();
+                    changeCurrentAnnotation();
+/*
+                    _status += " after segment";
+*/
+                }
+/*
+                _status += " (" + _segmentBegin + " to " + _segmentEnd + ")" + ", translated to " + _timecode;
+                console.log(_status);
+*/
+                _mashup.trigger("timeupdate", new IriSP.Model.Time(_timecode));
+            }
+        });
+        _media.on("seeked", function() {
+            _media.seeking = false;
+            if (_media === _currentMedia && _seeking) {
+                _seeking = false;
+                _seekdiv.hide();
+            }
+        });
+        _media.on("play", function() {
+            if (_media === _currentMedia) {
+                _mashup.trigger("play");
+            }
+        });
+        _media.on("pause", function() {
+            if (_media === _currentMedia) {
+                _mashup.trigger("pause");
+            }
+        });
+        _media.on("volumechange", function() {
+            _mashup.muted = _media.muted;
+            _mashup.volume = _media.volume;
+            _mashup.trigger("volumechange");
+        });
+    });
+    
+    _seekdiv = IriSP.jQuery('<div class="Ldt-MashupPlayer-Waiting"></div>');
+    
+    this.$.append(_seekdiv);
+
+    // Binding functions to Popcorn
+    
+    _mashup.on("setcurrenttime", function(_milliseconds) {
+        _timecode = _milliseconds;
+        changeCurrentAnnotation();
+    });
+    
+    _mashup.on("setvolume", function(_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 < medialist.length; _i++) {
+            medialist[_i].setMuted(_muted);
+        }
+        _mashup.muted = _muted;
+    });
+    
+    _mashup.on("setplay", function() {
+        _mashup.paused = false;
+        changeCurrentAnnotation();
+    });
+    
+    _mashup.on("setpause", function() {
+        _mashup.paused = true;
+        if (_currentMedia) {
+            _currentMedia.pause();
+        }
+    });
+    
+    _mashup.on("loadedmetadata", changeCurrentAnnotation);
+   
+};
\ No newline at end of file
--- a/src/ldt/ldt/static/ldt/metadataplayer/MultiSegments.js	Wed Jul 22 15:03:14 2015 +0200
+++ b/src/ldt/ldt/static/ldt/metadataplayer/MultiSegments.js	Thu Aug 20 13:25:40 2015 +0200
@@ -133,4 +133,4 @@
             }
         });*/
     }
-};
\ No newline at end of file
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ldt/ldt/static/ldt/metadataplayer/PlaceholderPlayer.css	Thu Aug 20 13:25:40 2015 +0200
@@ -0,0 +1,5 @@
+.Ldt-PlaceholderPlayer {
+    font-size: 40px; text-align: center;
+    padding: 40px 0; font-weight: bolder;
+    background: #333333; color: #FFFFFF;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ldt/ldt/static/ldt/metadataplayer/PlaceholderPlayer.js	Thu Aug 20 13:25:40 2015 +0200
@@ -0,0 +1,69 @@
+/* This is a fake player, for when no video is available */
+
+IriSP.Widgets.PlaceholderPlayer = function(player, config) {
+    IriSP.Widgets.Widget.call(this, player, config);
+};
+
+IriSP.Widgets.PlaceholderPlayer.prototype = new IriSP.Widgets.Widget();
+
+IriSP.Widgets.PlaceholderPlayer.prototype.defaults = {
+    autostart: false
+};
+
+IriSP.Widgets.PlaceholderPlayer.prototype.template = '<div class="Ldt-PlaceholderPlayer">(loading)</div>';
+
+IriSP.Widgets.PlaceholderPlayer.prototype.draw = function() {
+    
+    this.renderTemplate();
+    
+    var paused = true,
+        timeDelta = 0,
+        currentTime = new IriSP.Model.Time(0),
+        media = this.media,
+        sel = this.$.find(".Ldt-PlaceholderPlayer");
+    
+    function updateTime() {
+        if (!paused) {
+            currentTime = new IriSP.Model.Time(new Date().valueOf() - timeDelta);
+            if (currentTime <= media.duration) {
+                media.trigger("timeupdate", currentTime);
+                setTimeout(updateTime, 100);
+            } else {
+                currentTime = media.duration;
+                media.pause();
+            }
+        }
+        sel.text(currentTime.toString(true));
+    }
+    
+    
+    // Binding functions to Popcorn
+    media.on("setcurrenttime", function(_milliseconds) {
+        timeDelta = new Date().valueOf() - _milliseconds;
+        currentTime = new IriSP.Model.Time(_milliseconds);
+        media.trigger("seeked");
+        media.trigger("timeupdate", currentTime);
+        sel.text(currentTime.toString(true));
+    });
+    
+    media.on("setplay", function() {
+        paused = false;
+        timeDelta = new Date().valueOf() - currentTime
+        media.trigger("play");
+        updateTime();
+    });
+    
+    media.on("setpause", function() {
+        paused = true;
+        media.trigger("pause");
+        updateTime();
+    });
+    
+    media.trigger("loadedmetadata");
+    media.trigger("setcurrenttime", 0);
+    
+    if (this.autostart) {
+        media.trigger("setplay");
+    }
+    
+};
\ No newline at end of file
--- a/src/ldt/ldt/static/ldt/metadataplayer/Polemic.js	Wed Jul 22 15:03:14 2015 +0200
+++ b/src/ldt/ldt/static/ldt/metadataplayer/Polemic.js	Thu Aug 20 13:25:40 2015 +0200
@@ -24,7 +24,6 @@
     annotation_type : "tweet",
     defaultcolor : "#585858",
     foundcolor : "#fc00ff",
-    search_by_default: true,
     polemics : [
         {
             "name" : "OK",
@@ -88,9 +87,9 @@
                 }),
                 polemicStacks : []
             };
-        
+            
         for (var _j = 0; _j < this.polemics.length; _j++) {
-            var _polemic = this.search_by_default ? _res.annotations.searchByDescription(this.polemics[_j].keywords) : _res.annotations.searchByTextFields(this.polemics[_j].keywords);
+            var _polemic = _res.annotations.searchByDescription(this.polemics[_j].keywords);
             _count += _polemic.length;
             _res.polemicStacks.push(_polemic);
         }
@@ -108,7 +107,7 @@
             this.height = (2 + _max) * this.element_height;
             this.$zone.css({
                 width: this.width + "px",
-                height: (this.height+2) + "px",
+                height: this.height + "px",
                 position: "relative"
             });
             
@@ -140,22 +139,21 @@
                     return false;
                 });
                 IriSP.attachDndData(_el, {
-                    title: _annotation.title,
-                    description: _annotation.description,
-                    image: _annotation.thumbnail,
-                    uri: (typeof _annotation.url !== "undefined" 
-                        ? _annotation.url
-                        : (document.location.href.replace(/#.*$/,'') + '#id='  + _annotation.id))
+                	title: _annotation.title,
+                	description: _annotation.description,
+                	image: _annotation.thumbnail,
+                	uri: (typeof _annotation.url !== "undefined" 
+		                ? _annotation.url
+		                : (document.location.href.replace(/#.*$/,'') + '#id='  + _annotation.id))
                 });
-                // test if annotation has several colors.
-                var colAr = [];
-                for (var _j = 0; _j < _this.polemics.length; _j++) {
-                    if( IriSP.Model.regexpFromTextOrArray( _this.polemics[_j].keywords ).test( this.search_by_default ? _annotation.title : _annotation.getTagTexts()) ){
-                        colAr.push(_this.polemics[_j].color);
-                    }
+            	// test if annotation has several colors.
+            	var colAr = [];
+            	for (var _j = 0; _j < _this.polemics.length; _j++) {
+            		if( IriSP.Model.regexpFromTextOrArray( _this.polemics[_j].keywords ).test( _annotation.title ) ){
+            			colAr.push(_this.polemics[_j].color);
+            		}
                 }
-                
-                // display annotation
+            	// display annotation
                 _annotation.on("select", function() {
                     if (_this.tooltip) {
                         _this.tooltip.show(
@@ -234,7 +232,7 @@
         this.height = (2 + this.max_elements) * this.element_height;
         this.$zone.css({
             width: this.width + "px",
-            height: (this.height+2) + "px",
+            height: this.height + "px",
             position: "relative"
         });
         
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ldt/ldt/static/ldt/metadataplayer/Renkan.css	Thu Aug 20 13:25:40 2015 +0200
@@ -0,0 +1,130 @@
+
+.Ldt-Renkan-Container {
+    position: relative; width: 100%; height: 100%;
+}
+
+.Ldt-Renkan a {
+    color: #303080;
+}
+
+
+.Rk-Main {
+    position: absolute; left: 0; top: 0; right: 0; bottom: 0;
+}
+
+.Rk-Render {
+    position: absolute; left: 0; top: 0; right: 0; bottom: 0;
+    background: #ffffff;
+}
+
+/* Canvas */
+
+.Rk-Editing-Space {
+    position: absolute; left: 0; top: 35px; right: 0; bottom: 0; overflow: hidden;
+    background: -moz-radial-gradient( center, circle, #ffffff 40%, #e0e0e0 90%);
+    background: -webkit-radial-gradient( center, circle, #ffffff 40%, #e0e0e0 90%);
+    background: -ms-radial-gradient( center, circle, #ffffff 40%, #e0e0e0 90%);
+}
+
+.Rk-Editing-Space-Full {
+    top: 0;
+}
+
+.Rk-Canvas {
+    position: absolute; left: 0; top: 0; right: 0; bottom: 0; z-index: 2;
+}
+
+/* Node Labels */
+
+.Rk-Labels {
+    position: absolute; left: 0; top: 0; z-index: 1;
+    font-family: "Segoe UI", "Helvetica Neue", Arial, Helvetica, sans-serif;
+}
+
+.Rk-Label {
+    position: absolute; width: 160px; margin-left: -80px; text-align: center; font-size: 13px; line-height: 13px;
+}
+
+.Rk-Edge-Label {
+    font-size: 11px; transform-origin: 50% 0; -moz-transform-origin: 50% 0;
+    -webkit-transform-origin: 50% 0; -ms-transform-origin: 50% 0;
+}
+
+/* Editors */
+
+.Rk-Editor {
+    position: absolute; left: 0; top: 0; z-index: 3;
+}
+
+.Rk-Notifications {
+    position: absolute; right: 15px; top: 15px; width: 200px;
+    padding: 10px; border-radius: 8px; display: none;
+    color: #ffffff; font-size: 13px; text-align: center; font-weight: bold;
+    background: rgba(20,20,20,.7);
+    background: -moz-linear-gradient(top, rgba(40,40,40,.7) 20%, rgba(0,0,0,.7) 80%);
+    background: -webkit-linear-gradient(top, rgba(40,40,40,.7) 20%, rgba(0,0,0,.7) 80%);
+    background: -ms-linear-gradient(top, rgba(40,40,40,.7) 20%, rgba(0,0,0,.7) 80%);
+    filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#202020', endColorstr='#000000',GradientType=0 );
+}
+
+.Rk-CloseX {
+    float: right; cursor: pointer;
+}
+
+.Rk-Editor h2 {
+    font-size: 16px; font-weight: bold; padding: 0;
+}
+
+.Rk-Editor p, .Rk-Editor-p {
+    margin: 5px 0; font-size: 12px; clear: both; padding: 0;
+}
+
+.Rk-Editor-Label {
+    float: left; width: 80px;
+}
+
+.Rk-UserColor {
+    display: inline-block; width: 12px; height: 12px; border: 1px solid #666666; margin: -2px 2px;
+}
+
+.Rk-Display-Title a {
+    text-decoration: none; color: #000000;
+}
+
+.Rk-Display-Title a:hover {
+    text-decoration: underline;
+}
+
+.Rk-Display-URI {
+    font-style: italic;
+}
+
+.Rk-Display-ImgPreview {
+    margin: 5px auto; display: block; max-width: 255px !important; max-height: 120px !important;
+}
+
+
+.Rk-ZoomButtons {
+    position: absolute; left: 0; top: 35px; cursor: pointer;
+}
+
+.Rk-Editing-Space-Full .Rk-ZoomButtons {
+    top: 0;
+}
+
+.Rk-ZoomIn, .Rk-ZoomOut {
+    width: 21px; height: 20px; background: url(img/zoombuttons.png); margin: 5px;
+}
+
+.Rk-ZoomIn:hover {
+    background-position: 0 -20px;
+}
+
+.Rk-ZoomOut {
+    background-position: -21px 0;
+}
+
+.Rk-ZoomOut:hover {
+    background-position: -21px -20px;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ldt/ldt/static/ldt/metadataplayer/Renkan.js	Thu Aug 20 13:25:40 2015 +0200
@@ -0,0 +1,134 @@
+IriSP.Widgets.Renkan = function(player, config) {
+    IriSP.Widgets.Widget.call(this, player, config);
+};
+
+IriSP.Widgets.Renkan.prototype = new IriSP.Widgets.Widget();
+
+IriSP.Widgets.Renkan.prototype.defaults = {
+    annotation_regexp: /player\/([a-zA-Z0-9_-]+)\/.*id=([a-zA-Z0-9_-]+)/,
+    tag_regexp: /search=([^&=]+)/,
+    min_duration: 5000,
+    renkan_options: {}
+};
+
+IriSP.Widgets.Renkan.prototype.messages = {
+    "fr": {
+    },
+    "en": {
+    }
+};
+
+IriSP.Widgets.Renkan.prototype.template =
+    '<div class="Ldt-Renkan-Container"><div class="Ldt-Renkan"></div></div>';
+
+IriSP.Widgets.Renkan.prototype.draw = function() {
+    this.renderTemplate();
+    var _id = IriSP.Model.getUID();
+    this.$.find(".Ldt-Renkan").attr("id", _id);
+    var renkan_options = IriSP._.extend({
+        container: _id,
+        editor_mode: false,
+        show_bins: false,
+        show_top_bar: false,
+        force_resize: true,
+        language: IriSP.language
+    }, this.renkan_options);
+    this.renkan = new Rkns.Renkan(renkan_options);
+    this.node_times = [];
+    var _this = this,
+        _list = this.getWidgetAnnotations();
+    this.renkan.project.on("add:nodes", function(_node) {
+        var _uri = _node.get("uri"),
+            _annmatch = _uri.match(_this.annotation_regexp);
+        if (_annmatch) {
+            var _annotations = _list.filter(function(_ann) {
+                return _ann.getMedia().id == _annmatch[1] && _ann.id == _annmatch[2];
+            });
+            _annotations.forEach(function(_ann) {
+                var _duration = _ann.getDuration(),
+                    _preroll = + ( _duration < _this.min_duration ) * ( _this.min_duration / 2);
+                var _nt = {
+                    uri: _uri,
+                    selected: false,
+                    node: _node,
+                    annotation: _ann,
+                    begin: _ann.begin - _preroll,
+                    end: _ann.end + _preroll
+                };
+                _this.node_times.push(_nt);
+                var _annselected = false,
+                    _nodeselected = false;
+                _ann.on("select", function() {
+                    _annselected = true;
+                    if (!_nodeselected) {
+                        _node.trigger("select",true);
+                    }
+                });
+                _node.on("selected", function() {
+                    _nodeselected = true;
+                    if (!_annselected) {
+                        _ann.trigger("select",true);
+                    }
+                });
+                _ann.on("unselect", function() {
+                    _annselected = false;
+                    if (_nodeselected) {
+                        _node.trigger("unselect",true);
+                    }
+                });
+                _node.on("unselected", function() {
+                    _nodeselected = false;
+                    _nt.selected = false;
+                    if (_annselected) {
+                        _ann.trigger("unselect",true);
+                    }
+                });
+                _node.on("clicked", function() {
+                    _ann.trigger("click");
+                });
+            });
+        }
+        var _tagmatch = _uri.match(_this.tag_regexp);
+        if (_tagmatch) {
+            _node.on("select", function() {
+                _this.source.getAnnotations().search(_tagmatch[1]);
+            });
+            _node.on("unselect", function() {
+                _this.source.getAnnotations().search("");
+            });
+        }
+    });
+    Rkns.jsonIO(this.renkan, {
+        url: this.data
+    });
+    
+    this.onMediaEvent("timeupdate","onTimeupdate");
+    
+    this.$.find(".Rk-Editor").on("click", "a", function() {
+        var href = this.href,
+            times = _this.node_times.filter(function(t) {
+                return t.uri == href;
+            });
+        if (times.length) {
+            IriSP._(times).each(function(t) {
+                t.annotation.trigger("click");
+            });
+            return false;
+        }
+    });
+};
+
+IriSP.Widgets.Renkan.prototype.onTimeupdate = function(_time) {
+    IriSP._(this.node_times).each(function(_nt) {
+        if (_nt.begin <= _time && _nt.end >= _time) {
+            if (!_nt.selected) {
+                _nt.selected = true;
+                _nt.node.trigger("select");
+            }
+        } else {
+            if (_nt.selected) {
+                _nt.node.trigger("unselect");
+            }
+        }
+    });
+};
--- a/src/ldt/ldt/static/ldt/metadataplayer/Segments.js	Wed Jul 22 15:03:14 2015 +0200
+++ b/src/ldt/ldt/static/ldt/metadataplayer/Segments.js	Thu Aug 20 13:25:40 2015 +0200
@@ -13,7 +13,9 @@
     background: "#e0e0e0",
     overlap: .25,
     found_color: "#FF00FC",
-    faded_found_color: "#ff80fc"
+    faded_found_color: "#ff80fc",
+    // Display creator info in segment tooltip
+    show_creator: true
 };
 
 IriSP.Widgets.Segments.prototype.template =
@@ -77,7 +79,7 @@
             color : color,
             medcolor: medcolor,
             lowcolor: lowcolor,
-            text: (_annotation.creator ? (_annotation.creator + " : ") : "" ) + _fulltext.replace(/(\n|\r|\r\n)/mg,' ').replace(/(^.{120,140})[\s].+$/m,'$1&hellip;'),
+            text: ((_this.show_creator && _annotation.creator) ? (_annotation.creator + " : ") : "" ) + _fulltext.replace(/(\n|\r|\r\n)/mg,' ').replace(/(^.{120,140})[\s].+$/m,'$1&hellip;'),
             left : _left,
             width : _width,
             top: _top,
@@ -89,14 +91,17 @@
         };
         var _html = Mustache.to_html(_this.annotationTemplate, _data),
             _el = IriSP.jQuery(_html);
-        _el.mouseover(function() {
+            _el.mouseover(function() {
                 _annotation.trigger("select");
+                _this.player.trigger('annotation-select', _annotation);
             })
             .mouseout(function() {
                 _annotation.trigger("unselect");
+                _this.player.trigger('annotation-unselect', _annotation);
             })
             .click(function() {
                 _annotation.trigger("click");
+                _this.player.trigger('annotation-click', _annotation);
             })
             .appendTo(list_$);
         IriSP.attachDndData(_el, {
@@ -172,4 +177,4 @@
     this.$.find('.Ldt-Segments-Position').css({
         left: _x + "px"
     });
-};
\ No newline at end of file
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ldt/ldt/static/ldt/metadataplayer/Shortcuts.css	Thu Aug 20 13:25:40 2015 +0200
@@ -0,0 +1,1 @@
+/* Empty file */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ldt/ldt/static/ldt/metadataplayer/Shortcuts.js	Thu Aug 20 13:25:40 2015 +0200
@@ -0,0 +1,58 @@
+IriSP.Widgets.Shortcuts = function(player, config) {
+    IriSP.Widgets.Widget.call(this, player, config);
+};
+
+/**
+ * Keyboard shortcuts widget
+ * This widgets add global shortcuts for common actions.
+ * The default shortcuts are: 
+ * - Escape or Control-space for play/pause
+ * - Control-left for rewind (+shift to go faster)
+ * - Control-right for forward (+shift to go faster)
+ */
+IriSP.Widgets.Shortcuts.prototype = new IriSP.Widgets.Widget();
+
+IriSP.Widgets.Shortcuts.prototype.defaults =  {
+    // Time increment, in ms, for backward/forward navigation
+    time_increment: 2000
+}
+
+IriSP.Widgets.Shortcuts.prototype.draw = function() {
+    var  _this = this;
+    
+    /* Standard shortcuts */
+    Mousetrap.bindGlobal(["esc", "ctrl+space"], function (e) {
+        e.preventDefault();
+        if (! _this.media.getPaused()) {
+            _this.media.pause();
+        } else {
+            _this.media.play();
+        }
+        return false;
+    });
+    Mousetrap.bindGlobal("ctrl+left", function (e) {
+        // Backward
+        e.preventDefault();
+        _this.media.setCurrentTime(Math.max(0, _this.media.getCurrentTime() - _this.time_increment));
+        return false;
+    });
+    Mousetrap.bindGlobal("ctrl+shift+left", function (e) {
+        // Backward
+        e.preventDefault();
+        _this.media.setCurrentTime(Math.max(0, _this.media.getCurrentTime() - 5 * _this.time_increment));
+        return false;
+    });
+    Mousetrap.bindGlobal("ctrl+right", function (e) {
+        // Forward
+        e.preventDefault();
+        _this.media.setCurrentTime(Math.min(_this.media.duration, _this.media.getCurrentTime() + _this.time_increment));
+        return false;
+    });
+    Mousetrap.bindGlobal("ctrl+shift+right", function (e) {
+        // Forward
+        e.preventDefault();
+        _this.media.setCurrentTime(Math.min(_this.media.duration, _this.media.getCurrentTime() + 5 * _this.time_increment));
+        return false;
+    });
+
+};
--- a/src/ldt/ldt/static/ldt/metadataplayer/Slice.js	Wed Jul 22 15:03:14 2015 +0200
+++ b/src/ldt/ldt/static/ldt/metadataplayer/Slice.js	Thu Aug 20 13:25:40 2015 +0200
@@ -67,9 +67,16 @@
     
     this.getWidgetAnnotations().forEach(function(_a) {
         _a.on("enter", function() {
-            _this.$slider.slider("values",[_a.begin, _a.end]);
+            _this.setBounds(_a.begin, _a.end);
         });
     });
+    this.player.on("annotation-click", function(_a) {
+        _this.setBounds(_a.begin, _a.end);
+    });
+};
+
+IriSP.Widgets.Slice.prototype.setBounds = function(begin, end) {
+    this.$slider.slider("values", [ begin, end ]);
 };
 
 IriSP.Widgets.Slice.prototype.show = function() {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ldt/ldt/static/ldt/metadataplayer/SlideVideoPlayer.css	Thu Aug 20 13:25:40 2015 +0200
@@ -0,0 +1,23 @@
+/* Empty for now */
+
+.Ldt-SlideVideoPlayer-panel {
+    display: flex;
+    width: 50%;
+    float: left;
+}
+
+.Ldt-SlideVideoPlayer {
+    min-height: 400px;
+    max-height: 100%;
+}
+
+.Ldt-SlideVideoPlayer {
+    width: 100%;
+    max-width: 100%;
+    height: 100%;
+    max-height: 100%;
+}
+
+.Ldt-SlideVideoPlayer h2 {
+    display: none;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ldt/ldt/static/ldt/metadataplayer/SlideVideoPlayer.js	Thu Aug 20 13:25:40 2015 +0200
@@ -0,0 +1,46 @@
+IriSP.Widgets.SlideVideoPlayer = function(player, config) {
+    IriSP.loadCss(IriSP.getLib("cssSplitter"));
+    IriSP.Widgets.Widget.call(this, player, config);
+};
+
+IriSP.Widgets.SlideVideoPlayer.prototype = new IriSP.Widgets.Widget();
+
+
+IriSP.Widgets.SlideVideoPlayer.prototype.defaults = {
+};
+
+IriSP.Widgets.SlideVideoPlayer.prototype.template = '<div class="Ldt-SlideVideoPlayer">\
+<div class="Ldt-SlideVideoPlayer-slide Ldt-SlideVideoPlayer-panel">\
+</div>\
+<div class="Ldt-SlideVideoPlayer-video Ldt-SlideVideoPlayer-panel"></div>\
+</div>';
+
+IriSP.Widgets.SlideVideoPlayer.prototype.draw = function() {
+    var _this = this;
+
+    _this.renderTemplate();
+    this.insertSubwidget(
+        _this.$.find(".Ldt-SlideVideoPlayer-panel.Ldt-SlideVideoPlayer-slide"),
+            {
+                type: "ImageDisplay",
+                annotation_type: _this.annotation_type,
+                width: '100%'
+            },
+            "slide"
+        );
+    this.insertSubwidget(
+        _this.$.find(".Ldt-SlideVideoPlayer-panel.Ldt-SlideVideoPlayer-video"),
+            {
+                type: "HtmlPlayer",
+                video: _this.video,
+                width: '100%',
+                url_transform: _this.url_transform
+            },
+            "player"
+        );
+    // FIXME: this should be better implemented through a signal sent
+    // when widgets are ready (and not just loaded)
+    window.setTimeout(function () {
+        _this.$.find(".Ldt-SlideVideoPlayer").split({ orientation: 'vertical' });
+    }, 1000);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ldt/ldt/static/ldt/metadataplayer/SpelSyncHtml.js	Thu Aug 20 13:25:40 2015 +0200
@@ -0,0 +1,26 @@
+IriSP.Widgets.SpelSyncHtml = function(player, config) {
+    console.log("Calling IriSP.Widget's constructor from IriSP.HelloWorldWidget");
+    IriSP.Widgets.Widget.call(this, player, config);
+};
+
+IriSP.Widgets.SpelSyncHtml.prototype = new IriSP.Widgets.Widget();
+
+IriSP.Widgets.SpelSyncHtml.prototype.defaults = {
+    src: "about:blank"
+};
+
+IriSP.Widgets.SpelSyncHtml.prototype.template =
+    '<div class="Ldt-SpelSyncHtml"><iframe src="{{src}}" {{#width}}width="{{width}}"{{/width}} {{#height}}height="{{height}}"{{/height}}</div>';
+
+IriSP.Widgets.SpelSyncHtml.prototype.draw = function() {
+    this.renderTemplate();
+    var frame = this.$.find("iframe")[0],
+        basesrc = this.src.replace(/#.*$/,'');
+    this.getWidgetAnnotations().forEach(function(a) {
+        a.on("enter", function() {
+            if (a.content && a.content.data && a.content.data.ref_text) {
+                frame.src = basesrc + '#' + a.content.data.ref_text;
+            }
+        });
+    });
+};
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ldt/ldt/static/ldt/metadataplayer/Title.js	Thu Aug 20 13:25:40 2015 +0200
@@ -0,0 +1,17 @@
+IriSP.Widgets.Title = function(player, config) {
+    IriSP.Widgets.Widget.call(this, player, config);
+};
+
+IriSP.Widgets.Title.prototype = new IriSP.Widgets.Widget();
+
+IriSP.Widgets.Title.prototype.defaults = {
+    media_title: false
+};
+
+IriSP.Widgets.Title.prototype.template =
+    '<div class="Ldt-TitleWidget"><h2>{{#media_title}}{{media.title}}{{/media_title}}{{^media_title}}{{source.title}}{{/media_title}}</h2></div>';
+
+
+IriSP.Widgets.Title.prototype.draw = function() {
+    this.renderTemplate();
+};
\ No newline at end of file
--- a/src/ldt/ldt/static/ldt/metadataplayer/Trace.js	Wed Jul 22 15:03:14 2015 +0200
+++ b/src/ldt/ldt/static/ldt/metadataplayer/Trace.js	Thu Aug 20 13:25:40 2015 +0200
@@ -68,9 +68,9 @@
     
     
     this.tracer.trace("TraceWidgetInit", {});
-    
+    _this.player.trigger("trace-ready");
     this.mouseLocation = '';
-    IriSP.jQuery(".Ldt-Widget").on("click mouseenter mouseleave", ".Ldt-TraceMe", function(_e) {
+    IriSP.jQuery(".Ldt-Widget").on("mousedown mouseenter mouseleave", ".Ldt-TraceMe", function(_e) {
         var _target = IriSP.jQuery(this);
         
         var _widget = _target.attr("widget-type") || _target.parents(".Ldt-Widget").attr("widget-type"),
@@ -127,4 +127,4 @@
     if (this.js_console && typeof window.console !== "undefined" && typeof console.log !== "undefined") {
         console.log("tracer.trace('" + _traceName + "', " + JSON.stringify(_arg) + ");");
     }
-};
\ No newline at end of file
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ldt/ldt/static/ldt/metadataplayer/Transcript.js	Thu Aug 20 13:25:40 2015 +0200
@@ -0,0 +1,37 @@
+/* This widget displays annotations as a transcript */
+
+IriSP.Widgets.Transcript = function(player, config) {
+    IriSP.Widgets.Widget.call(this, player, config);
+}
+
+IriSP.Widgets.Transcript.prototype = new IriSP.Widgets.Widget();
+
+IriSP.Widgets.Transcript.prototype.defaults = {
+    annotation_type: "Caption"
+    // container: "transcriptContainer"
+}
+
+IriSP.Widgets.Transcript.prototype.template = '<div class="Ldt-TranscriptWidget"></div>';
+
+IriSP.Widgets.Transcript.prototype.annotationTemplate = '<a data-begin="{{ begin }}" data-end="{{ end }}" data-id="{{ id }}" class="Ldt-Transcript-Annotation" href="#{{id}}">{{ content }}</a> ';
+
+IriSP.Widgets.Transcript.prototype.draw = function() {
+    var _annotations = this.getWidgetAnnotations();
+    var _this = this;
+    var content;
+
+    _this.renderTemplate();
+    content = _this.$.find('.Ldt-TranscriptWidget');
+
+    _annotations.forEach(function(_a) {
+        var _data = {
+            id : _a.id,
+            content : IriSP.textFieldHtml(_a.title),
+            begin : _a.begin.toString(),
+            end : _a.end.toString()
+        };
+        var _html = Mustache.to_html(_this.annotationTemplate, _data);
+        var _el = IriSP.jQuery(_html);
+        content.append(_el);
+    });
+};
Binary file src/ldt/ldt/static/ldt/metadataplayer/img/hand_left.png has changed
Binary file src/ldt/ldt/static/ldt/metadataplayer/img/hand_right.png has changed
Binary file src/ldt/ldt/static/ldt/metadataplayer/img/reel.gif has changed