Video and soundcloud player
authorveltr
Thu, 23 May 2013 13:10:54 +0200
changeset 3 5a4dd4e6bbe7
parent 2 30e0ed21127c
child 4 92506f22f7cd
Video and soundcloud player
player/css/styles.css
player/data/rigoletto.json
player/index.html
player/js/ldt-serializer.js
player/js/metadataplayer-core.js
player/js/player.js
--- a/player/css/styles.css	Tue May 21 19:12:29 2013 +0200
+++ b/player/css/styles.css	Thu May 23 13:10:54 2013 +0200
@@ -80,14 +80,35 @@
 }
 
 .tags {
-    position: absolute; top: 0; left: 0; width: 110px;
+    position: absolute; top: 0; left: 0; width: 140px; z-index: 2; font-size: 12px; cursor: pointer;
 }
 
 .tags-title {
-    line-height: 40px; padding-left: 20px; font-size: 11px; color: #B8155F;
+    line-height: 40px; padding: 0 15px; font-size: 11px; color: #B8155F;
     font-family: 'Futura-Medium'; text-transform: uppercase;
 }
 
+.tags:hover .tags-title {
+    background: #404040; color: #ffffff;
+}
+
+.tags-list {
+    background: #303030;
+    padding: 10px 0; display: none;
+}
+
+.tags:hover .tags-list {
+    display: block;
+}
+
+.tag {
+    color: #cccccc; padding: 5px 15px;
+}
+
+.tag:hover {
+    color: #B8155F; background: #202020;
+}
+
 /* Pictograms on the left */
 
 .pictolist {
@@ -175,7 +196,7 @@
 /* Annotations */
 
 .annotation {
-    position: absolute;
+    position: absolute; display: none;
 }
 
 .annotation-title {
@@ -187,7 +208,7 @@
     font-weight: bold; font-size: 20px;
 }
 
-.text-annotation, .slideshow-annotation, .video-annotation {
+.text-annotation, .slideshow-annotation, .video-annotation, .audio-annotation {
     left: 45px; top: 20px; right: 35px;
 }
 
@@ -197,17 +218,24 @@
     background: #ffffff; max-height: 522px; overflow: auto;
 }
 
-.text-annotation .close-annotation {
+.text-annotation .close-annotation,
+.audio-annotation .close-annotation {
     color: #000000;
 }
 
-.text-annotation .annotation-title {
-    font-size: 23px; margin: 24px 0 0;
+.text-annotation .close-annotation:hover,
+.audio-annotation .close-annotation:hover {
+    color: #666666;
+}
+
+.text-annotation .annotation-title,
+.audio-annotation .annotation-title {
+    font-size: 23px; margin: 24px 0;
 }
 
 .text-annotation .annotation-contents {
     font-family: Georgia, 'Times New Roman', serif;
-    margin: 30px 45px 45px;
+    margin: 0 45px 45px;
     column-count: 2; column-gap: 60px;
     -moz-column-count: 2; -moz-column-gap: 60px;
     -webkit-column-count: 2; -webkit-column-gap: 60px;
@@ -217,6 +245,12 @@
     margin-bottom: 10px;
 }
 
+/* Audio annotation */
+
+.audio-annotation {
+    background: #ffffff;
+}
+
 /* Slideshow annotation */
 
 .slideshow-center {
@@ -228,7 +262,7 @@
 }
 
 .slideshow-frame {
-    float: left; width: 650px; min-height: 60px;
+    float: left;
 }
 
 .slideshow-image {
@@ -237,7 +271,7 @@
 
 .slideshow-play-pause {
     float: left; padding: 10px 15px 11px;
-    background: #3e3e3e; cursor: pointer;
+    background: #404040; cursor: pointer;
 }
 
 .slideshow-play-pause a {
@@ -259,17 +293,22 @@
 
 .slideshow-annotation .close-annotation,
 .video-annotation .close-annotation {
-    color: #ffffff;
+    color: #ffffff; margin-right: 10px;
+}
+
+.slideshow-annotation .close-annotation:hover,
+.video-annotation .close-annotation:hover {
+    color: #cccccc;
 }
 
 .slideshow-annotation p,
 .video-annotation p {
-    margin: 10px 25px 10px 10px;
+    margin: 10px 25px 10px 15px;
     font-size: 12px; color: #CCCCCC;
 }
 
 .slideshow-description {
-    margin-left: 650px; max-height: 452px; overflow: auto;
+    max-height: 452px; overflow: auto;
 }
 
 .slideshow-center:after {
@@ -357,18 +396,26 @@
     border-color: #000000;
 }
 
-.chapter:hover .chapter-block {
-    background: #3e3e3e;
+.chapter.active .chapter-block {
+    background: #404040;
+}
+
+.chapter.found .chapter-block {
+    background: #510829;
+}
+
+.chapter.found.active .chapter-block {
+    background: #8a1249;
 }
 
 .chapter-title {
-    position: absolute; top: 53px; left: -40px; right: -40px;
+    position: absolute; top: 53px; left: 50%; width: 240px; margin-left: -120px;
     font-size: 9px; text-transform: uppercase;
     text-align: center; color: #909090; display: none;
     font-family: 'Futura-Medium';
 }
 
-.chapter:hover .chapter-title {
+.chapter.active .chapter-title {
     display: block;
 }
 
@@ -415,7 +462,7 @@
 
 .prev-chapter, .next-chapter {
     width: 12px; height: 16px; margin: 8px 5px;
-    background: url(../img/sprites.png);
+    background: url(../img/sprites.png); cursor: pointer;
 }
 
 .next-chapter {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/player/data/rigoletto.json	Thu May 23 13:10:54 2013 +0200
@@ -0,0 +1,290 @@
+{
+    "views": [],
+    "tags": [
+        {
+            "meta": {
+                "dc:contributor": "IRI",
+                "dc:created": "2012-05-07T10:11:06.727228",
+                "dc:title": "Victor Hugo",
+                "dc:modified": "2012-05-07T10:11:06.727228",
+                "dc:creator": "IRI"
+            },
+            "id": "tag-victor-hugo"
+        }, {
+            "meta": {
+                "dc:contributor": "IRI",
+                "dc:created": "2012-05-07T10:11:06.727228",
+                "dc:title": "Melodrama",
+                "dc:modified": "2012-05-07T10:11:06.727228",
+                "dc:creator": "IRI"
+            },
+            "id": "tag-melodrama"
+        }, {
+            "meta": {
+                "dc:contributor": "IRI",
+                "dc:created": "2012-05-07T10:11:06.727228",
+                "dc:title": "Festival d’Aix",
+                "dc:modified": "2012-05-07T10:11:06.727228",
+                "dc:creator": "IRI"
+            },
+            "id": "tag-festival-aix"
+        }, {
+            "meta": {
+                "dc:contributor": "IRI",
+                "dc:created": "2012-05-07T10:11:06.727228",
+                "dc:title": "Argument",
+                "dc:modified": "2012-05-07T10:11:06.727228",
+                "dc:creator": "IRI"
+            },
+            "id": "tag-argument"
+        }, {
+            "meta": {
+                "dc:contributor": "IRI",
+                "dc:created": "2012-05-07T10:11:06.727228",
+                "dc:title": "Giuseppe Verdi",
+                "dc:modified": "2012-05-07T10:11:06.727228",
+                "dc:creator": "IRI"
+            },
+            "id": "tag-giuseppe-verdi"
+        }, {
+            "meta": {
+                "dc:contributor": "IRI",
+                "dc:created": "2012-05-07T10:11:06.727228",
+                "dc:title": "Gilda",
+                "dc:modified": "2012-05-07T10:11:06.727228",
+                "dc:creator": "IRI"
+            },
+            "id": "tag-gilda"
+        }, {
+            "meta": {
+                "dc:contributor": "IRI",
+                "dc:created": "2012-05-07T10:11:06.727228",
+                "dc:title": "Duc de Mantoue",
+                "dc:modified": "2012-05-07T10:11:06.727228",
+                "dc:creator": "IRI"
+            },
+            "id": "tag-duc-de-mantoue"
+        }, {
+            "meta": {
+                "dc:contributor": "IRI",
+                "dc:created": "2012-05-07T10:11:06.727228",
+                "dc:title": "La donna è mobile",
+                "dc:modified": "2012-05-07T10:11:06.727228",
+                "dc:creator": "IRI"
+            },
+            "id": "tag-la-donna-e-mobile"
+        }, {
+            "meta": {
+                "dc:contributor": "IRI",
+                "dc:created": "2012-05-07T10:11:06.727228",
+                "dc:title": "Gianandrea Noseda",
+                "dc:modified": "2012-05-07T10:11:06.727228",
+                "dc:creator": "IRI"
+            },
+            "id": "tag-gianandrea-noseda"
+        }, {
+            "meta": {
+                "dc:contributor": "IRI",
+                "dc:created": "2012-05-07T10:11:06.727228",
+                "dc:title": "London Symphony Orchestra",
+                "dc:modified": "2012-05-07T10:11:06.727228",
+                "dc:creator": "IRI"
+            },
+            "id": "tag-london-symphony-orchestra"
+        }
+    ],
+    "lists": [],
+    "medias": [
+        {
+            "origin": "0",
+            "url": "video/rigoletto.mp4",
+            "http://advene.liris.cnrs.fr/ns/frame_of_reference/ms": "o=0",
+            "meta": {
+                "dc:contributor": "IRI",
+                "item": {
+                    "name": "streamer",
+                    "value": "rtmp://media.iri.centrepompidou.fr/ddc_player/"
+                },
+                "dc:created": "2011-02-14T15:06:34.829919",
+                "dc:duration": 342000,
+                "dc:creator": "IRI",
+                "dc:created.contents": "2012-02-13",
+                "dc:title": "Présentation de Rigoletto",
+                "dc:creator.contents": "IRI",
+                "dc:modified": "2012-02-13T11:55:23.898905",
+                "dc:description": "Présentation de Rigoletto"
+            },
+            "id": "media-01",
+            "unit": "ms"
+        }
+    ],
+    "meta": {
+        "dc:contributor": "admin",
+        "dc:created": "2011-02-14T15:15:52.602502",
+        "dc:creator": "admin",
+        "main_media": {
+            "id-ref": "media-01"
+        },
+        "dc:description": "",
+        "dc:title": "Présentation de Rigoletto",
+        "id": "project-01",
+        "dc:modified": "2012-04-29T15:41:55.858453"
+    },
+    "annotations": [
+        {
+            "content": {
+                "mimetype": "application/x-ldt-structured",
+                "description": "",
+                "title": "Introduction : De Victor Hugo au Melodrama"
+            },
+            "begin": 0,
+            "meta": {
+                "dc:contributor": "perso",
+                "id-ref": "annotation-type-01",
+                "dc:created": "2012-05-07T10:11:06.721324",
+                "dc:modified": "2012-05-07T10:11:06.721324",
+                "dc:creator": "perso"
+            },
+            "end": 84000,
+            "tags": [
+                {
+                    "id-ref": "tag-victor-hugo"
+                }, {
+                    "id-ref": "tag-melodrama"
+                }, {
+                    "id-ref": "tag-festival-aix"
+                }, {
+                    "id-ref": "tag-giuseppe-verdi"
+                }
+            ],
+            "color": "3355443",
+            "media": "media-01",
+            "id": "annotation-01"
+        }, {
+            "content": {
+                "mimetype": "application/x-ldt-structured",
+                "description": "",
+                "title": "L’argument de Rigoletto"
+            },
+            "begin": 84000,
+            "meta": {
+                "dc:contributor": "perso",
+                "id-ref": "annotation-type-01",
+                "dc:created": "2012-05-07T10:11:06.721324",
+                "dc:modified": "2012-05-07T10:11:06.721324",
+                "dc:creator": "perso"
+            },
+            "end": 171000,
+            "tags": [
+                {
+                    "id-ref": "tag-argument"
+                }, {
+                    "id-ref": "tag-duc-de-mantoue"
+                }, {
+                    "id-ref": "tag-gilda"
+                }
+            ],
+            "color": "3355443",
+            "media": "media-01",
+            "id": "annotation-02"
+        }, {
+            "content": {
+                "mimetype": "application/x-ldt-structured",
+                "description": "",
+                "title": "Rigoletto dans la carrière de Verdi"
+            },
+            "begin": 171000,
+            "meta": {
+                "dc:contributor": "perso",
+                "id-ref": "annotation-type-01",
+                "dc:created": "2012-05-07T10:11:06.721324",
+                "dc:modified": "2012-05-07T10:11:06.721324",
+                "dc:creator": "perso"
+            },
+            "end": 204000,
+            "tags": [
+                {
+                    "id-ref": "tag-giuseppe-verdi"
+                }
+            ],
+            "color": "3355443",
+            "media": "media-01",
+            "id": "annotation-03"
+        }, {
+            "content": {
+                "mimetype": "application/x-ldt-structured",
+                "description": "",
+                "title": "Les Airs célèbres de Rigoletto"
+            },
+            "begin": 204000,
+            "meta": {
+                "dc:contributor": "perso",
+                "id-ref": "annotation-type-01",
+                "dc:created": "2012-05-07T10:11:06.721324",
+                "dc:modified": "2012-05-07T10:11:06.721324",
+                "dc:creator": "perso"
+            },
+            "end": 258000,
+            "tags": [
+                {
+                    "id-ref": "tag-la-donna-e-mobile"
+                }, {
+                    "id-ref": "tag-duc-de-mantoue"
+                }, {
+                    "id-ref": "tag-gilda"
+                }
+            ],
+            "color": "3355443",
+            "media": "media-01",
+            "id": "annotation-04"
+        }, {
+            "content": {
+                "mimetype": "application/x-ldt-structured",
+                "description": "",
+                "title": "Rigoletto au Festival d’Aix"
+            },
+            "begin": 258000,
+            "meta": {
+                "dc:contributor": "perso",
+                "id-ref": "annotation-type-01",
+                "dc:created": "2012-05-07T10:11:06.721324",
+                "dc:modified": "2012-05-07T10:11:06.721324",
+                "dc:creator": "perso"
+            },
+            "end": 342000,
+            "tags": [
+                {
+                    "id-ref": "tag-giuseppe-verdi"
+                }, {
+                    "id-ref": "tag-festival-aix"
+                }, {
+                    "id-ref": "tag-gianandrea-noseda"
+                }, {
+                    "id-ref": "tag-london-symphony-orchestra"
+                }
+            ],
+            "color": "3355443",
+            "media": "media-01",
+            "id": "annotation-05"
+        }
+    ],
+    "annotation-types": [
+        {
+            "dc:contributor": "perso",
+            "dc:creator": "perso",
+            "dc:title": "Chapitrage",
+            "id": "annotation-type-01",
+            "dc:created": "2012-05-07T10:11:06.721324",
+            "dc:description": "Chapitrage",
+            "dc:modified": "2012-05-07T10:11:06.721324"
+        }, {
+            "dc:contributor": "perso",
+            "dc:creator": "perso",
+            "dc:title": "Annotations",
+            "id": "annotation-type-02",
+            "dc:created": "2012-05-07T10:11:06.746777",
+            "dc:description": "Annotations",
+            "dc:modified": "2012-05-07T10:11:06.746777"
+        }
+    ]
+}
\ No newline at end of file
--- a/player/index.html	Tue May 21 19:12:29 2013 +0200
+++ b/player/index.html	Thu May 23 13:10:54 2013 +0200
@@ -11,6 +11,8 @@
         <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/underscore-min.js"></script>
+        <script src="js/metadataplayer-core.js"></script>
+        <script src="js/ldt-serializer.js"></script>
         <script type="text/javascript" src="js/player.js"></script>
     </head>
 
@@ -18,12 +20,12 @@
         <div class="top-bar">
             <div class="tags">
                 <h3 class="tags-title">Tags</h3>
-                <ul class="tag-list"></ul>
+                <ul class="tags-list"></ul>
             </div>
-            <h1 class="project-title">Rigoletto, histoire d’une malédiction</h1>
+            <h1 class="project-title"></h1>
         </div>
         <div class="main-video" style="background:#101010;">
-            <img src="../creation/Links/bc30-1000pxl.jpg" width="1000" height="562" />
+            <div class="video-container"></div>
             <ul class="pictolist">
                 <li class="picto video">
                     <a href="#"></a>
@@ -46,27 +48,37 @@
             </ul>
             <div class="annotations">
                 
-<!--
                 <div class="annotation text-annotation">
                     <a href="#" class="close-annotation">&times;</a>
-                    <h2 class="annotation-title">Lorem Ipsum</h2>
+                    <h2 class="annotation-title">Titre de l'annotation textuelle</h2>
                     <div class="annotation-contents">
                         <p>Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur?</p>
                         <p>Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur?</p>
                     </div>
                 </div>
--->
+                
+                <div class="annotation audio-annotation">
+                    <a href="#" class="close-annotation">&times;</a>
+                    <h2 class="annotation-title">La donna è mobile</h2>
+                    <div class="annotation-contents">
+                        <object height="81" width="100%">
+                            <param name="movie" value="https://player.soundcloud.com/player.swf?url=http%3A%2F%2Fapi.soundcloud.com%2Ftracks%2F36604077&amp;color=63be6c&amp;auto_play=false&amp;show_artwork=false&amp;show_playcount=false&amp;show_comments=false"></param>
+                            <param name="allowscriptaccess" value="always"></param>
+                            <embed allowscriptaccess="always" src="https://player.soundcloud.com/player.swf?url=http%3A%2F%2Fapi.soundcloud.com%2Ftracks%2F36604077&amp;color=63be6c&amp;auto_play=false&amp;show_artwork=false&amp;show_playcount=false&amp;show_comments=false" type="application/x-shockwave-flash" width="100%" height="81"></embed>
+                        </object>
+                    </div>
+                </div>
+
                 <div class="annotation slideshow-annotation">
                     <h2 class="annotation-title">
                         <a href="#" class="close-annotation">&times;</a>
-                        <span>Lorem Ipsum</span>
+                        <span>Titre du diaporama</span>
                     </h2>
                     <div class="slideshow-center">
                         <div class="slideshow-frame">
-                            <img class="slideshow-image" src="../creation/Links/bc30-1000pxl.jpg" />
+                            <img class="slideshow-image" />
                         </div>
                         <div class="slideshow-description">
-                            <p>Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur?</p>
                         </div>
                         <div class="slideshow-arrow slideshow-previous">
                             <a href="#"></a>
@@ -79,8 +91,8 @@
                         <div class="slideshow-play-pause">
                             <a href="#"></a>
                         </div>
-                        <div class="slideshow-title">Légende vue sur la mer</div>
-                        <div class="slideshow-credits">Crédit photographique Nom du photographe</div>
+                        <div class="slideshow-title"></div>
+                        <div class="slideshow-credits"></div>
                     </div>
                 </div>
                 
@@ -91,32 +103,7 @@
                 <a href="#"></a>
             </div>
             <div class="chapters-bar">
-                <ul class="chapters-list">
-                    <li class="chapter" style="left: 0; width: 5%;">
-                        <div class="chapter-block"></div>
-                        <div class="chapter-title">Histoire et contexte</div>
-                    </li>
-                    <li class="chapter" style="left: 5%; width: 30%;">
-                        <div class="chapter-block"></div>
-                        <div class="chapter-title">Histoire et contexte</div>
-                    </li>
-                    <li class="chapter" style="left: 35%; width: 25%;">
-                        <div class="chapter-block"></div>
-                        <div class="chapter-title">Histoire et contexte</div>
-                    </li>
-                    <li class="chapter" style="left: 60%; width: 14.5%;">
-                        <div class="chapter-block"></div>
-                        <div class="chapter-title">Histoire et contexte</div>
-                    </li>
-                    <li class="chapter" style="left: 74.5%; width: 10.5%;">
-                        <div class="chapter-block"></div>
-                        <div class="chapter-title">Histoire et contexte</div>
-                    </li>
-                    <li class="chapter" style="left: 85%; width: 15%;">
-                        <div class="chapter-block"></div>
-                        <div class="chapter-title">Histoire et contexte</div>
-                    </li>
-                </ul>
+                <ul class="chapters-list"></ul>
                 <ul class="chips-list">
                     <li class="chip video" style="left: 0%"></li>
                     <li class="chip slideshow" style="left: 1%"></li>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/player/js/ldt-serializer.js	Thu May 23 13:10:54 2013 +0200
@@ -0,0 +1,288 @@
+/* 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, _dest) {
+                var _res = {
+                    id : _data.id,
+                    url : _data.video,
+                    meta : {
+                        "dc:title": _data.title || "",
+                        "dc:description": _data.description || "",
+                        "dc:created" : IriSP.Model.dateToIso(_data.created || _source.created),
+                        "dc:modified" : IriSP.Model.dateToIso(_data.modified || _source.modified),
+                        "dc:creator" : _data.creator || _source.creator,
+                        "dc:contributor" : _data.contributor || _source.contributor || _data.creator || _source.creator,
+                        "dc:duration" : _data.duration.milliseconds
+                    }
+                }
+                _dest.medias.push(_res);
+                var _list = {
+                    id: IriSP.Model.getUID(),
+                    meta : {
+                        "dc:title": _data.title || "",
+                        "dc:description": _data.description || "",
+                        "dc:created" : IriSP.Model.dateToIso(_data.created || _source.created),
+                        "dc:modified" : IriSP.Model.dateToIso(_data.modified || _source.modified),
+                        "dc:creator" : _data.creator || _source.creator,
+                        "dc:contributor" : _data.contributor || _source.contributor || _data.creator || _source.creator,
+                        "id-ref": _data.id
+                    },
+                    items: _source.getAnnotationTypes().filter(function(_at) {
+                        return _at.media === _data;
+                    }).map(function(_at) {
+                        return {
+                            "id-ref": _at.id
+                        }
+                    })
+                }
+                _dest.lists.push(_list);
+                _dest.views[0].contents.push(_data.id);
+            }
+        },
+        tag : {
+            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, _dest) {
+                var _res = {
+                    id : _data.id,
+                    meta : {
+                        "dc:title": _data.title || "",
+                        "dc:description": _data.description || "",
+                        "dc:created" : IriSP.Model.dateToIso(_data.created || _source.created),
+                        "dc:modified" : IriSP.Model.dateToIso(_data.modified || _source.modified),
+                        "dc:creator" : _data.creator || _source.creator,
+                        "dc:contributor" : _data.contributor || _source.contributor || _data.creator || _source.creator,
+                    }
+                }
+                _dest.tags.push(_res);
+            }
+        },
+        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, _dest) {
+                var _res = {
+                    id : _data.id,
+                    "dc:title": _data.title || "",
+                    "dc:description": _data.description || "",
+                    "dc:created" : IriSP.Model.dateToIso(_data.created || _source.created),
+                    "dc:modified" : IriSP.Model.dateToIso(_data.modified || _source.modified),
+                    "dc:creator" : _data.creator || _source.creator,
+                    "dc:contributor" : _data.contributor || _source.contributor || _data.creator || _source.creator,
+                }
+                _dest["annotation-types"].push(_res);
+                _dest.views[0].annotation_types.push(_data.id);
+            }
+        },
+        annotation : {
+            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, _dest) {
+                var _color = parseInt(_data.color.replace(/^#/,''),16).toString();
+                var _res = {
+                    id : _data.id,
+                    begin : _data.begin.milliseconds,
+                    end : _data.end.milliseconds,
+                    content : {
+                        title : _data.title || "",
+                        description : _data.description || "",
+                        audio : _data.audio,
+                        img: {
+                            src: _data.thumbnail
+                        }
+                    },
+                    color: _color,
+                    media : _data.media.id,
+                    meta : {
+                        "id-ref" : _data.getAnnotationType().id,
+                        "dc:created" : IriSP.Model.dateToIso(_data.created || _source.created),
+                        "dc:modified" : IriSP.Model.dateToIso(_data.modified || _source.modified),
+                        "dc:creator" : _data.creator || _source.creator,
+                        "dc:contributor" : _data.contributor || _source.contributor || _data.creator || _source.creator,
+//                        project : _source.projectId
+                    },
+                    tags : IriSP._(_data.tag.id).map(function(_id) {
+                       return {
+                           "id-ref" : _id
+                       } 
+                    })
+                }
+                _dest.annotations.push(_res);
+            }
+        },
+        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, _dest) {
+                var _res = {
+                    meta : {
+                        "dc:title": _data.title || "",
+                        "dc:description": _data.description || "",
+                        "dc:created" : IriSP.Model.dateToIso(_data.created || _source.created),
+                        "dc:modified" : IriSP.Model.dateToIso(_data.modified || _source.modified),
+                        "dc:creator" : _data.creator || _source.creator,
+                        "dc:contributor" : _data.contributor || _source.contributor || _data.creator || _source.creator,
+                        listtype: "mashup"
+                    },
+                    items: _data.segments.map(function(_annotation) {
+                        return _annotation.annotation.id;
+                    }),
+                    id: _data.id
+                }
+                _dest.lists.push(_res);
+            }
+        }
+    },
+    serialize : function(_source) {
+        var _res = {
+                meta: {
+                    "dc:creator": _source.creator,
+                    "dc:contributor" : _source.contributor || _source.creator,
+                    "dc:created": IriSP.Model.dateToIso(_source.created),
+                    "dc:modified" : IriSP.Model.dateToIso(_source.modified),
+                    "dc:title": _source.title || "",
+                    "dc:description": _source.description || "",
+                    id: _source.projectId || _source.id
+                },
+                views: [
+                    {
+                        id: IriSP.Model.getUID(),
+                        contents: [],
+                        annotation_types: []
+                    }
+                ],
+                lists: [],
+                "annotation-types": [],
+                medias: [],
+                tags: [],
+                annotations: []
+            },
+            _this = this;
+        _source.forEach(function(_list, _typename) {
+            if (typeof _this.types[_typename] !== "undefined") {
+                _list.forEach(function(_el) {
+                    _this.types[_typename].serializer(_el, _source, _res);
+                });
+            }
+        });
+        return JSON.stringify(_res);
+    },
+    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;
+            _source.title = _data.meta["dc:title"] || _data.meta.title || "";
+            _source.description = _data.meta["dc:description"] || _data.meta.description || "";
+        }
+        
+        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"]);
+        }
+    }
+};
+
+/* End of LDT Platform Serializer */
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/player/js/metadataplayer-core.js	Thu May 23 13:10:54 2013 +0200
@@ -0,0 +1,1488 @@
+
+/* 
+ *
+  __  __      _            _       _              _                       
+ |  \/  | ___| |_ __ _  __| | __ _| |_ __ _ _ __ | | __ _ _   _  ___ _ __ 
+ | |\/| |/ _ \ __/ _` |/ _` |/ _` | __/ _` | '_ \| |/ _` | | | |/ _ \ '__|
+ | |  | |  __/ || (_| | (_| | (_| | || (_| | |_) | | (_| | |_| |  __/ |   
+ |_|  |_|\___|\__\__,_|\__,_|\__,_|\__\__,_| .__/|_|\__,_|\__, |\___|_|   
+                                           |_|            |___/         
+
+ *  Copyright 2010-2012 Institut de recherche et d'innovation 
+ *	contributor(s) : Karim Hamidou, Samuel Huron, Raphael Velt, Thibaut Cavalie
+ *	 
+ *	contact@iri.centrepompidou.fr
+ *	http://www.iri.centrepompidou.fr 
+ *	 
+ *	This software is a computer program whose purpose is to show and add annotations on a video .
+ *	This software is governed by the CeCILL-C license under French law and
+ *	abiding by the rules of distribution of free software. You can  use, 
+ *	modify and/ or redistribute the software under the terms of the CeCILL-C
+ *	license as circulated by CEA, CNRS and INRIA at the following URL
+ *	"http://www.cecill.info". 
+ *	
+ *	The fact that you are presently reading this means that you have had
+ *	knowledge of the CeCILL-C license and that you accept its terms.
+*/
+/* Initialization of the namespace */
+
+if (typeof window.IriSP === "undefined") {
+    window.IriSP = {};
+}
+
+if (typeof IriSP.jQuery === "undefined" && typeof window.jQuery !== "undefined" && parseFloat(window.jQuery().jquery) >= 1.7) {
+    IriSP.jQuery = window.jQuery;
+}
+
+if (typeof IriSP._ === "undefined" && typeof window._ !== "undefined" && parseFloat(window._.VERSION) >= 1.4) {
+    IriSP._ = window._;
+}
+/* utils.js - various utils that don't belong anywhere else */
+
+IriSP.jqEscape = function(_text) {
+    return _text.replace(/(:|\.)/g,'\\$1');
+};
+
+IriSP.getLib = function(lib) {
+    if (IriSP.libFiles.useCdn && typeof IriSP.libFiles.cdn[lib] == "string") {
+        return IriSP.libFiles.cdn[lib];
+    }
+    if (typeof IriSP.libFiles.locations[lib] == "string") {
+        return IriSP.libFiles.locations[lib];
+    }
+    if (typeof IriSP.libFiles.inDefaultDir[lib] == "string") {
+        return IriSP.libFiles.defaultDir + '/' + IriSP.libFiles.inDefaultDir[lib];
+    }
+}
+
+IriSP._cssCache = [];
+
+IriSP.loadCss = function(_cssFile) {
+    if (IriSP._(IriSP._cssCache).indexOf(_cssFile) === -1) {
+        IriSP.jQuery("<link>", {
+            rel : "stylesheet",
+            type : "text/css",
+            href : _cssFile
+        }).appendTo('head');
+        IriSP._cssCache.push(_cssFile);
+    }
+};
+
+IriSP.textFieldHtml = function(_text, _regexp, _extend) {
+    var list = [],
+        positions = [],
+        text = _text.replace(/(^\s+|\s+$)/g,'');
+    
+    function addToList(_rx, _startHtml, _endHtml) {
+        while(true) {
+            var result = _rx.exec(text);
+            if (!result) {
+                break;
+            }
+            var end = _rx.lastIndex,
+                start = result.index;
+            list.push({
+                start: start,
+                end: end,
+                startHtml: (typeof _startHtml === "function" ? _startHtml(result) : _startHtml),
+                endHtml: (typeof _endHtml === "function" ? _endHtml(result) : _endHtml)
+            });
+            positions.push(start);
+            positions.push(end);
+        }
+    }
+    
+    if (_regexp) {
+        addToList(_regexp, '<span class="Ldt-Highlight">', '</span>');
+    }
+    
+    addToList(/(https?:\/\/)?\w+\.\w+\S+/gm, function(matches) {
+        return '<a href="' + (matches[1] ? '' : 'http://') + matches[0] + '" target="_blank">'
+    }, '</a>');
+    addToList(/@([\d\w]{1,15})/gm, function(matches) {
+        return '<a href="http://twitter.com/' + matches[1] + '" target="_blank">'
+    }, '</a>');
+    addToList(/\*[^*]+\*/gm, '<b>', '</b>');
+    addToList(/[\n\r]+/gm, '', '<br />');
+    
+    IriSP._(_extend).each(function(x) {
+        addToList.apply(null, x);
+    });
+    
+    positions = IriSP._(positions)
+        .chain()
+        .uniq()
+        .sortBy(function(p) { return parseInt(p) })
+        .value();
+    
+    var res = "", lastIndex = 0;
+    
+    for (var i = 0; i < positions.length; i++) {
+        var pos = positions[i];
+        res += text.substring(lastIndex, pos);
+        for (var j = list.length - 1; j >= 0; j--) {
+            var item = list[j];
+            if (item.start < pos && item.end >= pos) {
+                res += item.endHtml;
+            }
+        }
+        for (var j = 0; j < list.length; j++) {
+            var item = list[j];
+            if (item.start <= pos && item.end > pos) {
+                res += item.startHtml;
+            }
+        }
+        lastIndex = pos;
+    }
+    
+    res += text.substring(lastIndex);
+    
+    return res;
+    
+};
+
+IriSP.log = function() {
+    if (typeof console !== "undefined" && typeof IriSP.logging !== "undefined" && IriSP.logging) {
+        console.log.apply(console, arguments);
+    }
+};
+
+IriSP.attachDndData = function(jqSel, data) {
+	jqSel.attr("draggable", "true").on("dragstart", function(_event) {
+		var d = (typeof data === "function" ? data.call(this) : data);
+		try {
+			IriSP._(d).each(function(v, k) {
+				if (v) {
+					_event.originalEvent.dataTransfer.setData("text/x-iri-" + k, v);
+				}
+			});
+		} catch(err) {
+			_event.originalEvent.dataTransfer.setData("Text", JSON.stringify(d));
+		}
+	});
+};
+
+IriSP.FakeClass = function(properties) {
+    var _this = this,
+        noop = (function() {});
+    IriSP._(properties).each(function(p) {
+        _this[p] = noop
+    });
+}
+
+/* js is where data is stored in a standard form, whatever the serializer */
+
+//TODO: Separate Project-specific data from Source
+
+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 charsub = [
+        '[aáàâä]',
+        '[cç]',
+        '[eéèêë]',
+        '[iíìîï]',
+        '[oóòôö]',
+        '[uùûü]'
+    ];
+    
+    var removeChars = [
+        String.fromCharCode(768), String.fromCharCode(769), String.fromCharCode(770), String.fromCharCode(771), String.fromCharCode(807),
+        "{", "}", "(", ")", "[", "]", "【", "】", "、", "・", "‥", "。", "「", "」", "『", "』", "〜", ":", "!", "?", " ",
+        ",", " ", ";", "(", ")", ".", "*", "+", "\\", "?", "|", "{", "}", "[", "]", "^", "#", "/"
+    ];
+    
+var Model = {},
+    _SOURCE_STATUS_EMPTY = Model._SOURCE_STATUS_EMPTY = 0,
+    _SOURCE_STATUS_WAITING = Model._SOURCE_STATUS_WAITING = 1,
+    _SOURCE_STATUS_READY = Model._SOURCE_STATUS_READY = 2,
+    extendPrototype = Model.extendPrototype = function(toClass, fromClass) {
+        var fromP = fromClass.prototype,
+            toP = toClass.prototype;
+        for (var k in fromP) {
+            if (fromP.hasOwnProperty(k)) {
+                toP[k] = fromP[k];
+            }
+        }
+    },
+    getUID = Model.getUID = function() {
+        return uidbase + pad(4, (++uidincrement % 0x10000), 16) + "-" + rand16(4) + "-" + rand16(6) + rand16(6);
+    },
+    isLocalURL = Model.isLocalURL = function(url) {
+        var matches = url.match(/^(\w+:)\/\/([^/]+)/);
+        if (matches) {
+            return(matches[1] === document.location.protocol && matches[2] === document.location.host)
+        }
+        return true;
+    },
+    regexpFromTextOrArray = Model.regexpFromTextOrArray = function(_textOrArray, _testOnly, _iexact) {
+        var _testOnly = _testOnly || false,
+            _iexact = _iexact || false;
+        function escapeText(_text) {
+            return _text.replace(/([\\\*\+\?\|\{\[\}\]\(\)\^\$\.\#\/])/gm, '\\$1');
+        }
+        var _source = 
+            typeof _textOrArray === "string"
+            ? escapeText(_textOrArray)
+            : ns._(_textOrArray).map(escapeText).join("|"),
+            _flags = 'im';
+        if (!_testOnly) {
+            _source = '(' + _source + ')';
+            _flags += 'g';
+        }
+        if (_iexact) {
+            _source = '^' + _source + '$';
+        }
+        return new RegExp( _source, _flags);
+    },
+    fullTextRegexps = Model.fullTextRegexps = function(_text) {
+        var remsrc = "[\\" + removeChars.join("\\") + "]",
+            remrx = new RegExp(remsrc,"gm"),
+            txt = _text.toLowerCase().replace(remrx,"")
+            res = [],
+            charsrx = ns._(charsub).map(function(c) {
+                return new RegExp(c);
+            }),
+            src = "";
+        for (var j = 0; j < txt.length; j++) {
+            if (j) {
+                src += remsrc + "*";
+            }
+            var l = txt[j];
+            ns._(charsub).each(function(v, k) {
+                l = l.replace(charsrx[k], v);
+            });
+            src += l;
+        }
+        return "(" + src + ")";
+    },
+    isoToDate = Model.isoToDate = function(_str) {
+        // http://delete.me.uk/2005/03/iso8601.html
+        var regexp = "([0-9]{4})(-([0-9]{2})(-([0-9]{2})(T([0-9]{2}):([0-9]{2})(:([0-9]{2})(\.([0-9]+))?)?(Z|(([-+])([0-9]{2}):([0-9]{2})))?)?)?)?";
+        var d = _str.match(new RegExp(regexp));
+    
+        var offset = 0;
+        var date = new Date(d[1], 0, 1);
+    
+        if (d[3]) { date.setMonth(d[3] - 1); }
+        if (d[5]) { date.setDate(d[5]); }
+        if (d[7]) { date.setHours(d[7]); }
+        if (d[8]) { date.setMinutes(d[8]); }
+        if (d[10]) { date.setSeconds(d[10]); }
+        if (d[12]) { date.setMilliseconds(Number("0." + d[12]) * 1000); }
+        if (d[14]) {
+            offset = (Number(d[16]) * 60) + Number(d[17]);
+            offset *= ((d[15] == '-') ? 1 : -1);
+        }
+    
+        offset -= date.getTimezoneOffset();
+        time = (Number(date) + (offset * 60 * 1000));
+        var _res = new Date();
+        _res.setTime(Number(time));
+        return _res;
+    },
+    dateToIso = Model.dateToIso = function(_d) {
+        var d = _d ? new Date(_d) : new Date();
+        return d.getUTCFullYear()+'-'  
+            + pad(2, d.getUTCMonth()+1)+'-'  
+            + pad(2, d.getUTCDate())+'T'  
+            + pad(2, d.getUTCHours())+':'  
+            + pad(2, d.getUTCMinutes())+':'  
+            + pad(2, d.getUTCSeconds())+'Z'  
+    };
+
+/*
+ * List is a class for a list of elements (e.g. annotations, medias, etc. that each have a distinct ID)
+ */
+var List = Model.List = function(_directory) {
+    Array.call(this);
+    this.directory = _directory;
+    this.idIndex = [];
+    this.__events = {};
+    if (typeof _directory == "undefined") {
+        console.trace();
+        throw "Error : new List(directory): directory is undefined";
+    }
+    var _this =  this;
+    this.on("clear-search", function() {
+        _this.searching = false;
+        _this.regexp = undefined;
+        _this.forEach(function(_element) {
+            _element.found = undefined;
+        });
+        _this.trigger("search-cleared");
+    })
+};
+
+List.prototype = new Array();
+
+List.prototype.hasId = function(_id) {
+    return ns._(this.idIndex).include(_id);
+};
+
+/* On recent browsers, forEach and map are defined and do what we want.
+ * Otherwise, we'll use the Underscore.js functions
+ */
+if (typeof Array.prototype.forEach === "undefined") {
+    List.prototype.forEach = function(_callback) {
+        var _this = this;
+        ns._(this).forEach(function(_value, _key) {
+            _callback(_value, _key, _this);
+        });
+    }
+};
+
+if (typeof Array.prototype.map === "undefined") {
+    List.prototype.map = function(_callback) {
+        var _this = this;
+        return ns._(this).map(function(_value, _key) {
+            return _callback(_value, _key, _this);
+        });
+    }
+};
+
+List.prototype.pluck = function(_key) {
+    return this.map(function(_value) {
+        return _value[_key];
+    });
+};
+
+/* We override Array's filter function because it doesn't return an List
+ */
+List.prototype.filter = function(_callback) {
+    var _this = this,
+        _res = new List(this.directory);
+    _res.addElements(ns._(this).filter(function(_value, _key) {
+        return _callback(_value, _key, _this);
+    }));
+    return _res;
+};
+
+List.prototype.slice = function(_start, _end) {
+    var _res = new List(this.directory);
+    _res.addElements(Array.prototype.slice.call(this, _start, _end));
+    return _res;
+};
+
+List.prototype.splice = function(_start, _end) {
+    var _res = new List(this.directory);
+    _res.addElements(Array.prototype.splice.call(this, _start, _end));
+    this.idIndex.splice(_start, _end);
+    return _res;
+};
+
+/* Array has a sort function, but it's not as interesting as Underscore.js's sortBy
+ * and won't return a new List
+ */
+List.prototype.sortBy = function(_callback) {
+    var _this = this,
+        _res = new List(this.directory);
+    _res.addElements(ns._(this).sortBy(function(_value, _key) {
+        return _callback(_value, _key, _this);
+    }));
+    return _res;
+};
+
+/* Title and Description are basic information for (almost) all element types,
+ * here we can search by these criteria
+ */
+List.prototype.searchByTitle = function(_text, _iexact) {
+    var _iexact = _iexact || false,
+        _rgxp = regexpFromTextOrArray(_text, true, _iexact);
+    return this.filter(function(_element) {
+        return _rgxp.test(_element.title);
+    });
+};
+
+List.prototype.searchByDescription = function(_text, _iexact) {
+    var _iexact = _iexact || false,
+        _rgxp = regexpFromTextOrArray(_text, true, _iexact);
+    return this.filter(function(_element) {
+        return _rgxp.test(_element.description);
+    });
+};
+
+List.prototype.searchByTextFields = function(_text, _iexact) {
+    var _iexact = _iexact || false,
+        _rgxp =  regexpFromTextOrArray(_text, true, _iexact);
+    return this.filter(function(_element) {
+        var keywords = (_element.keywords || _element.getTagTexts() || []).join(", ");
+        return _rgxp.test(_element.description) || _rgxp.test(_element.title) || _rgxp.test(keywords);
+    });
+};
+
+List.prototype.search = function(_text) {
+    if (!_text) {
+        this.trigger("clear-search");
+        return this;
+    }
+    this.searching = true;
+    this.trigger("search", _text);
+    var rxsource = fullTextRegexps(_text)
+        rgxp = new RegExp(rxsource,"im"),
+        this.regexp = new RegExp(rxsource,"gim");
+    var res = this.filter(function(_element, _k) {
+        var titlematch = rgxp.test(_element.title),
+            descmatch = rgxp.test(_element.description),
+            _isfound = !!(titlematch || descmatch);
+        _element.found = _isfound;
+        _element.trigger(_isfound ? "found" : "not-found");
+        return _isfound;
+    });
+    this.trigger(res.length ? "found" : "not-found",res);
+    return res;
+};
+
+List.prototype.getTitles = function() {
+    return this.map(function(_el) {
+        return _el.title;
+    });
+};
+
+List.prototype.addId = function(_id) {
+    var _el = this.directory.getElement(_id)
+    if (!this.hasId(_id) && typeof _el !== "undefined") {
+        this.idIndex.push(_id);
+        Array.prototype.push.call(this, _el);
+    }
+};
+
+List.prototype.push = function(_el) {
+    if (typeof _el === "undefined") {
+        return;
+    }
+    var _index = (ns._(this.idIndex).indexOf(_el.id));
+    if (_index === -1) {
+        this.idIndex.push(_el.id);
+        Array.prototype.push.call(this, _el);
+    } else {
+        this[_index] = _el;
+    }
+};
+
+List.prototype.addIds = function(_array) {
+    var _l = _array.length,
+        _this = this;
+    ns._(_array).forEach(function(_id) {
+        _this.addId(_id);
+    });
+};
+
+List.prototype.addElements = function(_array) {
+    var _this = this;
+    ns._(_array).forEach(function(_el) {
+        _this.push(_el);
+    });
+};
+
+List.prototype.removeId = function(_id, _deleteFromDirectory) {
+    var _deleteFromDirectory = _deleteFromDirectory || false,
+        _index = (ns._(this.idIndex).indexOf(_id));
+    if (_index !== -1) {
+        this.splice(_index,1);
+    }
+    if (_deleteFromDirectory) {
+        delete this.directory.elements[_id];
+    }
+};
+
+List.prototype.removeElement = function(_el, _deleteFromDirectory) {
+    var _deleteFromDirectory = _deleteFromDirectory || false;
+    this.removeId(_el.id);
+};
+
+List.prototype.removeIds = function(_list, _deleteFromDirectory) {
+    var _deleteFromDirectory = _deleteFromDirectory || false,
+        _this = this;
+    ns._(_list).forEach(function(_id) {
+        _this.removeId(_id);
+    });
+};
+
+List.prototype.removeElements = function(_list, _deleteFromDirectory) {
+    var _deleteFromDirectory = _deleteFromDirectory || false,
+        _this = this;
+    ns._(_list).forEach(function(_el) {
+        _this.removeElement(_el);
+    });
+};
+
+List.prototype.on = function(_event, _callback) {
+    if (typeof this.__events[_event] === "undefined") {
+        this.__events[_event] = [];
+    }
+    this.__events[_event].push(_callback);
+};
+
+List.prototype.off = function(_event, _callback) {
+    if (typeof this.__events[_event] !== "undefined") {
+        this.__events[_event] = ns._(this.__events[_event]).reject(function(_fn) {
+            return _fn === _callback;
+        });
+    }
+};
+
+List.prototype.trigger = function(_event, _data) {
+    var _list = this;
+    ns._(this.__events[_event]).each(function(_callback) {
+        _callback.call(_list, _data);
+    });
+};
+
+/* A simple time management object, that helps converting millisecs to seconds and strings,
+ * without the clumsiness of the original Date object.
+ */
+
+var Time = Model.Time = function(_milliseconds) {
+    this.milliseconds = 0;
+    this.setMilliseconds(_milliseconds);
+};
+
+Time.prototype.setMilliseconds = function(_milliseconds) {
+    var _ante = this.milliseconds;
+    switch(typeof _milliseconds) {
+        case "string":
+            this.milliseconds = parseInt(_milliseconds);
+            break;
+        case "number":
+            this.milliseconds = Math.floor(_milliseconds);
+            break;
+        case "object":
+            this.milliseconds = parseInt(_milliseconds.valueOf());
+            break;
+        default:
+            this.milliseconds = 0;
+    }
+    if (this.milliseconds === NaN) {
+        this.milliseconds = _ante;
+    }
+};
+
+Time.prototype.setSeconds = function(_seconds) {
+    this.milliseconds = 1000 * _seconds;
+};
+
+Time.prototype.getSeconds = function() {
+    return this.milliseconds / 1000;
+};
+
+Time.prototype.getHMS = function() {
+    var _totalSeconds = Math.abs(Math.floor(this.getSeconds()));
+    return {
+        hours : Math.floor(_totalSeconds / 3600),
+        minutes : (Math.floor(_totalSeconds / 60) % 60),
+        seconds : _totalSeconds % 60,
+        milliseconds: this.milliseconds % 1000
+    } 
+};
+
+Time.prototype.add = function(_milliseconds) {
+    this.milliseconds += new Time(_milliseconds).milliseconds;
+};
+
+Time.prototype.valueOf = function() {
+    return this.milliseconds;
+};
+
+Time.prototype.toString = function(showCs) {
+    var _hms = this.getHMS(),
+        _res = '';
+    if (_hms.hours) {
+        _res += _hms.hours + ':'
+    }
+    _res += pad(2, _hms.minutes) + ':' + pad(2, _hms.seconds);
+    if (showCs) {
+        _res += "." + Math.floor(_hms.milliseconds / 100)
+    }
+    return _res;
+};
+
+/* Reference handles references between elements
+ */
+
+var Reference = Model.Reference = function(_source, _idRef) {
+    this.source = _source;
+    this.id = _idRef;
+    if (typeof _idRef === "object") {
+        this.isList = true;
+    } else {
+        this.isList = false;
+    }
+    this.refresh();
+};
+
+Reference.prototype.refresh = function() {
+    if (this.isList) {
+        this.contents = new List(this.source.directory);
+        this.contents.addIds(this.id);
+    } else {
+        this.contents = this.source.getElement(this.id);
+    }
+    
+};
+
+Reference.prototype.getContents = function() {
+    if (typeof this.contents === "undefined" || (this.isList && this.contents.length != this.id.length)) {
+        this.refresh();
+    }
+    return this.contents;
+};
+
+Reference.prototype.isOrHasId = function(_idRef) {
+    if (this.isList) {
+        return (ns._(this.id).indexOf(_idRef) !== -1)
+    } else {
+        return (this.id == _idRef);
+    }
+};
+
+/* */
+
+var BaseElement = Model.Element = function(_id, _source) {
+    this.elementType = 'element';
+    this.title = "";
+    this.description = "";
+    this.__events = {}
+    if (typeof _source === "undefined") {
+        return;
+    }
+    if (typeof _id === "undefined" || !_id) {
+        _id = getUID();
+    }
+    this.id = _id;
+    this.source = _source;
+    if (_source !== this) {
+        this.source.directory.addElement(this);
+    }
+};
+
+BaseElement.prototype.toString = function() {
+    return this.elementType + (this.elementType !== 'element' ? ', id=' + this.id + ', title="' + this.title + '"' : '');
+};
+
+BaseElement.prototype.setReference = function(_elementType, _idRef) {
+    this[_elementType] = new Reference(this.source, _idRef);
+};
+
+BaseElement.prototype.getReference = function(_elementType) {
+    if (typeof this[_elementType] !== "undefined") {
+        return this[_elementType].getContents();
+    }
+};
+
+BaseElement.prototype.getRelated = function(_elementType, _global) {
+    _global = (typeof _global !== "undefined" && _global);
+    var _this = this;
+    return this.source.getList(_elementType, _global).filter(function(_el) {
+        var _ref = _el[_this.elementType];
+        return _ref && _ref.isOrHasId(_this.id);
+    });
+};
+
+BaseElement.prototype.on = function(_event, _callback) {
+    if (typeof this.__events[_event] === "undefined") {
+        this.__events[_event] = [];
+    }
+    this.__events[_event].push(_callback);
+};
+
+BaseElement.prototype.off = function(_event, _callback) {
+    if (typeof this.__events[_event] !== "undefined") {
+        this.__events[_event] = ns._(this.__events[_event]).reject(function(_fn) {
+            return _fn === _callback;
+        });
+    }
+};
+
+BaseElement.prototype.trigger = function(_event, _data) {
+    var _element = this;
+    ns._(this.__events[_event]).each(function(_callback) {
+        _callback.call(_element, _data);
+    });
+};
+
+/* */
+
+var Playable = Model.Playable = function(_id, _source) {
+    BaseElement.call(this, _id, _source);
+    if (typeof _source === "undefined") {
+        return;
+    }
+    this.elementType = 'playable';
+    this.currentTime = new Time();
+    this.volume = .5;
+    this.paused = true;
+    this.muted = false;
+    this.loadedMetadata = false;
+    var _this = this;
+    this.on("play", function() {
+        _this.paused = false;
+    });
+    this.on("pause", function() {
+        _this.paused = true;
+    });
+    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);
+        });
+    });
+    this.on("loadedmetadata", function() {
+        _this.loadedMetadata = true;
+    });
+};
+
+extendPrototype(Playable, BaseElement);
+
+Playable.prototype.getCurrentTime = function() { 
+    return this.currentTime;
+};
+
+Playable.prototype.getVolume = function() {
+    return this.volume;
+};
+
+Playable.prototype.getPaused = function() {
+    return this.paused;
+};
+
+Playable.prototype.getMuted = function() {
+    return this.muted;
+};
+
+Playable.prototype.setCurrentTime = function(_time) {
+    this.trigger("setcurrenttime",_time);
+};
+
+Playable.prototype.setVolume = function(_vol) {
+    this.trigger("setvolume",_vol);
+};
+
+Playable.prototype.setMuted = function(_muted) {
+    this.trigger("setmuted",_muted);
+};
+
+Playable.prototype.play = function() {
+    this.trigger("setplay");
+};
+
+Playable.prototype.pause = function() {
+    this.trigger("setpause");
+};
+
+Playable.prototype.show = function() {};
+
+Playable.prototype.hide = function() {};
+
+/* */
+
+var Media = Model.Media = function(_id, _source) {
+    Playable.call(this, _id, _source);
+    this.elementType = 'media';
+    this.duration = new Time();
+    this.video = '';
+    var _this = this;
+};
+
+extendPrototype(Media, Playable);
+
+/* Default functions to be overriden by players */
+    
+Media.prototype.setDuration = function(_durationMs) {
+    this.duration.setMilliseconds(_durationMs);
+};
+
+Media.prototype.getAnnotations = function() {
+    return this.getRelated("annotation");
+};
+
+Media.prototype.getAnnotationsByTypeTitle = function(_title) {
+    var _annTypes = this.source.getAnnotationTypes().searchByTitle(_title).pluck("id");
+    if (_annTypes.length) {
+        return this.getAnnotations().filter(function(_annotation) {
+            return ns._(_annTypes).indexOf(_annotation.getAnnotationType().id) !== -1;
+        });
+    } else {
+        return new List(this.source.directory)
+    }
+};
+
+/* */
+
+var Tag = Model.Tag = function(_id, _source) {
+    BaseElement.call(this, _id, _source);
+    this.elementType = 'tag';
+};
+
+extendPrototype(Tag, BaseElement);
+
+Tag.prototype.getAnnotations = function() {
+    return this.getRelated("annotation");
+};
+
+/* */
+var AnnotationType = Model.AnnotationType = function(_id, _source) {
+    BaseElement.call(this, _id, _source);
+    this.elementType = 'annotationType';
+};
+
+extendPrototype(AnnotationType, BaseElement);
+
+AnnotationType.prototype.getAnnotations = function() {
+    return this.getRelated("annotation");
+};
+
+/* Annotation
+ * */
+
+var Annotation = Model.Annotation = function(_id, _source) {
+    BaseElement.call(this, _id, _source);
+    this.elementType = 'annotation';
+    this.begin = new Time();
+    this.end = new Time();
+    this.tag = new Reference(_source, []);
+    this.playing = false;
+    var _this = this;
+    this.on("click", function() {
+        _this.getMedia().setCurrentTime(_this.begin);
+    });
+};
+
+extendPrototype(Annotation, BaseElement);
+
+Annotation.prototype.setBegin = function(_beginMs) {
+    this.begin.setMilliseconds(Math.max(0,_beginMs));
+    this.trigger("change-begin");
+    if (this.end < this.begin) {
+        this.setEnd(this.begin);
+    }
+};
+
+Annotation.prototype.setEnd = function(_endMs) {
+    this.end.setMilliseconds(Math.min(_endMs, this.getMedia().duration.milliseconds));
+    this.trigger("change-end");
+    if (this.end < this.begin) {
+        this.setBegin(this.end);
+    }
+};
+
+Annotation.prototype.setDuration = function(_durMs) {
+    this.setEnd(_durMs + this.begin.milliseconds);
+};
+
+Annotation.prototype.setMedia = function(_idRef) {
+    this.setReference("media", _idRef);
+};
+
+Annotation.prototype.getMedia = function() {
+    return this.getReference("media");
+};
+
+Annotation.prototype.setAnnotationType = function(_idRef) {
+    this.setReference("annotationType", _idRef);
+};
+
+Annotation.prototype.getAnnotationType = function() {
+    return this.getReference("annotationType");
+};
+
+Annotation.prototype.setTags = function(_idRefs) {
+    this.setReference("tag", _idRefs);
+};
+
+Annotation.prototype.getTags = function() {
+    return this.getReference("tag");
+};
+
+Annotation.prototype.getTagTexts = function() {
+    return this.getTags().getTitles();
+};
+
+Annotation.prototype.getDuration = function() {
+    return new Time(this.end.milliseconds - this.begin.milliseconds)
+};
+
+/* */
+
+var MashedAnnotation = Model.MashedAnnotation = function(_mashup, _annotation) {
+    BaseElement.call(this, _mashup.id + "_" + _annotation.id, _annotation.source);
+    this.elementType = 'mashedAnnotation';
+    this.annotation = _annotation;
+    this.begin = new Time();
+    this.end = new Time();
+    this.duration = new Time();
+    this.title = this.annotation.title;
+    this.description = this.annotation.description;
+    this.color = this.annotation.color;
+    var _this = this;
+    this.on("click", function() {
+        _mashup.setCurrentTime(_this.begin);
+    });
+    this.on("enter", function() {
+        _this.annotation.trigger("enter");
+    });
+    this.on("leave", function() {
+        _this.annotation.trigger("leave");
+    });
+};
+
+extendPrototype(MashedAnnotation, BaseElement);
+
+MashedAnnotation.prototype.getMedia = function() {
+    return this.annotation.getReference("media");
+};
+
+MashedAnnotation.prototype.getAnnotationType = function() {
+    return this.annotation.getReference("annotationType");
+};
+
+MashedAnnotation.prototype.getTags = function() {
+    return this.annotation.getReference("tag");
+};
+
+MashedAnnotation.prototype.getTagTexts = function() {
+    return this.annotation.getTags().getTitles();
+};
+
+MashedAnnotation.prototype.getDuration = function() {
+    return this.annotation.getDuration();
+};
+
+MashedAnnotation.prototype.setBegin = function(_begin) {
+    this.begin.setMilliseconds(_begin);
+    this.duration.setMilliseconds(this.annotation.getDuration());
+    this.end.setMilliseconds(_begin + this.duration);
+};
+
+/* */
+
+var Mashup = Model.Mashup = function(_id, _source) {
+    Playable.call(this, _id, _source);
+    this.elementType = 'mashup';
+    this.duration = new Time();
+    this.segments = new List(_source.directory);
+    this.loaded = false;
+    var _this = this;
+    this._updateTimes = function() {
+        _this.updateTimes();
+        _this.trigger("change");
+    }
+    this.on("add", this._updateTimes);
+    this.on("remove", this._updateTimes);
+};
+
+extendPrototype(Mashup, Playable);
+
+Mashup.prototype.updateTimes = function() {
+    var _time = 0;
+    this.segments.forEach(function(_segment) {
+        _segment.setBegin(_time);
+        _time = _segment.end;
+    });
+    this.duration.setMilliseconds(_time);
+};
+
+Mashup.prototype.addAnnotation = function(_annotation, _defer) {
+    var _mashedAnnotation = new MashedAnnotation(this, _annotation),
+        _defer = _defer || false;
+    this.segments.push(_mashedAnnotation);
+    _annotation.on("change-begin", this._updateTimes);
+    _annotation.on("change-end", this._updateTimes);
+    if (!_defer) {
+        this.trigger("add");
+    }
+};
+
+Mashup.prototype.addAnnotationById = function(_elId, _defer) {
+    var _annotation = this.source.getElement(_elId),
+        _defer = _defer || false;
+    if (typeof _annotation !== "undefined") {
+        this.addAnnotation(_annotation, _defer);
+    }
+};
+
+Mashup.prototype.addAnnotations = function(_segments) {
+    var _this = this;
+    ns._(_segments).forEach(function(_segment) {
+        _this.addAnnotation(_segment, true);
+    });
+    this.trigger("add");
+};
+
+Mashup.prototype.addAnnotationsById = function(_segments) {
+    var _this = this;
+    ns._(_segments).forEach(function(_segment) {
+        _this.addAnnotationById(_segment, true);
+    });
+    this.trigger("add");
+};
+
+Mashup.prototype.removeAnnotation = function(_annotation, _defer) {
+    var _defer = _defer || false;
+    _annotation.off("change-begin", this._updateTimes);
+    _annotation.off("change-end", this._updateTimes);
+    this.segments.removeId(this.id + "_" + _annotation.id);
+    if (!_defer) {
+        this.trigger("remove");
+    }
+};
+
+Mashup.prototype.removeAnnotationById = function(_annId, _defer) {
+    var _defer = _defer || false;
+    var _annotation = this.source.getElement(_annId);
+
+    if (_annotation) {
+        this.removeAnnotation(_annotation, _defer);
+    }
+    if (!_defer) {
+        this.trigger("remove");
+    }
+};
+
+Mashup.prototype.setAnnotations = function(_segments) {
+    while (this.segments.length) {
+        this.removeAnnotation(this.segments[0].annotation, true);
+    }
+    this.addAnnotations(_segments);
+};
+
+Mashup.prototype.setAnnotationsById = function(_segments) {
+    while (this.segments.length) {
+        this.removeAnnotation(this.segments[0].annotation, true);
+    }
+    this.addAnnotationsById(_segments);
+};
+
+Mashup.prototype.hasAnnotation = function(_annotation) {
+    return !!ns._(this.segments).find(function(_s) {
+        return _s.annotation === _annotation
+    });
+};
+
+Mashup.prototype.getAnnotation = function(_annotation) {
+    return ns._(this.segments).find(function(_s) {
+        return _s.annotation === _annotation
+    });
+};
+
+Mashup.prototype.getAnnotationById = function(_id) {
+    return ns._(this.segments).find(function(_s) {
+        return _s.annotation.id === _id
+    });
+};
+
+Mashup.prototype.getAnnotations = function() {
+    return this.segments;
+};
+
+Mashup.prototype.getOriginalAnnotations = function() {
+    var annotations = new List(this.source.directory);
+    this.segments.forEach(function(_s) {
+        annotations.push(_s.annotation);
+    });
+    return annotations;
+};
+
+Mashup.prototype.getMedias = function() {
+    var medias = new List(this.source.directory);
+    this.segments.forEach(function(_annotation) {
+        medias.push(_annotation.getMedia())
+    })
+    return medias;
+};
+
+Mashup.prototype.getAnnotationsByTypeTitle = function(_title) {
+    var _annTypes = this.source.getAnnotationTypes().searchByTitle(_title).pluck("id");
+    if (_annTypes.length) {
+        return this.getAnnotations().filter(function(_annotation) {
+            return ns._(_annTypes).indexOf(_annotation.getAnnotationType().id) !== -1;
+        });
+    } else {
+        return new List(this.source.directory)
+    }
+};
+
+Mashup.prototype.getAnnotationAtTime = function(_time) {
+    var _list = this.segments.filter(function(_annotation) {
+        return _annotation.begin <= _time && _annotation.end > _time;
+    });
+    if (_list.length) {
+        return _list[0];
+    } else {
+        return undefined;
+    }
+};
+
+Mashup.prototype.getMediaAtTime = function(_time) {
+    var _annotation = this.getAnnotationAtTime(_time);
+    if (typeof _annotation !== "undefined") {
+        return _annotation.getMedia();
+    } else {
+        return undefined;
+    }
+};
+
+/* */
+
+var Source = Model.Source = function(_config) {
+    BaseElement.call(this, false, this);
+    this.status = _SOURCE_STATUS_EMPTY;
+    this.elementType = "source";
+    if (typeof _config !== "undefined") {
+        var _this = this;
+        ns._(_config).forEach(function(_v, _k) {
+            _this[_k] = _v;
+        })
+        this.callbackQueue = [];
+        this.contents = {};
+        this.get();
+    }
+};
+
+extendPrototype(Source, BaseElement);
+
+Source.prototype.addList = function(_listId, _contents) {
+    if (typeof this.contents[_listId] === "undefined") {
+        this.contents[_listId] = new List(this.directory);
+    }
+    this.contents[_listId].addElements(_contents);
+};
+
+Source.prototype.getList = function(_listId, _global) {
+    _global = (typeof _global !== "undefined" && _global);
+    if (_global) {
+        return this.directory.getGlobalList().filter(function(_e) {
+            return (_e.elementType === _listId);
+        });
+    } else {
+        return this.contents[_listId] || new IriSP.List(this.directory);
+    }
+};
+
+Source.prototype.forEach = function(_callback) {
+    var _this = this;
+    ns._(this.contents).forEach(function(_value, _key) {
+        _callback.call(_this, _value, _key);
+    })
+};
+
+Source.prototype.getElement = function(_elId) {
+    return this.directory.getElement(_elId);
+};
+
+Source.prototype.get = function() {
+    this.status = _SOURCE_STATUS_WAITING;
+    this.handleCallbacks();
+};
+
+/* We defer the callbacks calls so they execute after the queue is cleared */
+Source.prototype.deferCallback = function(_callback) {
+    var _this = this;
+    ns._.defer(function() {
+        _callback.call(_this);
+    });
+};
+
+Source.prototype.handleCallbacks = function() {
+    this.status = _SOURCE_STATUS_READY;
+    while (this.callbackQueue.length) {
+        this.deferCallback(this.callbackQueue.splice(0,1)[0]);
+    }
+};
+Source.prototype.onLoad = function(_callback) {
+    if (this.status === _SOURCE_STATUS_READY) {
+        this.deferCallback(_callback);
+    } else {
+        this.callbackQueue.push(_callback);
+    }
+};
+
+Source.prototype.serialize = function() {
+    return this.serializer.serialize(this);
+};
+
+Source.prototype.deSerialize = function(_data) {
+    this.serializer.deSerialize(_data, this);
+};
+
+Source.prototype.getAnnotations = function(_global) {
+    _global = (typeof _global !== "undefined" && _global);
+    return this.getList("annotation", _global);
+};
+
+Source.prototype.getMedias = function(_global) {
+    _global = (typeof _global !== "undefined" && _global);
+    return this.getList("media", _global);
+};
+
+Source.prototype.getTags = function(_global) {
+    _global = (typeof _global !== "undefined" && _global);
+    return this.getList("tag", _global);
+};
+
+Source.prototype.getMashups = function(_global) {
+    _global = (typeof _global !== "undefined" && _global);
+    return this.getList("mashup", _global);
+};
+
+Source.prototype.getAnnotationTypes = function(_global) {
+    _global = (typeof _global !== "undefined" && _global);
+    return this.getList("annotationType", _global);
+};
+
+Source.prototype.getAnnotationsByTypeTitle = function(_title, _global) {
+    _global = (typeof _global !== "undefined" && _global);
+    var _res = new List(this.directory),
+        _annTypes = this.getAnnotationTypes(_global).searchByTitle(_title);
+    _annTypes.forEach(function(_annType) {
+        _res.addElements(_annType.getAnnotations(_global));
+    })
+    return _res;
+};
+
+Source.prototype.getDuration = function() {
+    var _m = this.currentMedia;
+    if (typeof _m !== "undefined") {
+        return this.currentMedia.duration;
+    }
+};
+
+Source.prototype.getCurrentMedia = function(_opts) {
+    if (typeof this.currentMedia === "undefined") {
+        if (_opts.is_mashup) {
+            var _mashups = this.getMashups();
+            if (_mashups.length) {
+                this.currentMedia = _mashups[0];
+            }
+        } else {
+            var _medias = this.getMedias();
+            if (_medias.length) {
+                this.currentMedia = _medias[0];
+            }
+        }
+    }
+    return this.currentMedia;
+};
+
+Source.prototype.merge = function(_source) {
+    var _this = this;
+    _source.forEach(function(_value, _key) {
+        _this.getList(_key).addElements(_value);
+    });
+};
+
+/* */
+
+var RemoteSource = Model.RemoteSource = function(_config) {
+    Source.call(this, _config);
+};
+
+extendPrototype(RemoteSource, Source);
+
+RemoteSource.prototype.get = function() {
+    this.status = _SOURCE_STATUS_WAITING;
+    var _this = this,
+        urlparams = this.url_params || {},
+        dataType = (isLocalURL(this.url) ? "json" : "jsonp");
+    urlparams.format = dataType;
+    ns.jQuery.ajax({
+        url: this.url,
+        dataType: dataType,
+        data: urlparams,
+        traditional: true,
+        success: function(_result) {
+            _this.deSerialize(_result);
+            _this.handleCallbacks();
+        }
+    });
+};
+
+/* */
+
+var Directory = Model.Directory = function() {
+    this.remoteSources = {};
+    this.elements = {};
+};
+
+Directory.prototype.remoteSource = function(_properties) {
+    if (typeof _properties !== "object" || typeof _properties.url === "undefined") {
+        throw "Error : Directory.remoteSource(configuration): configuration.url is undefined";
+    }
+    var _config = ns._({ directory: this }).extend(_properties);
+    _config.url_params = _config.url_params || {};
+    var _hash = _config.url + "?" + ns.jQuery.param(_config.url_params);
+    if (typeof this.remoteSources[_hash] === "undefined") {
+        this.remoteSources[_hash] = new RemoteSource(_config);
+    }
+    return this.remoteSources[_hash];
+};
+
+Directory.prototype.newLocalSource = function(_properties) {
+    var _config = ns._({ directory: this }).extend(_properties),
+        _res = new Source(_config);
+    return _res;
+};
+
+Directory.prototype.getElement = function(_id) {
+    return this.elements[_id];
+};
+
+Directory.prototype.addElement = function(_element) {
+    this.elements[_element.id] = _element;
+};
+
+Directory.prototype.getGlobalList = function() {
+    var _res = new List(this);
+    _res.addIds(ns._(this.elements).keys());
+    return _res;
+};
+return Model;
+
+})(IriSP);
+
+/* END js */
+
+/* HTML player, to be reused in a widget, or elsewhere */
+
+IriSP.htmlPlayer = function(media, jqselector, options) {
+    
+    var opts = options || {},
+        videoURL = opts.video || media.video;
+    
+    if (typeof opts.url_transform === "function") {
+        videoURL = opts.url_transform(videoURL);
+    }
+        
+    var videoEl = IriSP.jQuery('<video>');
+    
+    videoEl.attr({
+        width : opts.width || undefined,
+        height : opts.height || undefined,
+        controls : opts.controls || undefined,
+        autoplay : opts.autostart || opts.autoplay || undefined
+    });
+    
+    if(typeof videoURL === "string"){
+        videoEl.attr("src",videoURL);
+    } else {
+        for (var i = 0; i < videoURL.length; i++) {
+            var _srcNode = IriSP.jQuery('<source>');
+            _srcNode.attr({
+                src: videoURL[i].src,
+                type: videoURL[i].type
+            });
+            videoEl.append(_srcNode);
+        }
+    }
+    
+    jqselector.html(videoEl);
+    
+    var mediaEl = videoEl[0];
+    
+    // Binding HTML video functions to media events
+    media.on("setcurrenttime", function(_milliseconds) {
+        try {
+            mediaEl.currentTime = (_milliseconds / 1000);
+        } catch (err) {
+            
+        }
+    });
+    
+    media.on("setvolume", function(_vol) {
+        media.volume = _vol;
+        try {
+            mediaEl.volume = _vol;
+        } catch (err) {
+            
+        }
+    });
+    
+    media.on("setmuted", function(_muted) {
+        media.muted = _muted;
+        try {
+            mediaEl.muted = _muted;
+        } catch (err) {
+            
+        }
+    });
+    
+    media.on("setplay", function() {
+        try {
+            mediaEl.play();
+        } catch (err) {
+            
+        }
+    });
+    
+    media.on("setpause", function() {
+        try {
+            mediaEl.pause();
+        } catch (err) {
+            
+        }
+    });
+    
+    // Binding DOM events to media
+    function getVolume() {
+        media.muted = mediaEl.muted;
+        media.volume = mediaEl.volume;
+    }
+    
+    videoEl.on("loadedmetadata", function() {
+        getVolume();
+        media.trigger("loadedmetadata");
+        media.trigger("volumechange");
+    })
+    
+    videoEl.on("timeupdate", function() {
+        media.trigger("timeupdate", new IriSP.Model.Time(1000*mediaEl.currentTime));
+    });
+    
+    videoEl.on("volumechange", function() {
+        getVolume();
+        media.trigger("volumechange");
+    })
+    
+    videoEl.on("play", function() {
+        media.trigger("play");
+    });
+    
+    videoEl.on("pause", function() {
+        media.trigger("pause");
+    });
+    
+    videoEl.on("seeking", function() {
+        media.trigger("seeking");
+    });
+    
+    videoEl.on("seeked", function() {
+        media.trigger("seeked");
+    });
+    
+    
+};
--- a/player/js/player.js	Tue May 21 19:12:29 2013 +0200
+++ b/player/js/player.js	Thu May 23 13:10:54 2013 +0200
@@ -1,7 +1,153 @@
-$(function() {
+var myDir = new IriSP.Model.Directory(),
+    myProject = myDir.remoteSource({
+            url: "data/rigoletto.json",
+            serializer: IriSP.serializers.ldt
+        });
+
+myProject.onLoad(function() {
+    
+    $(".project-title").text(myProject.title);
+    
+    var myMedia = myProject.getCurrentMedia();
+    
+    IriSP.htmlPlayer(
+        myMedia,
+        $(".video-container"),
+        {
+            width: 1000,
+            height: 560,
+            autostart: true
+        }
+    );
+    
+    myMedia.on("timeupdate", function(t) {
+        var progress = $(".progress-indicator"),
+            pos = ($(".chapters-bar").width() - 2 * progress.width()) * t / myMedia.duration;
+        progress.css("left",pos);
+    });
+    myMedia.on("play", function() {
+        $(".play-button").addClass("pause");
+    });
+    myMedia.on("pause", function() {
+        $(".play-button").removeClass("pause");
+    });
+    
+    var tags = myProject.getTags().sortBy(function(t) {
+            return - t.getRelated("annotation").length;
+        }).slice(0,12),
+        tagTemplate = _.template('<li data-tag-id="<%- id %>" class="tag"><%- title %></li>'),
+        clickedTag = null,
+        lastTag = null;
+    
+    $(".tags-list").html(tags.map(tagTemplate).join(""));
+    
+    $("body").click(function() {
+        if (clickedTag) {
+            $(".chapter").removeClass("found");
+            clickedTag = null;
+        }
+    });
+    
+    function showTag(tagId) {
+        $(".chapter").removeClass("found");
+        var tag = myProject.getElement(tagId);
+        if (tag) {
+            tag.getRelated("annotation").forEach(function(a) {
+                a.trigger("found-tags");
+            });
+        }
+        lastTag = tagId;
+    }
+    
+    $(".tag").hover(function() {
+        showTag($(this).attr("data-tag-id"));
+    }, function() {
+        showTag(clickedTag);
+    }).click(function() {
+        clickedTag = lastTag;
+        return false;
+    });
+    
+    
+    var chapters = myProject.getAnnotationsByTypeTitle("chapitrage"),
+        chapterTemplate = _.template(
+            '<li class="chapter" style="left: <%- 100*begin/getMedia().duration %>%; width: <%- 100*getDuration()/getMedia().duration %>%;">'
+            + '<div class="chapter-block"></div><div class="chapter-title"><%- title %></div></li>'
+        ),
+        chapterList = $(".chapters-list"),
+        hoveredChapter = null,
+        currentChapter = null,
+        currentChapterI = 0;
+    
+    function highlightChapter() {
+        $(".chapter").removeClass("active");
+        if (hoveredChapter || currentChapter) {
+            (hoveredChapter || currentChapter).addClass("active");
+        }
+    }
+    
+    chapters.forEach(function(chapter, i) {
+        var element = $(chapterTemplate(chapter));
+        element.click(function() {
+           myMedia.setCurrentTime(chapter.begin); 
+        }).hover(function() {
+            hoveredChapter = element;
+            highlightChapter();
+        }, function() {
+            hoveredChapter = null;
+            highlightChapter();
+        });
+        chapter.on("enter", function() {
+            currentChapter = element;
+            currentChapterI = i;
+            if (i) {
+                $(".prev-chapter").removeClass("inactive");
+            } else {
+                $(".prev-chapter").addClass("inactive");
+            }
+            if (i < chapters.length - 1) {
+                $(".next-chapter").removeClass("inactive");
+            } else {
+                $(".next-chapter").addClass("inactive");
+            }
+            highlightChapter();
+        });
+        chapter.on("leave", function() {
+            currentChapter = null;
+            highlightChapter();
+        });
+        chapter.on("found-tags", function() {
+            element.addClass("found"); 
+        });
+        chapterList.append(element);
+    });
+    
+    $(".prev-chapter").click(function() {
+        if (i) {
+            myMedia.setCurrentTime(chapters[currentChapterI - 1].begin);
+        }
+    });
+    $(".next-chapter").click(function() {
+        if (i < chapters.length - 1) {
+            myMedia.setCurrentTime(chapters[currentChapterI + 1].begin);
+        }
+    });
+    
+    $(".play-button").click(function() {
+        if (myMedia.paused) {
+            myMedia.play();
+        } else {
+            myMedia.pause();
+        }
+    });
+    
     $(".progress-indicator").draggable({
         axis: "x",
-        containment: "parent"
+        containment: "parent",
+        drag: function(e, ui) {
+            var t = myMedia.duration * parseInt(ui.helper.css("left")) / ( $(".chapters-bar").width() - 2 * ui.helper.width() );
+            myMedia.setCurrentTime(t);
+        }
     });
     $(".chips-list").on( {
         mouseenter: function() {
@@ -41,37 +187,54 @@
    ];
    
    function slideshow(data) {
-       var currentslide = 0, slideInterval, playing = false;
+       var currentslide = 0, slideInterval, playing = false, loaded = false;
+       
+       function checkloaded() {
+           loaded = data.reduce(function(mem, slide) {
+               return (mem && !!slide.image && !!slide.image.width);
+           }, true);
+           if (loaded) {
+               showCurrentImage();
+           }
+       }
        
-       $(".slideshow-image").load(function(e) {
-           var imgData = data[currentslide],
-               el = $(this);
-           el.show();
-           var w = el.width(),
-               h = el.height();
+       data.forEach(function(slide) {
+           slide.image = new Image();
+           slide.image.onload = checkloaded;
+           slide.image.src = slide.url;
+       });
+              
+       function resizeImage() {
+           var imgel = $(".slideshow-image");
+           imgel.css("margin-top","");
+           var w = imgel.width(),
+               h = imgel.height();
            $(".slideshow-center").css("height","");
-           $(".slideshow-frame").css("width",w);
            $(".slideshow-description").css("margin-left",w);
-           $(".slideshow-credits").text(imgData.credit);
-           $(".slideshow-title").text(imgData.title);
+           var h2 = $(".slideshow-center").height();
+           if (h < h2) {
+               imgel.css("margin-top",Math.floor((h2-h)/2)+"px");
+           }
+       }
+       
+       function showCurrentImage() {
+           var slide = data[currentslide];
+           $(".slideshow-image").attr({
+               src: slide.image.src,
+               title: slide.title,
+               alt: slide.title
+           });
+           $(".slideshow-credits").text(slide.credit);
+           $(".slideshow-title").text(slide.title);
            $(".slideshow-description").html(
-               imgData.description.split(/\n/gm).map(function(l) {
+               slide.description.split(/\n/gm).map(function(l) {
                    return '<p>' + _.escape(l) + '</p>';
                }).join("")
            );
-           var h2 = $(".slideshow-center").height();
-           if (h < h2) {
-               $(".slideshow-image").css("margin-top",Math.floor((h2-h)/2)+"px");
-           }
-       });
+           resizeImage();
+       }
        
-       function showCurrentImage() {
-           $(".slideshow-center").css("height",$(".slideshow-center").height());
-           $(".slideshow-image").attr("src",data[currentslide].url).hide().css("margin-top","");
-           $(".slideshow-credits").empty();
-           $(".slideshow-title").empty();
-           $(".slideshow-description").empty();
-       }
+       $(window).on("resize", resizeImage);
        
        function nextImage() {
            currentslide = (currentslide + 1) % data.length;
@@ -89,8 +252,6 @@
            }
        }
        
-       showCurrentImage();
-       
        $(".slideshow-next").click(nextImage);
        $(".slideshow-previous").click(function() {
            currentslide = (currentslide ? currentslide : data.length) - 1;
@@ -102,4 +263,20 @@
    
    slideshow(slideshowData);
    
+   $(".slideshow").click(function() {
+       $(".slideshow-annotation").show();
+   });
+   
+   $(".text").click(function() {
+       $(".text-annotation").show();
+   });
+   
+   $(".audio").click(function() {
+       $(".audio-annotation").show();
+   });
+   
+   $(".close-annotation").click(function() {
+       $(".annotation").hide();
+   })
+   
 });