commit before merge
authorveltr
Mon, 29 Oct 2012 18:02:10 +0100
changeset 25 eea45f9b124b
parent 23 c9dc489913af
child 26 7c394ea40f28
commit before merge
integration/css/common.css
integration/css/edition.css
integration/edition.html
integration/img/player-sprites.png
integration/js/editor.js
integration/js/model.js
--- a/integration/css/common.css	Fri Oct 26 18:54:20 2012 +0200
+++ b/integration/css/common.css	Mon Oct 29 18:02:10 2012 +0100
@@ -62,6 +62,9 @@
 	color:#000;
 	text-decoration: none;
 }
+textarea {
+    resize: none;
+}
 /* common */
 .wrap{
 	min-height:100%;
@@ -161,15 +164,19 @@
 .popin-content input[type=text],
 .popin-content input[type=password], 
 .popin-content textarea{
-	padding: 0 6px;
-	height: 20px;
-	line-height: 20px;
+	padding: 0 4px;
+	font-family: 'OpenSans';
 	font-size: 12px;
 	border: 1px solid #867a97;
 	-webkit-border-radius: 4px;
   	-moz-border-radius: 4px;
   	border-radius: 4px;
 }
+.popin-content input[type=text],
+.popin-content input[type=password] {
+    line-height: 20px;
+    height: 20px;
+}
 /* popin - user */
 
 .popin {
--- a/integration/css/edition.css	Fri Oct 26 18:54:20 2012 +0200
+++ b/integration/css/edition.css	Mon Oct 29 18:02:10 2012 +0100
@@ -226,6 +226,14 @@
     content: "glisser pour modifier"
 }
 
+.time-tangle.deactivate:hover {
+    border: none; color: #7628DF;
+}
+
+.time-tangle.deactivate:hover:after {
+    display: none;
+}
+
 .segmentation form{
 	overflow: hidden;
 }
@@ -501,9 +509,8 @@
 }
 .item-video img{
 	float: left;
-}
-.item-video img{
 	margin-right: 4px;
+	box-shadow: 2px 2px 2px #333333;
 }
 .item-video .video-info{
 	display: block;
@@ -667,9 +674,45 @@
   background-position: -30px -50px;
 }
 
+.Ldt-Ctrl-InOutBlock {
+    display: none;
+}
+
+.segment-mode .Ldt-Ctrl-InOutBlock {
+    display: block;
+}
+
+.Ldt-Ctrl-SetIn, .Ldt-Ctrl-SetOut {
+    margin: 0 2px;
+}
+
+.Ldt-Ctrl-SetIn {
+    background-position: -60px 0;
+}
+
+.Ldt-Ctrl-SetIn:hover {
+  background-position: -60px -25px;
+}
+
+.Ldt-Ctrl-SetIn:active {
+  background-position: -60px -50px;
+}
+
+.Ldt-Ctrl-SetOut {
+    background-position: -90px 0;
+}
+
+.Ldt-Ctrl-SetOut:hover {
+  background-position: -90px -25px;
+}
+
+.Ldt-Ctrl-SetOut:active {
+  background-position: -90px -50px;
+}
+
 .Ldt-Ctrl-Time {
   float: left;
-  margin: 5px;
+  margin: 7px 5px 0;
   font-size: 12px;
   font-family: Arial, Verdana, sans-serif;
 }
--- a/integration/edition.html	Fri Oct 26 18:54:20 2012 +0200
+++ b/integration/edition.html	Mon Oct 29 18:02:10 2012 +0100
@@ -25,17 +25,17 @@
                         <form class="clearfix" action="#" method="">
                             <div class="form-left">
                                 <p class="titre-wrap">
-                                    <label for="titre-video">Titre :</label>
-                                    <input type="text" id="titre-video" name="" value="Mon Hashcut" />
+                                    <label for="hashcut-title">Titre :</label>
+                                    <input type="text" id="hashcut-title" name="" value="Mon Hashcut" />
                                 </p>
                                 <p>
-                                    <label for="tags-video">Tags :</label>
-                                    <input type="text" id="tags-video" name="" value="Lune, Espace, Lune" />
+                                    <label for="hashcut-tags">Tags :</label>
+                                    <input type="text" id="hashcut-tags" name="" value="Lune, Espace, Lune" />
                                 </p>
                             </div>
                             <p class="form-right">
