mashup edit view and template.
authorcavaliet
Tue, 27 Nov 2012 13:51:57 +0100
changeset 60 4824317e7b91
parent 59 c62fe82604ef
child 61 fd6aa7d6c840
mashup edit view and template.
src/hashcut/static/hashcut/js/hashcut.js
src/hashcut/templates/bpi_mashup_edit.html
src/hashcut/templates/bpi_mashup_home.html
src/hashcut/templates/iri_mashup_home.html
src/hashcut/urls.py
src/hashcut/views.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hashcut/static/hashcut/js/hashcut.js	Tue Nov 27 13:51:57 2012 +0100
@@ -0,0 +1,3411 @@
+/* 
+ *  Copyright 2012 Institut de recherche et d'innovation 
+ *  contributor(s) : Raphael Velt, Karim Hamidou, Samuel Huron, Thibaut Cavalie, Anthony Ly
+ *   
+ *  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.
+*/
+
+var IriSP = {
+    serializers: {}
+};
+
+IriSP._ = _;
+
+IriSP.jQuery = jQuery;
+
+/* COLOR SCHEMES
+ * Color Brewer Set1 (9 classes): [ "#E41A1C", "#377EB8", "#4DAF4A", "#984EA3", "#FF7F00", "#FFFF33", "#A65628", "#F781BF", "#999999" ]
+ * Color Brewer Set3 (12 classes): ["#8DD3C7", "#FFFFB3", "#BEBADA", "#FB8072", "#80B1D3", "#FDB462", "#B3DE69", "#FCCDE5", "#D9D9D9", "#BC80BD", "#CCEBC5", "#FFED6F"]
+ * Color Brewer Paired (11 classes): ["#A6CEE3", "#1F78B4", "#B2DF8A", "#33A02C", "#FB9A99", "#E31A1C", "#FDBF6F", "#FF7F00", "#CAB2D6", "#6A3D9A", "#FFFF99" ]
+ * d3 Category10: ["#1f77b4","#ff7f0e","#2ca02c","#d62728","#9467bd","#8c564b","#e377c2","#7f7f7f","#bcbd22","#17becf"];
+ * d3 Category20: ["#1f77b4 ", "#aec7e8 ", "#ff7f0e ", "#ffbb78 ", "#2ca02c ", "#98df8a ", "#d62728 ", "#ff9896 ", "#9467bd ", "#c5b0d5 ", "#8c564b ", "#c49c94 ", "#e377c2 ", "#f7b6d2 ", "#7f7f7f ", "#c7c7c7 ", "#bcbd22 ", "#dbdb8d ", "#17becf ", "#9edae5"];
+ * d3 Category20b: ["#393b79 ", "#5254a3 ", "#6b6ecf ", "#9c9ede ", "#637939 ", "#8ca252 ", "#b5cf6b ", "#cedb9c ", "#8c6d31 ", "#bd9e39 ", "#e7ba52 ", "#e7cb94 ", "#843c39 ", "#ad494a ", "#d6616b ", "#e7969c ", "#7b4173 ", "#a55194 ", "#ce6dbd ", "#de9ed6"];
+ * d3 Category20c: ["#3182bd ", "#6baed6 ", "#9ecae1 ", "#c6dbef ", "#e6550d ", "#fd8d3c ", "#fdae6b ", "#fdd0a2 ", "#31a354 ", "#74c476 ", "#a1d99b ", "#c7e9c0 ", "#756bb1 ", "#9e9ac8 ", "#bcbddc ", "#dadaeb ", "#636363 ", "#969696 ", "#bdbdbd ", "#d9d9d9"];
+ */
+
+IriSP.vizcolors = ["#1f77b4 ", "#aec7e8 ", "#ff7f0e ", "#ffbb78 ", "#2ca02c ", "#98df8a ", "#d62728 ", "#ff9896 ", "#9467bd ", "#c5b0d5 ", "#8c564b ", "#c49c94 ", "#e377c2 ", "#f7b6d2 ", "#7f7f7f ", "#c7c7c7 ", "#bcbd22 ", "#dbdb8d ", "#17becf ", "#9edae5"];
+
+/* END init.js */
+IriSP.messages = {
+    fr: {
+        "Duration:" : "Durée :",
+        "duration:" : "durée :",
+        "Edit segment": "Éditer le segment",
+        "Move segment down": "Descendre le segment",
+        "Move segment up": "Remonter le segment",
+        "Delete segment": "Supprimer le segment",
+        "Clone segment": "Cloner le segment",
+        "From:": "De :",
+        "to:": "à :",
+        "Untitled segment": "Segment sans titre",
+        "Untitled Hashcut": "Hashcut sans titre",
+        "Copy of ": "Copie de ",
+        "A segment must be at least one second long": "Le segment doit durer au moins une seconde",
+        "A segment must be at most three minutes long": "Le segment doit durer moins de trois minutes",
+        "A segment must have a title": "Le segment doit avoir un titre",
+        "A segment should have a description": "Il est recommandé de donner une description au segment",
+        "A segment should have tags": "Il est recommandé de taguer le segment",
+        "A hashcut must be made from at least three segments": "Un hashcut doit être composé d'au moins trois segments",
+        "A hashcut must have a title": "Un titre doit être donné au hashcut",
+        "A hashcut should have a description": "Il est recommandé de donner une description au hashcut",
+        "One or more segments are invalid": "Un ou plusieurs segments ne sont pas valides",
+        "Your hashcut is valid!": "Votre hashcut est valide !",
+        "This segment is valid!": "Ce segment est valide !",
+        "Add segment to hashcut": "Ajouter au Hashcut",
+        "Save segment": "Sauvegarder",
+        "Create new segment": "Créer un nouveau segment",
+        "Edit existing segment": "Modifier le segment",
+        "The hashcut can't be published because:": "Le Hashcut ne peut pas être publié pour les raisons suivantes :",
+        "Server error\nYour hashcut couldn't be published": "Erreur serveur\nVotre hashcut n'a pas pu être publié",
+        "You haven't published your hashcut yet.\nIf you leave this page, it will be lost": "Vous n'avez pas encore publié votre Hashcut.\nSi vous quittez cette page, il sera perdu.",
+        "From hashcut:": "Du hashcut :"
+    }
+};
+
+((function() {
+    var lang = IriSP.language || navigator.language || navigator.userLanguage || "en",
+        srcs = [ ];
+    if (typeof IriSP.messages[lang] === "object") {
+        srcs.push(IriSP.messages[lang])
+    }
+    if (lang.substr(0,2) !== lang && typeof IriSP.messages[lang.substr(0,2)] === "object") {
+        srcs.push(IriSP.messages[lang.substr(0,2)])
+    }
+    srcs.push({});
+    var l = srcs.length;
+    
+    IriSP.translate = function(_str) {
+        for (var i = 0; i < l; i++) {
+            if (typeof srcs[i][_str] !== "undefined") {
+                return srcs[i][_str];
+            }
+        }
+        return _str;
+    }
+    
+}) ());
+
+/* END i18n.js */
+/* TODO: Separate Project-specific data from Source */
+
+/* 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,
+    getUID : function() {
+        return uidbase + pad(4, (++uidincrement % 0x10000), 16) + "-" + rand16(4) + "-" + rand16(6) + rand16(6);
+    },
+    isLocalURL : function(url) {
+        var matches = url.match(/^(\w+:)\/\/([^/]+)/);
+        if (matches) {
+            return(matches[1] === document.location.protocol && matches[2] === document.location.host)
+        }
+        return true;
+    },
+    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);
+    },
+    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 : 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'  
+    }
+}
+
+/*
+ * Model.List is a class for a list of elements (e.g. annotations, medias, etc. that each have a distinct ID)
+ */
+Model.List = function(_directory) {
+    Array.call(this);
+    this.directory = _directory;
+    this.idIndex = [];
+    this.__events = {};
+    if (typeof _directory == "undefined") {
+        console.trace();
+        throw "Error : new Model.List(directory): directory is undefined";
+    }
+}
+
+Model.List.prototype = new Array();
+
+Model.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") {
+    Model.List.prototype.forEach = function(_callback) {
+        var _this = this;
+        ns._(this).forEach(function(_value, _key) {
+            _callback(_value, _key, _this);
+        });
+    }
+}
+
+if (typeof Array.prototype.map === "undefined") {
+    Model.List.prototype.map = function(_callback) {
+        var _this = this;
+        return ns._(this).map(function(_value, _key) {
+            return _callback(_value, _key, _this);
+        });
+    }
+}
+
+Model.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 Model.List
+ */
+Model.List.prototype.filter = function(_callback) {
+    var _this = this,
+        _res = new Model.List(this.directory);
+    _res.addElements(ns._(this).filter(function(_value, _key) {
+        return _callback(_value, _key, _this);
+    }));
+    return _res;
+}
+
+Model.List.prototype.slice = function(_start, _end) {
+    var _res = new Model.List(this.directory);
+    _res.addElements(Array.prototype.slice.call(this, _start, _end));
+    return _res;
+}
+
+Model.List.prototype.splice = function(_start, _end) {
+    var _res = new Model.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 Model.List
+ */
+Model.List.prototype.sortBy = function(_callback) {
+    var _this = this,
+        _res = new Model.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
+ */
+Model.List.prototype.searchByTitle = function(_text, _iexact) {
+    var _iexact = _iexact || false,
+        _rgxp = Model.regexpFromTextOrArray(_text, true);
+    return this.filter(function(_element) {
+        return _rgxp.test(_element.title);
+    });
+}
+
+Model.List.prototype.searchByDescription = function(_text, _iexact) {
+    var _iexact = _iexact || false,
+        _rgxp = Model.regexpFromTextOrArray(_text, true);
+    return this.filter(function(_element) {
+        return _rgxp.test(_element.description);
+    });
+}
+
+Model.List.prototype.searchByTextFields = function(_text, _iexact) {
+    var _iexact = _iexact || false,
+        _rgxp =  Model.regexpFromTextOrArray(_text, true);
+    return this.filter(function(_element) {
+        return _rgxp.test(_element.description) || _rgxp.test(_element.title);
+    });
+}
+
+Model.List.prototype.getTitles = function() {
+    return this.map(function(_el) {
+        return _el.title;
+    });
+}
+
+Model.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);
+    }
+}
+
+Model.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;
+    }
+}
+
+Model.List.prototype.addIds = function(_array) {
+    var _l = _array.length,
+        _this = this;
+    ns._(_array).forEach(function(_id) {
+        _this.addId(_id);
+    });
+}
+
+Model.List.prototype.addElements = function(_array) {
+    var _this = this;
+    ns._(_array).forEach(function(_el) {
+        _this.push(_el);
+    });
+}
+
+Model.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];
+    }
+}
+
+Model.List.prototype.removeElement = function(_el, _deleteFromDirectory) {
+    var _deleteFromDirectory = _deleteFromDirectory || false;
+    this.removeId(_el.id);
+}
+
+Model.List.prototype.removeIds = function(_list, _deleteFromDirectory) {
+    var _deleteFromDirectory = _deleteFromDirectory || false,
+        _this = this;
+    ns._(_list).forEach(function(_id) {
+        _this.removeId(_id);
+    });
+}
+
+Model.List.prototype.removeElements = function(_list, _deleteFromDirectory) {
+    var _deleteFromDirectory = _deleteFromDirectory || false,
+        _this = this;
+    ns._(_list).forEach(function(_el) {
+        _this.removeElement(_el);
+    });
+}
+
+Model.List.prototype.on = function(_event, _callback) {
+    if (typeof this.__events[_event] === "undefined") {
+        this.__events[_event] = [];
+    }
+    this.__events[_event].push(_callback);
+}
+
+Model.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;
+        });
+    }
+}
+
+Model.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.
+ */
+
+Model.Time = function(_milliseconds) {
+    this.milliseconds = 0;
+    this.setMilliseconds(_milliseconds);
+}
+
+Model.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;
+    }
+}
+
+Model.Time.prototype.setSeconds = function(_seconds) {
+    this.milliseconds = 1000 * _seconds;
+}
+
+Model.Time.prototype.getSeconds = function() {
+    return this.milliseconds / 1000;
+}
+
+Model.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
+    } 
+}
+
+Model.Time.prototype.add = function(_milliseconds) {
+    this.milliseconds += new Model.Time(_milliseconds).milliseconds;
+}
+
+Model.Time.prototype.valueOf = function() {
+    return this.milliseconds;
+}
+
+Model.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.round(_hms.milliseconds / 100)
+    }
+    return _res;
+}
+
+/* Model.Reference handles references between elements
+ */
+
+Model.Reference = function(_source, _idRef) {
+    this.source = _source;
+    this.id = _idRef;
+    if (typeof _idRef === "object") {
+        this.isList = true;
+    } else {
+        this.isList = false;
+    }
+    this.refresh();
+}
+
+Model.Reference.prototype.refresh = function() {
+    if (this.isList) {
+        this.contents = new Model.List(this.source.directory);
+        this.contents.addIds(this.id);
+    } else {
+        this.contents = this.source.getElement(this.id);
+    }
+    
+}
+
+Model.Reference.prototype.getContents = function() {
+    if (typeof this.contents === "undefined" || (this.isList && this.contents.length != this.id.length)) {
+        this.refresh();
+    }
+    return this.contents;
+}
+
+Model.Reference.prototype.isOrHasId = function(_idRef) {
+    if (this.isList) {
+        return (ns._(this.id).indexOf(_idRef) !== -1)
+    } else {
+        return (this.id == _idRef);
+    }
+}
+
+/* */
+
+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 = Model.getUID();
+    }
+    this.id = _id;
+    this.source = _source;
+    if (_source !== this) {
+        this.source.directory.addElement(this);
+    }
+}
+
+Model.Element.prototype.toString = function() {
+    return this.elementType + (this.elementType !== 'element' ? ', id=' + this.id + ', title="' + this.title + '"' : '');
+}
+
+Model.Element.prototype.setReference = function(_elementType, _idRef) {
+    this[_elementType] = new Model.Reference(this.source, _idRef);
+}
+
+Model.Element.prototype.getReference = function(_elementType) {
+    if (typeof this[_elementType] !== "undefined") {
+        return this[_elementType].getContents();
+    }
+}
+
+Model.Element.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);
+    });
+}
+
+Model.Element.prototype.on = function(_event, _callback) {
+    if (typeof this.__events[_event] === "undefined") {
+        this.__events[_event] = [];
+    }
+    this.__events[_event].push(_callback);
+}
+
+Model.Element.prototype.off = function(_event, _callback) {
+    if (typeof this.__events[_event] !== "undefined") {
+        this.__events[_event] = ns._(this.__events[_event]).reject(function(_fn) {
+            return _fn === _callback;
+        });
+    }
+}
+
+Model.Element.prototype.trigger = function(_event, _data) {
+    var _element = this;
+    ns._(this.__events[_event]).each(function(_callback) {
+        _callback.call(_element, _data);
+    });
+}
+
+/* */
+
+Model.Playable = function(_id, _source) {
+    Model.Element.call(this, _id, _source);
+    if (typeof _source === "undefined") {
+        return;
+    }
+    this.elementType = 'playable';
+    this.currentTime = new Model.Time();
+    this.volume = .5;
+    this.paused = true;
+    this.muted = 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);
+        });
+    });
+}
+
+Model.Playable.prototype = new Model.Element();
+
+Model.Playable.prototype.getCurrentTime = function() { 
+    return this.currentTime;
+}
+
+Model.Playable.prototype.getVolume = function() {
+    return this.volume;
+}
+
+Model.Playable.prototype.getPaused = function() {
+    return this.paused;
+}
+
+Model.Playable.prototype.getMuted = function() {
+    return this.muted;
+}
+
+Model.Playable.prototype.setCurrentTime = function(_time) {
+    this.trigger("setcurrenttime",_time);
+}
+
+Model.Playable.prototype.setVolume = function(_vol) {
+    this.trigger("setvolume",_vol);
+}
+
+Model.Playable.prototype.setMuted = function(_muted) {
+    this.trigger("setmuted",_muted);
+}
+
+Model.Playable.prototype.play = function() {
+    this.trigger("setplay");
+}
+
+Model.Playable.prototype.pause = function() {
+    this.trigger("setpause");
+}
+
+Model.Playable.prototype.show = function() {}
+
+Model.Playable.prototype.hide = function() {}
+
+/* */
+
+Model.Media = function(_id, _source) {
+    Model.Playable.call(this, _id, _source);
+    this.elementType = 'media';
+    this.duration = new Model.Time();
+    this.video = '';
+    var _this = this;
+}
+
+Model.Media.prototype = new Model.Playable();
+
+/* Default functions to be overriden by players */
+    
+Model.Media.prototype.setDuration = function(_durationMs) {
+    this.duration.setMilliseconds(_durationMs);
+}
+
+Model.Media.prototype.getAnnotations = function() {
+    return this.getRelated("annotation");
+}
+
+Model.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 Model.List(this.source.directory)
+    }
+}
+
+/* */
+
+Model.Tag = function(_id, _source) {
+    Model.Element.call(this, _id, _source);
+    this.elementType = 'tag';
+}
+
+Model.Tag.prototype = new Model.Element();
+
+Model.Tag.prototype.getAnnotations = function() {
+    return this.getRelated("annotation");
+}
+
+/* */
+Model.AnnotationType = function(_id, _source) {
+    Model.Element.call(this, _id, _source);
+    this.elementType = 'annotationType';
+}
+
+Model.AnnotationType.prototype = new Model.Element();
+
+Model.AnnotationType.prototype.getAnnotations = function() {
+    return this.getRelated("annotation");
+}
+
+/* Annotation
+ * */
+
+Model.Annotation = function(_id, _source) {
+    Model.Element.call(this, _id, _source);
+    this.elementType = 'annotation';
+    this.begin = new Model.Time();
+    this.end = new Model.Time();
+    this.tag = new Model.Reference(_source, []);
+    this.playing = false;
+    var _this = this;
+    this.on("click", function() {
+        _this.getMedia().setCurrentTime(_this.begin);
+    });
+}
+
+Model.Annotation.prototype = new Model.Element();
+
+Model.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);
+    }
+}
+
+Model.Annotation.prototype.setEnd = function(_endMs) {
+    this.end.setMilliseconds(Math.min(_endMs, this.getMedia().duration.milliseconds));
+    this.trigger("change-end");
+    if (this.end < this.begin) {
+        this.setBegin(this.end);
+    }
+}
+
+Model.Annotation.prototype.setDuration = function(_durMs) {
+    this.setEnd(_durMs + this.begin.milliseconds);
+}
+
+Model.Annotation.prototype.setMedia = function(_idRef) {
+    this.setReference("media", _idRef);
+}
+
+Model.Annotation.prototype.getMedia = function() {
+    return this.getReference("media");
+}
+
+Model.Annotation.prototype.setAnnotationType = function(_idRef) {
+    this.setReference("annotationType", _idRef);
+}
+
+Model.Annotation.prototype.getAnnotationType = function() {
+    return this.getReference("annotationType");
+}
+
+Model.Annotation.prototype.setTags = function(_idRefs) {
+    this.setReference("tag", _idRefs);
+}
+
+Model.Annotation.prototype.getTags = function() {
+    return this.getReference("tag");
+}
+
+Model.Annotation.prototype.getTagTexts = function() {
+    return this.getTags().getTitles();
+}
+
+Model.Annotation.prototype.getDuration = function() {
+    return new Model.Time(this.end.milliseconds - this.begin.milliseconds)
+}
+
+/* */
+
+Model.MashedAnnotation = function(_mashup, _annotation) {
+    Model.Element.call(this, _mashup.id + "_" + _annotation.id, _annotation.source);
+    this.elementType = 'mashedAnnotation';
+    this.annotation = _annotation;
+    this.begin = new Model.Time();
+    this.end = new Model.Time();
+    this.duration = new Model.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");
+    });
+}
+
+Model.MashedAnnotation.prototype = new Model.Element(null);
+
+Model.MashedAnnotation.prototype.getMedia = function() {
+    return this.annotation.getReference("media");
+}
+
+Model.MashedAnnotation.prototype.getAnnotationType = function() {
+    return this.annotation.getReference("annotationType");
+}
+
+Model.MashedAnnotation.prototype.getTags = function() {
+    return this.annotation.getReference("tag");
+}
+
+Model.MashedAnnotation.prototype.getTagTexts = function() {
+    return this.annotation.getTags().getTitles();
+}
+
+Model.MashedAnnotation.prototype.getDuration = function() {
+    return this.annotation.getDuration();
+}
+
+Model.MashedAnnotation.prototype.setBegin = function(_begin) {
+    this.begin.setMilliseconds(_begin);
+    this.duration.setMilliseconds(this.annotation.getDuration());
+    this.end.setMilliseconds(_begin + this.duration);
+}
+
+/* */
+
+Model.Mashup = function(_id, _source) {
+    Model.Playable.call(this, _id, _source);
+    this.elementType = 'mashup';
+    this.duration = new Model.Time();
+    this.segments = new Model.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);
+}
+
+Model.Mashup.prototype = new Model.Playable();
+
+Model.Mashup.prototype.checkLoaded = function() {
+    var loaded = !!this.segments.length;
+    this.getMedias().forEach(function(_m) {
+        loaded = loaded && _m.loaded;
+    });
+    this.loaded = loaded;
+    if (loaded) {
+        this.trigger("loadedmetadata");
+    }
+}
+
+Model.Mashup.prototype.updateTimes = function() {
+    var _time = 0;
+    this.segments.forEach(function(_segment) {
+        _segment.setBegin(_time);
+        _time = _segment.end;
+    });
+    this.duration.setMilliseconds(_time);
+}
+
+Model.Mashup.prototype.addAnnotation = function(_annotation, _defer) {
+    var _mashedAnnotation = new Model.MashedAnnotation(this, _annotation),
+        _defer = _defer || false;
+    this.segments.push(_mashedAnnotation);
+    _annotation.on("change-begin", this._updateTimes);
+    _annotation.on("change-end", this._updateTimes);
+    if (!_defer) {
+        this.trigger("add");
+    }
+}
+
+Model.Mashup.prototype.addAnnotationById = function(_elId, _defer) {
+    var _annotation = this.source.getElement(_elId),
+        _defer = _defer || false;
+    if (typeof _annotation !== "undefined") {
+        this.addAnnotation(_annotation, _defer);
+    }
+}
+
+Model.Mashup.prototype.addAnnotations = function(_segments) {
+    var _this = this;
+    ns._(_segments).forEach(function(_segment) {
+        _this.addAnnotation(_segment, true);
+    });
+    this.trigger("add");
+}
+
+Model.Mashup.prototype.addAnnotationsById = function(_segments) {
+    var _this = this;
+    ns._(_segments).forEach(function(_segment) {
+        _this.addAnnotationById(_segment, true);
+    });
+    this.trigger("add");
+}
+
+Model.Mashup.prototype.removeAnnotation = function(_annotation, _defer) {
+    var _defer = _defer || false;
+    _annotation.off("change-begin", this._updateTimes);
+    _annotation.off("change-end", this._updateTimes);
+    this.segments.removeId(this.id + "_" + _annotation.id);
+    if (!_defer) {
+        this.trigger("remove");
+    }
+}
+
+Model.Mashup.prototype.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");
+    }
+}
+
+Model.Mashup.prototype.setAnnotations = function(_segments) {
+    while (this.segments.length) {
+        this.removeAnnotation(this.segments[0].annotation, true);
+    }
+    this.addAnnotations(_segments);
+}
+
+Model.Mashup.prototype.setAnnotationsById = function(_segments) {
+    while (this.segments.length) {
+        this.removeAnnotation(this.segments[0].annotation, true);
+    }
+    this.addAnnotationsById(_segments);
+}
+
+Model.Mashup.prototype.hasAnnotation = function(_annotation) {
+    return !!ns._(this.segments).find(function(_s) {
+        return _s.annotation === _annotation
+    });
+}
+
+Model.Mashup.prototype.getAnnotation = function(_annotation) {
+    return ns._(this.segments).find(function(_s) {
+        return _s.annotation === _annotation
+    });
+}
+
+Model.Mashup.prototype.getAnnotationById = function(_id) {
+    return ns._(this.segments).find(function(_s) {
+        return _s.annotation.id === _id
+    });
+}
+
+Model.Mashup.prototype.getAnnotations = function() {
+    return this.segments;
+}
+
+Model.Mashup.prototype.getOriginalAnnotations = function() {
+    var annotations = new Model.List(this.source.directory);
+    this.segments.forEach(function(_s) {
+        annotations.push(_s.annotation);
+    });
+    return annotations;
+}
+
+Model.Mashup.prototype.getMedias = function() {
+    var medias = new Model.List(this.source.directory);
+    this.segments.forEach(function(_annotation) {
+        medias.push(_annotation.getMedia())
+    })
+    return medias;
+}
+
+Model.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 Model.List(this.source.directory)
+    }
+}
+
+Model.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;
+    }
+}
+
+Model.Mashup.prototype.getMediaAtTime = function(_time) {
+    var _annotation = this.getAnnotationAtTime(_time);
+    if (typeof _annotation !== "undefined") {
+        return _annotation.getMedia();
+    } else {
+        return undefined;
+    }
+}
+
+/* */
+
+Model.Source = function(_config) {
+    Model.Element.call(this, false, this);
+    this.status = Model._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();
+    }
+}
+
+Model.Source.prototype = new Model.Element();
+
+Model.Source.prototype.addList = function(_listId, _contents) {
+    if (typeof this.contents[_listId] === "undefined") {
+        this.contents[_listId] = new Model.List(this.directory);
+    }
+    this.contents[_listId].addElements(_contents);
+}
+
+Model.Source.prototype.getList = function(_listId, _global) {
+    _global = (typeof _global !== "undefined" && _global);
+    if (_global || typeof this.contents[_listId] === "undefined") {
+        return this.directory.getGlobalList().filter(function(_e) {
+            return (_e.elementType === _listId);
+        });
+    } else {
+        return this.contents[_listId];
+    }
+}
+
+Model.Source.prototype.forEach = function(_callback) {
+    var _this = this;
+    ns._(this.contents).forEach(function(_value, _key) {
+        _callback.call(_this, _value, _key);
+    })
+}
+
+Model.Source.prototype.getElement = function(_elId) {
+    return this.directory.getElement(_elId);
+}
+
+Model.Source.prototype.get = function() {
+    this.status = Model._SOURCE_STATUS_WAITING;
+    this.handleCallbacks();
+}
+
+/* We defer the callbacks calls so they execute after the queue is cleared */
+Model.Source.prototype.deferCallback = function(_callback) {
+    var _this = this;
+    ns._.defer(function() {
+        _callback.call(_this);
+    });
+}
+
+Model.Source.prototype.handleCallbacks = function() {
+    this.status = Model._SOURCE_STATUS_READY;
+    while (this.callbackQueue.length) {
+        this.deferCallback(this.callbackQueue.splice(0,1)[0]);
+    }
+}
+Model.Source.prototype.onLoad = function(_callback) {
+    if (this.status === Model._SOURCE_STATUS_READY) {
+        this.deferCallback(_callback);
+    } else {
+        this.callbackQueue.push(_callback);
+    }
+}
+
+Model.Source.prototype.serialize = function() {
+    return this.serializer.serialize(this);
+}
+
+Model.Source.prototype.deSerialize = function(_data) {
+    this.serializer.deSerialize(_data, this);
+}
+
+Model.Source.prototype.getAnnotations = function(_global) {
+    _global = (typeof _global !== "undefined" && _global);
+    return this.getList("annotation", _global);
+}
+
+Model.Source.prototype.getMedias = function(_global) {
+    _global = (typeof _global !== "undefined" && _global);
+    return this.getList("media", _global);
+}
+
+Model.Source.prototype.getTags = function(_global) {
+    _global = (typeof _global !== "undefined" && _global);
+    return this.getList("tag", _global);
+}
+
+Model.Source.prototype.getMashups = function(_global) {
+    _global = (typeof _global !== "undefined" && _global);
+    return this.getList("mashup", _global);
+}
+
+Model.Source.prototype.getAnnotationTypes = function(_global) {
+    _global = (typeof _global !== "undefined" && _global);
+    return this.getList("annotationType", _global);
+}
+
+Model.Source.prototype.getAnnotationsByTypeTitle = function(_title, _global) {
+    _global = (typeof _global !== "undefined" && _global);
+    var _res = new Model.List(this.directory),
+        _annTypes = this.getAnnotationTypes(_global).searchByTitle(_title);
+    _annTypes.forEach(function(_annType) {
+        _res.addElements(_annType.getAnnotations(_global));
+    })
+    return _res;
+}
+
+Model.Source.prototype.getDuration = function() {
+    var _m = this.currentMedia;
+    if (typeof _m !== "undefined") {
+        return this.currentMedia.duration;
+    }
+}
+
+Model.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;
+}
+
+Model.Source.prototype.merge = function(_source) {
+    var _this = this;
+    _source.forEach(function(_value, _key) {
+        _this.getList(_key).addElements(_value);
+    });
+}
+
+/* */
+
+Model.RemoteSource = function(_config) {
+    Model.Source.call(this, _config);
+}
+
+Model.RemoteSource.prototype = new Model.Source();
+
+Model.RemoteSource.prototype.get = function() {
+    this.status = Model._SOURCE_STATUS_WAITING;
+    var _this = this,
+        urlparams = this.url_params || {},
+        dataType = (Model.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();
+        }
+    });
+}
+
+/* */
+
+Model.Directory = function() {
+    this.remoteSources = {};
+    this.elements = {};
+}
+
+Model.Directory.prototype.remoteSource = function(_properties) {
+    if (typeof _properties !== "object" || typeof _properties.url === "undefined") {
+        throw "Error : Model.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 Model.RemoteSource(_config);
+    }
+    return this.remoteSources[_hash];
+}
+
+Model.Directory.prototype.newLocalSource = function(_properties) {
+    var _config = ns._({ directory: this }).extend(_properties),
+        _res = new Model.Source(_config);
+    return _res;
+}
+
+Model.Directory.prototype.getElement = function(_id) {
+    return this.elements[_id];
+}
+
+Model.Directory.prototype.addElement = function(_element) {
+    this.elements[_element.id] = _element;
+}
+
+Model.Directory.prototype.getGlobalList = function() {
+    var _res = new Model.List(this);
+    _res.addIds(ns._(this.elements).keys());
+    return _res;
+}
+
+return Model;
+
+})(IriSP);
+
+/* END model.js */
+
+/* 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;
+        }
+        
+        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 ldt-serializer.js */
+
+IriSP.serializers.segmentapi = {
+    deSerialize : function(_data, _source) {
+        var _annotationlist = new IriSP.Model.List(_source.directory),
+            _medialist = new IriSP.Model.List(_source.directory);
+        _source.addList("media", _medialist);
+        
+        function deserializeObject(_s) {
+            var _ann = new IriSP.Model.Annotation(_s.element_id, _source),
+                _media = _source.getElement(_s.iri_id);
+            if (!_media) {
+                _media = new IriSP.Model.Media(_s.iri_id, _source);
+                _source.getMedias().push(_media);
+            }
+            _ann.setMedia(_s.iri_id);
+            _ann.title = _s.title;
+            _ann.description = _s.abstract;
+            _ann.begin = new IriSP.Model.Time(_s.start_ts);
+            _ann.end = new IriSP.Model.Time(_s.start_ts + _s.duration);
+            _ann.keywords = _s.tags.split(",");
+            _ann.project_id = _s.project_id;
+            _annotationlist.push(_ann);
+        }
+        
+        if (typeof _data.objects !== "undefined") {
+            IriSP._(_data.objects).each(deserializeObject);
+        } else {
+            deserializeObject(_data);
+        }
+        _source.addList("annotation", _annotationlist);
+    }
+}
+
+/* END segmentapi-serializer.js */
+IriSP.serializers.content = {
+    deSerialize : function(_data, _source) {
+        var _medialist = new IriSP.Model.List(_source.directory);
+        
+        function deserializeObject(_m, i) {
+            var _media = new IriSP.Model.Media(_m.iri_id, _source);
+            _media.video = _m.media_url;
+            _media.title = _m.title;
+            _media.description = _m.description;
+            _media.setDuration(_m.duration);
+            _media.thumbnail = _m.image;
+            _media.color = IriSP.vizcolors[i % IriSP.vizcolors.length];
+            _media.keywords = _m.tags;
+            _medialist.push(_media);
+        }
+        
+        if (typeof _data.objects !== "undefined") {
+            IriSP._(_data.objects).each(deserializeObject);
+        } else {
+            deserializeObject(_data, 0);
+        }
+        
+        _source.addList("media", _medialist);
+    }
+};
+
+/* END contentapi-serializer.js */
+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",mashup.currentAnnotation);
+        }
+        
+    }
+    
+    /* 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) {
+        if (!currentMedia) {
+            return;
+        }
+        var _x = _e.pageX - timeSlider.offset().left,
+            _t = new IriSP.Model.Time(
+                Math.max(0, currentMedia.duration * Math.min(1, _x / timeSlider.width()))
+            );
+        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();
+            }
+        }
+    });
+    
+    /* 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;
+    mashup.currentAnnotation = null;
+    mashup.seeking = false;
+    var mashupSegmentBegin,
+        mashupSegmentEnd,
+        mashupTimecode = 0,
+        seekdiv = $(".video-wait"),
+        mashupTimedelta;
+    
+    function showSeek() {
+        if (currentMedia.seeking) {
+            seekdiv.show();
+        }
+    }
+    
+    function changeCurrentAnnotation() {
+        if (mashupTimecode >= mashup.duration) {
+            if (!mashup.paused) {
+                mashup.paused = true;
+                mashup.trigger("pause");
+            }
+            mashupTimecode = 0;
+        }
+        var _annotation = mashup.getAnnotationAtTime( mashupTimecode );
+        if (typeof _annotation === "undefined") {
+            if (mashup.currentMedia) {
+                mashup.currentMedia.pause();
+                if (!mashup.paused) {
+                    mashup.paused = true;
+                    mashup.trigger("pause");
+                }
+            }
+            return;
+        }
+        mashup.currentAnnotation = _annotation;
+        mashupSegmentBegin = mashup.currentAnnotation.annotation.begin.milliseconds;
+        mashupSegmentEnd = mashup.currentAnnotation.annotation.end.milliseconds;
+        mashupTimedelta = mashupSegmentBegin - mashup.currentAnnotation.begin.milliseconds;
+        mashup.currentMedia = mashup.currentAnnotation.getMedia();
+        
+        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();
+            mashup.seeking = 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.seeking = true;
+                setTimeout(showSeek,200);
+            }
+        });
+        
+        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) {
+                $(".frise-position").css("left",(100*_time/media.duration)+"%");
+                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 && mashup.seeking) {
+                mashup.seeking = 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.on("volumechange", function() {
+        if (mashup === currentMedia) {
+            ctrlVolumeUpdater();
+        }
+    });
+    /* 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;
+        }
+        var segment = mashup.getAnnotation(annotation);
+        $(".annotation[data-segment-id='" + annotation.id + "']").addClass("active");
+        $(".mashup-tooltip").show().css({
+            left: (100 * ( segment.begin + segment.end ) / ( 2 * mashup.duration ) ) + "%"
+        });
+        $(".mashup-tooltip .segment-tooltip").text(annotation.title);
+    });
+    
+    project.on("mouseout-annotation", function() {
+        if (currentMedia === mashup && mashup.currentAnnotation) {
+            $(".annotation").removeClass("active");
+            $(".item-video.annotation[data-segment-id='" + mashup.currentAnnotation.annotation.id + "']").addClass("active");
+        }
+        $(".mashup-tooltip").hide();
+    });
+    
+    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");
+    });
+    
+    var mouseoverSegment;
+    
+    $(".mashup-frise")
+    .mousemove(function(evt) {
+        if (!mashup.duration.milliseconds) {
+            return;
+        }
+        var el = $(this), t = ( evt.pageX - el.offset().left ) * mashup.duration / el.width(), segment = mashup.getAnnotationAtTime(t);
+        if (segment) {
+            if (segment !== mouseoverSegment) {
+                project.trigger("mouseover-annotation", segment.annotation);
+            }
+        } else {
+            if (mouseoverSegment) {
+                project.trigger("mouseout-annotation");
+            }
+        }
+        mouseoverSegment = segment;
+    })
+    .mouseleave(function() {
+        project.trigger("mouseout-annotation");
+        mouseoverSegment = undefined;
+    })
+    .click(function(evt) {
+        if (!mashup.duration.milliseconds) {
+            return;
+        }
+        var el = $(this), t = ( evt.pageX - el.offset().left ) * mashup.duration / el.width(), segment = mashup.getAnnotationAtTime(t);
+        if (segment) {
+            project.trigger("click-annotation", segment.annotation);
+        }
+    });
+    
+    mashup.trigger("add");
+    
+}
+
+/* END mashupcore.js */
+
+IriSP.editor = function(options) {
+    
+    /* Load Media List */
+    
+    var directory = new IriSP.Model.Directory(),
+        apidirectory = new IriSP.Model.Directory(),
+        project = directory.remoteSource({
+            url: IriSP.endpoints.content,
+            url_params: _({}).extend(options.filter),
+            serializer: IriSP.serializers.content
+        }),
+/*        project = directory.remoteSource({
+            url: options.url,
+            serializer: IriSP.serializers.medialist
+        }),
+*/
+        mashup = new IriSP.Model.Mashup(false, project),
+        mediatemplate = _.template(
+            '<li class="item-video media" data-media-id="<%= id %>"><div class="media-count-wrap"><span class="media-count"></span></div>'
+            + '<img class="thumbnail" src="<%= thumbnail %>" alt="<%= title %>" />'
+            + '<div class="video-info"><h3 class="title-video"><%= title %></h3><p class="description"><%= description %></p>'
+            + '<p class="time-length"><%= IriSP.translate("Duration:") %> <span><%= duration.toString() %></span></p></div><div class="media-found-segments"></div></li>'
+        ),
+        segmenttemplate = _.template(
+            '<li class="item-video annotation" data-segment-id="<%= annotation.id %>" data-media-id="<%= annotation.getMedia().id %>">'
+            + '<img class="thumbnail" src="<%= annotation.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><div class="video-info"><h3 class="title-video"><%= annotation.getMedia().title %></h3>'
+            + '<p class="subtitle"><%= annotation.title %></p><p class="duration"><%= annotation.begin.toString() %> - <%= annotation.end.toString() %> (<%= annotation.getDuration().toString() %>)</p>'
+            + '<ul class="tools"><li><a class="edit" href="#" title="<%= IriSP.translate("Edit segment") %>"></a></li><li><a class="bottom" href="#" title="<%= IriSP.translate("Move segment down") %>"></a></li>'
+            + '<li><a class="top" href="#" title="<%= IriSP.translate("Move segment up") %>"></a></li><li><a class="delete" href="#" title="<%= IriSP.translate("Delete segment") %>"></a></li></ul></div></li>'
+        ),
+        mediasegmenttemplate = _.template(
+            '<div class="media-segment">'
+            + '<div class="media-segment-section" style="left:<%= left %>px; width:<%= width %>px; background:<%= color %>; top: <%= top %>px;" data-segment-id="<%= annotation.id %>"></div>'
+            + '<div class="popin media-segment-popin" style="left:<%= popleft %>px; top: <%= 5+top %>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.translate("Clone segment") %></a>'
+            + '<p><%= IriSP.translate("From:") %> <span><%= annotation.begin.toString() %></span> <%= IriSP.translate("to:") %> <span><%= annotation.end.toString() %></span> (<%= IriSP.translate("duration:") %> <span><%= annotation.getDuration().toString() %></span>)</p>'
+            + '</div></div></div>'
+        ),
+        mediasegmentlisttemplate = _.template(
+            '<div class="media-segment-list" style="height: <%= height %>px"><div class="media-current-section" style="left: <%= left %>px; width: <%= width %>px;"></div><div class="media-segment-list-inner"></div><%= segments %></div>'
+        ),
+        mediafoundtemplate = _.template(
+            '<div class="media-segment"><div class="media-segment-section" style="left:<%= left %>px; width:<%= width %>px; background:<%= color %>; top: <%= top %>px;"></div>'
+            + '<div class="popin media-found-popin" style="left:<%= popleft %>px; top: <%= 5+top %>px;"><img style="left:<%= pointerpos %>px;" class="pointer" src="img/popin-triangle.png" alt="" /><div class="popin-content">'
+            + '<h3><%= title %></h3><a href="#" class="button clone-segment" data-segment-id="<%= annotation.id %>"><%= IriSP.translate("Clone segment") %></a>'
+            + '</div></div></div>'
+        ),
+        mediafoundlisttemplate = _.template(
+            '<div class="media-found-list" style="height: <%= height %>px"><div class="media-segment-list-inner"></div><%= segments %></div>'
+        ),
+        mashupstatus = '',
+        addMode, currentMedia, currentSegment;
+        
+    IriSP.mashupcore(project, mashup);
+    
+    /* Validation of segments and mashup */
+    
+    var segmentcritical = [
+        {
+            validate: function(_s) {
+                return (_s.getDuration() >= 1000);
+            },
+            message: "A segment must be at least one second long"
+        },
+        {
+            validate: function(_s) {
+                return (_s.getDuration() < 180000);
+            },
+            message: "A segment must be at most three minutes long"
+        },
+        {
+            validate: function(_s) {
+                return (!!_s.title && _s.title !== IriSP.translate("Untitled segment"));
+            },
+            message: "A segment must have a title"
+        }
+    ];
+    var segmentwarning = [
+        {
+            validate: function(_s) {
+                return (!!_s.description);
+            },
+            message: "A segment should have a description"
+        },
+        {
+            validate: function(_s) {
+                return (!!_s.keywords.length);
+            },
+            message: "A segment should have tags"
+        }
+    ];
+    
+    var mashupcritical = [
+        {
+            validate: function(_m) {
+                return _m.segments.length > 2;
+            },
+            message: "A hashcut must be made from at least three segments"
+        },
+        {
+            validate: function(_m) {
+                return (!!_m.title && _m.title !== IriSP.translate("Untitled Hashcut"));
+            },
+            message: "A hashcut must have a title"
+        }
+    ];
+    var mashupwarning = [
+        {
+            validate: function(_m) {
+                return !!_m.description
+            },
+            message: "A hashcut should have a description"
+        }
+    ];
+
+    /* Fill left column with Media List */
+
+    project.onLoad(function() {
+        var html = '';
+        project.getMedias().forEach(function(_m) {
+            html += mediatemplate(_m);
+        });
+        $(".col-left .list-video").html(html);
+        project.getMedias().forEach(function(_m) {
+            apidirectory.remoteSource({
+                url: IriSP.endpoints.segment,
+                url_params: {
+                    iri_id: _m.id,
+                    limit: 0
+                },
+                serializer: IriSP.serializers.segmentapi
+            }).onLoad(function() {
+                var medias = this.getMedias(),
+                    annotations = this.getAnnotations();
+                if (medias && medias.length) {
+                    var mediaid = medias[0].id;
+                    el = $(".item-video[data-media-id='" + mediaid + "'] .media-count");
+                    el.text(annotations.length).parent().show();
+                    mediasegmentscache[mediaid] = annotations;
+                    if (currentMedia && mediaid === currentMedia.id && currentSegment) {
+                        showOtherSegments();
+                    }
+                }
+            });
+        });
+    });
+    
+    /* Search Media with left column form */
+    
+    $(".col-left input").on("keyup change input paste", function() {
+        var val = $(this).val();
+        if (val) {
+            var find = IriSP.Model.regexpFromTextOrArray(val, true),
+                replace = IriSP.Model.regexpFromTextOrArray(val, false);
+        }
+        $(".col-left .item-video").each(function() {
+            var li = $(this),
+                mediaid = li.attr("data-media-id"),
+                media = directory.getElement(mediaid);
+            if (!val) {
+                li.find(".title-video").text(media.title);
+                li.find(".description").text(media.description);
+                li.find(".media-found-segments").html("");
+                li.show();
+            } else {
+                var apimedia = apidirectory.getElement(mediaid);
+                if (apimedia) {
+                    var annotations = apimedia.getAnnotations().searchByTextFields(val);
+                } else {
+                    var annotations = [];
+                }   
+                var found = find.test(media.title) || find.test(media.description) || annotations.length;
+                if (found) {
+                    li.find(".title-video").html(media.title.replace(replace, '<span style="background: #fc00ff; color: #ffffff;">$1</span>'));
+                    li.find(".description").html(media.description.replace(replace, '<span style="background: #fc00ff; color: #ffffff;">$1</span>'));
+                    var html = '',
+                        k = 230 / media.duration,
+                        lines = [];
+                    _(annotations).each(function(_a, i) {
+                        var pos = k * (_a.begin + _a.end) / 2,
+                            corrpos = Math.max(76, Math.min(156, pos)),
+                            line = IriSP._(lines).find(function(line) {
+                                return !IriSP._(line.annotations).find(function(ann) {
+                                    return ann.begin < _a.end && ann.end > _a.begin
+                                });
+                            });
+                        if (!line) {
+                            line = { index: lines.length, annotations: []};
+                            lines.push(line); 
+                        }
+                        line.annotations.push(_a);
+                        vizdata = {
+                            annotation : _a,
+                            left : k * _a.begin,
+                            width : k * _a.getDuration(),
+                            top: 8 * line.index,
+                            color: IriSP.vizcolors[i % IriSP.vizcolors.length],
+                            title: _a.title.replace(replace, '<span style="background: #fc00ff; color: #ffffff;">$1</span>'),
+                            popleft : corrpos,
+                            pointerpos : (pos - corrpos),
+                        }
+                        html += mediafoundtemplate(vizdata);
+                    });
+                    html = mediafoundlisttemplate({
+                        height: 8 * lines.length,
+                        segments: html
+                    });
+                    li.find(".media-found-segments").html(html);
+                    li.show();
+                } else {
+                    li.hide();
+                }
+            }
+        })
+    });
+    
+    /* Fill right column when mashup is updated */
+    
+    function updateMashupUI() {
+        var listhtml = '', critical = false, warning = false, messages = [];
+        mashup.segments.forEach(function(_s) {
+            listhtml += segmenttemplate(_s);
+            if (_s.annotation.status === "critical") {
+                critical = true;
+            }
+        });
+        if (critical) {
+            messages.push("One or more segments are invalid");
+        }
+        
+        _(mashupcritical).each(function(sc) {
+            if (!sc.validate(mashup)) {
+                critical = true;
+                messages.push(sc.message);
+            }
+        });
+        _(mashupwarning).each(function(sc) {
+            if (!sc.validate(mashup)) {
+                warning = true;
+                messages.push(sc.message);
+            }
+        });
+        mashup.status = critical ? "critical" : (warning ? "warning" : "valid");
+        if (!messages.length) {
+            messages.push("Your hashcut is valid!");
+        }
+        mashupstatus = ' - ' + _(messages).map(IriSP.translate).join('\n - ');
+        
+        $(".publier-button").toggleClass("disable", critical);
+        
+        $(".liste-segment .validate").removeClass("critical warning valid").addClass(mashup.status);
+        $(".liste-segment .validate-tooltip").html("<ul><li>" + messages.join("</li><li>")+"</li></ul>");
+        
+        $(".col-right .list-video").html(listhtml).find(".item-video:last-child .bottom, .item-video:first-child .top").addClass("disable");
+        
+        project.trigger("mouseout-annotation");
+    }
+    
+    mashup.on("setcurrent", function() {
+        currentMedia = mashup;
+    });
+    
+    mashup.on("change",updateMashupUI);
+    
+    /* Slice Widget */
+   
+    var sliceSlider = $(".Ldt-Slice"),
+        sliceStartTime,
+        slidersRange = 920;
+    
+    sliceSlider.slider({
+        range: true,
+        values: [0, slidersRange],
+        min: 0,
+        max: slidersRange,
+        start: function() {
+            if (currentMedia) {
+                if (!currentMedia.getPaused()) {
+                    currentMedia.pause();
+                }
+            }
+        },
+        slide: function(event, ui) {
+            if (currentMedia && currentSegment) {
+                var t = currentMedia.duration * ui.value / slidersRange;
+                if (ui.value === ui.values[0]) {
+                    currentSegment.setBegin(t);
+                } else {
+                    currentSegment.setEnd(t);
+                }
+            }
+        }
+    });
+    
+    sliceSlider.find(".ui-slider-handle:first")
+        .addClass("Ldt-Slice-left-handle")
+        .click(function() {
+            if (currentMedia && currentSegment) {
+                currentMedia.setCurrentTime(currentSegment.begin);
+            }
+        });
+    sliceSlider.find(".ui-slider-handle:last")
+        .addClass("Ldt-Slice-right-handle")
+        .click(function() {
+            if (currentMedia && currentSegment) {
+                currentMedia.setCurrentTime(currentSegment.end);
+            }
+        });
+    
+    
+    /* Update Segment UI */
+    
+    function updateSegmentUI() {
+        if (currentMedia && currentSegment) {
+            var start = currentSegment.begin,
+                end = currentSegment.end,
+                dur = currentSegment.getDuration(),
+                f = slidersRange / currentMedia.duration,
+                tangleStart = $(".tangle-start"),
+                tangleEnd = $(".tangle-end"),
+                tangleDuration = $(".tangle-duration"),
+                k = 100 / currentMedia.duration,
+                p = k * (start + end) / 2;
+            sliceSlider.slider( "values", [ f * start, f * end ] );
+            tangleStart.text(start.toString(tangleStart.hasClass("active"))).attr("data-milliseconds",start.milliseconds);
+            tangleEnd.text(end.toString(tangleEnd.hasClass("active"))).attr("data-milliseconds",end.milliseconds);
+            tangleDuration.text(dur.toString(tangleDuration.hasClass("active"))).attr("data-milliseconds",dur.milliseconds);
+            $(".segmentation .pointer").css("left", p + "%");
+            $(".media-current-section").css({
+                left: (k * start) + "%",
+                width: (k * dur) + "%"
+            });
+            var messages = [],
+                critical = false,
+                warning = false;
+            _(segmentcritical).each(function(sc) {
+                if (!sc.validate(currentSegment)) {
+                    critical = true;
+                    messages.push(sc.message);
+                }
+            });
+            _(segmentwarning).each(function(sc) {
+                if (!sc.validate(currentSegment)) {
+                    warning = true;
+                    messages.push(sc.message);
+                }
+            });
+            currentSegment.status = critical ? "critical" : (warning ? "warning" : "valid");
+            if (!messages.length) {
+                messages.push("This segment is valid!")
+            }
+            currentSegment.status_messages = _(messages).map(IriSP.translate);
+            
+            $(".segmentation .validate").removeClass("critical warning valid").addClass(currentSegment.status);
+            $(".segmentation .validate-tooltip").html("<ul><li>" + currentSegment.status_messages.join("</li><li>")+"</li></ul>");
+        }
+    }
+    
+    var mediasegmentscache = {};
+    
+    function setMedia(media) {
+        if (currentMedia) {
+            currentMedia.pause();
+        }
+        currentMedia = media;
+        project.trigger("set-current", media);
+        if (currentMedia.elementType == "media") {
+            showSegmentation();
+            $(".tab-media-title").text(currentMedia.title);
+            
+            addMode = !(currentSegment && mashup.hasAnnotation(currentSegment));
+            
+            if (!currentSegment) {
+                currentSegment = new IriSP.Model.Annotation(false, project);
+                currentSegment.setMedia(currentMedia.id);
+                currentSegment.setBegin(currentMedia.getCurrentTime());
+                currentSegment.setEnd(Math.min(currentMedia.getCurrentTime() +  180000, currentMedia.duration));
+                currentSegment.title = IriSP.translate("Untitled segment");
+                currentSegment.color = currentMedia.color;
+                currentSegment.thumbnail = currentMedia.thumbnail;
+                currentSegment.created = new Date();
+                currentSegment.keywords = [];
+                currentSegment.description = "";
+                currentSegment.on("change-begin", function() {
+                    if (currentMedia && currentSegment === this) {
+                        currentMedia.setCurrentTime(this.begin);
+                        updateSegmentUI();
+                    }
+                });
+                currentSegment.on("change-end", function() {
+                    if (currentMedia && currentSegment === this) {
+                        currentMedia.setCurrentTime(this.end);
+                        updateSegmentUI();
+                    }
+                });
+            }
+            if (currentMedia.loaded) {
+                currentMedia.setCurrentTime(currentSegment.begin);
+            }
+            $(".add-segment").val(IriSP.translate(addMode ? "Add segment to hashcut" : "Save segment"));
+            $(".create-or-edit").text(IriSP.translate(addMode ? "Create new segment" : "Edit existing segment"));
+            media.show();
+            $("#segment-title").val(currentSegment.title);
+            $("#segment-description").val(currentSegment.description);
+            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;
+            });
+            if (relatedSegments.length) {
+                $(".self-media-segments").show();
+            } else {
+                $(".self-media-segments").hide();
+            }
+            $(".self-media-segments .media-segments-list").html(mediaSegmentList(_(relatedSegments).pluck("annotation")));
+            showOtherSegments();
+            project.trigger("mouseout-annotation");
+        }
+        if (currentMedia.elementType === "mashup") {
+            showPreview();
+        }
+    }
+    
+    function mediaSegmentList(_annotations) {
+        var html = '',
+            k = $(".Ldt-Slider").width() / currentMedia.duration,
+            lines = [];
+        _(_annotations).each(function(_a, i) {
+            var pos = k * (_a.begin + _a.end) / 2,
+                corrpos = Math.max(145, Math.min(305, pos)),
+                line = IriSP._(lines).find(function(line) {
+                    return !IriSP._(line.annotations).find(function(ann) {
+                        return ann.begin < _a.end && ann.end > _a.begin
+                    });
+                });
+            if (!line) {
+                line = { index: lines.length, annotations: []};
+                lines.push(line); 
+            }
+            line.annotations.push(_a);
+            vizdata = {
+                annotation : _a,
+                popleft : corrpos,
+                left : k * _a.begin,
+                width : k * _a.getDuration(),
+                height: 8,
+                top: 8 * line.index,
+                pointerpos : (pos - corrpos),
+                color: IriSP.vizcolors[i % IriSP.vizcolors.length]
+            }
+            html += mediasegmenttemplate(vizdata);
+        });
+        return mediasegmentlisttemplate({
+            height: 8 * lines.length,
+            left: k * currentSegment.begin,
+            width: k * currentSegment.getDuration(),
+            segments: html
+        });
+    }
+    
+    /* Show Related Segments */
+    
+    function showOtherSegments() {
+        var annotations = mediasegmentscache[currentMedia.id];
+        $(".other-media-segments .media-segments-list").html(mediaSegmentList(annotations));
+        if (annotations && annotations.length) {
+            $(".other-media-segments").show();
+        }
+        else {
+            $(".other-media-segments").hide();
+        }
+     }
+    /* Set In, Out */
+   
+    $(".Ldt-Ctrl-SetIn").click(function() {
+        if (currentMedia && currentSegment) {
+            currentSegment.setBegin(currentMedia.getCurrentTime());
+        }
+    });
+    $(".Ldt-Ctrl-SetOut").click(function() {
+        if (currentMedia && currentSegment) {
+            currentSegment.setEnd(currentMedia.getCurrentTime());
+        }
+    });
+       
+    /* Segment Form interaction */
+   
+    $("#segment-title").on("keyup change input paste", function() {
+        if (currentMedia && currentSegment) {
+            currentSegment.title = $(this).val();
+            updateSegmentUI();
+            mashup.trigger("change");
+        }
+    });
+    $("#segment-title").on("focus click", function() {
+        if ($(this).val() === IriSP.translate("Untitled segment")) {
+            $(this).val("");
+        }
+    });
+    $("#segment-description").on("keyup change input paste", function() {
+        if (currentMedia && currentSegment) {
+            currentSegment.description = $(this).val();
+            mashup.trigger("change");
+        }
+    });
+    $("#segment-form").submit(function() {
+        if (addMode) {
+            mashup.addAnnotation(currentSegment);
+            currentSegment = undefined;
+            setMedia(currentMedia);
+        } else {
+            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 */
+   
+    $(".col-left").on("click", ".item-video", function() {
+        currentSegment = undefined;
+        setMedia(project.getElement($(this).attr("data-media-id")));
+    });
+    
+    /* Click on Tabs */
+    
+    function showSegmentation() {
+        $(".col-middle").removeClass("empty-mode pvw-mode").addClass("segment-mode");
+        return false;
+    }
+    function showPreview() {
+        $(".col-middle").removeClass("empty-mode segment-mode").addClass("pvw-mode");
+        return false;
+    }
+    function showEmpty() {
+        $("video").hide();
+        $(".col-middle").removeClass("pvw-mode segment-mode").addClass("empty-mode");
+        return false;
+    }
+    
+    $(".tab-pvw").click(function() {
+        if (mashup.segments.length) {
+            setMedia(mashup);
+        }
+    });
+    
+    /* Click on segments */
+    
+    function reorganizeMashup() {
+        var ids = $(".organize-segments .item-video").map(function(){return $(this).attr("data-segment-id")});
+        mashup.setAnnotationsById(ids);
+    }
+    
+    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() {
+        $(".media").removeClass("active");
+        var mediaid = undefined;
+        if (currentMedia && currentMedia.elementType === "media") {
+            mediaid = currentMedia.id;
+            if (currentSegment) {
+                $(".annotation").removeClass("active");
+                $(".annotation[data-segment-id='" + currentSegment.id + "']").addClass("active");
+            }
+        }
+        if (currentMedia === mashup && mashup.currentMedia) {
+            mediaid = mashup.currentMedia.id
+        }
+        $(".media[data-media-id='" + mediaid + "']").addClass("active");
+    });
+    
+    $(".organize-segments")
+    .sortable({
+        stop : reorganizeMashup
+    })
+    .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"),
+            media = project.getElement(currentItem.attr("data-media-id")),
+            segment = project.getElement(currentItem.attr("data-segment-id"));
+        currentSegment = segment;
+        setMedia(media);
+        return false;
+    })
+    .on("click", ".top", function(e){
+        var currentItem = $(this).parents(".item-video");
+        currentItem.insertBefore(currentItem.prev());
+		reorganizeMashup();
+		return false;
+    })
+    .on("click", ".bottom", function(e){
+        var currentItem = $(this).parents(".item-video");
+        currentItem.insertAfter(currentItem.next());
+		reorganizeMashup();
+        return false;
+    })
+    .on("click", ".delete", function(e){
+        var id = $(this).parents(".item-video").attr("data-segment-id");
+        mashup.removeAnnotationById(id);
+        if (!mashup.segments.length) {
+            showEmpty();
+        }
+        return false;
+    });
+    
+    /* Tangles */
+    var tangleMsPerPixel = 100,
+        activeTangle,
+        tangleStartX,
+        tangleStartVal,
+        tangleHasMoved;
+    
+    $(".time-tangle").mousedown(function(evt) {
+        activeTangle = $(this);
+        activeTangle.addClass("active");
+        tangleStartVal = +activeTangle.attr("data-milliseconds");
+        tangleStartX = evt.pageX;
+        tangleHasMoved = false;
+        $(this).siblings(".time-tangle").addClass("deactivate");
+        return false;
+    });
+    $(document)
+        .mousemove(function(evt) {
+            if (activeTangle) {
+                tangleHasMoved = true;
+                var newval = new IriSP.Model.Time(tangleMsPerPixel * (evt.pageX - tangleStartX) + tangleStartVal);
+                activeTangle.trigger("valuechange", newval);
+                return false;
+            }
+        })
+        .mouseup(function() {
+            if (activeTangle) {
+                activeTangle.text(activeTangle.text().replace(/\.\d+$/,''));
+                $(".time-tangle").removeClass("active deactivate");
+                activeTangle = undefined;
+            }
+        });
+        
+    $(".tangle-start")
+        .mouseup(function(evt) {
+            if (!tangleHasMoved && currentMedia && currentSegment) {
+                currentMedia.setCurrentTime(currentSegment.begin);
+            }
+        })
+        .on("valuechange", function(evt, val) {
+            if (currentMedia && currentSegment) {
+                currentSegment.setBegin(val);
+            }
+        });
+    $(".tangle-end")
+        .mouseup(function(evt) {
+            if (!tangleHasMoved && currentMedia && currentSegment) {
+                currentMedia.setCurrentTime(currentSegment.end);
+            }
+        })
+        .on("valuechange", function(evt, val) {
+            if (currentMedia && currentSegment) {
+                currentSegment.setEnd(val);
+            }
+        });
+    $(".tangle-duration").on("valuechange", function(evt, val) {
+        if (currentMedia && currentSegment) {
+            currentSegment.setDuration(val);
+        }
+    });
+    
+    /* Click on current segment in Preview */
+    
+    $(".mashup-description .edit").click(function() {
+        if (mashup.currentAnnotation) {
+            currentSegment = mashup.currentAnnotation.annotation;
+            setMedia(mashup.currentAnnotation.getMedia());
+        }
+        return false;
+    });
+    
+    /* Handling related segments */
+   
+    function cloneSegment(sid) {
+        var s = directory.getElement(sid) || apidirectory.getElement(sid),
+            media = directory.getElement(s.getMedia().id);
+        
+        currentSegment = new IriSP.Model.Annotation(false, project);
+        currentSegment.setMedia(media.id);
+        currentSegment.setBegin(s.begin);
+        currentSegment.setEnd(s.end);
+        currentSegment.title = IriSP.translate("Copy of ") + s.title;
+        currentSegment.description = s.description;
+        currentSegment.keywords = s.keywords;
+        currentSegment.color = media.color;
+        currentSegment.thumbnail = media.thumbnail;
+        currentSegment.created = new Date();
+        currentSegment.on("change-begin", function() {
+            if (currentMedia && currentSegment === this) {
+                currentMedia.setCurrentTime(this.begin);
+                updateSegmentUI();
+            }
+        });
+        currentSegment.on("change-end", function() {
+            if (currentMedia && currentSegment === this) {
+                currentMedia.setCurrentTime(this.end);
+                updateSegmentUI();
+            }
+        });
+        setMedia(media);
+    }
+    
+    $(".media-segments-list").on("mouseover", ".media-segment", function() {
+        $(this).find(".media-segment-popin").show();
+    }).on("mouseout", ".media-segment", function() {
+        $(this).find(".media-segment-popin").hide();
+    }).on("click", ".reprendre-segment", function() {
+        cloneSegment($(this).attr("data-segment-id"));
+        return false;
+    }).on("click", ".media-segment-section", function() {
+        var sid = $(this).attr("data-segment-id"),
+            s = directory.getElement(sid) || apidirectory.getElement(sid);
+        if (s.media.id === currentMedia.id) {
+            currentMedia.setCurrentTime(s.begin);
+        }
+    });
+    
+    $(".col-left").on("mouseover", ".media-segment", function() {
+        $(this).find(".media-found-popin").show();
+    }).on("mouseout", ".media-segment", function() {
+        $(this).find(".media-found-popin").hide();
+    }).on("click", ".clone-segment", function() {
+        cloneSegment($(this).attr("data-segment-id"));
+        return false;
+    });
+    /* Changing Hashcut Title and description */
+    
+    mashup.title = IriSP.translate("Untitled Hashcut");
+    $(".title-video-wrap a").text(mashup.title);
+    $("#hashcut-title").val(mashup.title);
+    
+    $("#hashcut-title").on("keyup change input paste", function() {
+        mashup.title = $(this).val();
+        $(".title-video-wrap a").text(mashup.title);
+        mashup.trigger("change");
+    });
+    $("#hashcut-title").on("focus click", function() {
+        if ($(this).val() === IriSP.translate("Untitled Hashcut")) {
+            $(this).val("");
+        }
+    });
+    
+    $("#hashcut-description").on("keyup change input paste", function() {
+        mashup.description = $(this).val();
+        mashup.trigger("change");
+    });
+    
+    $("#hashcut-form").submit(function() {
+        $(".update-title").hide();
+        return false;
+    })
+    
+    /* Publication */
+   
+    function onLeave() {
+        return IriSP.translate("You haven't published your hashcut yet.\nIf you leave this page, it will be lost");
+    }
+    
+    $(window).on("beforeunload", onLeave);
+   
+    $(".publier-button").click(function() {
+        if ($(this).hasClass("disable")) {
+            alert(IriSP.translate("The mashup can't be published because:")+"\n\n"+mashupstatus);
+            return false;
+        }
+        var postproject = directory.newLocalSource(),
+            medias = mashup.getMedias()
+            annotations = mashup.getOriginalAnnotations();
+        postproject.addList("annotationType");
+        postproject.addList("tag");
+        medias.forEach(function(_m) {
+            var anntype = new IriSP.Model.AnnotationType(false, postproject);
+            anntype.title = "Segments from " + _m.title;
+            anntype.media = _m;
+            postproject.getAnnotationTypes().push(anntype);
+        });
+        annotations.forEach(function(_a) {
+            _a.setAnnotationType(
+                postproject.getAnnotationTypes().filter(
+                    function(_at) { return _at.media === _a.getMedia() }
+                )[0].id);
+            var tagids = [];
+            _(_a.keywords).each(function(keyword) {
+                var tags = postproject.getTags().searchByTitle(keyword);
+                if (tags.length) {
+                    tagids.push(tags[0].id);
+                } else {
+                    var tag = new IriSP.Model.Tag(false, postproject);
+                    tag.title = tag.description = keyword;
+                    postproject.getTags().push(tag);
+                    tagids.push(tag.id);
+                }
+            });
+            _a.setTags(tagids);
+        });
+        postproject.addList("annotation",annotations);
+        postproject.addList("media",medias);
+        postproject.addList("mashup",[mashup]);
+        postproject.creator = "admin";
+        postproject.created = new Date();
+        postproject.modified = new Date();
+        postproject.title = mashup.title;
+        postproject.description = mashup.description;
+        $.ajax({
+            type: "POST",
+            url: IriSP.endpoints.project,
+            data: IriSP.serializers.ldt.serialize(postproject),
+            contentType: "application/cinelab",
+//            headers: {"X-CSRFToken": "{{csrf_token}}"},
+            success: function(data, status, request){
+                var location = request.getResponseHeader("Location"),
+                    projid = location.match(/([^/]+)\/?$/)[1],
+                    destination = IriSP.endpoints.hashcut_page + projid;
+                $(window).off("beforeunload", onLeave);
+                document.location.href = destination;
+            },
+            error: function(jqXHR, textStatus, errorThrown){
+                console.log(arguments);
+                alert(IriSP.translate("Server error\nYour hashcut couldn't be published"));
+            }
+        });
+        //TODO: ADD WAITING SCREEN
+        
+        return false;
+    });
+    
+    mashup.trigger("change");
+}
+
+/* END editor.js */
+IriSP.mashupplayer = function(options) {
+    
+    var directory = new IriSP.Model.Directory(),
+        project = directory.remoteSource({
+            url: IriSP.endpoints.ldt + options.id,
+            serializer: IriSP.serializers.ldt
+        }),
+        apidirectory = new IriSP.Model.Directory(),
+        mashup,
+        mediatemplate = _.template('<li class="item-video media" data-media-id="<%= media.id %>">'
+            + '<a href="<%= IriSP.endpoints.media_page + media.id %>"><img class="thumbnail" alt="<%= media.title %>" src="<%= media.thumbnail %>"></a><div class="video-info">'
+            + '<h3 class="title-video"><a href="<%= IriSP.endpoints.media_page + media.id %>"><%= media.title %></a></h3><p class="description"><%= media.description %></p>'
+            + '<p class="time-length">Durée : <span><%= media.duration.toString() %></span></p><div class="frise">'
+            + '<div class="frise-overflow"><div class="frise-segments"><%= segments %></div></div></div></div></li>');
+        segmenttemplate = _.template('<div style="background-color:<%= annotation.color %>; left:<%= left %>%; width: <%= width %>%;"'
+            + ' class="frise-segment annotation" data-segment-id="<%= annotation.id %>" title="<%= annotation.title %>"></div>')
+    
+    project.onLoad(function() {
+        mashup = project.getMashups()[0];
+        IriSP.mashupcore(project, mashup);
+        project.trigger("set-current",mashup);
+        
+        $(".info-title a").text(mashup.title);
+        $(".title-video-wrap .title-video").text(mashup.title);
+        $(".info-duration td").text(mashup.duration.toString());
+        $(".info-author a").text(mashup.creator);
+        $(".info-description td").text(mashup.description);
+        
+        mashup.getMedias().forEach(function(media) {
+            apidirectory.remoteSource({
+                url: IriSP.endpoints.content + media.id,
+                serializer: IriSP.serializers.content
+            }).onLoad(function() {
+                var m = apidirectory.getElement(media.id);
+                if (m) {
+                    media.thumbnail = m.thumbnail;
+                }
+                var segments = mashup.segments.filter(function(segment) {
+                    return segment.getMedia() === media;
+                });
+                var segmentshtml = '', k = media.duration ? (100 / media.duration) : 0;
+                segments.forEach(function(segment) {
+                    var vizdata = {
+                        annotation: segment.annotation,
+                        left: k * segment.annotation.begin,
+                        width: k * segment.annotation.getDuration()
+                    }
+                    segmentshtml += segmenttemplate(vizdata);
+                });
+                var mediadata = {
+                    media: media,
+                    segments: segmentshtml
+                }
+                
+                $(".list-video").append(mediatemplate(mediadata));
+            });
+        });
+        
+        
+        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 = mashup.currentMedia.id;
+            $(".media[data-media-id='" + mediaid + "']").addClass("active");
+        });
+        
+        $(".list-video")
+        .on("mouseover", ".frise-segment", function() {
+            project.trigger("mouseover-annotation", project.getElement($(this).attr("data-segment-id")));
+        })
+        .on("click", ".frise-segment", function() {
+            project.trigger("click-annotation", project.getElement($(this).attr("data-segment-id")));
+        })
+        .on("mouseover", ".item-video", function() {
+            $(".media").removeClass("active");
+        })
+        .on("mouseout", ".item-video", function() {
+            project.trigger("mouseout-annotation");
+        })
+        
+        
+    });
+}
+
+/* END mashupplayer.js */
+IriSP.mediaplayer = function(options) {
+    
+    var directory = new IriSP.Model.Directory(),
+        content = directory.remoteSource({
+            url: IriSP.endpoints.content + options.id,
+            serializer: IriSP.serializers.content
+        }),
+        apidirectory = new IriSP.Model.Directory(),
+        segmenttemplate = _.template(
+            '<div class="media-segment" data-mashup-id="<%= annotation.project_id %>">'
+            + '<div class="media-segment-section" style="left:<%= left %>px; width:<%= width %>px; background:<%= color %>; top: <%= top %>px;" data-segment-id="<%= annotation.id %>"></div>'
+            + '<div class="popin media-segment-popin" style="left:<%= popleft %>px; top: <%= 5+top %>px;"><img style="left:<%= pointerpos %>px;" class="pointer" src="img/popin-triangle.png" alt="" /><div class="popin-content">'
+            + '<h3><%= annotation.title %></h3>'
+            + '<p><%= IriSP.translate("From:") %> <span><%= annotation.begin.toString() %></span> <%= IriSP.translate("to:") %> <span><%= annotation.end.toString() %></span> (<%= IriSP.translate("duration:") %> <span><%= annotation.getDuration().toString() %></span>)</p>'
+            + '<p class="mashup-link"><%= IriSP.translate("From hashcut:") %> <a href="<%= IriSP.endpoints.hashcut_page + annotation.project_id %>"></a></p>'
+            + '</div></div></div>'
+        ),
+        segmentlisttemplate = _.template(
+            '<div class="media-segment-list" style="height: <%= height %>px"><div class="media-segment-list-inner"></div><%= segments %><div class="frise-position"></div></div>'
+        ),
+        projtemplate = _.template(
+            '<li class="item-video mashup" data-mashup-id="<%= ldt_id %>">'
+            + '<a href="<%= IriSP.endpoints.hashcut_page + ldt_id %>"><img class="thumbnail" alt="<%= title %>" src="<%= image %>"></a><div class="video-info">'
+            + '<h3 class="title-video"><a href="<%= IriSP.endpoints.hashcut_page + ldt_id %>"><%= title %></a></h3><p class="description"><%= description %></p>'
+            + '</p></div></li>'
+        ),
+        media;
+    
+    function mediaSegmentList(_annotations) {
+        var html = '',
+            k = $(".Ldt-Slider").width() / media.duration,
+            lines = [];
+        _(_annotations).each(function(_a, i) {
+            var pos = k * (_a.begin + _a.end) / 2,
+                corrpos = Math.max(106, Math.min(516, pos)),
+                line = IriSP._(lines).find(function(line) {
+                    return !IriSP._(line.annotations).find(function(ann) {
+                        return ann.begin < _a.end && ann.end > _a.begin
+                    });
+                });
+            if (!line) {
+                line = { index: lines.length, annotations: []};
+                lines.push(line); 
+            }
+            line.annotations.push(_a);
+            vizdata = {
+                annotation : _a,
+                popleft : corrpos,
+                left : k * _a.begin,
+                width : k * _a.getDuration(),
+                height: 8,
+                top: 8 * line.index,
+                pointerpos : (pos - corrpos),
+                color: IriSP.vizcolors[i % IriSP.vizcolors.length]
+            }
+            html += segmenttemplate(vizdata);
+        });
+        return segmentlisttemplate({
+            height: 8 * lines.length,
+            segments: html
+        });
+    }
+    
+    content.onLoad(function() {
+        
+        IriSP.mashupcore(content, new IriSP.Model.Mashup(false, content));
+        
+        media = content.getMedias()[0];
+        
+        apidirectory.remoteSource({
+            url: IriSP.endpoints.segment,
+            url_params: {
+                iri_id: options.id,
+                limit: 0
+            },
+            serializer: IriSP.serializers.segmentapi
+        }).onLoad(function() {
+            var medias = this.getMedias(),
+                annotations = this.getAnnotations(),
+                projlist = {};
+            $(".media-segments").html(mediaSegmentList(annotations));
+            annotations.forEach(function(a) {
+                projlist[a.project_id] = 1 + (projlist[a.project_id] || 0);
+            });
+            var projkeys = _(projlist)
+                .chain()
+                .keys()
+                .sortBy(function(v) {
+                    return - projlist[v];
+                })
+                .first(8)
+                .value();
+            $.ajax({
+                url: IriSP.endpoints.project,
+                dataType: "json",
+                data: {
+                    format: "json",
+                    ldt_id__in: projkeys
+                },
+                traditional: true,
+                success: function(data) {
+                    var proj = _(data.objects)
+                        .filter(function(p) {
+                            return /<inst/gm.test(p.ldt);
+                        });
+                    _(proj).each(function(p) {
+                        $(".media-segment[data-mashup-id='" + p.ldt_id + "']").each(function() {
+                            $(this)
+                                .find(".mashup-link").show()
+                                .find("a").text(p.title);
+                        });
+                    });
+                    var html = _(proj)
+                        .chain()
+                        .sortBy(function(p) {
+                            return - projlist[p.ldt_id];
+                        })
+                        .map(projtemplate)
+                        .value()
+                        .join("");
+                    $(".list-video").html(html);
+                }
+            });
+        });
+    
+        content.trigger("set-current", media);
+        
+        $(".info-title a").text(media.title);
+        $(".title-video-wrap .title-video").text(media.title);
+        $(".info-duration td").text(media.duration.toString());
+        $(".info-description td").text(media.description);
+        $(".info-tags td").text(media.keywords);
+        
+    });
+    
+    $(".media-segments").on("mouseover", ".media-segment", function() {
+        var el = $(this);
+        el.find(".media-segment-popin").show();
+        var pid = el.attr("data-mashup-id");
+        $(".item-video[data-mashup-id='" + pid + "']").addClass("active");
+    }).on("mouseout", ".media-segment", function() {
+        $(this).find(".media-segment-popin").hide();
+        $(".item-video").removeClass("active");
+    }).on("click", ".media-segment-section", function() {
+        var sid = $(this).attr("data-segment-id"),
+            s = apidirectory.getElement(sid);
+        media.setCurrentTime(s.begin);
+    });
+    
+    $(".list-video").on("mouseover", ".item-video", function() {
+        var pid = $(this).attr("data-mashup-id");
+        $(".media-segment[data-mashup-id='" + pid + "']").addClass("active");
+    }).on("mouseout", ".item-video", function() {
+        $(".media-segment").removeClass("active");
+    });
+}
+
+/* END mediaplayer.js */
+
+IriSP.social = function(sel, url) {
+    
+    var sel = sel || $(".Ldt-Social"),
+        url = url || document.location.href,
+        text = sel.find("title").text(),
+        clipId = IriSP.Model.getUID(),
+        clip;
+
+    sel.find(".Ldt-Social-CopyBtn").attr("id", this.clipId);
+
+    sel.find(".Ldt-Social-Url").click(function() {
+        var _pop = sel.find(".Ldt-Social-UrlPop");
+        _pop.toggle();
+        if (_pop.is(":visible")) {
+            if (typeof clip == "undefined") {
+                clip = new ZeroClipboard.Client();
+                clip.setHandCursor( true );
+                clip.glue(clipId);
+                
+                clip.addEventListener( 'onMouseUp', function() {
+                    _pop.hide();
+                    clip.hide();
+                });
+            }
+            clip.show();
+            clip.setText( url );
+            sel.find(".Ldt-Social-Input").val(url).focus();
+        } else {
+            clip.hide();
+        }
+        return false;
+    });
+    sel.find(".Ldt-Social-Input").focus(function() {
+        this.select();
+    });
+    sel.find(".Ldt-Social-Ext").click(function() {
+        window.open(
+            sel.find(this).attr("href"),
+            "_blank",
+            "height=300,width=450,left=100,top=100,toolbar=0,menubar=0,status=0,location=0");
+        return false;
+    });
+    
+    sel.find(".Ldt-Social-Fb").attr("href", "http://www.facebook.com/share.php?" + $.param({ u: url, t: text }));
+    sel.find(".Ldt-Social-Twitter").attr("href", "https://twitter.com/intent/tweet?" + $.param({ url: url, text: text }));
+    sel.find(".Ldt-Social-Gplus").attr("href", "https://plus.google.com/share?" + $.param({ url: url, title: text }));
+    sel.find(".Ldt-Social-Mail").attr("href", "mailto:?" + $.param({ subject: text, body: text + ": " + url }));
+}
+
+/* END social.js */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hashcut/templates/bpi_mashup_edit.html	Tue Nov 27 13:51:57 2012 +0100
@@ -0,0 +1,351 @@
+{% extends "bpi_mashup_home.html" %}
+{% load static %}
+{% load i18n %}
+{% load analytics %}
+{% load thumbnail %}
+    <title>{% block title %}Hashcut Edition - BPI{% endblock %}</title>    
+    {% block css_import %}
+    {{ block.super }}
+    <link rel="stylesheet" href="{% static 'hashcut/lib/jquery.tagit.css' %}" />
+    <link rel="stylesheet" href="{% static 'hashcut/bpi/css/edition.css' %}" />
+    {% endblock %}
+
+{% block wrap_header %}
+<div class="wrap-header"> 
+    <div class="header">
+
+    <!-- popin user -->
+    
+        <div class="popin user info" id="user">
+            <img class="pointer" src="img/popin-triangle.png" alt="" />
+            <div class="popin-content">
+                <h2>Mashup75</h2>
+                <h3>mash@cinecast.fr</h3>
+                <a href="#" class="nb-hashcut">12 Hashcuts</a>
+                <p>
+                    <a href="#" class="change-account button">Changer de compte</a>
+                </p>
+            </div>
+        </div><!-- popin user info-->
+
+        <div class="popin user signup" id="user">
+            <img class="pointer" src="img/popin-triangle.png" alt="" />
+            <div class="popin-content">
+                <h2>Créer un compte :</h2>
+                <form action="#" class="signup-form">
+                    <p>
+                        <label for="signup-pseudo">Pseudonyme : </label>
+                        <input type="text" id="signup-pseudo" name="" />
+                    </p>
+                    <p >
+                        <label for="signup-email">Email : </label>
+                        <input type="text" id="signup-email" name="" />
+                    </p>
+                    <p >
+                        <label for="signup-password">Mot de passe : </label>
+                        <input type="password" id="signup-password" name="" />
+                    </p>
+                    <p>
+                        <label for="signup-password">Confirmer le mot de passe : </label>
+                        <input type="password" id="signup-password" name="" />
+                    </p>
+                    <p>
+                        <input class="button" type="submit" value="Créer le compte">
+                    </p>
+                </form>
+
+            </div>
+        </div><!-- popin user signup-->
+
+        <div class="popin user login" id="user">
+            <img class="pointer" src="img/popin-triangle.png" alt="" />
+            <div class="popin-content">
+                <h2>Connexion :</h2>
+                <form action="#" class="login-form">
+                    <p>
+                        <label for="signup-pseudo">E-mail : </label>
+                        <input type="text" id="signup-pseudo" name="" />
+                    </p>
+                    <p >
+                        <label for="signup-email">Mot de passe : </label>
+                        <input type="password" id="signup-email" name="" />
+                    </p>
+                    <p>
+                        <input class="button" type="submit" value="Se connecter">
+                    </p>
+                    <p>
+                        <a class="button signup-button" href="#">Créer un compte</a>
+                    </p>
+                </form>
+
+            </div>
+        </div><!-- popin user login-->
+        
+    <!-- /popin user -->
+    
+    <!-- popin update-title -->
+        <div class="popin update-title" id="update-title">
+            <img class="pointer" src="img/popin-triangle.png" alt="" />
+            <div class="popin-content">
+                <form class="clearfix" action="#" id="hashcut-form" method="">
+                    <p>
+                        <label for="hashcut-title">Titre :</label>
+                        <input type="text" id="hashcut-title" name="" value="Hashcut sans titre" />
+                    </p>
+                    <p>
+                        <label for="hashcut-description">Description :</label>
+                        <textarea name="" id="hashcut-description"></textarea>
+                    </p>
+                </form>
+            </div>
+        </div>
+    <!-- /popin update-title -->
+        
+        <h1><a title="Hashcut - Le Mashup cliquable" class="clearfix" href="#">
+            <img src="{% static 'hashcut/bpi/img/hashcut-logo.png' %}" alt="Hashcut" />
+            <span>Le Mashup<br />Cliquable</span>
+        </a></h1>
+        <div class="title-video-wrap">
+            <p class="title-video">
+                <a title="Modifier le titre et la description" class="open-popin" href="#update-title">Hashcut sans titre</a>
+            </p>
+            <p class="time-length">Durée: <span class="mashup-total-duration">00:00</span></p>
+        </div>
+        <div class="profil-wrap">
+            <a href="{% url mashup_home branding=branding %}" class="all-hashcut">Tous les Hashcuts</a>
+            <a href="#user" class="my-profil open-popin">Mon profil</a>
+        </div>
+    </div><!-- header -->
+</div> <!-- wrap-header -->
+{% endblock %}
+
+{% block content %}
+<div class="content clearfix">
+
+    <div class="col-left">
+        <div class="left-head">
+            <h2>Sélectionner les vidéos</h2>
+            <input type="text" name="" placeholder="Rechercher une vidéo" />
+        </div>
+        <ul class="list-video">
+
+        </ul>
+    </div><!-- col-left -->
+
+    <div class="col-middle empty-mode">
+        
+        <div class="col-middle-header">
+            <a href="#" class="tab tab-segment">Segmenter <span class="tab-media-title"></span></a>
+            <a href="#" class="tab tab-pvw">Prévisualiser</a>
+        </div>
+        
+        <div class="video">
+            <div class="tutorial">
+                <h2>Créer un Hashcut, c’est facile !</h2>
+                <ol>
+                    <li>Commencez par choisir une vidéo dans le volet de gauche</li>
+                    <li>Créez un segment dans la vidéo et profitez-en pour annoter votre segment</li>
+                    <li>Ajoutez votre segment à la liste, réorganisez les segments entre eux</li>
+                    <li>Rajoutez quelques commentaires à votre Hashcut. Vous êtes prêts à publier !</li>
+                </ol>
+            </div>
+            <div class="video-wait"></div>
+            <a class="publier-button disable" title="Publier" href="#"></a>
+        </div>
+
+        <div class="widget">
+            <div class="Ldt-Slider-Container">
+                <div class="Ldt-Slider"></div>
+            </div>
+            <div class="Ldt-Slider-Time">00:00</div>
+
+            <div class="Ldt-Ctrl">
+                <div class="Ldt-Ctrl-Left">
+                    <div class="Ldt-Ctrl-button Ldt-Ctrl-Play Ldt-Ctrl-Play-PlayState" title="Lecture/Pause"></div>
+                    <div class="Ldt-Ctrl-spacer"></div>
+                    <div class="Ldt-Ctrl-InOutBlock">
+                        <div class="Ldt-Ctrl-button Ldt-Ctrl-SetIn" title="Débuter le segment ici"></div>
+                        <div class="Ldt-Ctrl-spacer"></div>
+                        <div class="Ldt-Ctrl-button Ldt-Ctrl-SetOut" title="Finir le segment ici"></div>
+                        <div class="Ldt-Ctrl-spacer"></div>
+                    </div>
+                </div>
+                <div class="Ldt-Ctrl-Right">
+                   <div class="Ldt-Ctrl-spacer"></div>
+                   <div class="Ldt-Ctrl-Time">
+                       <div class="Ldt-Ctrl-Time-Elapsed" title="Temps écoulé">00:00</div>
+                       <div class="Ldt-Ctrl-Time-Separator">/</div>
+                       <div class="Ldt-Ctrl-Time-Total" title="Temps total">00:00</div>
+                   </div>
+                   <div class="Ldt-Ctrl-spacer"></div>
+                   <div class="Ldt-Ctrl-button Ldt-Ctrl-Sound Ldt-Ctrl-Sound-Full" title="Couper/Activer le son"></div>
+                </div>
+                <div class="Ldt-Ctrl-Volume-Control" title="Changer le volume">
+                   <div class="Ldt-Ctrl-Volume-Bar"></div>
+                </div>
+            </div>
+            
+        </div>
+
+        <div class="bloc-segmentation">
+            <div class="Ldt-Slice"></div>
+            <div class="segmentation">
+                <div class="pointer-padder">
+                    <img class="pointer" src="img/popin-triangle.png" alt="" />
+                </div>
+                <div class="popin-content">
+                    <div class="validate">
+                        <div class="validate-tooltip"></div>
+                    </div>
+                    <h2>
+                        <span class="create-or-edit">Créer un nouveau segment</span>
+                        de
+                        <span class="time-tangle tangle-start"></span>
+                        à
+                        <span class="time-tangle tangle-end"></span>
+                        (durée:
+                        <span class="time-tangle tangle-duration"></span>)
+                    </h2>
+                    <form action="#" id="segment-form">
+                        <div class="form-segment-left">
+                            <p>
+                                <label for="segment-title">Titre :</label>
+                                <input type="text" id="segment-title" />
+                            </p>
+                            <p>
+                                <label for="segment-tags">Tags :</label>
+                                <ul id="segment-tags"></ul>
+                            </p>
+                        </div>
+                        <div class="form-segment-right">
+                            <p>
+                                <label for="segment-description">Description :</label>
+                                <textarea id="segment-description"></textarea>
+                            </p>
+                        </div>
+                        <input class="button add-segment" type="submit" value="Ajouter au Hashcut" />
+
+                    </form>
+                </div>
+            </div><!-- popin segmentation -->
+            
+            <div class="media-segments self-media-segments">
+                <h2>Mes segments sur ce média :</h2>
+                <div class="media-segments-list">
+                </div>
+            </div>
+            
+            <div class="media-segments other-media-segments">
+                <h2>Segments existants sur ce média :</h2>
+                <div class="media-segments-list">
+                </div>
+            </div>
+
+        </div><!-- bloc-segmentation -->
+
+        <div class="bloc-pvw">
+
+            <div class="frise mashup-frise">
+                <div class="frise-overflow">
+                    <div class="frise-segments">
+                    </div>
+                    <div class="frise-indications">
+                    </div>
+                </div>
+                <div class="frise-position"></div>
+                <div class="mashup-tooltip segment-tooltip-wrapper">
+                    <div class="segment-tooltip"></div>
+                    <div class="segment-tooltip-tip"></div>
+                </div>
+                
+            </div>
+                
+                <div class="segment-info mashup-description">
+                    <div class="pointer-padder">
+                        <img class="pointer" src="img/popin-triangle.png" alt="" />
+                    </div>
+                    <div class="popin-content">
+                        <ul class="tools">
+                            <li><a title="Éditer" class="edit" href="#"></a></li>
+                        </ul>
+
+                        <h2><span class="annotation-title"></span></h2>
+                        <table>
+                            <tbody>
+                                <tr>
+                                    <th>Extrait de :</th>
+                                    <td><span class="annotation-media-title"></span> (<span class="annotation-time annotation-begin"></span> - <span class="annotation-time annotation-end"></span>)</td>
+                                </tr>
+                                <tr>
+                                    <th>Description :</th>
+                                    <td><span class="annotation-description"></span></td>
+                                </tr>
+                                <tr>
+                                    <th>Tags :</th>
+                                    <td><span class="annotation-tags"></span></td>
+                                </tr>
+                            </tbody>
+                        </table>
+                    </div>
+                </div>
+        </div><!-- bloc-pvw -->
+
+    </div><!-- col-middle -->
+
+    <div class="col-right">
+        <div class="liste-segment">
+            <div class="validate critical">
+                <div class="validate-tooltip"></div>
+            </div>
+            <h2>Liste des segments</h2>
+            <div class="frise mashup-frise">
+                <p class="aucun-segment">Aucun segment</p>
+                <div class="frise-overflow">
+                    <div class="frise-segments">
+                    </div>
+                    <div class="frise-indications">
+                    </div>
+                </div>
+                <div class="mashup-tooltip segment-tooltip-wrapper">
+                    <div class="segment-tooltip"></div>
+                    <div class="segment-tooltip-tip"></div>
+                </div>
+
+            </div>
+        </div>
+
+        <ul class="list-video organize-segments">
+            
+        </ul>
+    </div><!-- col-right -->
+
+</div><!-- content -->
+{% endblock %}
+<!-- JavaScript -->
+{% block js_import %}
+<script type="text/javascript" src="{% static 'hashcut/lib/jquery.min.js' %}"></script>
+<script type="text/javascript" src="{% static 'hashcut/lib/jquery-ui.min.js' %}"></script>
+<script type="text/javascript" src="{% static 'hashcut/lib/tag-it.js' %}"></script>
+<script type="text/javascript" src="{% static 'hashcut/lib/underscore-min.js' %}"></script>
+<script type="text/javascript" src="{% static 'hashcut/lib/popcorn-complete.min.js' %}"></script>
+<script type="text/javascript" src="{% static 'hashcut/js/hashcut.js' %}"></script>
+<script type="text/javascript">
+    IriSP.endpoints = {
+        content: "{% url api_dispatch_list api_name='1.0' resource_name='contents' %}",
+        project: "{% url api_dispatch_list api_name='1.0' resource_name='projects' %}",
+        segment: "{% url api_dispatch_list api_name='1.0' resource_name='segments' %}",
+        ldt: "{% url projectjson_id id='' %}",
+        hashcut_page: "hashcut.html?project="
+    };
+    $(function() {
+        var hashcut = IriSP.editor({
+           //url: "data/bpidata.json",
+           //url: "data/moon-local.json",
+           filter: {
+               tags__icontains: "Mash Up 2012"
+           }
+       });
+    });
+</script>
+{% endblock %}
+
--- a/src/hashcut/templates/bpi_mashup_home.html	Tue Nov 27 12:32:30 2012 +0100
+++ b/src/hashcut/templates/bpi_mashup_home.html	Tue Nov 27 13:51:57 2012 +0100
@@ -33,6 +33,7 @@
     </div>
 </div><!-- wrap-header-top -->
 
