Refactoring to have common code between editor and player
authorveltr
Thu, 08 Nov 2012 18:24:47 +0100
changeset 41 3ec2343f2b85
parent 40 76efa2333f31
child 42 40909e8d6855
Refactoring to have common code between editor and player
integration/css/common.css
integration/css/edition.css
integration/css/hashcut.css
integration/data/moon.json
integration/edition.html
integration/hashcut.html
integration/img/tooltip.png
integration/js/editor.js
integration/js/ldt-serializer.js
integration/js/mashupcore.js
integration/js/mashupplayer.js
integration/js/model.js
integration/lib/jquery.tagit.css
integration/lib/tag-it.js
--- a/integration/css/common.css	Tue Nov 06 18:49:13 2012 +0100
+++ b/integration/css/common.css	Thu Nov 08 18:24:47 2012 +0100
@@ -380,17 +380,17 @@
 /* content */
 .left-content, 
 .right-content{
-	padding-top: 8px;
 	float: left;
 }
 .left-content h2,
 .right-content h2{
 	padding: 6px 0;
-	margin-bottom: 14px;
+	margin: 8px 0 14px;
 	border-bottom: 1px solid #333333;
 	color: #30036d;
 	font-size: 18px;
 	font-weight: 600;
+	clear: both;
 }
 /* left-content */
 .left-content{
@@ -499,6 +499,7 @@
 	background: #CCCCCC;
 }
 .frise-segment{
+    cursor: pointer;
 	height: 22px;
 	position: absolute;
 	background-image: url(../img/border-right-segment.png);
--- a/integration/css/edition.css	Tue Nov 06 18:49:13 2012 +0100
+++ b/integration/css/edition.css	Thu Nov 08 18:24:47 2012 +0100
@@ -46,27 +46,12 @@
 .update-title .form-left{
 	float: left;
 }
-.update-title textarea{
-	width: 200px;
-	height: 66px;
-	max-width: 530px;
-}
 .update-title .form-left{
 	margin-right: 12px;
 }
 .update-title .pointer{
 	left: 110px;
 }
-.update-title label{
-	display: block;
-	margin-bottom: 4px;
-}
-.update-title input[type=text]{
-	width: 200px;
-}
-.update-title p.titre-wrap{
-	margin-bottom: 6px;
-}
 /* col */
 .col-middle, 
 .col-left, 
@@ -140,7 +125,7 @@
 	font-size: 14px;
 	font-weight: 600;
 	color:#de2500;
-	margin: 5px 0 10px;
+	margin: 5px 0 2px;
 }
 
 .time-tangle {
@@ -173,31 +158,34 @@
 	float: left;
 }
 .segmentation .form-segment-left{
-	width: 228px;
+	width: 230px;
 }
-.segmentation form p{
-	margin-bottom: 8px;
-}
-.segmentation label{
+
+.segmentation label, .update-title label {
 	display: block;
 	font-size: 12px;
 	font-weight: 600;
-	margin-bottom: 4px;
+	margin: 8px 0 4px;
 }
-.segmentation textarea{
+.segmentation textarea, .update-title textarea {
 	width: 200px;
-	height: 66px;
+	height: 69px;
 	max-width: 200px;
 	font-size: 12px;
 }
-.segmentation input[type=text]{
+
+.segmentation textarea {
+    margin-bottom: 32px;
+}
+
+.segmentation input[type=text], .update-title input[type=text]{
 	font-size: 12px;
 	width: 200px;
 	height: 20px;
 }
 
 .add-segment {
-    float: right; margin: 4px 0;
+    position: absolute; right: 10px; bottom: 10px;
 }
 .media-segments h2{
     color: #30036d;
@@ -536,3 +524,21 @@
     left: 0; position: absolute; width: 100%;
     background: url(../img/tooltip.png) bottom no-repeat;
 }
+
+.tagit {
+    border: 1px solid #867A97;
+    padding: 0 4px; width: 200px;
+    font-size: 12px; font-family: OpenSans;
+}
+
+ul.tagit li.tagit-choice {
+    padding: 3px 18px 3px 3px;
+}
+
+ul.tagit li.tagit-new {
+    padding: 0;
+}
+
+ul.tagit input[type="text"] {
+    width: 100px;
+}
--- a/integration/css/hashcut.css	Tue Nov 06 18:49:13 2012 +0100
+++ b/integration/css/hashcut.css	Thu Nov 08 18:24:47 2012 +0100
@@ -35,10 +35,11 @@
 	color: #7628df;
 }
 .cloner{
-	float: right;
+	float: right; margin-bottom: 8px;
 }
 .list-video{
-	height: 450px;
+    height: auto;
+    margin-left: -10px;
 }
 .item-video:hover,
 .item-video.active{
--- a/integration/data/moon.json	Tue Nov 06 18:49:13 2012 +0100
+++ b/integration/data/moon.json	Thu Nov 08 18:24:47 2012 +0100
@@ -1,20 +1,20 @@
 [
     {
-        "id": "melies",
+        "id": "f72aa2f4-29bb-11e2-a193-08002791f1b7",
         "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",
+        "id": "7e96d10c-29be-11e2-a481-08002791f1b7",
         "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",
+        "id": "7c3f5e0e-29c0-11e2-8017-08002791f1b7",
         "video": "http://www.iri.centrepompidou.fr/dev/~veltr/mashup/moon/juno.mp4",
         "title": "Launch of Juno!",
         "author": "NASA",
--- a/integration/edition.html	Tue Nov 06 18:49:13 2012 +0100
+++ b/integration/edition.html	Thu Nov 08 18:24:47 2012 +0100
@@ -8,6 +8,7 @@
         <meta name="author" content="Anthony Ly, Raphaël Velt" />
         <title>Hashcut &gt; Edition</title>
         <link rel="stylesheet" href="lib/jquery-ui.css" />
+        <link rel="stylesheet" href="lib/jquery.tagit.css" />
         <link rel="stylesheet" href="css/reset.css" />
         <link rel="stylesheet" href="css/common.css" />
         <link rel="stylesheet" href="css/edition.css" />
@@ -24,19 +25,15 @@
                     <div class="popin-content">
                         <form class="clearfix" action="#" method="">
                             <div class="form-left">
-                                <p class="titre-wrap">
-                                    <label for="hashcut-title">Titre :</label>
-                                    <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="" />
-                                </p>
+                                <label for="hashcut-title">Titre :</label>
+                                <input type="text" id="hashcut-title" name="" value="Hashcut sans titre" />
+                                <label for="hashcut-tags">Tags :</label>
+                                <ul id="hashcut-tags"></ul>
                             </div>
-                            <p class="form-right">
+                            <div class="form-right">
                                 <label for="hashcut-description">Description :</label>
                                 <textarea name="" id="hashcut-description"></textarea>
-                            </p>
+                            </div>
                         </form>
                     </div>
                 </div><!-- popin update-title -->
@@ -212,7 +209,7 @@
                                         </p>
                                         <p>
                                             <label for="segment-tags">Tags :</label>
-                                            <input type="text" id="segment-tags" />
+                                            <ul id="segment-tags"></ul>
                                         </p>
                                     </div>
                                     <div class="form-segment-right">
@@ -237,7 +234,7 @@
 
                     <div class="bloc-pvw">
 
-                        <div class="frise">
+                        <div class="frise mashup-frise">
                             <div class="frise-overflow">
                                 <div class="frise-segments">
                                 </div>
@@ -270,7 +267,7 @@
                                             </tr>
                                             <tr>
                                                 <th>Tags :</th>
-                                                <td><span class="annotation-tags">(fonctionnalité à venir)</span></td>
+                                                <td><span class="annotation-tags"></span></td>
                                             </tr>
                                         </tbody>
                                     </table>
@@ -286,7 +283,7 @@
                             <div class="validate-tooltip"></div>
                         </div>
                         <h2>Liste des segments</h2>
-                        <div class="frise">
+                        <div class="frise mashup-frise">
 							<p class="aucun-segment">Aucun segment</p>
                             <div class="frise-overflow">
                                 <div class="frise-segments">
@@ -320,16 +317,18 @@
         <!-- 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/tag-it.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="js/init.js"></script>
         <script type="text/javascript" src="js/medialist-serializer.js"></script>
         <script type="text/javascript" src="js/model.js"></script>
+        <script type="text/javascript" src="js/mashupcore.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/bpidata.json"});
+        var hashcut = IriSP.editor({url: "data/moon.json"});
     });
         </script>
     </body>
--- a/integration/hashcut.html	Tue Nov 06 18:49:13 2012 +0100
+++ b/integration/hashcut.html	Thu Nov 08 18:24:47 2012 +0100
@@ -158,13 +158,11 @@
                         </div>
                         
                     </div>
-                        <div class="frise">
+                        <div class="frise mashup-frise">
                             <div class="frise-overflow">
                                 <div class="frise-segments">
-                                    <div style="background-color:#9467bd; left:0%; width:41.02564102564102%;" class="frise-segment"></div><div style="background-color:#d62728; left:41.02564102564102%; width:58.97435897435897%;" class="frise-segment"></div>
                                 </div>
                                 <div class="frise-indications">
-                                    <span style="left:12.82051282051282%;" class="frise-indication">15:00</span><span style="left:25.64102564102564%;" class="frise-indication">30:00</span><span style="left:38.46153846153846%;" class="frise-indication">45:00</span><span style="left:51.28205128205128%;" class="frise-indication">1:00:00</span><span style="left:64.1025641025641%;" class="frise-indication">1:15:00</span><span style="left:76.92307692307692%;" class="frise-indication">1:30:00</span><span style="left:89.74358974358974%;" class="frise-indication">1:45:00</span>
                                 </div>
                             </div>
                             <div class="frise-position"></div>
@@ -197,7 +195,7 @@
                 
                 <div class="right-content">
                     <h2>Plus d’informations</h2>
-                    <div class="more-info-wrap clearfix">
+                    <div class="more-info-wrap">
                         <table class="more-info ">
                             <tbody>
                                 <tr class="info-title">
@@ -206,20 +204,20 @@
                                 </tr>
                                 <tr class="info-duration">
                                     <th>Durée :</th>
-                                    <td>17:03</td>
+                                    <td></td>
                                 </tr>
                                 <tr class="info-author">
                                     <th>Auteur : </th>
-                                    <td><a href="#">Hashcutter75</a></td>
+                                    <td><a href="#"></a></td>
                                 </tr>
                                 <tr class="info-description">
                                     <th>Description : </th>
-                                    <td> An Atlas V rocket lofted the Juno spacecraft toward Jupiter from Space Launch Complex-41. The 4-ton Juno spacecraft will take five years to reach Jupiter on a mission to study its structure and decipher its history.</td>
+                                    <td></td>
                                 </tr>
-                                <tr class="info-tags">
+<!--                                <tr class="info-tags">
                                     <th>Tags : </th>
                                     <td>Mashup, Création, Art</td>
-                                </tr>
+                                </tr> -->
                             </tbody>
                         </table>
                         <a href="#" class="button cloner">Cloner le Hashcut</a>
@@ -280,6 +278,18 @@
         <script type="text/javascript" src="lib/jquery-ui.min.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="js/init.js"></script>
+        <script type="text/javascript" src="js/ldt-serializer.js"></script>
+        <script type="text/javascript" src="js/model.js"></script>
+        <script type="text/javascript" src="js/mashupcore.js"></script>
+        <script type="text/javascript" src="js/mashupplayer.js"></script>
         <script type="text/javascript" src="js/common.js"></script>
+        <script type="text/javascript">
+            $(function() {
+                IriSP.player({
+                    url: "data/moon-mashup.json"
+                })
+            })
+        </script>
     </body>
 </html>
Binary file integration/img/tooltip.png has changed
--- a/integration/js/editor.js	Tue Nov 06 18:49:13 2012 +0100
+++ b/integration/js/editor.js	Thu Nov 08 18:24:47 2012 +0100
@@ -13,8 +13,7 @@
     mashup_title_placeholder: "Hashcut sans titre"
 }
 