-                                <label for="description-video">Description :</label>
-                                <textarea name="" id="description-video" cols="30" rows="10"></textarea>
+                                <label for="hashcut-description">Description :</label>
+                                <textarea name="" id="hashcut-description"></textarea>
                             </p>
                         </form>
                     </div>
@@ -162,6 +162,12 @@
                             <div class="Ldt-Ctrl-Left">
                                 <div class="Ldt-Ctrl-button Ldt-Ctrl-Play Ldt-Ctrl-Play-PlayState" title="Lecture/Pause"></div>
                                 <div class="Ldt-Ctrl-spacer"></div>
+                                <div class="Ldt-Ctrl-InOutBlock">
+                                    <div class="Ldt-Ctrl-button Ldt-Ctrl-SetIn" title="Débuter le segment ici"></div>
+                                    <div class="Ldt-Ctrl-spacer"></div>
+                                    <div class="Ldt-Ctrl-button Ldt-Ctrl-SetOut" title="Finir le segment ici"></div>
+                                    <div class="Ldt-Ctrl-spacer"></div>
+                                </div>
                             </div>
                             <div class="Ldt-Ctrl-Right">
                                <div class="Ldt-Ctrl-spacer"></div>
@@ -194,21 +200,21 @@
                                     (durée:
                                     <span class="time-tangle tangle-duration"></span>)
                                 </h2>
-                                <form action="#">
+                                <form action="#" id="segment-form">
                                     <div class="form-segment-left">
                                         <p>
-                                            <label for="titre">Titre :</label>
-                                            <input type="text" id="titre" />
+                                            <label for="segment-title">Titre :</label>
+                                            <input type="text" id="segment-title" />
                                         </p>
                                         <p>
-                                            <label for="tags">Tags :</label>
-                                            <input type="text" id="tags" />
+                                            <label for="segment-tags">Tags :</label>
+                                            <input type="text" id="segment-tags" />
                                         </p>
                                     </div>
                                     <div class="form-segment-right">
                                         <p>
-                                            <label for="description">Description :</label>
-                                            <textarea id="description"></textarea>
+                                            <label for="segment-description">Description :</label>
+                                            <textarea id="segment-description"></textarea>
                                         </p>
                                     </div>
                                     <input class="button add-segment" type="submit" value="Ajouter au Hashcut" />
@@ -216,7 +222,7 @@
                                 </form>
                             </div>
                         </div><!-- popin segmentation -->
-
+<!-- //TODO: Add "Existing Segments"
                         <div class="existant">
                             <h2>Segments existants :</h2>
                             <div class="segments">
@@ -232,7 +238,8 @@
                                             <a href="#" class="button reprendre-segment">Reprendre le segment</a>
                                         </div>
                                     </div><!-- popin segment-section -->
-                                </div>
+<!--
+                                    </div>
                                 <div class="segment">
                                     <div class="section" style="left:20%;width:20%;"></div>
                                 </div>
@@ -244,6 +251,7 @@
                                 </div>
                             </div>
                         </div>
+-->
                     </div><!-- bloc-segmentation -->
 
                     <div class="bloc-pvw">
@@ -303,8 +311,10 @@
                         <h2>Liste des segments</h2>
                         <div class="frise clearfix">
 
-                            <span class="indication" style="left:64%;">00:30</span>
-                            <span class="indication" style="left:84%;">00:30</span>
+                            <div class="indications">
+                                <span class="indication" style="left:30%;">00:30</span>
+                                <span class="indication" style="left:84%;">00:30</span>
+                            </div>
                             <div class="segments">
                                  <div class="segment" style="background-color:red;width:20%;"></div>
                                  <div class="segment" style="background-color:yellow;width:20%;"></div>
@@ -312,52 +322,11 @@
                                  <div class="segment" style="background-color:green;width:20%;"></div>
                                  <div class="segment" style="background-color:brown;width:20%;"></div>
                             </div>
+
                         </div>
                     </div>
 
                     <ul class="list-video organize-segments">