+{% block wrap_header %}
 <div class="wrap-header">
     <div class="header">
     
@@ -109,7 +110,7 @@
             <span>Le Mashup<br />Cliquable</span>
         </a></h1>
         <div class="title-header">
-            <h2><a href="#">Créer un hashcut !</a></h2>
+            <h2><a href="{% url mashup_edit branding=branding %}">Créer un hashcut !</a></h2>
         </div>
         <div class="profil-wrap">
            
@@ -117,15 +118,18 @@
         </div>
     </div><!-- header -->
 </div><!-- wrap-header -->
-   
+{% endblock %}
+
+{% block wrapper %}
 <div class="wrapper">
     <div class="wrap">
+        {% block content %}
         <div class="content">
             <div class="left-content">
                 <h2>Hashcuts à la Une</h2>
                 <div class="hashcut-video-wrap news">
                     <div class="video-item">
-                        <a href="#" class="screenshot">{% thumbnail p1.image "302x202" crop="center" format="PNG" as im %}<img src="{{ im.url }}" width="{{ im.width }}" height="{{ im.height }}" alt=""/>{% empty %}&nbsp;{% endthumbnail %}</a>
+                        <a href="" class="screenshot">{% thumbnail p1.image "302x202" crop="center" format="PNG" as im %}<img src="{{ im.url }}" width="{{ im.width }}" height="{{ im.height }}" alt=""/>{% empty %}&nbsp;{% endthumbnail %}</a>
                         <h3><strong>{{ p1.title }}</strong> par <a href="#">{{ p1.created_by }}</a> | <span>02:24</span> </h3>
                     </div>
                     <div class="video-item">
