integration/js/editor.js
author veltr
Tue, 01 Oct 2013 16:16:07 +0200
changeset 154 60ca7678f074
parent 144 57f1d252b8f9
permissions -rw-r--r--
Updated metadataplayer files

IriSP.editor = function(options) {
    
    window.shortenText = function(text, minlength, maxlength) {
        var min = (typeof minlength === "undefined" ? 100 : minlength),
            max = (typeof maxlength === "undefined" ? min + 20 : maxlength),
            rx = new RegExp("(^.{" + min + "," + max + "})[\s].+$");
        return text.replace(/[\n\r\s]+/mg,' ').replace(rx,'$1…');
    };
    
    /* 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
        }),
        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"><%= shortenText(description) %></p>'
            + '<p class="time-length"><%= gettext("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="segment-title"><%= annotation.title %></h3>'
            + '<p class="title-video"><%= annotation.getMedia().title %></p><p class="duration"><%= annotation.begin.toString() %> - <%= annotation.end.toString() %> (<%= annotation.getDuration().toString() %>)</p>'
            + '<ul class="tools"><li><a class="edit" href="#" title="<%= gettext("Edit segment") %>"></a></li><li><a class="bottom" href="#" title="<%= gettext("Move segment down") %>"></a></li>'
            + '<li><a class="top" href="#" title="<%= gettext("Move segment up") %>"></a></li><li><a class="delete" href="#" title="<%= gettext("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;"><div style="left:<%= pointerpos %>px;" class="pointer"></div><div class="popin-content">'
            + '<h3 class="segment-title"><%= annotation.title %></h3>'
            + '<p><%= shortenText(annotation.description) %></p><% if (annotation.keywords.length) { print("<p><strong>" + gettext("Tags:") + "</strong> " + annotation.keywords.join(", ") + "</p>"); } %>'
            + '<a href="#" class="button reprendre-segment" data-segment-id="<%= annotation.id %>"><%= gettext("Clone segment") %></a>'
            + '<p><%= gettext("From:") %> <span><%= annotation.begin.toString() %></span> <%= gettext("to:") %> <span><%= annotation.end.toString() %></span> (<%= gettext("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: <%= top-5 %>px;"><div class="popin-content">'
            + '<h3 class="segment-title"><%= title %></h3>'
            + '<p><%= description %></p><% if (tags) { print("<p><strong>" + gettext("Tags:") + "</strong> " + tags + "</p>"); } %>'
            + '<a href="#" class="button clone-segment" data-segment-id="<%= annotation.id %>"><%= gettext("Clone segment") %></a>'
            + '</div><div style="left:<%= pointerpos %>px;" class="pointer"></div></div></div>'
        ),
        mediafoundlisttemplate = _.template(
            '<div class="media-found-list" style="height: <%= height %>px"><div class="media-segment-list-inner"></div><%= segments %></div>'
        ),
        mashupstatus = '',
        mediasegmentscache = {},
        mashupModeAfterSave = false,
        addMode = false,
        currentMedia, currentSegment;
        
    IriSP.mashupcore(project, mashup);
    
    /* Validation of segments and mashup */
    
    var segmentcritical = [
        {
            validate: function(_s) {
                return (_s.getDuration() >= 1000);
            },
            message: gettext("A segment must be at least one second long")
        },
        {
            validate: function(_s) {
                return (!!_s.title && _s.title !== gettext("Untitled segment"));
            },
            message: gettext("A segment must have a title")
        }
    ];
    var segmentwarning = [
        {
            validate: function(_s) {
                return (!!_s.description);
            },
            message: gettext("A segment should have a description")
        },
        {
            validate: function(_s) {
                return (!!_s.keywords.length);
            },
            message: gettext("A segment should have tags")
        }
    ];
    
    var mashupcritical = [
        {
            validate: function(_m) {
                return _m.segments.length > 2;
            },
            message: gettext("A hashcut must be made from at least three segments")
        },
        {
            validate: function(_m) {
                return (!!_m.title && _m.title !== gettext("Untitled Hashcut"));
            },
            message: gettext("A hashcut must have a title")
        }
    ];
    var mashupwarning = [
        {
            validate: function(_m) {
                return !!_m.description;
            },
            message: gettext("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().filter(function(annotation) {
                        return annotation.getDuration() > 0;
                    });;
                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.length < 2) {
            val = false;
        }
        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(shortenText(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(shortenText(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>'),
                            description: shortenText(_a.description).replace(replace, '<span style="background: #fc00ff; color: #ffffff;">$1</span>'),
                            tags: _a.keywords.join(", ").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(gettext("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(gettext("Your hashcut is valid!"));
        }
        mashupstatus = ' - ' + _(messages).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(gettext("This segment is valid!"));
            }
            currentSegment.status_messages = _(messages);
            
            $(".segmentation .validate").removeClass("critical warning valid").addClass(currentSegment.status);
            $(".segmentation .validate-tooltip").html("<ul><li>" + currentSegment.status_messages.join("</li><li>")+"</li></ul>");
        }
    }
    
    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 = gettext("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(addMode ? gettext("Add segment to hashcut") : gettext("Save segment"));
            $(".create-or-edit").text(addMode ? gettext("Create new segment") : gettext("Edit existing segment"));
            $("#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() === gettext("Untitled segment")) {
            $(this).val("");
        }
    });
    $("#segment-description").on("keyup change input paste", function() {
        if (currentMedia && currentSegment) {
            currentSegment.description = $(this).val();
            updateSegmentUI();
            mashup.trigger("change");
        }
    });
    $("#segment-form").submit(function() {
        currentSegment.title = $("#segment-title").val();
        currentSegment.description = $("#segment-description").val();
        currentSegment.keywords = $("#segment-tags").tagit("assignedTags");
        updateSegmentUI();
        if (addMode) {
            mashup.addAnnotation(currentSegment);
            currentSegment = undefined;
            setMedia(currentMedia);
        } else {
            mashup.trigger("change");
            var segment = mashup.getAnnotation(currentSegment);
            currentSegment = undefined;
            if (mashupModeAfterSave) {
                setMedia(mashup);
                if (segment) {
                    mashup.setCurrentTime(segment.begin);
                    mashup.trigger("enter-annotation",segment);
                }
            } else {
                setMedia(currentMedia);
            }
        }
        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");
                updateSegmentUI();
                mashup.trigger("change");
            }
        }, 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() {
        var segment = project.getElement($(this).attr("data-segment-id"));
        if (currentMedia === mashup) {
            project.trigger("click-annotation", segment);
        } else {
            currentSegment = segment;
            setMedia(segment.getMedia());
        }
    })
    .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"));
        mashupModeAfterSave = !!(currentMedia === mashup);
        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;
    });
    
    $(".mashup-frise").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) {
            if (currentMedia === mashup) {
                project.trigger("click-annotation", segment.annotation);
            } else {
                currentSegment = segment.annotation;
                setMedia(currentSegment.getMedia());
            }
        }
    });
    
    /* 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) {
            mashupModeAfterSave = !!(currentMedia === mashup);
            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 = gettext("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 = gettext("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() === gettext("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 gettext("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(gettext("The hashcut 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 = options.creator;
        postproject.created = new Date();
        postproject.modified = new Date();
        postproject.title = mashup.title;
        postproject.description = mashup.description;
        var waitscreen = $('<div class="full-wait">');
        waitscreen.appendTo('body');
        $.ajax({
            type: "POST",
            url: IriSP.endpoints.project,
            data: IriSP.serializers.ldt.serialize(postproject),
            contentType: "application/cinelab",
            headers: {
                "X-CSRFToken": options.csrf_token
            },
            success: function(data, status, request){
                var location = request.getResponseHeader("Location"),
                    projid = location.match(/([^/]+)\/?$/)[1],
                    destination = IriSP.endpoints.hashcut_page.replace('__PROJECT_ID__', projid);
                $(window).off("beforeunload", onLeave);
                document.location.href = destination;
            },
            error: function(jqXHR, textStatus, errorThrown){
                alert(gettext("Server error\nYour hashcut couldn't be published"));
                waitscreen.remove();
            }
        });
        
        return false;
    });
    
    mashup.trigger("change");
};

/* END editor.js */