-                        <li class="item-video">
-                            <img src="img/apercu-video.jpg" alt="aperçu" />
-                            <span class="video-info">
-                                <span class="title-video">Voyage à la Lune 1</span>
-                                <span class="subtitle">Chargement de la Capsule</span>
-                                <span class="duration">01:17 - 02:01 (00:44)</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><!-- item-video -->
-                        <li class="item-video">
-                            <img src="img/apercu-video.jpg" alt="aperçu" />
-                            <span class="video-info">
-                                <span class="title-video">Voyage à la Lune 2</span>
-                                <span class="subtitle">Chargement de la Capsule</span>
-                                <span class="duration">01:17 - 02:01 (00:44)</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><!-- item-video -->
-                        <li class="item-video">
-                            <img src="img/apercu-video.jpg" alt="aperçu" />
-                            <span class="video-info">
-                                <span class="title-video">Voyage à la Lune 3</span>
-                                <span class="subtitle">Chargement de la Capsule</span>
-                                <span class="duration">01:17 - 02:01 (00:44)</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><!-- item-video -->
                         
                     </ul>
                 </div><!-- col-right -->
Binary file integration/img/player-sprites.png has changed
--- a/integration/js/editor.js	Fri Oct 26 18:54:20 2012 +0200
+++ b/integration/js/editor.js	Mon Oct 29 18:02:10 2012 +0100
@@ -7,12 +7,18 @@
             url: "data/bpidata.json",
             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>';
+            + '<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>';
 
     /* Fill left column with Media List */
-    
+
     project.onLoad(function() {
         var html = '';
         project.getMedias().forEach(function(_m) {
@@ -47,6 +53,18 @@
         })
     });
     
+    /* 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);
+    }
+    
+    mashup.on("add-segments",fillRightColumn);
+    
     /* Slider */
    
     var timeSlider = $(".Ldt-Slider"),
@@ -150,8 +168,13 @@
     /* Controller Widget */
    
     var volBlock = $(".Ldt-Ctrl-Volume-Control");
+    
     $('.Ldt-Ctrl-Sound')
-        .click(function(){}) //TODO: Add Mute and Volume Handlers
+        .click(function() {
+            if (currentMedia) {
+                currentMedia.setMuted(!currentMedia.getMuted());
+            }
+        })
         .mouseover(function() {
             volBlock.show();
         })
@@ -165,13 +188,33 @@
     });
     
     var volBar = $(".Ldt-Ctrl-Volume-Bar");
+    
+    function ctrlVolumeUpdater() {
+        if (currentMedia) {
+            var _muted = currentMedia.getMuted(),
+                _vol = currentMedia.getVolume();
+            if (_vol === false) {
+                _vol = .5;
+            }
+            var _soundCtl = $(".Ldt-Ctrl-Sound");
+            _soundCtl.removeClass("Ldt-Ctrl-Sound-Mute Ldt-Ctrl-Sound-Half Ldt-Ctrl-Sound-Full");
+            if (_muted) {        
+                _soundCtl.attr("title", "Activer le son")
+                    .addClass("Ldt-Ctrl-Sound-Mute");    
+            } else {
+                _soundCtl.attr("title", "Couper le son")
+                    .addClass(_vol < .5 ? "Ldt-Ctrl-Sound-Half" : "Ldt-Ctrl-Sound-Full" )
+            }
+            volBar.slider("value", _muted ? 0 : 100 * _vol);
+            volBar.attr("title",'Volume : ' + Math.floor(100 * _vol) + '%');
+        }
+    }
+    
     volBar.slider({
         slide: function(event, ui) {
-            volBar.attr("title",'Volume : ' + ui.value + '%');
-            //_this.media.setVolume(ui.value / 100);
-        },
-        stop: function() {
-            // IriSP.Widgets.Controller.prototype.volumeUpdater
+            if (currentMedia) {
+                currentMedia.setVolume(ui.value / 100);
+            }
         }
     });
     
@@ -185,54 +228,60 @@
         }
     });
     
+    $(".Ldt-Ctrl-SetIn").click(function() {
+        if (currentMedia && currentMedia.currentSegment) {
+            currentMedia.currentSegment.setBegin(currentMedia.getCurrentTime());
+        }
+    });
+    $(".Ldt-Ctrl-SetOut").click(function() {
+        if (currentMedia && currentMedia.currentSegment) {
+            currentMedia.currentSegment.setEnd(currentMedia.getCurrentTime());
+        }
+    });
+    
     /* Slice Widget */
+   
     var sliceSlider = $(".Ldt-Slice"),
         sliceStartTime;