@@ -183,7 +187,8 @@
                 
             </div><!-- left-content -->
         </div><!-- content -->
-
+        {% endblock %}
+        {% block footer %}
         <div class="footer clearfix">
             <ul class="links-left">
                 <li><a href="#">Mentions légales</a></li>
@@ -195,8 +200,10 @@
                 <li>© 2012</li>
             </ul>
         </div><!-- footer -->
+        {% endblock %}
     </div><!-- wrap -->
 </div><!-- wrapper -->
+{% endblock %}
 <!-- JavaScript -->
 {% block js_import %}
 <script type="text/javascript" src="{% static 'hashcut/lib/jquery.min.js' %}"></script>
--- a/src/hashcut/templates/iri_mashup_home.html	Tue Nov 27 12:32:30 2012 +0100
+++ b/src/hashcut/templates/iri_mashup_home.html	Tue Nov 27 13:51:57 2012 +0100
@@ -29,7 +29,7 @@
 </head>
 <body>
 {% block body %}
-<p>TEMPLATE PAR DEFAUT IRI. Ta variable coucou = {{coucou}}</p>
+<p>TEMPLATE PAR DEFAUT IRI. Ta variable branding = {{branding}}</p>
 {% endblock %}
 {% analytics %}
 </body>
--- a/src/hashcut/urls.py	Tue Nov 27 12:32:30 2012 +0100
+++ b/src/hashcut/urls.py	Tue Nov 27 13:51:57 2012 +0100
@@ -1,7 +1,8 @@
 from django.conf.urls.defaults import patterns, url