-IriSP.Hashcut = function(options) {
-    
+IriSP.editor = function(options) {
     
     /* Load Media List */
     
@@ -24,48 +23,60 @@
             serializer: IriSP.serializers.medialist
         }),
         mashup = new IriSP.Model.Mashup(false, project),
-        mediatemplate = _.template('<li class="item-video" data-media-id="<%= id %>"><img src="<%= thumbnail %>" alt="<%= title %>" />'
+        mediatemplate = _.template('<li class="item-video media" 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"><%= IriSP.hc_messages.Duration_ %> <span><%= duration.toString() %></span></span></span></li>'),
-        segmenttemplate = _.template('<li class="item-video" data-segment-id="<%= annotation.id %>" data-media-id="<%= annotation.getMedia().id %>">'
+        segmenttemplate = _.template('<li class="item-video annotation" data-segment-id="<%= annotation.id %>" data-media-id="<%= annotation.getMedia().id %>">'
             + '<img src="<%= annotation.getMedia().thumbnail %>" alt="<%= annotation.getMedia().title %>" />'
             + '<div class="validate <%= annotation.status %>"><div class="validate-tooltip"><ul><li><%= annotation.status_messages.join("</li><li>") %></li></ul></div></div><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="<%= IriSP.hc_messages.edit_segment %>"></a></li><li><a class="bottom" href="#" title="<%= IriSP.hc_messages.segment_down %>"></a></li>'
             + '<li><a class="top" href="#" title="<%= IriSP.hc_messages.segment_up %>"></a></li><li><a class="delete" href="#" title="<%= IriSP.hc_messages.delete_segment %>"></a></li></ul></span></li>'),
-        viztemplate = _.template('<div class="frise-segment" data-segment-id="<%= segmentid %>" style="background-color:<%= color %>; left:<%= left %>%; width:<%= width %>%;"></div>'),
-        intervaltemplate = _.template('<span class="frise-indication" style="left:<%= left %>%;"><%= time.toString() %></span>'),
         mediasegmenttemplate = _.template('<div class="media-segments-list"><div class="media-segment">'
-            + '<div class="media-section media-segment-section" style="left:<%= left %>px; width:<%= width %>px; background:<%= annotation.getMedia().color %>"></div>'
+            + '<div class="media-section media-segment-section" style="left:<%= left %>px; width:<%= width %>px; background:<%= annotation.color %>"></div>'
             + '<div class="media-section media-current-section" style="left:<%= currentleft %>px; width:<%= currentwidth %>px;"><div class="media-current-section-inner"></div></div>'
             + '<div class="popin media-segment-popin" style="left:<%= popleft %>px"><img style="left:<%= pointerpos %>px;" class="pointer" src="img/popin-triangle.png" alt="" /><div class="popin-content">'
             + '<h3><%= annotation.title %></h3><a href="#" class="button reprendre-segment" data-segment-id="<%= annotation.id %>"><%= IriSP.hc_messages.clone_segment %></a>'
             + '<p><%= IriSP.hc_messages.From_ %> <span><%= annotation.begin.toString() %></span> <%= IriSP.hc_messages.to_ %> <span><%= annotation.end.toString() %></span> (<%= IriSP.hc_messages.duration_ %> <span><%= annotation.getDuration().toString() %></span>)</p>'
-            + '</div></div></div></div>');
+            + '</div></div></div></div>'),
+        addMode, currentMedia, currentSegment;
+    
+    IriSP.mashupcore(project, mashup);
     
     /* Validation of segments and mashup */
     
     var segmentcritical = [
         {
             validate: function(_s) {
-                var _d = _s.getDuration();
-                return (_d > 1000 && _d < 180000);
+                return (_s.getDuration() >= 1000);
             },
-            message: "La durée du segment doit être comprise entre 1 seconde et trois minutes"
+            message: "Le segment doit durer au moins une seconde"
+        },
+        {
+            validate: function(_s) {
+                return (_s.getDuration() < 180000);
+            },
+            message: "Le segment doit durer moins de trois minutes"
         },
         {
             validate: function(_s) {
                 return (!!_s.title && _s.title !== IriSP.hc_messages.segment_title_placeholder);
             },
-            message: "Un titre doit être donné au segment"
+            message: "Le segment doit avoir un titre"
         }
     ];
     var segmentwarning = [
         {
             validate: function(_s) {
-                return (_s.description);
+                return (!!_s.description);
             },
             message: "Il est recommandé de donner une description au segment"
+        },
+        {
+            validate: function(_s) {
+                return (!!_s.keywords.length);
+            },
+            message: "Il est recommandé de tagguer le segment"
         }
     ];
     
@@ -129,30 +140,14 @@
     });
     
     /* Fill right column when mashup is updated */
