Mashup editing and preview
authorveltr
Tue, 30 Oct 2012 18:44:45 +0100
changeset 27 b2d068afdbd8
parent 26 7c394ea40f28
child 28 2100ddc308f0
Mashup editing and preview
integration/css/edition.css
integration/data/bpidata.json
integration/data/moon.json
integration/edition.html
integration/js/editor.js
integration/js/medialist-serializer.js
integration/js/model.js
integration/lib/mustache.js
--- a/integration/css/edition.css	Mon Oct 29 18:11:09 2012 +0100
+++ b/integration/css/edition.css	Tue Oct 30 18:44:45 2012 +0100
@@ -33,7 +33,7 @@
 	color: #333333;
 	font-size: 14px;
 }
-.title-video-wrap p.time-length span{
+.mashup-total-duration {
 	color: #de2500;
 }
 /* popin - update-title */
@@ -122,32 +122,22 @@
     display: none;
 }
 
+/* frise */
 .frise{
 	width: 458px;
-	position: relative;
 	height: 22px;
 	overflow: hidden;
-}
-.frise .segment{
-	height: 22px;
-}
-.frise span.indication{
-	height: 22px;
-	line-height: 22px;
-}
-/* frise */
-.frise{
 	position: relative;
 	border: 1px solid #333333;
 }