-from hashcut.views import MashupHome
+from hashcut.views import MashupHome, MashupEdit
 
 urlpatterns = patterns('',
+    url(r'^(?P<branding>.*)/edit/$', MashupEdit.as_view(), name="mashup_edit"),
+    url(r'^(?P<branding>.*)/$', MashupHome.as_view(), name="mashup_home"),
     url(r'^$', MashupHome.as_view()),
-    url(r'^(?P<branding>.*)/$', MashupHome.as_view()),
 )
--- a/src/hashcut/views.py	Tue Nov 27 12:32:30 2012 +0100
+++ b/src/hashcut/views.py	Tue Nov 27 13:51:57 2012 +0100
@@ -1,12 +1,14 @@
 from django.core.exceptions import ImproperlyConfigured
+from django.http import HttpResponseNotFound
 from django.views.generic.base import View, TemplateResponseMixin
 from ldt.ldt_utils.models import Project
+import logging
 
 
 class MashupHome(TemplateResponseMixin, View):
     
     # iri = default brand name
-    brand_name = "iri"
+    branding = "iri"
     template_suffix = "mashup_home.html"
     template_name = "iri_mashup_home.html"
     
@@ -21,19 +23,58 @@
             raise ImproperlyConfigured("Class MashupHome requires either a definition of 'template_name'")
 
         # the branding template is supposed to override the default template. So we insert instead of append