-   
-    function setPointerToCurrentAnnotation() {
-        if (mashupCurrentAnnotation) {
-            var p = (mashupCurrentAnnotation.begin + mashupCurrentAnnotation.end) / (2 * mashup.duration);
-            $(".mashup-description .pointer").css("left", (100 * p) + "%");
-        }
-    }
     
     function updateMashupUI() {
-        var listhtml = '', vizhtml = '', t = 0, k = mashup.duration ? (100 / mashup.duration) : 0;
-        var critical = false, warning = false, messages = [];
+        var listhtml = '', critical = false, warning = false, messages = [];
         mashup.segments.forEach(function(_s) {
             listhtml += segmenttemplate(_s);
-            var vizdata = {
-                left: k * t,
-                width: k * _s.duration,
-                color: _s.getMedia().color,
-                segmentid: _s.annotation.id
-            }
-            vizhtml += viztemplate(vizdata);
             if (_s.annotation.status === "critical") {
                 critical = true;
             }
-            t += _s.duration.milliseconds;
         });
         if (critical) {
             messages.push("Certains segments ne sont pas valides");
@@ -179,217 +174,22 @@
         $(".liste-segment .validate").removeClass("critical warning valid").addClass(mashup.status);
         $(".liste-segment .validate-tooltip").html("<ul><li>" + messages.join("</li><li>")+"</li></ul>");
         
-        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");
         
-        $(".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 */
-   
-    var timeSlider = $(".Ldt-Slider"),
-        timeSliderContainer = $(".Ldt-Slider-Container"),
-        slidersRange = 920;
-    timeSlider.slider({
-        range: "min",
-        value: 0,
-        min: 0,
-        max: slidersRange,
-        slide: function(event, ui) {
-            if (currentMedia) {
-                var t = currentMedia.duration * ui.value / slidersRange;
-                currentMedia.setCurrentTime(t);
-            }
-        }
-    });
-    
-    var timeSliderHandle = timeSlider.find('.ui-slider-handle'),
-        timeSliderMaximized = false,
-        timeSliderTimeoutId = false,
-        timeSliderMinimizedHeight = 4,
-        timeSliderMaximizedHeight = 10,
-        timeSliderTimeoutDuration = 1500,
-        timeTooltip = $(".Ldt-Slider-Time");
-    
-    timeSliderContainer.css(calculateSliderCss(timeSliderMinimizedHeight));
-    timeSliderHandle.css(calculateHandleCss(timeSliderMinimizedHeight));
-    
-    function timeSliderMouseOver() {
-        if (timeSliderTimeoutId) {
-            window.clearTimeout(timeSliderTimeoutId);
-            timeSliderTimeoutId = false;
-        }
-        if (!timeSliderMaximized) {
-           timeSliderAnimateToHeight(timeSliderMaximizedHeight);
-           timeSliderMaximized = true;
-        }
-    }
-    
-    function timeSliderMouseOut() {
-        timeTooltip.hide();
-        if (timeSliderTimeoutId) {
-            clearTimeout(timeSliderTimeoutId);
-            timeSliderTimeoutId = false;
-        }
-        timeSliderTimeoutId = setTimeout(function() {
-            if (timeSliderMaximized) {
-                timeSliderAnimateToHeight(timeSliderMinimizedHeight);
-                timeSliderMaximized = false;
-            }
-            timeSliderTimeoutId = false;
-        }, timeSliderTimeoutDuration);
+        project.trigger("mouseout-annotation");
     }
     
-    timeSliderContainer
-        .mouseover(function() {
-            timeTooltip.show();
-            timeSliderMouseOver();
-        })
-        .mouseout(timeSliderMouseOut);
-    timeSlider.mousemove(function(_e) {
-            var _x = _e.pageX - timeSlider.offset().left,
-                _t = new IriSP.Model.Time(
-                );
-            timeTooltip.text(_t.toString()).css("left",_x);
-        });
-    
-    $(".Ldt-Ctrl").mouseover(timeSliderMouseOver).mouseout(timeSliderMouseOut);
-    
-    function timeSliderAnimateToHeight(_height) {
-        timeSliderContainer.stop().animate(
-            calculateSliderCss(_height),
-            500,
-            function() {
-                IriSP.jQuery(this).css("overflow","visible");
-            });
-        timeSliderHandle.stop().animate(
-            calculateHandleCss(_height),
-            500,
-            function() {
-                IriSP.jQuery(this).css("overflow","visible");
-            });
-    }
-
-    function calculateSliderCss(_size) {
-        return {
-            height: _size + "px",
-            "margin-top": (timeSliderMinimizedHeight - _size) + "px"
-        };
-    }
-
-    function calculateHandleCss(_size) {
-        return {
-            height: (2 + _size) + "px",
-            width: (2 + _size) + "px",
-            "margin-left": -Math.ceil(2 + _size / 2) + "px" 
-        }
-    }
-    
-    /* Controller Widget */
-   
-    var volBlock = $(".Ldt-Ctrl-Volume-Control");
-    
-    $('.Ldt-Ctrl-Sound')
-        .click(function() {
-            if (currentMedia) {
-                currentMedia.setMuted(!currentMedia.getMuted());
-            }
-        })
-        .mouseover(function() {
-            volBlock.show();
-        })
-        .mouseout(function() {
-            volBlock.hide();
-        });
-    volBlock.mouseover(function() {
-        volBlock.show();
-    }).mouseout(function() {
-        volBlock.hide();
+    mashup.on("setcurrent", function() {
+        currentMedia = mashup;
     });
     
-    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) {
-            if (currentMedia) {
-                currentMedia.setVolume(ui.value / 100);
-            }
-        }
-    });
-    
-    $(".Ldt-Ctrl-Play").click(function() {
-        if (currentMedia) {
-            if (currentMedia.getPaused()) {        
-                currentMedia.play();
-            } else {
-                currentMedia.pause();
-            }
-        }
-    });
-    
-    $(".Ldt-Ctrl-SetIn").click(function() {
-        if (currentMedia && currentSegment) {
-            currentSegment.setBegin(currentMedia.getCurrentTime());
-        }
-    });
-    $(".Ldt-Ctrl-SetOut").click(function() {
-        if (currentMedia && currentSegment) {
-            currentSegment.setEnd(currentMedia.getCurrentTime());
-        }
-    });
+    mashup.on("change",updateMashupUI);
     
     /* Slice Widget */
    
     var sliceSlider = $(".Ldt-Slice"),
-        sliceStartTime;
+        sliceStartTime,
+        slidersRange = 920;
     
     sliceSlider.slider({
         range: true,
@@ -430,93 +230,8 @@
             }
         });
     
-    /* UI Events */
-
-    function onCurrentMediaPlay() {
-        $(".Ldt-Ctrl-Play")
-            .attr("title", "Pause")
-            .removeClass("Ldt-Ctrl-Play-PlayState")
-            .addClass("Ldt-Ctrl-Play-PauseState")
-    }
     
-    function onCurrentMediaPause() {
-        $(".Ldt-Ctrl-Play")
-            .attr("title", "Lecture")
-            .removeClass("Ldt-Ctrl-Play-PauseState")
-            .addClass("Ldt-Ctrl-Play-PlayState")
-    }
-    
-    function onCurrentMediaTimeupdate(_time) {
-        $(".Ldt-Ctrl-Time-Elapsed").text(_time.toString());
-        timeSlider.slider("value",slidersRange * _time / currentMedia.duration);
-    }
-    
-    /* Mashup Player */
-
-    var mashupCurrentMedia = null,
-        mashupCurrentAnnotation = null,
-        mashupSegmentBegin,
-        mashupSegmentEnd,
-        mashupTimecode = 0,
-        mashupSeeking = false,
-        seekdiv = $(".video-wait"),
-        mashupTimedelta;
-    
-    function showSeek() {
-        if (mashupSeeking) {
-            seekdiv.show();
-        }
-    }
-    
-    function changeCurrentAnnotation() {
-        if (mashupTimecode >= mashup.duration) {
-            if (!mashup.paused) {
-                mashup.paused = true;
-                mashup.trigger("pause");
-            }
-            mashupTimecode = 0;
-        }
-        var _annotation = mashup.getAnnotationAtTime( mashupTimecode );
-        if (typeof _annotation === "undefined") {
-            if (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;
-            setTimeout(showSeek,200);
-        }
-        mashup.trigger("timeupdate", new IriSP.Model.Time(mashupTimecode));
-
-    }
-    
-    /* Set current Media */
-   
-    var currentMedia, currentSegment;
+    /* Update Segment UI */
     
     function updateSegmentUI() {
         if (currentMedia && currentSegment) {
@@ -564,23 +279,14 @@
         }
     }
     
-    var addMode;
-    
     function setMedia(media) {
         if (currentMedia) {
             currentMedia.pause();
         }
         currentMedia = media;
+        project.trigger("set-current", media);
         if (currentMedia.elementType == "media") {
-            $("video").hide();
             showSegmentation();
-            if (!currentMedia.loaded) {
-                seekdiv.show();
-            }
-            var currentvideo = $('#video_' + currentMedia.id);
-            if (!currentvideo.length) {
-                addMediaPlayer(currentMedia);
-            }
             $(".tab-media-title").text(currentMedia.title);
             
             addMode = !(currentSegment && mashup.hasAnnotation(currentSegment));
@@ -591,6 +297,8 @@
                 currentSegment.setBegin(0);
                 currentSegment.setEnd(currentMedia.duration);
                 currentSegment.title = IriSP.hc_messages.segment_title_placeholder;
+                currentSegment.color = currentMedia.color;
+                currentSegment.keywords = [];
                 currentSegment.description = "";
                 currentSegment.on("change-begin", function() {
                     if (currentMedia && currentSegment === this) {
@@ -604,28 +312,25 @@
                         updateSegmentUI();
                     }
                 });
-                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();
-                        highlightCurrentSegment();
-                    }
-                });
             }
             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");
-            updateSegmentUI();
             media.show();
             $("#segment-title").val(currentSegment.title);
             $("#segment-description").val(currentSegment.description);
-            $("#segment-tags").val("");
+            var segment_tags = $("#segment-tags");
+            segment_tags.tagit("option","onTagRemoved",function(){});
+            segment_tags.tagit("option","onTagAdded",function(){});
+            segment_tags.tagit("removeAll");
+            _(currentSegment.keywords).each(function(tag) {
+                segment_tags.tagit("createTag",tag);
+            });
+            segment_tags.tagit("option","onTagRemoved",updateSegmentTags);
+            segment_tags.tagit("option","onTagAdded",updateSegmentTags);
+            updateSegmentUI();
             var relatedSegments = mashup.segments.filter(function(_s) {
                 return _s.getMedia() === currentMedia && _s.annotation !== currentSegment;
             });
@@ -652,254 +357,15 @@
             } else {
                 $(".self-media-segments").hide();
             }
+            
+            //TODO: Show Related Segments from http://capsicum/pf/ldtplatform/api/ldt/1.0/segments/bytimecode/f72aa2f4-29bb-11e2-a193-08002791f1b7/0/674000?format=json
+            
         }
         $(".self-media-segments .media-segments-list").html(html);
         if (currentMedia.elementType === "mashup") {
             showPreview();
-            mashup.checkLoaded();
         }
-        $(".Ldt-Ctrl-Time-Total").text(currentMedia.duration.toString());
-        // TODO: Do something with the tags !
-        onCurrentMediaTimeupdate(currentMedia.getCurrentTime());
-        onCurrentMediaPause();
-        highlightCurrentSegment();
     }
-    
-    function addMediaPlayer(media) {
-        var videoid = "video_" + media.id,
-            videoEl = $('<video>'),
-            width = $(".video").width(),
-            height = $(".video").height(),
-            mp4_file = media.video.replace(/\.webm$/i,'.mp4'),
-            webm_file = media.video.replace(/\.mp4$/i,'.webm'),
-            mp4_src = $('<source>'),
-            webm_src = $('<source>');
-        mp4_src.attr({
-            src: mp4_file,
-            type: "video/mp4"
-        });
-        webm_src.attr({
-            src: webm_file,
-            type: "video/webm"
-        });
-        videoEl.attr({
-            id : videoid,
-            width : width,
-            height : height
-        }).css({
-            position : "absolute",
-            left: 0,
-            top: 0,
-            width : width,
-            height : height
-        });
-        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) {
-            if (media.loaded) {
-                popcorn.currentTime(_milliseconds / 1000);
-            }
-        });
-        
-        media.on("setvolume", function(_vol) {
-            media.volume = _vol;
-            if (media.loaded) {
-                popcorn.volume(_vol);
-            }
-        });
-        
-        media.on("setmuted", function(_muted) {
-            media.muted = _muted;
-            if (media.loaded) {
-                popcorn.muted(_muted);
-            }
-        });
-        
-        media.on("setplay", function() {
-            if (media.loaded) {
-                popcorn.play();
-            }
-        });
-        
-        media.on("setpause", function() {
-            if (media.loaded) {
-                popcorn.pause();
-            }
-        });
-        
-        // Binding Popcorn events to media
-        
-        function getVolume() {
-            media.muted = popcorn.muted();
-            media.volume = popcorn.volume();
-        }
-        
-        popcorn.on("loadedmetadata", function() {
-            getVolume();
-            media.loaded = true;
-            media.trigger("loadedmetadata");
-            media.trigger("volumechange");
-        })
-        
-        popcorn.on("timeupdate", function() {
-            media.trigger("timeupdate", new IriSP.Model.Time(1000*popcorn.currentTime()));
-        });
-        
-        popcorn.on("volumechange", function() {
-            getVolume();
-            media.trigger("volumechange");
-        })
-        
-        popcorn.on("play", function() {
-            media.trigger("play");
-        });
-        
-        popcorn.on("pause", function() {
-            media.trigger("pause");
-        });
-        
-        popcorn.on("seeked", function() {
-            media.trigger("seeked");
-        });
-        
-        // Binding UI Events and Mashup Playing to Media
-        
-        media.on("loadedmetadata", function() {
-            if (media === currentMedia) {
-                seekdiv.hide();
-            }
-            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;
-                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 */
    
@@ -907,29 +373,43 @@
         if (currentMedia && currentSegment) {
             currentSegment.title = $(this).val();
             updateSegmentUI();
-            updateMashupUI();
+            mashup.trigger("change");
         }
     });
     $("#segment-description").on("keyup change input paste", function() {
         if (currentMedia && currentSegment) {
             currentSegment.description = $(this).val();
-            updateSegmentUI();
+            mashup.trigger("change");
         }
     });
     $("#segment-form").submit(function() {
         if (addMode) {
             mashup.addAnnotation(currentSegment);
         } else {
-            updateMashupUI();
+            mashup.trigger("change");
         }
         var segment = mashup.getAnnotation(currentSegment);
         currentSegment = undefined;
         setMedia(mashup);
         if (segment) {
             mashup.setCurrentTime(segment.begin);
+            mashup.trigger("enter-annotation",segment);
         }
         return false;
-    })
+    });
+    
+    $("#segment-tags").tagit();
+    
+    
+    /* We have to defer this function because the tagit events
+     * are triggered before the data are updated */
+    function updateSegmentTags() {
+        window.setTimeout(function() {
+            if (currentMedia && currentSegment) {
+                currentSegment.keywords = $("#segment-tags").tagit("assignedTags");
+            }
+        }, 0);
+    }
     
     /* Click on media items */
    