-        
-    function setTangles(sliderValues) {
-        //TODO: Move to Annotation.on("changebounds")
-        if (currentMedia) {
-            startTime = new IriSP.Model.Time(currentMedia.duration * sliderValues[0] / slidersRange),
-            endTime = new IriSP.Model.Time(currentMedia.duration * sliderValues[1] / slidersRange),
-            duration = new IriSP.Model.Time(endTime - startTime);
-            $(".tangle-start").text(startTime.toString()).attr("data-milliseconds",startTime.milliseconds);
-            $(".tangle-end").text(endTime.toString()).attr("data-milliseconds",endTime.milliseconds);
-            $(".tangle-duration").text(duration.toString()).attr("data-milliseconds",duration.milliseconds);
-        }
-    }
     
     sliceSlider.slider({
         range: true,
         values: [0, slidersRange],
         min: 0,
         max: slidersRange,
-        change: function(event, ui) {
-            setTangles(ui.values); // Not the right place to put it
-        },
         start: function() {
             if (currentMedia) {
                 if (!currentMedia.getPaused()) {
                     currentMedia.pause();
                 }
-//                sliceStartTime = currentMedia.getCurrentTime(); 
             }
         },
         slide: function(event, ui) {
-            if (currentMedia) {
+            if (currentMedia && currentMedia.currentSegment) {
                 var t = currentMedia.duration * ui.value / slidersRange;
-                currentMedia.setCurrentTime(t);
-            }
-            setTangles(ui.values);
-        },
-        stop: function() {
-            if (currentMedia && sliceStartTime) {
-//                currentMedia.setCurrentTime(sliceStartTime);
+                if (ui.value === ui.values[0]) {
+                    currentMedia.currentSegment.setBegin(t);
+                } else {
+                    currentMedia.currentSegment.setEnd(t);
+                }
             }
         }
     });
     
-    sliceSlider.find(".ui-slider-handle:first").addClass("Ldt-Slice-left-handle");
-    sliceSlider.find(".ui-slider-handle:last").addClass("Ldt-Slice-right-handle");
+    sliceSlider.find(".ui-slider-handle:first")
+        .addClass("Ldt-Slice-left-handle")
+        .click(function() {
+            if (currentMedia && currentMedia.currentSegment) {
+                currentMedia.setCurrentTime(currentMedia.currentSegment.begin);
+            }
+        });
+    sliceSlider.find(".ui-slider-handle:last")
+        .addClass("Ldt-Slice-right-handle")
+        .click(function() {
+            if (currentMedia && currentMedia.currentSegment) {
+                currentMedia.setCurrentTime(currentMedia.currentSegment.end);
+            }
+        });
     
     /* UI Events */
 
@@ -256,8 +305,23 @@
     }
     
     /* Set current Media */
+   
     var currentMedia;
     
+    function updateSliderAndTangles() {
+        if (currentMedia && currentMedia.currentSegment) {
+            var start = currentMedia.currentSegment.begin,
+                end = currentMedia.currentSegment.end,
+                dur = currentMedia.currentSegment.getDuration(),
+                f = slidersRange / currentMedia.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);
+            $(".segment-info .pointer").css("left",(parseFloat($(".Ldt-Slice-left-handle").css("left")) + parseFloat($(".Ldt-Slice-right-handle").css("left")))/2)
+        }
+    }
+    
     function setMedia(mediaid) {
         $(".col-left .item-video").removeClass("active");
         $(".tutorial").hide();
@@ -275,10 +339,34 @@
                 currentvideo = $('#video_' + mediaid);
             }
             $(".tab-media-title").text(currentMedia.title);
-            sliceSlider.slider("values",[0, slidersRange]);
+            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) {
+                        currentMedia.setCurrentTime(this.begin);
+                        updateSliderAndTangles();
+                    }
+                });
+                currentMedia.currentSegment.on("change-end", function() {
+                    if (currentMedia && currentMedia.currentSegment === this) {
+                        currentMedia.setCurrentTime(this.end);
+                        updateSliderAndTangles();
+                    }
+                });
+            }
+            updateSliderAndTangles();
         }
         currentvideo.show();
         $(".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();
     }
@@ -392,9 +480,33 @@
             if (media === currentMedia) {
                 onCurrentMediaTimeupdate(_time);
             }
+        });
+        
+        media.on("volumechange", function() {
+            if (media === currentMedia) {
+                ctrlVolumeUpdater();
+            }
         })
         
     }