-        if self.brand_name and self.brand_name != "":
-            #names.insert(0,"%s_%s" % (self.brand_name, self.template_name))
-            names.insert(0,"%s_%s" % (self.brand_name, self.template_suffix))
+        if self.branding and self.branding != "":
+            #names.insert(0,"%s_%s" % (self.branding, self.template_name))
+            names.insert(0,"%s_%s" % (self.branding, self.template_suffix))
         
         return names
     
     
     def get(self, request, branding=None, **kwargs):
         if branding and branding!="":
-            self.brand_name = branding
+            self.branding = branding
         projects = Project.safe_objects.all()[:10]
         p1 = projects[0]
         p2 = projects[1]
         projects = projects[2:]
-        context = {"coucou":self.brand_name, "projects":projects, "p1":p1, "p2":p2}
-        return self.render_to_response(context)
\ No newline at end of file
+        context = {"branding":self.branding, "projects":projects, "p1":p1, "p2":p2}
+        return self.render_to_response(context)
+    
+
+
+class MashupEdit(TemplateResponseMixin, View):
+    
+    # iri = default brand name
+    branding = "iri"
+    template_suffix = "mashup_edit.html"
+    template_name = "iri_mashup_edit.html"
+    
+    def get_template_names(self):
+        """
+        Return a list of template names to be used for the request. Must return
+        a list. May not be called if get_template is overridden.
+        """
+        try:
+            names = super(MashupEdit, self).get_template_names()
+        except ImproperlyConfigured:
+            raise ImproperlyConfigured("Class MashupEdit requires either a definition of 'template_name'")
+
+        # the branding template is supposed to override the default template. So we insert instead of append
+        if self.branding and self.branding != "":
+            #names.insert(0,"%s_%s" % (self.branding, self.template_name))
+            names.insert(0,"%s_%s" % (self.branding, self.template_suffix))
+        
+        return names
+    
+    def get(self, request, branding="iri", **kwargs):
+        logging.debug("COUCOU")
+        self.branding = branding
+        context = {"branding":self.branding}
+        return self.render_to_response(context)
+    
+    
+    
+    
+    
+    
+    
\ No newline at end of file