@@ -967,50 +447,40 @@
         mashup.setAnnotationsById(ids);
     }
     
-    function highlightCurrentSegment() {
-        $(".organize-segments .item-video, .col-left .item-video, .frise-segment").removeClass("active");
-        var segmentid = undefined;
-        if (currentMedia && currentSegment) {
-            segmentid = currentSegment.id;
-        }
-        if (currentMedia === mashup && mashupCurrentAnnotation) {
-            segmentid = mashupCurrentAnnotation.annotation.id;
-        }
-        $(".item-video[data-segment-id='" + segmentid + "']").addClass("active");
+    project.on("mouseover-annotation", function(annotation) {
+        var mediaid = annotation.getMedia().id;
+        $(".media").removeClass("active");
+        $(".media[data-media-id='" + mediaid + "']").addClass("active");
+    });
+    
+    project.on("mouseout-annotation", function(annotation) {
+        $(".media").removeClass("active");
         var mediaid = undefined;
-        if (currentMedia) {
+        if (currentMedia && currentMedia.elementType === "media") {
             mediaid = currentMedia.id;
+            if (currentSegment) {
+                $(".annotation").removeClass("active");
+                $(".annotation[data-segment-id='" + currentSegment.id + "']").addClass("active");
+            }
         }
-        if (currentMedia === mashup && mashupCurrentMedia) {
-            mediaid = mashupCurrentMedia.id
+        if (currentMedia === mashup && mashup.currentMedia) {
+            mediaid = mashup.currentMedia.id
         }
-        $(".col-left .item-video[data-media-id='" + mediaid + "']").addClass("active");
-    }
-    
-    function hoverSegment() {
-        var segmentid = $(this).attr("data-segment-id");
-        $(".organize-segments .item-video, .frise-segment").removeClass("active");
-        $(".item-video[data-segment-id='" + segmentid + "'], .frise-segment[data-segment-id='" + segmentid + "']").addClass("active");
-    }
-    
-    $(".frise")
-    .on("mouseover", ".frise-segment", hoverSegment)
-    .on("mouseout", ".frise-segment", highlightCurrentSegment)
+        $(".media[data-media-id='" + mediaid + "']").addClass("active");
+    });
     
     $(".organize-segments")
     .sortable({
         stop : reorganizeMashup
     })
-    .on("mouseover", ".item-video", hoverSegment)
-    .on("mouseout", ".item-video", highlightCurrentSegment)
-    .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;
+    .on("mouseover", ".item-video", function() {
+        project.trigger("mouseover-annotation", project.getElement($(this).attr("data-segment-id")));
+    })
+    .on("mouseout", ".item-video", function() {
+        project.trigger("mouseout-annotation");
+    })
+    .on("click", ".item-video", function() {
+        project.trigger("click-annotation", project.getElement($(this).attr("data-segment-id")));
     })
     .on("click", ".edit", function(e) {
         var currentItem = $(this).parents(".item-video"),
@@ -1130,6 +600,11 @@
     
     /* Changing Hashcut Title and description */
     
+    $("#hashcut-tags").tagit({
+        onTagRemoved: updateSegmentTags,
+        onTagAdded: updateSegmentTags
+    });
+    
     mashup.title = IriSP.hc_messages.mashup_title_placeholder;
     $(".title-video-wrap a").text(mashup.title);
     $("#hashcut-title").val(mashup.title);
@@ -1137,13 +612,19 @@
     $("#hashcut-title").on("keyup change input paste", function() {
         mashup.title = $(this).val();
         $(".title-video-wrap a").text(mashup.title);
-        updateMashupUI();
+        mashup.trigger("change");
     });
     
     $("#hashcut-description").on("keyup change input paste", function() {
         mashup.description = $(this).val();
-        updateMashupUI();
+        mashup.trigger("change");
     });
     
-    updateMashupUI();
+    function updateMashupTags() {
+        window.setTimeout(function() {
+            mashup.keywords = $("#segment-tags").tagit("assignedTags");
+        }, 0);
+    }
+    
+    mashup.trigger("change");
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/integration/js/ldt-serializer.js	Thu Nov 08 18:24:47 2012 +0100
@@ -0,0 +1,215 @@
+/* LDT Platform Serializer */
+
+if (typeof IriSP.serializers === "undefined") {
+    IriSP.serializers = {}
+}
+
+IriSP.serializers.ldt = {
+    types :  {
+        media : {
+            serialized_name : "medias",
+            deserializer : function(_data, _source) {
+                var _res = new IriSP.Model.Media(_data.id, _source);
+                _res.video = (
+                    typeof _data.url !== "undefined"
+                    ? _data.url
+                    : (
+                        typeof _data.href !== "undefined"
+                        ? _data.href
+                        : null
+                    )
+                );
+                if (typeof _data.meta.item !== "undefined" && _data.meta.item.name === "streamer") {
+                    _res.streamer = _data.meta.item.value;
+                }
+                _res.title = _data.meta["dc:title"];
+                _res.description = _data.meta["dc:description"];
+                _res.setDuration(_data.meta["dc:duration"]);
+                _res.url = _data.meta.url;
+                if (typeof _data.meta.img !== "undefined" && _data.meta.img.src !== "undefined") {
+                    _res.thumbnail = _data.meta.img.src;
+                }
+                return _res;        
+            },
+            serializer : function(_data, _source) {
+                return {
+                    id : _data.id,
+                    url : _data.video,
+                    meta : {
+                        "dc:title" : _data.title,
+                        "dc:description" : _data.description,
+                        "dc:duration" : _data.duration.milliseconds
+                    }
+                }
+            }
+        },
+        tag : {
+            serialized_name : "tags",
+            model_name : "tag",
+            deserializer : function(_data, _source) {
+                var _res = new IriSP.Model.Tag(_data.id, _source);
+                _res.title = _data.meta["dc:title"];
+                return _res;        
+            },
+            serializer : function(_data, _source) {
+                return {
+                    id : _data.id,
+                    meta : {
+                        "dc:title" : _data.title
+                    }
+                }
+            }
+        },
+        annotationType : {
+            serialized_name : "annotation-types",
+            deserializer : function(_data, _source) {
+                var _res = new IriSP.Model.AnnotationType(_data.id, _source);
+                _res.title = _data["dc:title"];
+                _res.description = _data["dc:description"];
+                return _res;        
+            },
+            serializer : function(_data, _source) {
+                return {
+                    id : _data.id,
+                    "dc:title" : _data.title,
+                    "dc:description" : _data.description
+                }
+            }
+        },
+        annotation : {
+            serialized_name : "annotations",
+            deserializer : function(_data, _source) {
+                var _res = new IriSP.Model.Annotation(_data.id, _source);
+                _res.title = _data.content.title || "";
+                _res.description = _data.content.description || "";
+                if (typeof _data.content.img !== "undefined" && _data.content.img.src !== "undefined") {
+                    _res.thumbnail = _data.content.img.src;
+                }
+                _res.created = IriSP.Model.isoToDate(_data.meta["dc:created"]);
+                if (typeof _data.color !== "undefined") {
+                    var _c = parseInt(_data.color).toString(16);
+                    while (_c.length < 6) {
+                        _c = '0' + _c;
+                    }
+                    _res.color = '#' + _c;
+                }
+                _res.setMedia(_data.media);
+                _res.setAnnotationType(_data.meta["id-ref"]);
+                _res.setTags(IriSP._(_data.tags).pluck("id-ref"));
+                _res.keywords = _res.getTagTexts();
+                _res.setBegin(_data.begin);
+                _res.setEnd(_data.end);
+                _res.creator = _data.meta["dc:creator"] || "";
+                _res.project = _data.meta.project || "";
+                if (typeof _data.meta["dc:source"] !== "undefined" && typeof _data.meta["dc:source"].content !== "undefined") {
+                    _res.source = JSON.parse(_data.meta["dc:source"].content);
+                }
+                if (typeof _data.content.audio !== "undefined" && _data.content.audio.href) {
+                    _res.audio = _data.content.audio;
+                }
+                return _res;
+            },
+            serializer : function(_data, _source) {
+                return {
+                    id : _data.id,
+                    begin : _data.begin.milliseconds,
+                    end : _data.end.milliseconds,
+                    content : {
+                        title : _data.title,
+                        description : _data.description,
+                        audio : _data.audio
+                    },
+                    media : _data.media.id,
+                    meta : {
+                        "id-ref" : _data.annotationType.id,
+                        "dc:created" : IriSP.Model.dateToIso(_data.created),
+                        "dc:creator" : _data.creator,
+                        project : _source.projectId
+                    },
+                    tags : IriSP._(_data.tag.id).map(function(_id) {
+                       return {
+                           "id-ref" : _id
+                       } 
+                    })
+                }
+            }
+        },
+        mashup : {
+            serialized_name : "lists",
+            deserializer : function(_data, _source) {
+                if (typeof _data.meta !== "object" || typeof _data.meta.listtype !== "string" || _data.meta.listtype !== "mashup") {
+                    return undefined;
+                }
+                var _res = new IriSP.Model.Mashup(_data.id, _source);
+                _res.title = _data.meta["dc:title"];
+                _res.description = _data.meta["dc:description"];
+                _res.creator = _data.meta["dc:creator"];
+                _res.setAnnotationsById(_data.items);
+                return _res;        
+            },
+            serializer : function(_data, _source) {
+                return {
+                    meta : {
+                        "dc:title": _data.title,
+                        "dc:description": _data.description,
+                        listtype: "mashup"
+                    },
+                    items: _data.segments.map(function(_annotation) {
+                        return _id;
+                    }),
+                    id: _data.id
+                }
+            }
+        }
+    },
+    serialize : function(_source) {
+        var _res = {},
+            _this = this;
+        _source.forEach(function(_list, _typename) {
+            if (typeof _this.types[_typename] !== "undefined") {
+                _res[_this.types[_typename].serialized_name] = _list.map(function(_el) {
+                    return _this.types[_typename].serializer(_el, _source);
+                });
+            }
+        });
+        return JSON.stringify(_res);
+    },
+    loadData : function(_url, _callback) {
+        IriSP.jQuery.getJSON(_url, _callback)
+    },
+    deSerialize : function(_data, _source) {
+        if (typeof _data !== "object" || _data === null) {
+            return;
+        }
+        IriSP._(this.types).forEach(function(_type, _typename) {
+            var _listdata = _data[_type.serialized_name],
+                _list = new IriSP.Model.List(_source.directory);
+            if (typeof _listdata !== "undefined" && _listdata !== null) {
+                if (_listdata.hasOwnProperty("length")) {
+                    var _l = _listdata.length;
+                    for (var _i = 0; _i < _l; _i++) {
+                        var _element = _type.deserializer(_listdata[_i], _source);
+                        if (typeof _element !== "undefined" && _element) {
+                            _list.push(_element);
+                        }
+                    }
+                } else {
+                    var _element = _type.deserializer(_listdata, _source);
+                    if (typeof _element !== "undefined" && _element) {
+                        _list.push(_element);
+                    }
+                }
+            }
+            _source.addList(_typename, _list);
+        });
+        
+        if (typeof _data.meta !== "undefined") {
+            _source.projectId = _data.meta.id;
+        }
+        
+        if (typeof _data.meta !== "undefined" && typeof _data.meta.main_media !== "undefined" && typeof _data.meta.main_media["id-ref"] !== "undefined") {
+            _source.currentMedia = _source.getElement(_data.meta.main_media["id-ref"]);
+        }
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/integration/js/mashupcore.js	Thu Nov 08 18:24:47 2012 +0100
@@ -0,0 +1,632 @@
+IriSP.mashupcore = function(project, mashup) {
+    
+    var currentMedia,
+        intervaltemplate = _.template('<span class="frise-indication" style="left:<%= left %>%;"><%= time.toString() %></span>'),
+        viztemplate = _.template('<div class="frise-segment annotation" data-segment-id="<%= segmentid %>" style="background-color:<%= color %>; left:<%= left %>%; width:<%= width %>%;"></div>');
+
+    function updateMashupUI() {
+        var vizhtml = '', t = 0, k = mashup.duration ? (100 / mashup.duration) : 0;
+        mashup.segments.forEach(function(_s) {
+            var vizdata = {
+                left: k * t,
+                width: k * _s.duration,
+                color: _s.color,
+                segmentid: _s.annotation.id
+            }
+            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 "";
+        }
+        
+        $(".mashup-total-duration").text(mashup.duration.toString());
+        $(".mashup-frise .frise-segments").html(vizhtml);
+        $(".mashup-frise .frise-indications").html(createIntervals(6));
+        
+        if (currentMedia === mashup) {
+            $(".Ldt-Ctrl-Time-Total").text(currentMedia.duration.toString());
+            if (mashupTimecode > mashup.duration) {
+                mashup.setCurrentTime(mashup.duration);
+            }
+            changeCurrentAnnotation();
+            mashup.trigger("enter-annotation",mashupCurrentAnnotation);
+        }
+        
+    }
+    
+    /* Slider */
+    
+    var timeSlider = $(".Ldt-Slider"),
+        timeSliderContainer = $(".Ldt-Slider-Container"),
+        slidersRange = 920;
+    timeSlider.slider({
+        range: "min",
+        value: 0,
+        min: 0,
+        max: slidersRange,
+        slide: function(event, ui) {
+            if (currentMedia) {
+                var t = currentMedia.duration * ui.value / slidersRange;
+                currentMedia.setCurrentTime(t);
+            }
+        }
+    });
+    
+    var timeSliderHandle = timeSlider.find('.ui-slider-handle'),
+        timeSliderMaximized = false,
+        timeSliderTimeoutId = false,
+        timeSliderMinimizedHeight = 4,
+        timeSliderMaximizedHeight = 10,
+        timeSliderTimeoutDuration = 1500,
+        timeTooltip = $(".Ldt-Slider-Time");
+    
+    timeSliderContainer.css(calculateSliderCss(timeSliderMinimizedHeight));
+    timeSliderHandle.css(calculateHandleCss(timeSliderMinimizedHeight));
+    
+    function timeSliderMouseOver() {
+        if (timeSliderTimeoutId) {
+            window.clearTimeout(timeSliderTimeoutId);
+            timeSliderTimeoutId = false;
+        }
+        if (!timeSliderMaximized) {
+           timeSliderAnimateToHeight(timeSliderMaximizedHeight);
+           timeSliderMaximized = true;
+        }
+    }
+    
+    function timeSliderMouseOut() {
+        timeTooltip.hide();
+        if (timeSliderTimeoutId) {
+            clearTimeout(timeSliderTimeoutId);
+            timeSliderTimeoutId = false;
+        }
+        timeSliderTimeoutId = setTimeout(function() {
+            if (timeSliderMaximized) {
+                timeSliderAnimateToHeight(timeSliderMinimizedHeight);
+                timeSliderMaximized = false;
+            }
+            timeSliderTimeoutId = false;
+        }, timeSliderTimeoutDuration);
+    }
+    
+    timeSliderContainer
+        .mouseover(function() {
+            timeTooltip.show();
+            timeSliderMouseOver();
+        })
+        .mouseout(timeSliderMouseOut);
+    timeSlider.mousemove(function(_e) {
+            var _x = _e.pageX - timeSlider.offset().left,
+                _t = new IriSP.Model.Time(
+                );
+            timeTooltip.text(_t.toString()).css("left",_x);
+        });
+    
+    $(".Ldt-Ctrl").mouseover(timeSliderMouseOver).mouseout(timeSliderMouseOut);
+    
+    function timeSliderAnimateToHeight(_height) {
+        timeSliderContainer.stop().animate(
+            calculateSliderCss(_height),
+            500,
+            function() {
+                IriSP.jQuery(this).css("overflow","visible");
+            });
+        timeSliderHandle.stop().animate(
+            calculateHandleCss(_height),
+            500,
+            function() {
+                IriSP.jQuery(this).css("overflow","visible");
+            });
+    }
+
+    function calculateSliderCss(_size) {
+        return {
+            height: _size + "px",
+            "margin-top": (timeSliderMinimizedHeight - _size) + "px"
+        };
+    }
+
+    function calculateHandleCss(_size) {
+        return {
+            height: (2 + _size) + "px",
+            width: (2 + _size) + "px",
+            "margin-left": -Math.ceil(2 + _size / 2) + "px" 
+        }
+    }
+    
+    /* Controller Widget */
+   
+    var volBlock = $(".Ldt-Ctrl-Volume-Control");
+    
+    $('.Ldt-Ctrl-Sound')
+        .click(function() {
+            if (currentMedia) {
+                currentMedia.setMuted(!currentMedia.getMuted());
+            }
+        })
+        .mouseover(function() {
+            volBlock.show();
+        })
+        .mouseout(function() {
+            volBlock.hide();
+        });
+    volBlock.mouseover(function() {
+        volBlock.show();
+    }).mouseout(function() {
+        volBlock.hide();
+    });
+    
+    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) {
+            if (currentMedia) {
+                currentMedia.setVolume(ui.value / 100);
+            }
+        }
+    });
+    
+    $(".Ldt-Ctrl-Play").click(function() {
+        if (currentMedia) {
+            if (currentMedia.getPaused()) {        
+                currentMedia.play();
+            } else {
+                currentMedia.pause();
+            }
+        }
+    });
+    
+    $(".Ldt-Ctrl-SetIn").click(function() {
+        if (currentMedia && currentSegment) {
+            currentSegment.setBegin(currentMedia.getCurrentTime());
+        }
+    });
+    $(".Ldt-Ctrl-SetOut").click(function() {
+        if (currentMedia && currentSegment) {
+            currentSegment.setEnd(currentMedia.getCurrentTime());
+        }
+    });
+    
+    /* UI Events */
+
+    function onCurrentMediaPlay() {
+        $(".Ldt-Ctrl-Play")
+            .attr("title", "Pause")
+            .removeClass("Ldt-Ctrl-Play-PlayState")
+            .addClass("Ldt-Ctrl-Play-PauseState")
+    }
+    
+    function onCurrentMediaPause() {
+        $(".Ldt-Ctrl-Play")
+            .attr("title", "Lecture")
+            .removeClass("Ldt-Ctrl-Play-PauseState")
+            .addClass("Ldt-Ctrl-Play-PlayState")
+    }
+    
+    function onCurrentMediaTimeupdate(_time) {
+        $(".Ldt-Ctrl-Time-Elapsed").text(_time.toString());
+        timeSlider.slider("value",slidersRange * _time / currentMedia.duration);
+    }
+    
+    /* Mashup Player */
+   
+    mashup.currentMedia = null;
+   
+    var mashupCurrentAnnotation = null,
+        mashupSegmentBegin,
+        mashupSegmentEnd,
+        mashupTimecode = 0,
+        mashupSeeking = false,
+        seekdiv = $(".video-wait"),
+        mashupTimedelta;
+    
+    function showSeek() {
+        if (mashupSeeking) {
+            seekdiv.show();
+        }
+    }
+    
+    function changeCurrentAnnotation() {
+        if (mashupTimecode >= mashup.duration) {
+            if (!mashup.paused) {
+                mashup.paused = true;
+                mashup.trigger("pause");
+            }
+            mashupTimecode = 0;
+        }
+        var _annotation = mashup.getAnnotationAtTime( mashupTimecode );
+        if (typeof _annotation === "undefined") {
+            if (mashup.currentMedia) {
+                mashup.currentMedia.pause();
+                if (!mashup.paused) {
+                    mashup.paused = true;
+                    mashup.trigger("pause");
+                }
+            }
+            return;
+        }
+        mashupCurrentAnnotation = _annotation;
+        mashupSegmentBegin = mashupCurrentAnnotation.annotation.begin.milliseconds;
+        mashupSegmentEnd = mashupCurrentAnnotation.annotation.end.milliseconds;
+        mashupTimedelta = mashupSegmentBegin - mashupCurrentAnnotation.begin.milliseconds;
+        mashup.currentMedia = mashupCurrentAnnotation.getMedia();
+        
+        project.getMedias().forEach(function(_media) {
+            if (_media !== mashup.currentMedia) {
+                _media.hide();
+                _media.pause();
+            } else {
+                _media.show();
+            }
+        });
+        
+        mashup.currentMedia.setCurrentTime( mashupTimecode + mashupTimedelta);
+        mashup.currentMedia.seeking = true;
+        
+        if (!mashup.paused) {
+            mashup.currentMedia.play();
+            mashupSeeking = true;
+            setTimeout(showSeek,200);
+        }
+        mashup.trigger("timeupdate", new IriSP.Model.Time(mashupTimecode));
+
+    }
+    
+    function addMedia(media) {
+        if (media.has_player) {
+            return;
+        }
+        media.has_player = true;
+        var videoid = "video_" + media.id,
+            videoEl = $('<video>'),
+            width = $(".video").width(),
+            height = $(".video").height(),
+            mp4_file = media.video.replace(/\.webm$/i,'.mp4'),
+            webm_file = media.video.replace(/\.mp4$/i,'.webm'),
+            mp4_src = $('<source>'),
+            webm_src = $('<source>');
+        mp4_src.attr({
+            src: mp4_file,
+            type: "video/mp4"
+        });
+        webm_src.attr({
+            src: webm_file,
+            type: "video/webm"
+        });
+        videoEl.attr({
+            id : videoid,
+            width : width,
+            height : height
+        }).css({
+            position : "absolute",
+            left: 0,
+            top: 0,
+            width : width,
+            height : height
+        });
+        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) {
+            if (media.loaded) {
+                popcorn.currentTime(_milliseconds / 1000);
+            }
+        });
+        
+        media.on("setvolume", function(_vol) {
+            media.volume = _vol;
+            if (media.loaded) {
+                popcorn.volume(_vol);
+            }
+        });
+        
+        media.on("setmuted", function(_muted) {
+            media.muted = _muted;
+            if (media.loaded) {
+                popcorn.muted(_muted);
+            }
+        });
+        
+        media.on("setplay", function() {
+            if (media.loaded) {
+                popcorn.play();
+            }
+        });
+        
+        media.on("setpause", function() {
+            if (media.loaded) {
+                popcorn.pause();
+            }
+        });
+        
+        // Binding Popcorn events to media
+        
+        function getVolume() {
+            media.muted = popcorn.muted();
+            media.volume = popcorn.volume();
+        }
+        
+        popcorn.on("loadedmetadata", function() {
+            getVolume();
+            media.loaded = true;
+            media.trigger("loadedmetadata");
+            media.trigger("volumechange");
+        })
+        
+        popcorn.on("timeupdate", function() {
+            media.trigger("timeupdate", new IriSP.Model.Time(1000*popcorn.currentTime()));
+        });
+        
+        popcorn.on("volumechange", function() {
+            getVolume();
+            media.trigger("volumechange");
+        })
+        
+        popcorn.on("play", function() {
+            media.trigger("play");
+        });
+        
+        popcorn.on("pause", function() {
+            media.trigger("pause");
+        });
+        
+        popcorn.on("seeked", function() {
+            media.trigger("seeked");
+        });
+        
+        // Binding UI Events and Mashup Playing to Media
+        
+        media.on("loadedmetadata", function() {
+            if (media === currentMedia) {
+                seekdiv.hide();
+            }
+            mashup.checkLoaded();
+        });
+        
+        media.on("play", function() {
+            if (media === currentMedia) {
+                onCurrentMediaPlay();
+            }
+            if (mashup === currentMedia && media === mashup.currentMedia) {
+                mashup.trigger("play");
+            }
+        });
+        
+        media.on("pause", function() {
+            if (media === currentMedia) {
+                onCurrentMediaPause();
+            }
+            if (mashup === currentMedia && media === mashup.currentMedia) {
+                mashup.trigger("pause");
+            }
+        });
+        
+        media.on("timeupdate", function(_time) {
+            if (media === currentMedia) {
+                onCurrentMediaTimeupdate(_time);
+            }
+            if (mashup === currentMedia && !mashup.paused && media === mashup.currentMedia && !media.seeking) {
+                if ( _time < mashupSegmentEnd ) {
+                    if ( _time >= mashupSegmentBegin ) {
+                        mashupTimecode = _time - mashupTimedelta;
+                    } else {
+                        mashupTimecode = mashupSegmentBegin - mashupTimedelta;
+                        media.setCurrentTime(mashupSegmentBegin);
+                    }
+                } else {
+                    mashupTimecode = mashupSegmentEnd - mashupTimedelta;
+                    media.pause();
+                    changeCurrentAnnotation();
+                }
+                mashup.trigger("timeupdate", new IriSP.Model.Time(mashupTimecode));
+            }
+        });
+        
+        media.on("seeked", function() {
+            media.seeking = false;
+            if (mashup === currentMedia && media === mashup.currentMedia && mashupSeeking) {
+                mashupSeeking = false;
+                seekdiv.hide();
+            }
+        });
+        
+        media.on("volumechange", function() {
+            if (media === currentMedia) {
+                ctrlVolumeUpdater();
+            }
+            mashup.muted = media.muted;
+            mashup.volume = media.volume;
+            mashup.trigger("volumechange");
+        });
+        
+        project.on("set-current", function(_m) {
+            if (_m !== media) {
+                media.hide();
+            }
+        });
+        
+    }
+
+    // Mashup Events
+    
+    mashup.on("setcurrenttime", function(_milliseconds) {
+        mashupTimecode = _milliseconds;
+        changeCurrentAnnotation();
+    });
+    
+    mashup.on("setvolume", function(_vol) {
+        mashup.getMedias().forEach(function(_media) {
+            _media.setVolume(_vol);
+        });
+        mashup.volume = _vol;
+    });
+    
+    mashup.on("setmuted", function(_muted) {
+        mashup.getMedias().forEach(function(_media) {
+            _media.setMuted(_muted);
+        });
+        mashup.muted = _muted;
+    });
+    
+    mashup.on("setplay", function() {
+        mashup.paused = false;
+        changeCurrentAnnotation();
+    });
+    
+    mashup.on("setpause", function() {
+        mashup.paused = true;
+        if (mashup.currentMedia) {
+            mashup.currentMedia.pause();
+        }
+    });
+    
+    mashup.on("loadedmetadata", function() {
+        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);
+        }
+    });
+    
+    mashup.on("add", function() {
+        mashup.getMedias().forEach(addMedia);
+    })
+    
+    mashup.on("change",updateMashupUI);
+    
+    mashup.on("enter-annotation", function(segment) {
+        var a = segment.annotation;
+        $(".annotation-title").text(a.title);
+        $(".annotation-begin").text(a.begin.toString());
+        $(".annotation-end").text(a.end.toString());
+        $(".annotation-tags").text(a.keywords.join(", "));
+        $(".annotation-media-title").text(a.getMedia().title);
+        $(".annotation-description").text(a.description);
+        var p = (segment.begin + segment.end) / (2 * mashup.duration);
+        $(".mashup-description .pointer").css("left", (100 * p) + "%");
+        project.trigger("mouseout-annotation");
+    });
+    
+    project.on("mouseover-annotation", function(annotation) {
+        $(".annotation").removeClass("active");
+        if (!annotation) {
+            return;
+        }
+        $(".annotation[data-segment-id='" + annotation.id + "']").addClass("active");
+    });
+    
+    project.on("mouseout-annotation", function() {
+        if (currentMedia === mashup && mashupCurrentAnnotation) {
+            $(".annotation").removeClass("active");
+            $(".item-video.annotation[data-segment-id='" + mashupCurrentAnnotation.annotation.id + "']").addClass("active");
+        }
+    });
+    
+    project.on("click-annotation", function(annotation) {
+        if (!annotation) {
+            return;
+        }
+        var segment = mashup.getAnnotation(annotation);
+        project.trigger("set-current", mashup);
+        if (segment) {
+            mashup.setCurrentTime(segment.begin);
+        }
+    })
+    
+    project.on("set-current", function(media) {
+        currentMedia = media;
+        if (currentMedia.elementType === "media") {
+            if (!media.has_player) {
+                addMedia(media);
+            }
+            media.show();
+            if (!currentMedia.loaded) {
+                seekdiv.show();
+            }
+        }
+        if (currentMedia.elementType === "mashup") {
+            mashup.checkLoaded();
+        }
+        $(".Ldt-Ctrl-Time-Total").text(currentMedia.duration.toString());
+        currentMedia.trigger("timeupdate",currentMedia.getCurrentTime());
+        currentMedia.trigger("pause");
+    });
+    
+    $(".frise")
+    .on("mouseover", ".frise-segment", function() {
+        project.trigger("mouseover-annotation", project.getElement($(this).attr("data-segment-id")));
+    })
+    .on("mouseout", ".frise-segment", function() {
+        project.trigger("mouseout-annotation");
+    })
+    .on("click", ".frise-segment", function() {
+        project.trigger("click-annotation", project.getElement($(this).attr("data-segment-id")));
+    });
+    
+    mashup.trigger("add");
+    
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/integration/js/mashupplayer.js	Thu Nov 08 18:24:47 2012 +0100
@@ -0,0 +1,21 @@
+IriSP.player = function(options) {
+    
+    var directory = new IriSP.Model.Directory(),
+        project = directory.remoteSource({
+            url: options.url,
+            serializer: IriSP.serializers.ldt
+        }),
+        mashup;
+    
+    project.onLoad(function() {
+        mashup = project.getMashups()[0];
+        IriSP.mashupcore(project, mashup);
+        project.trigger("set-current",mashup);
+        
+        $(".info-title a").text(mashup.title);
+        $(".info-duration td").text(mashup.duration.toString());
+        $(".info-author a").text(mashup.creator);
+        $(".info-description td").text(mashup.description);
+        
+    });
+}
--- a/integration/js/model.js	Tue Nov 06 18:49:13 2012 +0100
+++ b/integration/js/model.js	Thu Nov 08 18:24:47 2012 +0100
@@ -2,32 +2,28 @@
 
 /* model.js is where data is stored in a standard form, whatever the serializer */
 IriSP.Model = (function (ns) {
+    
+    function pad(n, x, b) {
+        b = b || 10;
+        var s = (x).toString(b);
+        while (s.length < n) {
+            s = "0" + s;
+        }
+        return s;
+    }
+    
+    function rand16(n) {
+        return pad(n, Math.floor(Math.random()*Math.pow(16,n)), 16);
+    }
+    
+    var uidbase = rand16(8) + "-" + rand16(4) + "-", uidincrement = Math.floor(Math.random()*0x10000);
 
 var Model = {
     _SOURCE_STATUS_EMPTY : 0,
     _SOURCE_STATUS_WAITING : 1,
     _SOURCE_STATUS_READY : 2,
-    _ID_AUTO_INCREMENT : 0,
-    _ID_BASE : (function(_d) {
-        function pad(n){return n<10 ? '0'+n : n}
-        function fillrand(n) {
-            var _res = ''
-            for (var i=0; i<n; i++) {
-                _res += Math.floor(16*Math.random()).toString(16);
-            }
-            return _res;
-        }
-        return _d.getUTCFullYear() + '-'  
-            + pad(_d.getUTCMonth()+1) + '-'  
-            + pad(_d.getUTCDate()) + '-'
-            + fillrand(16);
-    })(new Date()),
     getUID : function() {
-        var _n = (++this._ID_AUTO_INCREMENT).toString();
-        while (_n.length < 4) {
-            _n = '0' + _n
-        }
-        return "autoid-" + this._ID_BASE + '-' + _n;
+        return uidbase + pad(4, (++uidincrement % 0x10000), 16) + "-" + rand16(4) + "-" + rand16(6) + rand16(6);
     },
     regexpFromTextOrArray : function(_textOrArray, _testOnly, _iexact) {
         var _testOnly = _testOnly || false,
@@ -75,13 +71,12 @@
         return _res;
     },
     dateToIso : function(d) {
-        function pad(n){return n<10 ? '0'+n : n}  
         return d.getUTCFullYear()+'-'  
-            + pad(d.getUTCMonth()+1)+'-'  
-            + pad(d.getUTCDate())+'T'  
-            + pad(d.getUTCHours())+':'  
-            + pad(d.getUTCMinutes())+':'  
-            + pad(d.getUTCSeconds())+'Z'  
+            + pad(2, d.getUTCMonth()+1)+'-'  
+            + pad(2, d.getUTCDate())+'T'  
+            + pad(2, d.getUTCHours())+':'  
+            + pad(2, d.getUTCMinutes())+':'  
+            + pad(2, d.getUTCSeconds())+'Z'  
     }
 }
 
@@ -347,19 +342,12 @@
 }
 
 Model.Time.prototype.toString = function(showCs) {
-    function pad(_n) {
-        var _res = _n.toString();
-        while (_res.length < 2) {
-            _res = '0' + _res;
-        }
-        return _res;
-    }
     var _hms = this.getHMS(),
         _res = '';
     if (_hms.hours) {
         _res += _hms.hours + ':'
     }
-    _res += pad(_hms.minutes) + ':' + pad(_hms.seconds);
+    _res += pad(2, _hms.minutes) + ':' + pad(2, _hms.seconds);
     if (showCs) {
         _res += "." + Math.round(_hms.milliseconds / 100)
     }
@@ -409,6 +397,9 @@
 
 Model.Element = function(_id, _source) {
     this.elementType = 'element';
+    this.title = "";
+    this.description = "";
+    this.__events = {}
     if (typeof _source === "undefined") {
         return;
     }
@@ -417,9 +408,6 @@
     }
     this.source = _source;
     this.id = _id;
-    this.title = "";
-    this.description = "";
-    this.__events = {}
     this.source.directory.addElement(this);
 }
 
@@ -489,6 +477,20 @@
     });
     this.on("timeupdate", function(_time) {
         _this.currentTime = _time;
+        _this.getAnnotations().filter(function(_a) {
+            return (_a.end <= _time || _a.begin > _time) && _a.playing
+        }).forEach(function(_a) {
+            _a.playing = false;
+            _a.trigger("leave");
+            _this.trigger("leave-annotation",_a);
+        });
+        _this.getAnnotations().filter(function(_a) {
+            return _a.begin <= _time && _a.end > _time && !_a.playing
+        }).forEach(function(_a) {
+            _a.playing = true;
+            _a.trigger("enter");
+            _this.trigger("enter-annotation",_a);
+        });
     });
 }
 
