integration/js/editor.js
author veltr
Thu, 08 Nov 2012 18:24:47 +0100
changeset 41 3ec2343f2b85
parent 39 d3d8a88878ed
child 42 40909e8d6855
permissions -rw-r--r--
Refactoring to have common code between editor and player


IriSP.hc_messages = {
    Duration_ : "Durée :",
    duration_ : "durée :",
    edit_segment: "Éditer le segment",
    segment_down: "Descendre le segment",
    segment_up: "Remonter le segment",
    delete_segment: "Supprimer le segment",
    clone_segment: "Cloner le segment",
    From_: "De :",
    to_: "à :",
    segment_title_placeholder: "Segment sans titre",
    mashup_title_placeholder: "Hashcut sans titre"
}

IriSP.editor = function(options) {
    
    /* Load Media List */
    
    var directory = new IriSP.Model.Directory(),
        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 %>"><img src="<%= thumbnail %>" alt="<%= title %>" />'
            + '<span class="video-info"><span class="title-video"><%= title %></span><span class="author"><%= description %></span>'
            + '<span class="time-length"><%= IriSP.hc_messages.Duration_ %> <span><%= duration.toString() %></span></span></span></li>'),
        segmenttemplate = _.template('<li class="item-video annotation" data-segment-id="<%= annotation.id %>" data-media-id="<%= annotation.getMedia().id %>">'
            + '<img src="<%= annotation.getMedia().thumbnail %>" alt="<%= annotation.getMedia().title %>" />'
            + '<div class="validate <%= annotation.status %>"><div class="validate-tooltip"><ul><li><%= annotation.status_messages.join("</li><li>") %></li></ul></div></div><span class="video-info"><span class="title-video"><%= annotation.getMedia().title %></span>'
            + '<span class="subtitle"><%= annotation.title %></span><span class="duration"><%= annotation.begin.toString() %> - <%= annotation.end.toString() %> (<%= annotation.getDuration().toString() %>)</span>'
            + '<ul class="tools"><li><a class="edit" href="#" title="<%= IriSP.hc_messages.edit_segment %>"></a></li><li><a class="bottom" href="#" title="<%= IriSP.hc_messages.segment_down %>"></a></li>'
            + '<li><a class="top" href="#" title="<%= IriSP.hc_messages.segment_up %>"></a></li><li><a class="delete" href="#" title="<%= IriSP.hc_messages.delete_segment %>"></a></li></ul></span></li>'),
        mediasegmenttemplate = _.template('<div class="media-segments-list"><div class="media-segment">'
            + '<div class="media-section media-segment-section" style="left:<%= left %>px; width:<%= width %>px; background:<%= annotation.color %>"></div>'
            + '<div class="media-section media-current-section" style="left:<%= currentleft %>px; width:<%= currentwidth %>px;"><div class="media-current-section-inner"></div></div>'
            + '<div class="popin media-segment-popin" style="left:<%= popleft %>px"><img style="left:<%= pointerpos %>px;" class="pointer" src="img/popin-triangle.png" alt="" /><div class="popin-content">'
            + '<h3><%= annotation.title %></h3><a href="#" class="button reprendre-segment" data-segment-id="<%= annotation.id %>"><%= IriSP.hc_messages.clone_segment %></a>'
            + '<p><%= IriSP.hc_messages.From_ %> <span><%= annotation.begin.toString() %></span> <%= IriSP.hc_messages.to_ %> <span><%= annotation.end.toString() %></span> (<%= IriSP.hc_messages.duration_ %> <span><%= annotation.getDuration().toString() %></span>)</p>'
            + '</div></div></div></div>'),
        addMode, currentMedia, currentSegment;
    
    IriSP.mashupcore(project, mashup);
    
    /* Validation of segments and mashup */
    
    var segmentcritical = [
        {
            validate: function(_s) {
                return (_s.getDuration() >= 1000);
            },
            message: "Le segment doit durer au moins une seconde"
        },
        {
            validate: function(_s) {
                return (_s.getDuration() < 180000);
            },
            message: "Le segment doit durer moins de trois minutes"
        },
        {
            validate: function(_s) {
                return (!!_s.title && _s.title !== IriSP.hc_messages.segment_title_placeholder);
            },
            message: "Le segment doit avoir un titre"
        }
    ];
    var segmentwarning = [
        {
            validate: function(_s) {
                return (!!_s.description);
            },
            message: "Il est recommandé de donner une description au segment"
        },
        {
            validate: function(_s) {
                return (!!_s.keywords.length);
            },
            message: "Il est recommandé de tagguer le segment"
        }
    ];
    
    var mashupcritical = [
        {
            validate: function(_m) {
                return _m.segments.length > 2;
            },
            message: "Un hashcut doit être composé d'au moins trois segments"
        },
        {
            validate: function(_m) {
                return (!!_m.title && _m.title !== IriSP.hc_messages.mashup_title_placeholder);
            },
            message: "Un titre doit être donné au hashcut"
        }
    ];
    var mashupwarning = [
        {
            validate: function(_m) {
                return !!_m.description
            },
            message: "Il est recommandé de donner une description au hashcut"
        }
    ];

    /* 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);
    });
    
    /* 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),
                title = $(this).find(".title-video"),
                titletext = title.text();
            if (val && find.test(titletext)) {
                title.html(titletext.replace(replace, '<span style="background: yellow;">$1</span>'));
                li.show();
            } else {
                title.text(titletext);
                if (val) {
                    li.hide();
                } else {
                    li.show();
                }
            }
        })
    });
    
    /* 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("Certains segments ne sont pas valides");
        }
        
        _(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("Le hashcut est valide !");
        }
        $(".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("Le segment est valide !")
            }
            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(0);
                currentSegment.setEnd(currentMedia.duration);
                currentSegment.title = IriSP.hc_messages.segment_title_placeholder;
                currentSegment.color = currentMedia.color;
                currentSegment.keywords = [];
                currentSegment.description = "";
                currentSegment.on("change-begin", function() {
                    if (currentMedia && currentSegment === this) {
                        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 ? "Ajouter au Hashcut" : "Sauvegarder");
            $(".create-or-edit").text(addMode ? "Créer un nouveau segment" : "Modifier le 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;
            });
            var html = "";
            if (relatedSegments.length) {
                var k = $(".Ldt-Slider").width() / currentSegment.getMedia().duration,
                    currentleft = k * currentSegment.begin,
                    currentwidth = k * currentSegment.getDuration();
                relatedSegments.forEach(function(_s) {
                    var pos = k * (_s.annotation.begin + _s.annotation.end) / 2,
                        corrpos = Math.max(145, Math.min(305, pos));
                    vizdata = {
                        annotation : _s.annotation,
                        currentleft : currentleft,
                        currentwidth : currentwidth,
                        popleft : corrpos,
                        left : k * _s.annotation.begin,
                        width : k * _s.annotation.getDuration(),
                        pointerpos : (pos - corrpos)
                    }
                    html += mediasegmenttemplate(vizdata);
                });
                $(".self-media-segments").show();
            } else {
                $(".self-media-segments").hide();
            }
            
            //TODO: Show Related Segments from http://capsicum/pf/ldtplatform/api/ldt/1.0/segments/bytimecode/f72aa2f4-29bb-11e2-a193-08002791f1b7/0/674000?format=json
            
        }
        $(".self-media-segments .media-segments-list").html(html);
        if (currentMedia.elementType === "mashup") {
            showPreview();
        }
    }
        
    /* Segment Form interaction */
   
    $("#segment-title").on("keyup change input paste", function() {
        if (currentMedia && currentSegment) {
            currentSegment.title = $(this).val();
            updateSegmentUI();
            mashup.trigger("change");
        }
    });
    $("#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);
        } 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(annotation) {
        $(".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 (mashupCurrentAnnotation) {
            currentSegment = mashupCurrentAnnotation.annotation;
            setMedia(mashupCurrentAnnotation.getMedia());
        }
    });
    
    /* Handling related segments */
   
    $(".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() {
        var s = project.getElement($(this).attr("data-segment-id"));
        currentSegment.title = s.title;
        currentSegment.description = s.description;
        $("#segment-title").val(s.title);
        $("#segment-description").val(s.description);
        currentSegment.setBegin(s.begin);
        currentSegment.setEnd(s.end);
        return false;
    });
    
    /* Changing Hashcut Title and description */
    
    $("#hashcut-tags").tagit({
        onTagRemoved: updateSegmentTags,
        onTagAdded: updateSegmentTags
    });
    
    mashup.title = IriSP.hc_messages.mashup_title_placeholder;
    $(".title-video-wrap a").text(mashup.title);
    $("#hashcut-title").val(mashup.title);
    
    $("#hashcut-title").on("keyup change input paste", function() {
        mashup.title = $(this).val();
        $(".title-video-wrap a").text(mashup.title);
        mashup.trigger("change");
    });
    
    $("#hashcut-description").on("keyup change input paste", function() {
        mashup.description = $(this).val();
        mashup.trigger("change");
    });
    
    function updateMashupTags() {
        window.setTimeout(function() {
            mashup.keywords = $("#segment-tags").tagit("assignedTags");
        }, 0);
    }
    
    mashup.trigger("change");
}