+    
+    /* Segment Form interaction */
+   
+    $("#segment-title").on("keyup change input paste", function() {
+        if (currentMedia && currentMedia.currentSegment) {
+            currentMedia.currentSegment.title = $(this).val();
+        }
+    });
+    $("#segment-description").on("keyup change input paste", function() {
+        if (currentMedia && currentMedia.currentSegment) {
+            currentMedia.currentSegment.title = $(this).val();
+        }
+    });
+    $("#segment-form").submit(function() {
+        mashup.addSegment(currentMedia.currentSegment);
+        currentMedia.currentSegment = undefined;
+    })
+    
     /* Click on media items */
    
     $(".col-left").on("click", ".item-video", function() {
@@ -437,34 +549,65 @@
         disableMoveItemVideo();
     });
     
-    /* Tangle */
-    var activeTangle,
+    /* Tangles */
+    var tangleMsPerPixel = 100,
+        activeTangle,
         tangleStartX,
-        tangleStartVal;
+        tangleStartVal,
+        tangleHasMoved;
+    
     $(".time-tangle").mousedown(function(evt) {
         activeTangle = $(this);
         activeTangle.addClass("active");
         tangleStartVal = +activeTangle.attr("data-milliseconds");
         tangleStartX = evt.pageX;
+        tangleHasMoved = false;
+        $(this).siblings(".time-tangle").addClass("deactivate");
         return false;
     });
     $(document)
         .mousemove(function(evt) {
             if (activeTangle) {
-                var newval = new IriSP.Model.Time(100 * (evt.pageX - tangleStartX) + tangleStartVal);
-                activeTangle.text(newval.toString());
-                activeTangle.attr("data-milliseconds",newval.milliseconds);
+                tangleHasMoved = true;
+                var newval = new IriSP.Model.Time(tangleMsPerPixel * (evt.pageX - tangleStartX) + tangleStartVal);
                 activeTangle.trigger("valuechange", newval);
                 return false;
             }
         })
         .mouseup(function() {
             if (activeTangle) {
-                activeTangle.removeClass("active");
+                $(".time-tangle").removeClass("active deactivate");
                 activeTangle = undefined;
             }
+        });
+        
+    $(".tangle-start")
+        .mouseup(function(evt) {
+            if (!tangleHasMoved && currentMedia && currentMedia.currentSegment) {
+                currentMedia.setCurrentTime(currentMedia.currentSegment.begin);
+            }
         })