@@ -541,22 +543,7 @@
     this.elementType = 'media';
     this.duration = new Model.Time();
     this.video = '';
-    
     var _this = this;
-    this.on("timeupdate", function(_time) {
-        _this.getAnnotations().filter(function(_a) {
-            return (_a.end <= _time || _a.begin > _time) && _a.playing
-        }).forEach(function(_a) {
-            _a.playing = false;
-            _a.trigger("leave");
-        });
-        _this.getAnnotations().filter(function(_a) {
-            return _a.begin <= _time && _a.end > _time && !_a.playing
-        }).forEach(function(_a) {
-            _a.playing = true;
-            _a.trigger("enter");
-        });
-    });
 }
 
 Model.Media.prototype = new Model.Playable();
@@ -737,30 +724,7 @@
     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.getSegments().filter(function(_a) {
-            return (_a.end <= _time || _a.begin > _time) && _a.playing
-        }).forEach(function(_a) {
-            _a.playing = false;
-            _a.trigger("leave");
-        });
-        _this.getSegments().filter(function(_a) {
-            return _a.begin <= _time && _a.end > _time && !_a.playing
-        }).forEach(function(_a) {
-            _a.playing = true;
-            _a.trigger("enter");
-            var _m = _a.getMedia();
-            if (_m !== _currentMedia) {
-                if (_currentMedia) {
-                    _currentMedia.trigger("leave");
-                }
-                _m.trigger("enter");
-                _currentMedia = _m;
-            }
-        });
-    });
     this._updateTimes = function() {
         _this.updateTimes();
         _this.trigger("change");
@@ -880,7 +844,7 @@
     });
 }
 