-.segments,.indications{
+.frise-segments,.frise-indications{
 	width: 100%;
 	height: 100%;
 	position: absolute;
 	top: 0;
 	left: 0;
 }
-.indications{
+.frise-indications{
 	z-index: 2;
 }
 .aucun-segment{
@@ -157,8 +147,10 @@
 	line-height: 20px;
 	width: 100%;
 	z-index:0;
+	background: #CCCCCC;
 }
-.frise .segment{
+.frise-segment{
+	height: 22px;
 	position: absolute;
 	background-image: url(../img/border-right-segment.png);
 	background-repeat: repeat-y;
@@ -167,15 +159,17 @@
   	-webkit-box-shadow: inset 0px 2px 2px 0px #333;
   	box-shadow: inset 0px 2px 2px 0px #333;
 }
-.frise .segment:first-child{
+.frise-segment:first-child{
 	-moz-box-shadow: inset 2px 2px 2px 0px #333;
   	-webkit-box-shadow: inset 2px 2px 2px 0px #333;
   	box-shadow: inset 2px 2px 2px 0px #333;
 }
-.frise .segment:last-child{
+.frise-segment:last-child{
 	background-image: none;
 }
-.frise span.indication{
+.frise-indication{
+	height: 22px;
+	line-height: 22px;
 	display: inline-block;
 	position: absolute;
 	color: #FFF;
@@ -187,6 +181,9 @@
 	background: url(../img/bg-indication.png) 15px 0 no-repeat;
 	margin-left: -15px;
 }
+.frise-position {
+    width: 1px; margin-left: -0.5px; position: absolute; height: 100%; background: #FF00FC;
+}
 /* col-middle - bloc-segmentation */
 .bloc-segmentation{
 	position: relative;
@@ -417,10 +414,10 @@
 	position: relative;
 	height: 20px;
 }
-.col-right .frise .segment{
+.col-right .frise-segment{
 	height: 20px;
 }
-.col-right .frise .indication{
+.col-right .frise-indication{
 	height: 20px;
 	line-height: 20px;
 }
@@ -435,11 +432,14 @@
 .mashup-description img.pointer{
 	right: 36px;
 }
-.mashup-description h2{
+.annotation-title {
 	color: #de2500;
 	font-size: 14px;
 	font-weight: bold;
 }
+.annotation-time {
+    color: #7628DF;
+}
 .mashup-description table{
 	font-size: 12px;
 }
@@ -532,6 +532,8 @@
 .item-video img{
 	float: left;
 	margin-right: 4px;
+	max-width: 80px;
+	max-height: 60px;
 	box-shadow: 2px 2px 2px #333333;
 }
 .item-video .video-info{
@@ -602,6 +604,7 @@
     background: url(../img/publier-button-sprite.png) 0 0 no-repeat;
     left: 382px;
     top: 10px;
+    z-index: 4;
 }
 a.publier-button:hover{
     background-position: 0 -67px;
--- a/integration/data/bpidata.json	Mon Oct 29 18:11:09 2012 +0100
+++ b/integration/data/bpidata.json	Tue Oct 30 18:44:45 2012 +0100
@@ -69,5 +69,19 @@
         "author": "Harun Farocki",
         "thumbnail": "thumbnails/comparaison-80.jpg",
         "duration": 3707000
+    }, {
+        "id": "fabcc0ae-2287-11e2-a537-00145ea4a2be",
+        "video": "http://media.iri.centrepompidou.fr/video/mashup/bpimashup_lesfiguiersdebarbarieontilsuneame.mp4",
+        "title": "Les figuiers de Barbarie ont-ils une âme ?",
+        "author": "Rachel Mizrahi, Gilles Dinnematin",
+        "thumbnail": "thumbnails/figuiers-80.jpg",
+        "duration": 3356000
+    }, {
+        "id": "75de2d46-2287-11e2-aaec-00145ea4a2be",
+        "video": "http://media.iri.centrepompidou.fr/video/mashup/bpimashup_heureduberger.mp4",
+        "title": "L'heure du berger",
+        "author": "Pierre Creton",
+        "thumbnail": "thumbnails/berger-80.jpg",
+        "duration": 2333000
     }
 ]
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/integration/data/moon.json	Tue Oct 30 18:44:45 2012 +0100
@@ -0,0 +1,24 @@
+[
+    {
+        "id": "melies",
+        "video": "http://www.iri.centrepompidou.fr/dev/~veltr/mashup/moon/melies.mp4",
+        "title": "Le voyage à la lune",
+        "author": "Georges Méliès",
+        "thumbnail": "http://www.iri.centrepompidou.fr/dev/~veltr/mashup/moon/melies.jpg",
+        "duration": 674000
+    }, {
+        "id": "apollo",
+        "video": "http://www.iri.centrepompidou.fr/dev/~veltr/mashup/moon/apollo.mp4",
+        "title": "Apollo 11 Overview",
+        "author": "NASA",
+        "thumbnail": "http://www.iri.centrepompidou.fr/dev/~veltr/mashup/moon/apollo.jpg",
+        "duration": 137000
+    }, {
+        "id": "juno",
+        "video": "http://www.iri.centrepompidou.fr/dev/~veltr/mashup/moon/juno.mp4",
+        "title": "Launch of Juno!",
+        "author": "NASA",
+        "thumbnail": "http://www.iri.centrepompidou.fr/dev/~veltr/mashup/moon/atlas.jpg",
+        "duration": 130000
+    }
+]
\ No newline at end of file
--- a/integration/edition.html	Mon Oct 29 18:11:09 2012 +0100
+++ b/integration/edition.html	Tue Oct 30 18:44:45 2012 +0100
@@ -26,11 +26,11 @@
                             <div class="form-left">
                                 <p class="titre-wrap">
                                     <label for="hashcut-title">Titre :</label>
-                                    <input type="text" id="hashcut-title" name="" value="Mon Hashcut" />
+                                    <input type="text" id="hashcut-title" name="" value="Hashcut sans titre" />
                                 </p>
                                 <p>
                                     <label for="hashcut-tags">Tags :</label>
-                                    <input type="text" id="hashcut-tags" name="" value="Lune, Espace, Lune" />
+                                    <input type="text" id="hashcut-tags" name="" value="" />
                                 </p>
                             </div>
                             <p class="form-right">
@@ -112,7 +112,7 @@
                 </a></h1>
                 <div class="title-video-wrap">
                     <a title="Modifier le titre et la description" class="open-popin" href="#update-title">Hashcut sans titre</a>
-                    <p class="time-length">Durée: <span>00:00</span></p>
+                    <p class="time-length">Durée: <span class="mashup-total-duration">00:00</span></p>
                 </div>
                 <div class="profil-wrap">
                     <a href="#" class="all-hashcut">Tous les Hashcuts</a>
@@ -257,21 +257,11 @@
                     <div class="bloc-pvw">
 
                         <div class="frise clearfix">
-
-                            <div class="indications">
-                                <span class="indication" style="left:24%;">00:30</span>
-                                <span class="indication" style="left:44%;">00:30</span>
-                                <span class="indication" style="left:64%;">00:30</span>
-                                <span class="indication" style="left:84%;">00:30</span>
+                            <div class="frise-indications">
                             </div>
-
-                            <div class="segments">
-                                <div class="segment" style="background-color:red;left:0;width:20%;"></div>
-                                <div class="segment" style="background-color:green;left:20%;width:20%;"></div>
-                                <div class="segment" style="background-color:red;left:40%;width:20%;"></div>
-                                <div class="segment" style="background-color:green;left:60%;width:20%;"></div>
-                                <div class="segment" style="background-color:red;left:80%;width:20%;"></div>
+                            <div class="frise-segments">
                             </div>
+                            <div class="frise-position"></div>
                             
                         </div>
                             
@@ -279,26 +269,23 @@
                                 <img class="pointer" src="img/popin-triangle.png" alt="" />
                                 <div class="popin-content">
                                     <ul class="tools">
-                                        <li><a title="éditer" class="edit" href="#"></a></li>
-                                        <li><a title="descendre" class="bottom" href="#"></a></li>
-                                        <li><a title="remonter" class="top" href="#"></a></li>
-                                        <li><a title="supprimer" class="delete" href="#"></a></li>
+                                        <li><a title="Éditer" class="edit" href="#"></a></li>
                                     </ul>
 
-                                    <h2>Chargement de la capsule lunaire</h2>
+                                    <h2><span class="annotation-title"></span></h2>
                                     <table>
                                         <tbody>
                                             <tr>
                                                 <th>Extrait de :</th>
-                                                <td>Le Voyage dans la Lune (05:01 - 05:24)</td>
+                                                <td><span class="annotation-media-title"></span> (<span class="annotation-time annotation-begin"></span> - <span class="annotation-time annotation-end"></span>)</td>
                                             </tr>
                                             <tr>
                                                 <th>Description :</th>
-                                                <td>Lors d'un colloque d'astronomie, le professeur Barbenfouillis crée l'événement en faisant part à l'assemblée de son projet de voyage dans la Lune. Il organise ensuite la visite à ses confrères de l'atelier où l'obus spatial est en chantier. Il sera propulsé en direction de la Lune au moyen d'un canon géant.</td>
+                                                <td><span class="annotation-description"></span></td>
                                             </tr>
                                             <tr>
                                                 <th>Tags :</th>
-                                                <td>astronomie, lune, fiction, histoire du cinéma, canon, projectile</td>
+                                                <td><span class="annotation-tags">(fonctionnalité à venir)</span></td>
                                             </tr>
                                         </tbody>
                                     </table>
@@ -313,16 +300,9 @@
                         <h2>Liste des segments</h2>
                         <div class="frise clearfix">
 							<p class="aucun-segment">Aucun segment</p>
-                            <div class="indications">
-                                <span class="indication" style="left:64%;">00:30</span>
-                                <span class="indication" style="left:84%;">00:30</span>
+                            <div class="frise-indications">
                             </div>
-                            <div class="segments">
-                                <div class="segment" style="background-color:red;left:0;width:20%;"></div>
-                                <div class="segment" style="background-color:yellow;left:20%;width:20%;"></div>
-                                <div class="segment" style="background-color:blue;left:40%;width:20%;"></div>
-                                <div class="segment" style="background-color:green;left:60%;width:20%;"></div>
-                                <div class="segment" style="background-color:brown;left:80%;width:20%;"></div>
+                            <div class="frise-segments">
                             </div>
 
                         </div>
@@ -350,7 +330,6 @@
         <!-- JavaScript -->
         <script type="text/javascript" src="lib/jquery.min.js"></script>
         <script type="text/javascript" src="lib/jquery-ui.min.js"></script>
-        <script type="text/javascript" src="lib/mustache.js"></script>
         <script type="text/javascript" src="lib/underscore-min.js"></script>
         <script type="text/javascript" src="lib/popcorn-complete.min.js"></script>
         <script type="text/javascript" src="lib/raphael-min.js"></script>
@@ -359,5 +338,10 @@
         <script type="text/javascript" src="js/model.js"></script>
         <script type="text/javascript" src="js/editor.js"></script>
         <script type="text/javascript" src="js/common.js"></script>
+        <script type="text/javascript">
+    $(function() {
+        var hashcut = new IriSP.Hashcut({url: "data/moon.json"});
+    });
+        </script>
     </body>
 </html>
--- a/integration/js/editor.js	Mon Oct 29 18:11:09 2012 +0100
+++ b/integration/js/editor.js	Tue Oct 30 18:44:45 2012 +0100
@@ -1,28 +1,31 @@
-IriSP.Hashcut = function() {
+IriSP.Hashcut = function(options) {
     
     /* Load Media List */
     
     var directory = new IriSP.Model.Directory(),
         project = directory.remoteSource({
-            url: "data/bpidata.json",
+            url: options.url,
             serializer: IriSP.serializers.medialist
         }),
         mashup = new IriSP.Model.Mashup(false, project),
-        mediatemplate = '<li class="item-video" data-media-id="{{id}}"><img src="{{thumbnail}}" alt="{{title}}" />'
-            + '<span class="video-info"><span class="title-video">{{title}}</span><span class="author">{{description}}</span>'
-            + '<span class="time-length">Durée : <span>{{duration}}</span></span></span></li>',
-        segmenttemplate = '<li class="item-video" data-segment-id="{{id}}"><img src="{{annotation.thumbnail}}" alt="{{media_title}}" />'
-            + '<span class="video-info"><span class="title-video">{{media_title}}</span>'
-            + '<span class="subtitle">{{title}}</span><span class="duration">{{begin}} - {{end}} ({{duration}})</span>'
-            + '<ul class="tools"><li><a class="edit" href="#"></a></li><li><a class="bottom" href="#"></a></li>'
-            + '<li><a class="top" href="#"></a></li><li><a class="delete" href="#"></a></li></ul></span></li>';
+        mediatemplate = _.template('<li class="item-video" data-media-id="<%= id %>"><img src="<%= thumbnail %>" alt="<%= title %>" />'
+            + '<span class="video-info"><span class="title-video"><%= title %></span><span class="author"><%= description %></span>'
+            + '<span class="time-length">Durée : <span><%= duration.toString() %></span></span></span></li>'),
+        segmenttemplate = _.template('<li class="item-video" data-segment-id="<%= annotation.id %>" data-media-id="<%= annotation.getMedia().id %>">'
+            + '<img src="<%= annotation.getMedia().thumbnail %>" alt="<%= annotation.getMedia().title %>" />'
+            + '<span class="video-info"><span class="title-video"><%= annotation.getMedia().title %></span>'
+            + '<span class="subtitle"><%= annotation.title %></span><span class="duration"><%= annotation.begin.toString() %> - <%= annotation.end.toString() %> (<%= annotation.getDuration().toString() %>)</span>'
+            + '<ul class="tools"><li><a class="edit" href="#" title="Éditer le segment"></a></li><li><a class="bottom" href="#" title="Descendre le segment"></a></li>'
+            + '<li><a class="top" href="#" title="Remonter le segment"></a></li><li><a class="delete" href="#" title="Supprimer le segment"></a></li></ul></span></li>'),
+        viztemplate = _.template('<div class="frise-segment" style="background-color:<%= color %>; left:<%= left %>%; width:<%= width %>%;"></div>'),
+        intervaltemplate = _.template('<span class="frise-indication" style="left:<%= left %>%;"><%= time.toString() %></span>');
 
     /* Fill left column with Media List */
 
     project.onLoad(function() {
         var html = '';
         project.getMedias().forEach(function(_m) {
-            html += Mustache.to_html(mediatemplate, _m);
+            html += mediatemplate(_m);
         });
         $(".col-left .list-video").html(html);
     });
@@ -55,15 +58,58 @@
     
     /* Fill right column when mashup is updated */
    
-    function fillRightColumn() {
-        var html = '';
-        mashup.segments.forEach(function(_s) {
-            html += Mustache.to_html(segmenttemplate, _s);
-        });
-        $(".col-right .list-video").html(html);
+    function setPointerToCurrentAnnotation() {
+        if (mashupCurrentAnnotation) {
+            var p = (mashupCurrentAnnotation.begin + mashupCurrentAnnotation.end) / (2 * mashup.duration);
+            $(".mashup-description .pointer").css("left", (100 * p) + "%");
+        }
     }
     
-    mashup.on("add-segments",fillRightColumn);
+    function updateMashupUI() {
+        var listhtml = '', vizhtml = '', t = 0, k = mashup.duration ? (100 / mashup.duration) : 0;
+        mashup.segments.forEach(function(_s) {
+            listhtml += segmenttemplate(_s);
+            var vizdata = {
+                left: k * t,
+                width: k * _s.duration,
+                color: _s.getMedia().color
+            }
+            vizhtml += viztemplate(vizdata);
+            t += _s.duration.milliseconds;
+        });
+        
+        var intervals = [ 1000, 2000, 5000, 10000, 30000, 60000, 120000, 300000, 600000, 900000, 1800000, 3600000, 7200000 ];
+        
+        function createIntervals(maxn) {
+            for (var i = 0; i < intervals.length; i++) {
+                if (mashup.duration / intervals[i] <= maxn) {
+                    var html = '';
+                    for (var j = intervals[i]; j < mashup.duration; j += intervals[i]) {
+                        html += intervaltemplate({ left: k * j, time: new IriSP.Model.Time(j) });
+                    }
+                    return html;
+                }
+            }
+            return "";
+        }
+        
+        $(".col-right .list-video").html(listhtml).find(".item-video:last-child .bottom, .item-video:first-child .top").addClass("disable");
+        $(".mashup-total-duration").text(mashup.duration.toString());
+        $(".frise-segments").html(vizhtml);
+        $(".col-right .frise-indications").html(createIntervals(4));
+        $(".bloc-pvw .frise-indications").html(createIntervals(8));
+        highlightCurrentSegment();
+        if (currentMedia === mashup) {
+            $(".Ldt-Ctrl-Time-Total").text(currentMedia.duration.toString());
+            if (mashupTimecode > mashup.duration) {
+                mashup.setCurrentTime(mashup.duration);
+            }
+            changeCurrentAnnotation();
+            setPointerToCurrentAnnotation();
+        }
+    }
+    
+    mashup.on("change",updateMashupUI);
     
     /* Slider */
    
@@ -229,13 +275,13 @@
     });
     
     $(".Ldt-Ctrl-SetIn").click(function() {
-        if (currentMedia && currentMedia.currentSegment) {
-            currentMedia.currentSegment.setBegin(currentMedia.getCurrentTime());
+        if (currentMedia && currentSegment) {
+            currentSegment.setBegin(currentMedia.getCurrentTime());
         }
     });
     $(".Ldt-Ctrl-SetOut").click(function() {
-        if (currentMedia && currentMedia.currentSegment) {
-            currentMedia.currentSegment.setEnd(currentMedia.getCurrentTime());
+        if (currentMedia && currentSegment) {
+            currentSegment.setEnd(currentMedia.getCurrentTime());
         }
     });
     
@@ -257,12 +303,12 @@
             }
         },
         slide: function(event, ui) {
-            if (currentMedia && currentMedia.currentSegment) {
+            if (currentMedia && currentSegment) {
                 var t = currentMedia.duration * ui.value / slidersRange;
                 if (ui.value === ui.values[0]) {
-                    currentMedia.currentSegment.setBegin(t);
+                    currentSegment.setBegin(t);
                 } else {
-                    currentMedia.currentSegment.setEnd(t);
+                    currentSegment.setEnd(t);
                 }
             }
         }
@@ -271,15 +317,15 @@
     sliceSlider.find(".ui-slider-handle:first")
         .addClass("Ldt-Slice-left-handle")
         .click(function() {
-            if (currentMedia && currentMedia.currentSegment) {
-                currentMedia.setCurrentTime(currentMedia.currentSegment.begin);
+            if (currentMedia && currentSegment) {
+                currentMedia.setCurrentTime(currentSegment.begin);
             }
         });
     sliceSlider.find(".ui-slider-handle:last")
         .addClass("Ldt-Slice-right-handle")
         .click(function() {
-            if (currentMedia && currentMedia.currentSegment) {
-                currentMedia.setCurrentTime(currentMedia.currentSegment.end);
+            if (currentMedia && currentSegment) {
+                currentMedia.setCurrentTime(currentSegment.end);
             }
         });
     
@@ -304,71 +350,155 @@
         timeSlider.slider("value",slidersRange * _time / currentMedia.duration);
     }
     
+    /* Mashup Player */
+
+    var mashupCurrentMedia = null,
+        mashupCurrentAnnotation = null,
+        mashupSegmentBegin,
+        mashupSegmentEnd,
+        mashupTimecode = 0,
+        mashupSeeking = false,
+        mashupSeekdiv,
+        mashupTimedelta;
+        
+    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 (mashupCurrentMedia) {
+                mashupCurrentMedia.pause();
+                if (!mashup.paused) {
+                    mashup.paused = true;
+                    mashup.trigger("pause");
+                }
+            }
+            return;
+        }
+        mashupCurrentAnnotation = _annotation;
+        mashupSegmentBegin = mashupCurrentAnnotation.annotation.begin.milliseconds;
+        mashupSegmentEnd = mashupCurrentAnnotation.annotation.end.milliseconds;
+        mashupTimedelta = mashupSegmentBegin - mashupCurrentAnnotation.begin.milliseconds;
+        mashupCurrentMedia = mashupCurrentAnnotation.getMedia();
+        
+        project.getMedias().forEach(function(_media) {
+            if (_media !== mashupCurrentMedia) {
+                _media.hide();
+                _media.pause();
+            } else {
+                _media.show();
+            }
+        });
+        
+        mashupCurrentMedia.setCurrentTime( mashupTimecode + mashupTimedelta);
+        mashupCurrentMedia.seeking = true;
+        
+        if (!mashup.paused) {
+            mashupCurrentMedia.play();
+            mashupSeeking = true;
+//TODO:            _seekdiv.show();
+        }
+        mashup.trigger("timeupdate", new IriSP.Model.Time(mashupTimecode));
+
+    }
+    
     /* Set current Media */
    
-    var currentMedia;
+    var currentMedia, currentSegment;
     
     function updateSliderAndTangles() {
-        if (currentMedia && currentMedia.currentSegment) {
-            var start = currentMedia.currentSegment.begin,
-                end = currentMedia.currentSegment.end,
-                dur = currentMedia.currentSegment.getDuration(),
-                f = slidersRange / currentMedia.duration;
+        if (currentMedia && currentSegment) {
+            var start = currentSegment.begin,
+                end = currentSegment.end,
+                dur = currentSegment.getDuration(),
+                f = slidersRange / currentMedia.duration,
+                tangleStart = $(".tangle-start"),
+                tangleEnd = $(".tangle-end"),
+                tangleDuration = $(".tangle-duration");
             sliceSlider.slider( "values", [ f * start, f * end ] );
-            $(".tangle-start").text(start.toString()).attr("data-milliseconds",start.milliseconds);
-            $(".tangle-end").text(end.toString()).attr("data-milliseconds",end.milliseconds);
-            $(".tangle-duration").text(dur.toString()).attr("data-milliseconds",dur.milliseconds);
+            tangleStart.text(start.toString(tangleStart.hasClass("active"))).attr("data-milliseconds",start.milliseconds);
+            tangleEnd.text(end.toString(tangleEnd.hasClass("active"))).attr("data-milliseconds",end.milliseconds);
+            tangleDuration.text(dur.toString(tangleDuration.hasClass("active"))).attr("data-milliseconds",dur.milliseconds);
             $(".segment-info .pointer").css("left",(parseFloat($(".Ldt-Slice-left-handle").css("left")) + parseFloat($(".Ldt-Slice-right-handle").css("left")))/2)
         }
     }
     
-    function setMedia(mediaid) {
+    var addMode;
+    
+    function setMedia(media) {
         $(".col-left .item-video").removeClass("active");
         $(".tutorial").hide();
-        $("video").hide();
         if (currentMedia) {
             currentMedia.pause();
         }
-        currentMedia = project.getElement(mediaid);
+        currentMedia = media;
         if (currentMedia.elementType == "media") {
-            $(".col-left .item-video[data-media-id='" + mediaid + "']").addClass("active");
+            $("video").hide();
+            $(".col-left .item-video[data-media-id='" + currentMedia.id + "']").addClass("active");
             showSegmentation();
-            var currentvideo = $('#video_' + mediaid);
+            var currentvideo = $('#video_' + currentMedia.id);
             if (!currentvideo.length) {
                 addMediaPlayer(currentMedia);
-                currentvideo = $('#video_' + mediaid);
             }
             $(".tab-media-title").text(currentMedia.title);
-            if (!currentMedia.currentSegment) {
-                currentMedia.currentSegment = new IriSP.Model.Annotation(false, project);
-                currentMedia.currentSegment.setMedia(currentMedia.id);
-                currentMedia.currentSegment.setBegin(0);
-                currentMedia.currentSegment.setEnd(currentMedia.duration);
-                currentMedia.currentSegment.thumbnail = currentMedia.thumbnail;
-                currentMedia.currentSegment.title = "Segment sans titre";
-                currentMedia.currentSegment.description = "Extrait de « " + currentMedia.title + " »";
-                currentMedia.currentSegment.on("change-begin", function() {
-                    if (currentMedia && currentMedia.currentSegment === this) {
+            
+            addMode = !(currentSegment && mashup.hasAnnotation(currentSegment));
+            
+            if (!currentSegment) {
+                currentSegment = new IriSP.Model.Annotation(false, project);
+                currentSegment.setMedia(currentMedia.id);
+                currentSegment.setBegin(0);
+                currentSegment.setEnd(currentMedia.duration);
+                currentSegment.title = "Segment sans titre";
+                currentSegment.description = "Extrait de « " + currentMedia.title + " »";
+                currentSegment.on("change-begin", function() {
+                    if (currentMedia && currentSegment === this) {
                         currentMedia.setCurrentTime(this.begin);
                         updateSliderAndTangles();
                     }
                 });
-                currentMedia.currentSegment.on("change-end", function() {
-                    if (currentMedia && currentMedia.currentSegment === this) {
+                currentSegment.on("change-end", function() {
+                    if (currentMedia && currentSegment === this) {
                         currentMedia.setCurrentTime(this.end);
                         updateSliderAndTangles();
                     }
                 });
+                currentSegment.on("enter", function() {
+                    if (currentMedia === mashup) {
+                        $(".annotation-title").text(this.title);
+                        $(".annotation-begin").text(this.begin.toString());
+                        $(".annotation-end").text(this.end.toString());
+                        $(".annotation-media-title").text(this.getMedia().title);
+                        $(".annotation-description").text(this.description);
+                        setPointerToCurrentAnnotation();
+                    }
+                })
             }
+            if (currentMedia.loaded) {
+                currentMedia.setCurrentTime(currentSegment.begin);
+            }
+            $(".add-segment").val(addMode ? "Ajouter au Hashcut" : "Sauvegarder");
+            $(".create-or-edit").text(addMode ? "Créer un nouveau segment" : "Modifier le segment");
             updateSliderAndTangles();
+            media.show();
+            $("#segment-title").val(currentSegment.title);
+            $("#segment-description").val(currentSegment.description);
+            $("#segment-tags").val("");
         }
-        currentvideo.show();
+        if (currentMedia.elementType === "mashup") {
+            showPreview();
+            mashup.checkLoaded();
+        }
         $(".Ldt-Ctrl-Time-Total").text(currentMedia.duration.toString());
-        $("#segment-title").val(currentMedia.currentSegment.title);
-        $("#segment-description").text(currentMedia.currentSegment.description);
         // TODO: Do something with the tags !
         onCurrentMediaTimeupdate(currentMedia.getCurrentTime());
         onCurrentMediaPause();
+        highlightCurrentSegment();
     }
     
     function addMediaPlayer(media) {
@@ -401,31 +531,48 @@
         });
         videoEl.append(mp4_src).append(webm_src);
         $(".video").append(videoEl);
-
+        
+        media.show = function() {
+            videoEl.show();
+        }
+        media.hide = function() {
+            videoEl.hide();
+        }
+        
         var popcorn = Popcorn("#" + videoid);
         
         // Binding functions to Popcorn
         
         media.on("setcurrenttime", function(_milliseconds) {
-            popcorn.currentTime(_milliseconds / 1000);
+            if (media.loaded) {
+                popcorn.currentTime(_milliseconds / 1000);
+            }
         });
         
         media.on("setvolume", function(_vol) {
-            popcorn.volume(_vol);
             media.volume = _vol;
+            if (media.loaded) {
+                popcorn.volume(_vol);
+            }
         });
         
         media.on("setmuted", function(_muted) {
-            popcorn.muted(_muted);
             media.muted = _muted;
+            if (media.loaded) {
+                popcorn.muted(_muted);
+            }
         });
         
         media.on("setplay", function() {
-            popcorn.play();
+            if (media.loaded) {
+                popcorn.play();
+            }
         });
         
         media.on("setpause", function() {
-            popcorn.pause();
+            if (media.loaded) {
+                popcorn.pause();
+            }
         });
         
         // Binding Popcorn events to media
@@ -437,6 +584,7 @@
         
         popcorn.on("loadedmetadata", function() {
             getVolume();
+            media.loaded = true;
             media.trigger("loadedmetadata");
             media.trigger("volumechange");
         })
@@ -462,55 +610,162 @@
             media.trigger("seeked");
         });
         
-        // Binding UI Events to Media
+        // Binding UI Events and Mashup Playing to Media
+        
+        media.on("loadedmetadata", function() {
+            mashup.checkLoaded();
+        });
         
         media.on("play", function() {
             if (media === currentMedia) {
                 onCurrentMediaPlay();
             }
+            if (mashup === currentMedia && media === mashupCurrentMedia) {
+                mashup.trigger("play");
+            }
         });
         
         media.on("pause", function() {
             if (media === currentMedia) {
                 onCurrentMediaPause();
             }
+            if (mashup === currentMedia && media === mashupCurrentMedia) {
+                mashup.trigger("pause");
+            }
         });
         
         media.on("timeupdate", function(_time) {
             if (media === currentMedia) {
                 onCurrentMediaTimeupdate(_time);
             }
+            if (mashup === currentMedia && !mashup.paused && media === mashupCurrentMedia && !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 (mashup === currentMedia && media === mashupCurrentMedia && mashupSeeking) {
+                mashupSeeking = false;
+//TODO:                _seekdiv.hide();
+            }
         });
         
         media.on("volumechange", function() {
             if (media === currentMedia) {
                 ctrlVolumeUpdater();
             }
+            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 (mashupCurrentMedia) {
+            mashupCurrentMedia.pause();
+        }
+    });
+    
+    mashup.on("loadedmetadata", function() {
+        if (mashup === currentMedia) {
+            changeCurrentAnnotation();
+        }
+    });
+    
+    /* Mashup Events to UI */
+   
+    mashup.on("play", function() {
+        if (mashup === currentMedia) {
+            onCurrentMediaPlay();
+        }
+    });
+    
+    mashup.on("pause", function() {
+        if (mashup === currentMedia) {
+            onCurrentMediaPause();
+        }
+    });
+    
+    mashup.on("timeupdate", function(_time) {
+        if (mashup === currentMedia) {
+            $(".frise-position").css("left",(100*_time/mashup.duration)+"%");
+            onCurrentMediaTimeupdate(_time);
+        }
+    });
+        
     /* Segment Form interaction */
    
     $("#segment-title").on("keyup change input paste", function() {
-        if (currentMedia && currentMedia.currentSegment) {
-            currentMedia.currentSegment.title = $(this).val();
+        if (currentMedia && currentSegment) {
+            currentSegment.title = $(this).val();
+            updateMashupUI();
         }
     });
     $("#segment-description").on("keyup change input paste", function() {
-        if (currentMedia && currentMedia.currentSegment) {
-            currentMedia.currentSegment.title = $(this).val();
+        if (currentMedia && currentSegment) {
+            currentSegment.description = $(this).val();
         }
     });
     $("#segment-form").submit(function() {
-        mashup.addSegment(currentMedia.currentSegment);
-        currentMedia.currentSegment = undefined;
+        if (addMode) {
+            mashup.addAnnotation(currentSegment);
+        } else {
+            updateMashupUI();
+        }
+        var segment = mashup.getAnnotation(currentSegment);
+        currentSegment = undefined;
+        setMedia(mashup);
+        if (segment) {
+            mashup.setCurrentTime(segment.begin);
+        }
     })
     
     /* Click on media items */
    
     $(".col-left").on("click", ".item-video", function() {
-        setMedia($(this).attr("data-media-id"));
+        currentSegment = undefined;
+        setMedia(project.getElement($(this).attr("data-media-id")));
     });
     
     /* Click on Tabs */
@@ -524,30 +779,67 @@
         return false;
     }
     
-    $(".tab-pvw").click(showPreview);
-    
-    function disableMoveItemVideo() {
-        $(".organize-segments .top, .organize-segments .bottom").removeClass("disable");
-        $(".organize-segments .item-video:last-child .bottom, .organize-segments .item-video:first-child .top").addClass("disable");
-    }
-    disableMoveItemVideo();
-    
-    $(".organize-segments").sortable({
-        stop : function(){
-            disableMoveItemVideo();
+    $(".tab-pvw").click(function() {
+        if (mashup.segments.length) {
+            setMedia(mashup);
         }
     });
     
-    $(".organize-segments .top").click(function(e){
+    /* Click on segments */
+    
+    function reorganizeMashup() {
+        var ids = $(".organize-segments .item-video").map(function(){return $(this).attr("data-segment-id")});
+        mashup.setAnnotationsById(ids);
+    }
+    
+    function highlightCurrentSegment() {
+        $(".organize-segments .item-video").removeClass("active");
+        if (currentMedia && currentSegment) {
+            $(".item-video[data-segment-id='" + currentSegment.id + "']").addClass("active");
+        }
+    }
+    
+    $(".organize-segments").sortable({
+        stop : reorganizeMashup
+    });
+    
+    $(".organize-segments").on("click", ".item-video", function(e) {
+        var el = $(this),
+            segment = mashup.getAnnotationById(el.attr("data-segment-id"));
+        setMedia(mashup);
+        if (segment) {
+            mashup.setCurrentTime(segment.begin);
+        }
+        return false;
+    });
+    
+    $(".organize-segments").on("click", ".edit", function(e) {
+        var currentItem = $(this).parents(".item-video"),
+            media = project.getElement(currentItem.attr("data-media-id")),
+            segment = project.getElement(currentItem.attr("data-segment-id"));
+        currentSegment = segment;
+        setMedia(media);
+        return false;
+    });
+    
+    $(".organize-segments").on("click", ".top", function(e){
         var currentItem = $(this).parents(".item-video");
         currentItem.insertBefore(currentItem.prev());
-        disableMoveItemVideo();
+		reorganizeMashup();
+		return false;
     });
     
-    $(".organize-segments .bottom").click(function(e){
+    $(".organize-segments").on("click", ".bottom", function(e){
         var currentItem = $(this).parents(".item-video");
         currentItem.insertAfter(currentItem.next());
-        disableMoveItemVideo();
+		reorganizeMashup();
+        return false;
+    });
+    
+    $(".organize-segments").on("click", ".delete", function(e){
+        var id = $(this).parents(".item-video").attr("data-segment-id");
+        mashup.removeAnnotationById(id);
+        return false;
     });
     
     /* Tangles */
@@ -577,6 +869,7 @@
         })
         .mouseup(function() {
             if (activeTangle) {
+                activeTangle.text(activeTangle.text().replace(/\.\d+$/,''));
                 $(".time-tangle").removeClass("active deactivate");
                 activeTangle = undefined;
             }
@@ -584,33 +877,36 @@
         
     $(".tangle-start")
         .mouseup(function(evt) {
-            if (!tangleHasMoved && currentMedia && currentMedia.currentSegment) {
-                currentMedia.setCurrentTime(currentMedia.currentSegment.begin);
+            if (!tangleHasMoved && currentMedia && currentSegment) {
+                currentMedia.setCurrentTime(currentSegment.begin);
             }
         })
         .on("valuechange", function(evt, val) {
-            if (currentMedia && currentMedia.currentSegment) {
-                currentMedia.currentSegment.setBegin(val);
+            if (currentMedia && currentSegment) {
+                currentSegment.setBegin(val);
             }
         });
     $(".tangle-end")
         .mouseup(function(evt) {
-            if (!tangleHasMoved && currentMedia && currentMedia.currentSegment) {
-                currentMedia.setCurrentTime(currentMedia.currentSegment.end);
+            if (!tangleHasMoved && currentMedia && currentSegment) {
+                currentMedia.setCurrentTime(currentSegment.end);
             }
         })
         .on("valuechange", function(evt, val) {
-            if (currentMedia && currentMedia.currentSegment) {
-                currentMedia.currentSegment.setEnd(val);
+            if (currentMedia && currentSegment) {
+                currentSegment.setEnd(val);
             }
         });
     $(".tangle-duration").on("valuechange", function(evt, val) {
-        if (currentMedia && currentMedia.currentSegment) {
-            currentMedia.currentSegment.setDuration(val);
+        if (currentMedia && currentSegment) {
+            currentSegment.setDuration(val);
         }
     });
+    
+    $(".mashup-description .edit").click(function() {
+        if (mashupCurrentAnnotation) {
+            currentSegment = mashupCurrentAnnotation.annotation;
+            setMedia(mashupCurrentAnnotation.getMedia());
+        }
+    })
 }
-
-$(function() {
-    var hashcut = new IriSP.Hashcut();
-});
--- a/integration/js/medialist-serializer.js	Mon Oct 29 18:11:09 2012 +0100
+++ b/integration/js/medialist-serializer.js	Tue Oct 30 18:44:45 2012 +0100
@@ -3,14 +3,17 @@
         IriSP.jQuery.getJSON(_url, _callback);
     },
     deSerialize : function(_data, _source) {
+        var colors = ["#1f77b4","#ff7f0e","#2ca02c","#d62728","#9467bd","#8c564b","#e377c2","#7f7f7f","#bcbd22","#17becf"];
         var _medialist = new IriSP.Model.List(_source.directory);
-        IriSP._(_data).each(function(_m) {
+        _data = IriSP._(_data).shuffle();
+        IriSP._(_data).each(function(_m, i) {
             var _media = new IriSP.Model.Media(_m.id, _source);
             _media.video = _m.video;
             _media.title = _m.title;
             _media.description = "par " + _m.author;
             _media.setDuration(_m.duration);
             _media.thumbnail = _m.thumbnail;
+            _media.color = colors[i % colors.length];
             _medialist.push(_media);
         });
         _source.addList("media", _medialist);
--- a/integration/js/model.js	Mon Oct 29 18:11:09 2012 +0100
+++ b/integration/js/model.js	Tue Oct 30 18:44:45 2012 +0100
@@ -301,16 +301,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;
@@ -333,7 +333,8 @@
     return {
         hours : Math.floor(_totalSeconds / 3600),
         minutes : (Math.floor(_totalSeconds / 60) % 60),
-        seconds : _totalSeconds % 60
+        seconds : _totalSeconds % 60,
+        milliseconds: this.milliseconds % 1000
     } 
 }
 
@@ -345,7 +346,7 @@
     return this.milliseconds;
 }
 
-Model.Time.prototype.toString = function() {
+Model.Time.prototype.toString = function(showCs) {
     function pad(_n) {
         var _res = _n.toString();
         while (_res.length < 2) {
@@ -359,6 +360,9 @@
         _res += _hms.hours + ':'
     }
     _res += pad(_hms.minutes) + ':' + pad(_hms.seconds);
+    if (showCs) {
+        _res += "." + Math.round(_hms.milliseconds / 100)
+    }
     return _res;
 }
 
@@ -526,6 +530,9 @@
     this.trigger("setpause");
 }
 
+Model.Playable.prototype.show = function() {}
+
+Model.Playable.prototype.hide = function() {}
 
 /* */
 
@@ -680,13 +687,18 @@
     this.end = new Model.Time();
     this.duration = new Model.Time();
     this.title = this.annotation.title;
-    this.media_title = this.getMedia().title;
     this.description = this.annotation.description;
     this.color = this.annotation.color;
     var _this = this;
     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);
@@ -724,16 +736,17 @@
     this.elementType = 'mashup';
     this.duration = new Model.Time();
     this.segments = new Model.List(_source.directory);
+    this.loaded = false;
     var _currentMedia = null;
     var _this = this;
     this.on("timeupdate", function(_time) {
-        _this.getAnnotations().filter(function(_a) {
+        _this.getSegments().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) {
+        _this.getSegments().filter(function(_a) {
             return _a.begin <= _time && _a.end > _time && !_a.playing
         }).forEach(function(_a) {
             _a.playing = true;
@@ -750,13 +763,25 @@
     });
     this._updateTimes = function() {
         _this.updateTimes();
+        _this.trigger("change");
     }
-    this.on("add-segments", this._updateTimes);
-    this.on("remove-segments", this._updateTimes);
+    this.on("add", this._updateTimes);
+    this.on("remove", this._updateTimes);
 }
 
 Model.Mashup.prototype = new Model.Playable();
 
+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) {
@@ -766,70 +791,102 @@
     this.duration.setMilliseconds(_time);
 }
 
-Model.Mashup.prototype.addSegment = function(_annotation, _defer) {
+Model.Mashup.prototype.addAnnotation = function(_annotation, _defer) {
     var _mashedAnnotation = new Model.MashedAnnotation(this, _annotation),
         _defer = _defer || false;
     this.segments.push(_mashedAnnotation);
     _annotation.on("change-begin", this._updateTimes);
     _annotation.on("change-end", this._updateTimes);
     if (!_defer) {
-        this.trigger("add-segments");
+        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.addSegmentById = function(_elId, _defer) {
-    var _annotation = this.source.getElement(_elId),
-        _defer = _defer || false;
-    if (typeof _annotation !== "undefined") {
-        this.addSegment(_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.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.addSegments = function(_segments) {
-    var _this = this;
-    ns._(_segments).forEach(function(_segment) {
-        _this.addSegment(_segment, true);
-    });
-    this.trigger("add-segments");
-}
+Model.Mashup.prototype.removeAnnotationById = function(_annId, _defer) {
+    var _defer = _defer || false;
+    var _annotation = this.source.getElement(_annId);
 
-Model.Mashup.prototype.addSegmentsById = function(_segments) {
-    var _this = this;
-    ns._(_segments).forEach(function(_segment) {
-        _this.addSegmentById(_segment, true);
-    });
-    this.trigger("add-segments");
-}
-
-Model.Mashup.prototype.removeSegment = function(_annotation, _defer) {
-    var _defer = _defer || false;
-    _annotation.off("change-begin", this._updateTimes);
-    _annotation.off("change-end", this._updateTimes);
-    this.segments.removeElement(_annotation);
+    if (_annotation) {
+        this.removeAnnotation(_annotation, _defer);
+    }
     if (!_defer) {
-        this.trigger("remove-segments");
+        this.trigger("remove");
     }
 }
 
-Model.Mashup.prototype.removeSegmentById = function(_annId, _defer) {
-    var _defer = _defer || false;
-    var _annotation = this.source.getElementById(_annId);
-    if (_annotation) {
-        this.removeSegment(_annotation, _defer);
+Model.Mashup.prototype.setAnnotations = function(_segments) {
+    while (this.segments.length) {
+        this.removeAnnotation(this.segments[0].annotation, true);
     }
-    this.segments.removeElement(_annotation);
-    if (!_defer) {
-        this.trigger("remove-segments");
+    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.getAnnotations = function() {
+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.getSegments = function() {
     return this.segments;
 }
 
 Model.Mashup.prototype.getMedias = function() {
-    var medias = new Model.List(_source.directory);
-    this.segments.each(function(_annotation) {
+    var medias = new Model.List(this.source.directory);
+    this.segments.forEach(function(_annotation) {
         medias.push(_annotation.getMedia())
     })
     return medias;
--- a/integration/lib/mustache.js	Mon Oct 29 18:11:09 2012 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,436 +0,0 @@
-/*
-  mustache.js — Logic-less templates in JavaScript
-
-  See http://mustache.github.com/ for more info.
-*/
-
-var Mustache = function () {
-  var _toString = Object.prototype.toString;
-
-  Array.isArray = Array.isArray || function (obj) {
-    return _toString.call(obj) == "[object Array]";
-  }
-
-  var _trim = String.prototype.trim, trim;
-
-  if (_trim) {
-    trim = function (text) {
-      return text == null ? "" : _trim.call(text);
-    }
-  } else {
-    var trimLeft, trimRight;
-
-    // IE doesn't match non-breaking spaces with \s.
-    if ((/\S/).test("\xA0")) {
-      trimLeft = /^[\s\xA0]+/;
-      trimRight = /[\s\xA0]+$/;
-    } else {
-      trimLeft = /^\s+/;
-      trimRight = /\s+$/;
-    }
-
-    trim = function (text) {
-      return text == null ? "" :
-        text.toString().replace(trimLeft, "").replace(trimRight, "");
-    }
-  }
-
-  var escapeMap = {
-    "&": "&amp;",
-    "<": "&lt;",
-    ">": "&gt;",
-    '"': '&quot;',
-    "'": '&#39;'
-  };
-
-  function escapeHTML(string) {
-    return String(string).replace(/&(?!\w+;)|[<>"']/g, function (s) {
-      return escapeMap[s] || s;
-    });
-  }
-
-  var regexCache = {};
-  var Renderer = function () {};
-
-  Renderer.prototype = {
-    otag: "{{",
-    ctag: "}}",
-    pragmas: {},
-    buffer: [],
-    pragmas_implemented: {
-      "IMPLICIT-ITERATOR": true
-    },
-    context: {},
-
-    render: function (template, context, partials, in_recursion) {
-      // reset buffer & set context
-      if (!in_recursion) {
-        this.context = context;
-        this.buffer = []; // TODO: make this non-lazy
-      }
-
-      // fail fast
-      if (!this.includes("", template)) {
-        if (in_recursion) {
-          return template;
-        } else {
-          this.send(template);
-          return;
-        }
-      }
-
-      // get the pragmas together
-      template = this.render_pragmas(template);
-
-      // render the template
-      var html = this.render_section(template, context, partials);
-
-      // render_section did not find any sections, we still need to render the tags
-      if (html === false) {
-        html = this.render_tags(template, context, partials, in_recursion);
-      }
-
-      if (in_recursion) {
-        return html;
-      } else {
-        this.sendLines(html);
-      }
-    },
-
-    /*
-      Sends parsed lines
-    */
-    send: function (line) {
-      if (line !== "") {
-        this.buffer.push(line);
-      }
-    },
-
-    sendLines: function (text) {
-      if (text) {
-        var lines = text.split("\n");
-        for (var i = 0; i < lines.length; i++) {
-          this.send(lines[i]);
-        }
-      }
-    },
-
-    /*
-      Looks for %PRAGMAS
-    */
-    render_pragmas: function (template) {
-      // no pragmas
-      if (!this.includes("%", template)) {
-        return template;
-      }
-
-      var that = this;
-      var regex = this.getCachedRegex("render_pragmas", function (otag, ctag) {
-        return new RegExp(otag + "%([\\w-]+) ?([\\w]+=[\\w]+)?" + ctag, "g");
-      });
-
-      return template.replace(regex, function (match, pragma, options) {
-        if (!that.pragmas_implemented[pragma]) {
-          throw({message:
-            "This implementation of mustache doesn't understand the '" +
-            pragma + "' pragma"});
-        }
-        that.pragmas[pragma] = {};
-        if (options) {
-          var opts = options.split("=");
-          that.pragmas[pragma][opts[0]] = opts[1];
-        }
-        return "";
-        // ignore unknown pragmas silently
-      });
-    },
-
-    /*
-      Tries to find a partial in the curent scope and render it
-    */
-    render_partial: function (name, context, partials) {
-      name = trim(name);
-      if (!partials || partials[name] === undefined) {
-        throw({message: "unknown_partial '" + name + "'"});
-      }
-      if (!context || typeof context[name] != "object") {
-        return this.render(partials[name], context, partials, true);
-      }
-      return this.render(partials[name], context[name], partials, true);
-    },
-
-    /*
-      Renders inverted (^) and normal (#) sections
-    */
-    render_section: function (template, context, partials) {
-      if (!this.includes("#", template) && !this.includes("^", template)) {
-        // did not render anything, there were no sections
-        return false;
-      }
-
-      var that = this;
-
-      var regex = this.getCachedRegex("render_section", function (otag, ctag) {
-        // This regex matches _the first_ section ({{#foo}}{{/foo}}), and captures the remainder
-        return new RegExp(
-          "^([\\s\\S]*?)" +         // all the crap at the beginning that is not {{*}} ($1)
-
-          otag +                    // {{
-          "(\\^|\\#)\\s*(.+)\\s*" + //  #foo (# == $2, foo == $3)
-          ctag +                    // }}
-
-          "\n*([\\s\\S]*?)" +       // between the tag ($2). leading newlines are dropped
-
-          otag +                    // {{
-          "\\/\\s*\\3\\s*" +        //  /foo (backreference to the opening tag).
-          ctag +                    // }}
-
-          "\\s*([\\s\\S]*)$",       // everything else in the string ($4). leading whitespace is dropped.
-
-        "g");
-      });
-
-
-      // for each {{#foo}}{{/foo}} section do...
-      return template.replace(regex, function (match, before, type, name, content, after) {
-        // before contains only tags, no sections
-        var renderedBefore = before ? that.render_tags(before, context, partials, true) : "",
-
-        // after may contain both sections and tags, so use full rendering function
-            renderedAfter = after ? that.render(after, context, partials, true) : "",
-
-        // will be computed below
-            renderedContent,
-
-            value = that.find(name, context);
-
-        if (type === "^") { // inverted section
-          if (!value || Array.isArray(value) && value.length === 0) {
-            // false or empty list, render it
-            renderedContent = that.render(content, context, partials, true);
-          } else {
-            renderedContent = "";
-          }
-        } else if (type === "#") { // normal section
-          if (Array.isArray(value)) { // Enumerable, Let's loop!
-            renderedContent = that.map(value, function (row) {
-              return that.render(content, that.create_context(row), partials, true);
-            }).join("");
-          } else if (that.is_object(value)) { // Object, Use it as subcontext!
-            renderedContent = that.render(content, that.create_context(value),
-              partials, true);
-          } else if (typeof value == "function") {
-            // higher order section
-            renderedContent = value.call(context, content, function (text) {
-              return that.render(text, context, partials, true);
-            });
-          } else if (value) { // boolean section
-            renderedContent = that.render(content, context, partials, true);
-          } else {
-            renderedContent = "";
-          }
-        }
-
-        return renderedBefore + renderedContent + renderedAfter;
-      });
-    },
-
-    /*
-      Replace {{foo}} and friends with values from our view
-    */
-    render_tags: function (template, context, partials, in_recursion) {
-      // tit for tat
-      var that = this;
-
-      var new_regex = function () {
-        return that.getCachedRegex("render_tags", function (otag, ctag) {
-          return new RegExp(otag + "(=|!|>|&|\\{|%)?([^#\\^]+?)\\1?" + ctag + "+", "g");
-        });
-      };
-
-      var regex = new_regex();
-      var tag_replace_callback = function (match, operator, name) {
-        switch(operator) {
-        case "!": // ignore comments
-          return "";
-        case "=": // set new delimiters, rebuild the replace regexp
-          that.set_delimiters(name);
-          regex = new_regex();
-          return "";
-        case ">": // render partial
-          return that.render_partial(name, context, partials);
-        case "{": // the triple mustache is unescaped
-        case "&": // & operator is an alternative unescape method
-          return that.find(name, context);
-        default: // escape the value
-          return escapeHTML(that.find(name, context));
-        }
-      };
-      var lines = template.split("\n");
-      for(var i = 0; i < lines.length; i++) {
-        lines[i] = lines[i].replace(regex, tag_replace_callback, this);
-        if (!in_recursion) {
-          this.send(lines[i]);
-        }
-      }
-
-      if (in_recursion) {
-        return lines.join("\n");
-      }
-    },
-
-    set_delimiters: function (delimiters) {
-      var dels = delimiters.split(" ");
-      this.otag = this.escape_regex(dels[0]);
-      this.ctag = this.escape_regex(dels[1]);
-    },
-
-    escape_regex: function (text) {
-      // thank you Simon Willison
-      if (!arguments.callee.sRE) {
-        var specials = [
-          '/', '.', '*', '+', '?', '|',
-          '(', ')', '[', ']', '{', '}', '\\'
-        ];
-        arguments.callee.sRE = new RegExp(
-          '(\\' + specials.join('|\\') + ')', 'g'
-        );
-      }
-      return text.replace(arguments.callee.sRE, '\\$1');
-    },
-
-    /*
-      find `name` in current `context`. That is find me a value
-      from the view object
-    */
-    find: function (name, context) {
-      name = trim(name);
-
-      // Checks whether a value is thruthy or false or 0
-      function is_kinda_truthy(bool) {
-        return bool === false || bool === 0 || bool;
-      }
-
-      var value;
-
-      // check for dot notation eg. foo.bar
-      if (name.match(/([a-z_]+)\./ig)) {
-        var childValue = this.walk_context(name, context);
-        if (is_kinda_truthy(childValue)) {
-          value = childValue;
-        }
-      } else {
-        if (is_kinda_truthy(context[name])) {
-          value = context[name];
-        } else if (is_kinda_truthy(this.context[name])) {
-          value = this.context[name];
-        }
-      }
-
-      if (typeof value == "function") {
-        return value.apply(context);
-      }
-      if (value !== undefined) {
-        return value;
-      }
-      // silently ignore unkown variables
-      return "";
-    },
-
-    walk_context: function (name, context) {
-      var path = name.split('.');
-      // if the var doesn't exist in current context, check the top level context
-      var value_context = (context[path[0]] != undefined) ? context : this.context;
-      var value = value_context[path.shift()];
-      while (value != undefined && path.length > 0) {
-        value_context = value;
-        value = value[path.shift()];
-      }
-      // if the value is a function, call it, binding the correct context
-      if (typeof value == "function") {
-        return value.apply(value_context);
-      }
-      return value;
-    },
-
-    // Utility methods
-
-    /* includes tag */
-    includes: function (needle, haystack) {
-      return haystack.indexOf(this.otag + needle) != -1;
-    },
-
-    // by @langalex, support for arrays of strings
-    create_context: function (_context) {
-      if (this.is_object(_context)) {
-        return _context;
-      } else {
-        var iterator = ".";
-        if (this.pragmas["IMPLICIT-ITERATOR"]) {
-          iterator = this.pragmas["IMPLICIT-ITERATOR"].iterator;
-        }
-        var ctx = {};
-        ctx[iterator] = _context;
-        return ctx;
-      }
-    },
-
-    is_object: function (a) {
-      return a && typeof a == "object";
-    },
-
-    /*
-      Why, why, why? Because IE. Cry, cry cry.
-    */
-    map: function (array, fn) {
-      if (typeof array.map == "function") {
-        return array.map(fn);
-      } else {
-        var r = [];
-        var l = array.length;
-        for(var i = 0; i < l; i++) {
-          r.push(fn(array[i]));
-        }
-        return r;
-      }
-    },
-
-    getCachedRegex: function (name, generator) {
-      var byOtag = regexCache[this.otag];
-      if (!byOtag) {
-        byOtag = regexCache[this.otag] = {};
-      }
-
-      var byCtag = byOtag[this.ctag];
-      if (!byCtag) {
-        byCtag = byOtag[this.ctag] = {};
-      }
-
-      var regex = byCtag[name];
-      if (!regex) {
-        regex = byCtag[name] = generator(this.otag, this.ctag);
-      }
-
-      return regex;
-    }
-  };
-
-  return({
-    name: "mustache.js",
-    version: "0.5.0-dev",
-
-    /*
-      Turns a template and view into HTML
-    */
-    to_html: function (template, view, partials, send_fun) {
-      var renderer = new Renderer();
-      if (send_fun) {
-        renderer.send = send_fun;
-      }
-      renderer.render(template, view || {}, partials);
-      if (!send_fun) {
-        return renderer.buffer.join("\n");
-      }
-    }
-  });
-}();