-    
+        .on("valuechange", function(evt, val) {
+            if (currentMedia && currentMedia.currentSegment) {
+                currentMedia.currentSegment.setBegin(val);
+            }
+        });
+    $(".tangle-end")
+        .mouseup(function(evt) {
+            if (!tangleHasMoved && currentMedia && currentMedia.currentSegment) {
+                currentMedia.setCurrentTime(currentMedia.currentSegment.end);
+            }
+        })
+        .on("valuechange", function(evt, val) {
+            if (currentMedia && currentMedia.currentSegment) {
+                currentMedia.currentSegment.setEnd(val);
+            }
+        });
+    $(".tangle-duration").on("valuechange", function(evt, val) {
+        if (currentMedia && currentMedia.currentSegment) {
+            currentMedia.currentSegment.setDuration(val);
+        }
+    });
 }
 
 $(function() {
--- a/integration/js/model.js	Fri Oct 26 18:54:20 2012 +0200
+++ b/integration/js/model.js	Mon Oct 29 18:02:10 2012 +0100
@@ -356,7 +356,7 @@
     var _hms = this.getHMS(),
         _res = '';
     if (_hms.hours) {
-        _res += pad(_hms.hours) + ':'
+        _res += _hms.hours + ':'
     }
     _res += pad(_hms.minutes) + ':' + pad(_hms.seconds);
     return _res;
@@ -619,11 +619,23 @@
 Model.Annotation.prototype = new Model.Element();
 
 Model.Annotation.prototype.setBegin = function(_beginMs) {
-    this.begin.setMilliseconds(_beginMs);
+    this.begin.setMilliseconds(Math.max(0,_beginMs));
+    this.trigger("change-begin");
+    if (this.end < this.begin) {
+        this.setEnd(this.begin);
+    }
 }
 
-Model.Annotation.prototype.setEnd = function(_beginMs) {
-    this.end.setMilliseconds(_beginMs);
+Model.Annotation.prototype.setEnd = function(_endMs) {
+    this.end.setMilliseconds(Math.min(_endMs, this.getMedia().duration.milliseconds));
+    this.trigger("change-end");
+    if (this.end < this.begin) {
+        this.setBegin(this.end);
+    }
+}
+
+Model.Annotation.prototype.setDuration = function(_durMs) {
+    this.setEnd(_durMs + this.begin.milliseconds);
 }
 
 Model.Annotation.prototype.setMedia = function(_idRef) {
@@ -664,9 +676,11 @@
     Model.Element.call(this, _mashup.id + "_" + _annotation.id, _annotation.source);
     this.elementType = 'mashedAnnotation';
     this.annotation = _annotation;
-    this.begin = new Model.Time(_mashup.duration);
-    this.end = new Model.Time(_mashup.duration + _annotation.getDuration());
+    this.begin = new Model.Time();
+    this.end = new Model.Time();
+    this.duration = new Model.Time();
     this.title = this.annotation.title;
+    this.media_title = this.getMedia().title;
     this.description = this.annotation.description;
     this.color = this.annotation.color;
     var _this = this;
@@ -697,6 +711,12 @@
     return this.annotation.getDuration();
 }
 
+Model.MashedAnnotation.prototype.setBegin = function(_begin) {
+    this.begin.setMilliseconds(_begin);
+    this.duration.setMilliseconds(this.annotation.getDuration());
+    this.end.setMilliseconds(_begin + this.duration);
+}
+
 /* */
 
 Model.Mashup = function(_id, _source) {
@@ -704,7 +724,6 @@
     this.elementType = 'mashup';
     this.duration = new Model.Time();
     this.segments = new Model.List(_source.directory);
-    this.medias = new Model.List(_source.directory);
     var _currentMedia = null;
     var _this = this;
     this.on("timeupdate", function(_time) {
@@ -729,21 +748,78 @@
             }
         });
     });
+    this._updateTimes = function() {
+        _this.updateTimes();
+    }
+    this.on("add-segments", this._updateTimes);
+    this.on("remove-segments", this._updateTimes);
 }
 
 Model.Mashup.prototype = new Model.Playable();
 
-Model.Mashup.prototype.addSegment = function(_annotation) {
-    var _mashedAnnotation = new Model.MashedAnnotation(this, _annotation);
-    this.duration.setMilliseconds(_mashedAnnotation.end);
+Model.Mashup.prototype.updateTimes = function() {
+    var _time = 0;
+    this.segments.forEach(function(_segment) {
+        _segment.setBegin(_time);
+        _time = _segment.end;
+    });
+    this.duration.setMilliseconds(_time);
+}
+
+Model.Mashup.prototype.addSegment = function(_annotation, _defer) {
+    var _mashedAnnotation = new Model.MashedAnnotation(this, _annotation),
+        _defer = _defer || false;
     this.segments.push(_mashedAnnotation);
-    this.medias.push(_annotation.getMedia());
+    _annotation.on("change-begin", this._updateTimes);
+    _annotation.on("change-end", this._updateTimes);
+    if (!_defer) {
+        this.trigger("add-segments");
+    }
+}
+
+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.addSegmentById = function(_elId) {
-    var _annotation = this.source.getElement(_elId);
-    if (typeof _annotation !== "undefined") {
-        this.addSegment(_annotation);
+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.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 (!_defer) {
+        this.trigger("remove-segments");
+    }
+}
+
+Model.Mashup.prototype.removeSegmentById = function(_annId, _defer) {
+    var _defer = _defer || false;
+    var _annotation = this.source.getElementById(_annId);
+    if (_annotation) {
+        this.removeSegment(_annotation, _defer);
+    }
+    this.segments.removeElement(_annotation);
+    if (!_defer) {
+        this.trigger("remove-segments");
     }
 }
 
@@ -752,7 +828,11 @@
 }
 
 Model.Mashup.prototype.getMedias = function() {
-    return this.medias;
+    var medias = new Model.List(_source.directory);
+    this.segments.each(function(_annotation) {
+        medias.push(_annotation.getMedia())
+    })
+    return medias;
 }
 
 Model.Mashup.prototype.getAnnotationsByTypeTitle = function(_title) {