-Model.Mashup.prototype.getSegments = function() {
+Model.Mashup.prototype.getAnnotations = function() {
     return this.segments;
 }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/integration/lib/jquery.tagit.css	Thu Nov 08 18:24:47 2012 +0100
@@ -0,0 +1,54 @@
+ul.tagit {
+    padding: 1px 5px;
+    overflow: auto;
+    margin-left: inherit; /* usually we don't want the regular ul margins. */
+    margin-right: inherit;
+}
+ul.tagit li {
+    display: block;
+    float: left;
+    margin: 2px 5px 2px 0;
+}
+ul.tagit li.tagit-choice {
+    padding: .2em 18px .2em .5em;
+    position: relative;
+    line-height: inherit;
+}
+ul.tagit li.tagit-new {
+    padding: .25em 4px .25em 0;
+}
+
+ul.tagit li.tagit-choice a.tagit-label {
+    cursor: pointer;
+    text-decoration: none;
+}
+ul.tagit li.tagit-choice .tagit-close {
+    cursor: pointer;
+    position: absolute;
+    right: .1em;
+    top: 50%;
+    margin-top: -8px;
+}
+
+/* used for some custom themes that don't need image icons */
+ul.tagit li.tagit-choice .tagit-close .text-icon {
+    display: none;
+}
+
+ul.tagit li.tagit-choice input {
+    display: block;
+    float: left;
+    margin: 2px 5px 2px 0;
+}
+ul.tagit input[type="text"] {
+    -moz-box-sizing:    border-box;
+    -webkit-box-sizing: border-box;
+    box-sizing:         border-box;
+
+    border: none;
+    margin: 0;
+    padding: 0;
+    width: inherit;
+    background-color: inherit;
+    outline: none;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/integration/lib/tag-it.js	Thu Nov 08 18:24:47 2012 +0100
@@ -0,0 +1,392 @@
+/*
+* jQuery UI Tag-it!
+*
+* @version v2.0 (06/2011)
+*
+* Copyright 2011, Levy Carneiro Jr.
+* Released under the MIT license.
+* http://aehlke.github.com/tag-it/LICENSE
+*
+* Homepage:
+*   http://aehlke.github.com/tag-it/
+*
+* Authors:
+*   Levy Carneiro Jr.
+*   Martin Rehfeld
+*   Tobias Schmidt
+*   Skylar Challand
+*   Alex Ehlke
+*
+* Maintainer:
+*   Alex Ehlke - Twitter: @aehlke
+*
+* Dependencies:
+*   jQuery v1.4+
+*   jQuery UI v1.8+
+*/
+(function($) {
+
+    $.widget('ui.tagit', {
+        options: {
+            itemName          : 'item',
+            fieldName         : 'tags',
+            availableTags     : [],
+            tagSource         : null,
+            removeConfirmation: false,
+            caseSensitive     : true,
+            placeholderText   : null,
+
+            // When enabled, quotes are not neccesary
+            // for inputting multi-word tags.
+            allowSpaces: false,
+
+            // Whether to animate tag removals or not.
+            animate: true,
+
+            // The below options are for using a single field instead of several
+            // for our form values.
+            //
+            // When enabled, will use a single hidden field for the form,
+            // rather than one per tag. It will delimit tags in the field
+            // with singleFieldDelimiter.
+            //
+            // The easiest way to use singleField is to just instantiate tag-it
+            // on an INPUT element, in which case singleField is automatically
+            // set to true, and singleFieldNode is set to that element. This 
+            // way, you don't need to fiddle with these options.
+            singleField: false,
+
+            singleFieldDelimiter: ',',
+
+            // Set this to an input DOM node to use an existing form field.
+            // Any text in it will be erased on init. But it will be
+            // populated with the text of tags as they are created,
+            // delimited by singleFieldDelimiter.
+            //
+            // If this is not set, we create an input node for it,
+            // with the name given in settings.fieldName, 
+            // ignoring settings.itemName.
+            singleFieldNode: null,
+
+            // Optionally set a tabindex attribute on the input that gets
+            // created for tag-it.
+            tabIndex: null,
+
+
+            // Event callbacks.
+            onTagAdded  : null,
+            onTagRemoved: null,
+            onTagClicked: null
+        },
+
+
+        _create: function() {
+            // for handling static scoping inside callbacks
+            var that = this;
+
+            // There are 2 kinds of DOM nodes this widget can be instantiated on:
+            //     1. UL, OL, or some element containing either of these.
+            //     2. INPUT, in which case 'singleField' is overridden to true,
+            //        a UL is created and the INPUT is hidden.
+            if (this.element.is('input')) {
+                this.tagList = $('<ul></ul>').insertAfter(this.element);
+                this.options.singleField = true;
+                this.options.singleFieldNode = this.element;
+                this.element.css('display', 'none');
+            } else {
+                this.tagList = this.element.find('ul, ol').andSelf().last();
+            }
+
+            this._tagInput = $('<input type="text" />').addClass('ui-widget-content');
+            if (this.options.tabIndex) {
+                this._tagInput.attr('tabindex', this.options.tabIndex);
+            }
+            if (this.options.placeholderText) {
+                this._tagInput.attr('placeholder', this.options.placeholderText);
+            }
+
+            this.options.tagSource = this.options.tagSource || function(search, showChoices) {
+                var filter = search.term.toLowerCase();
+                var choices = $.grep(this.options.availableTags, function(element) {
+                    // Only match autocomplete options that begin with the search term.
+                    // (Case insensitive.)
+                    return (element.toLowerCase().indexOf(filter) === 0);
+                });
+                showChoices(this._subtractArray(choices, this.assignedTags()));
+            };
+
+            // Bind tagSource callback functions to this context.
+            if ($.isFunction(this.options.tagSource)) {
+                this.options.tagSource = $.proxy(this.options.tagSource, this);
+            }
+
+            this.tagList
+                .addClass('tagit')
+                .addClass('ui-widget ui-widget-content ui-corner-all')
+                // Create the input field.
+                .append($('<li class="tagit-new"></li>').append(this._tagInput))
+                .click(function(e) {
+                    var target = $(e.target);
+                    if (target.hasClass('tagit-label')) {
+                        that._trigger('onTagClicked', e, target.closest('.tagit-choice'));
+                    } else {
+                        // Sets the focus() to the input field, if the user
+                        // clicks anywhere inside the UL. This is needed
+                        // because the input field needs to be of a small size.
+                        that._tagInput.focus();
+                    }
+                });
+
+            // Add existing tags from the list, if any.
+            this.tagList.children('li').each(function() {
+                if (!$(this).hasClass('tagit-new')) {
+                    that.createTag($(this).html(), $(this).attr('class'));
+                    $(this).remove();
+                }
+            });
+
+            // Single field support.
+            if (this.options.singleField) {
+                if (this.options.singleFieldNode) {
+                    // Add existing tags from the input field.
+                    var node = $(this.options.singleFieldNode);
+                    var tags = node.val().split(this.options.singleFieldDelimiter);
+                    node.val('');
+                    $.each(tags, function(index, tag) {
+                        that.createTag(tag);
+                    });
+                } else {
+                    // Create our single field input after our list.
+                    this.options.singleFieldNode = this.tagList.after('<input type="hidden" style="display:none;" value="" name="' + this.options.fieldName + '" />');
+                }
+            }
+
+            // Events.
+            this._tagInput
+                .keydown(function(event) {
+                    // Backspace is not detected within a keypress, so it must use keydown.
+                    if (event.which == $.ui.keyCode.BACKSPACE && that._tagInput.val() === '') {
+                        var tag = that._lastTag();
+                        if (!that.options.removeConfirmation || tag.hasClass('remove')) {
+                            // When backspace is pressed, the last tag is deleted.
+                            that.removeTag(tag);
+                        } else if (that.options.removeConfirmation) {
+                            tag.addClass('remove ui-state-highlight');
+                        }
+                    } else if (that.options.removeConfirmation) {
+                        that._lastTag().removeClass('remove ui-state-highlight');
+                    }
+
+                    // Comma/Space/Enter are all valid delimiters for new tags,
+                    // except when there is an open quote or if setting allowSpaces = true.
+                    // Tab will also create a tag, unless the tag input is empty, in which case it isn't caught.
+                    if (
+                        event.which == $.ui.keyCode.COMMA ||
+                        event.which == $.ui.keyCode.ENTER ||
+                        (
+                            event.which == $.ui.keyCode.TAB &&
+                            that._tagInput.val() !== ''
+                        ) ||
+                        (
+                            event.which == $.ui.keyCode.SPACE &&
+                            that.options.allowSpaces !== true &&
+                            (
+                                $.trim(that._tagInput.val()).replace( /^s*/, '' ).charAt(0) != '"' ||
+                                (
+                                    $.trim(that._tagInput.val()).charAt(0) == '"' &&
+                                    $.trim(that._tagInput.val()).charAt($.trim(that._tagInput.val()).length - 1) == '"' &&
+                                    $.trim(that._tagInput.val()).length - 1 !== 0
+                                )
+                            )
+                        )
+                    ) {
+                        event.preventDefault();
+                        that.createTag(that._cleanedInput());
+
+                        // The autocomplete doesn't close automatically when TAB is pressed.
+                        // So let's ensure that it closes.
+                        that._tagInput.autocomplete('close');
+                    }
+                }).blur(function(e){
+                    // Create a tag when the element loses focus (unless it's empty).
+                    that.createTag(that._cleanedInput());
+                });
+                
+
+            // Autocomplete.
+            if (this.options.availableTags || this.options.tagSource) {
+                this._tagInput.autocomplete({
+                    source: this.options.tagSource,
+                    select: function(event, ui) {
+                        // Delete the last tag if we autocomplete something despite the input being empty
+                        // This happens because the input's blur event causes the tag to be created when
+                        // the user clicks an autocomplete item.
+                        // The only artifact of this is that while the user holds down the mouse button
+                        // on the selected autocomplete item, a tag is shown with the pre-autocompleted text,
+                        // and is changed to the autocompleted text upon mouseup.
+                        if (that._tagInput.val() === '') {
+                            that.removeTag(that._lastTag(), false);
+                        }
+                        that.createTag(ui.item.value);
+                        // Preventing the tag input to be updated with the chosen value.
+                        return false;
+                    }
+                });
+            }
+        },
+
+        _cleanedInput: function() {
+            // Returns the contents of the tag input, cleaned and ready to be passed to createTag
+            return $.trim(this._tagInput.val().replace(/^"(.*)"$/, '$1'));
+        },
+
+        _lastTag: function() {
+            return this.tagList.children('.tagit-choice:last');
+        },
+
+        assignedTags: function() {
+            // Returns an array of tag string values
+            var that = this;
+            var tags = [];
+            if (this.options.singleField) {
+                tags = $(this.options.singleFieldNode).val().split(this.options.singleFieldDelimiter);
+                if (tags[0] === '') {
+                    tags = [];
+                }
+            } else {
+                this.tagList.children('.tagit-choice').each(function() {
+                    tags.push(that.tagLabel(this));
+                });
+            }
+            return tags;
+        },
+
+        _updateSingleTagsField: function(tags) {
+            // Takes a list of tag string values, updates this.options.singleFieldNode.val to the tags delimited by this.options.singleFieldDelimiter
+            $(this.options.singleFieldNode).val(tags.join(this.options.singleFieldDelimiter));
+        },
+
+        _subtractArray: function(a1, a2) {
+            var result = [];
+            for (var i = 0; i < a1.length; i++) {
+                if ($.inArray(a1[i], a2) == -1) {
+                    result.push(a1[i]);
+                }
+            }
+            return result;
+        },
+
+        tagLabel: function(tag) {
+            // Returns the tag's string label.
+            if (this.options.singleField) {
+                return $(tag).children('.tagit-label').text();
+            } else {
+                return $(tag).children('input').val();
+            }
+        },
+
+        _isNew: function(value) {
+            var that = this;
+            var isNew = true;
+            this.tagList.children('.tagit-choice').each(function(i) {
+                if (that._formatStr(value) == that._formatStr(that.tagLabel(this))) {
+                    isNew = false;
+                    return false;
+                }
+            });
+            return isNew;
+        },
+
+        _formatStr: function(str) {
+            if (this.options.caseSensitive) {
+                return str;
+            }
+            return $.trim(str.toLowerCase());
+        },
+
+        createTag: function(value, additionalClass) {
+            var that = this;
+            // Automatically trims the value of leading and trailing whitespace.
+            value = $.trim(value);
+
+            if (!this._isNew(value) || value === '') {
+                return false;
+            }
+
+            var label = $(this.options.onTagClicked ? '<a class="tagit-label"></a>' : '<span class="tagit-label"></span>').text(value);
+
+            // Create tag.
+            var tag = $('<li></li>')
+                .addClass('tagit-choice ui-widget-content ui-state-default ui-corner-all')
+                .addClass(additionalClass)
+                .append(label);
+
+            // Button for removing the tag.
+            var removeTagIcon = $('<span></span>')
+                .addClass('ui-icon ui-icon-close');
+            var removeTag = $('<a><span class="text-icon">\xd7</span></a>') // \xd7 is an X
+                .addClass('tagit-close')
+                .append(removeTagIcon)
+                .click(function(e) {
+                    // Removes a tag when the little 'x' is clicked.
+                    that.removeTag(tag);
+                });
+            tag.append(removeTag);
+
+            // Unless options.singleField is set, each tag has a hidden input field inline.
+            if (this.options.singleField) {
+                var tags = this.assignedTags();
+                tags.push(value);
+                this._updateSingleTagsField(tags);
+            } else {
+                var escapedValue = label.html();
+                tag.append('<input type="hidden" style="display:none;" value="' + escapedValue + '" name="' + this.options.itemName + '[' + this.options.fieldName + '][]" />');
+            }
+
+            this._trigger('onTagAdded', null, tag);
+
+            // Cleaning the input.
+            this._tagInput.val('');
+
+            // insert tag
+            this._tagInput.parent().before(tag);
+        },
+        
+        removeTag: function(tag, animate) {
+            animate = animate || this.options.animate;
+
+            tag = $(tag);
+
+            this._trigger('onTagRemoved', null, tag);
+
+            if (this.options.singleField) {
+                var tags = this.assignedTags();
+                var removedTagLabel = this.tagLabel(tag);
+                tags = $.grep(tags, function(el){
+                    return el != removedTagLabel;
+                });
+                this._updateSingleTagsField(tags);
+            }
+            // Animate the removal.
+            if (animate) {
+                tag.fadeOut('fast').hide('blind', {direction: 'horizontal'}, 'fast', function(){
+                    tag.remove();
+                }).dequeue();
+            } else {
+                tag.remove();
+            }
+        },
+
+        removeAll: function() {
+            // Removes all tags.
+            var that = this;
+            this.tagList.children('.tagit-choice').each(function(index, tag) {
+                that.removeTag(tag, false);
+            });
+        }
+
+    });
+
+})(jQuery);
+
+