integration/js/editor.js
changeset 41 3ec2343f2b85
parent 39 d3d8a88878ed
child 42 40909e8d6855
equal deleted inserted replaced
40:76efa2333f31 41:3ec2343f2b85
    11     to_: "à :",
    11     to_: "à :",
    12     segment_title_placeholder: "Segment sans titre",
    12     segment_title_placeholder: "Segment sans titre",
    13     mashup_title_placeholder: "Hashcut sans titre"
    13     mashup_title_placeholder: "Hashcut sans titre"
    14 }
    14 }
    15 
    15 
    16 IriSP.Hashcut = function(options) {
    16 IriSP.editor = function(options) {
    17     
       
    18     
    17     
    19     /* Load Media List */
    18     /* Load Media List */
    20     
    19     
    21     var directory = new IriSP.Model.Directory(),
    20     var directory = new IriSP.Model.Directory(),
    22         project = directory.remoteSource({
    21         project = directory.remoteSource({
    23             url: options.url,
    22             url: options.url,
    24             serializer: IriSP.serializers.medialist
    23             serializer: IriSP.serializers.medialist
    25         }),
    24         }),
    26         mashup = new IriSP.Model.Mashup(false, project),
    25         mashup = new IriSP.Model.Mashup(false, project),
    27         mediatemplate = _.template('<li class="item-video" data-media-id="<%= id %>"><img src="<%= thumbnail %>" alt="<%= title %>" />'
    26         mediatemplate = _.template('<li class="item-video media" data-media-id="<%= id %>"><img src="<%= thumbnail %>" alt="<%= title %>" />'
    28             + '<span class="video-info"><span class="title-video"><%= title %></span><span class="author"><%= description %></span>'
    27             + '<span class="video-info"><span class="title-video"><%= title %></span><span class="author"><%= description %></span>'
    29             + '<span class="time-length"><%= IriSP.hc_messages.Duration_ %> <span><%= duration.toString() %></span></span></span></li>'),
    28             + '<span class="time-length"><%= IriSP.hc_messages.Duration_ %> <span><%= duration.toString() %></span></span></span></li>'),
    30         segmenttemplate = _.template('<li class="item-video" data-segment-id="<%= annotation.id %>" data-media-id="<%= annotation.getMedia().id %>">'
    29         segmenttemplate = _.template('<li class="item-video annotation" data-segment-id="<%= annotation.id %>" data-media-id="<%= annotation.getMedia().id %>">'
    31             + '<img src="<%= annotation.getMedia().thumbnail %>" alt="<%= annotation.getMedia().title %>" />'
    30             + '<img src="<%= annotation.getMedia().thumbnail %>" alt="<%= annotation.getMedia().title %>" />'
    32             + '<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>'
    31             + '<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>'
    33             + '<span class="subtitle"><%= annotation.title %></span><span class="duration"><%= annotation.begin.toString() %> - <%= annotation.end.toString() %> (<%= annotation.getDuration().toString() %>)</span>'
    32             + '<span class="subtitle"><%= annotation.title %></span><span class="duration"><%= annotation.begin.toString() %> - <%= annotation.end.toString() %> (<%= annotation.getDuration().toString() %>)</span>'
    34             + '<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>'
    33             + '<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>'
    35             + '<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>'),
    34             + '<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>'),
    36         viztemplate = _.template('<div class="frise-segment" data-segment-id="<%= segmentid %>" style="background-color:<%= color %>; left:<%= left %>%; width:<%= width %>%;"></div>'),
       
    37         intervaltemplate = _.template('<span class="frise-indication" style="left:<%= left %>%;"><%= time.toString() %></span>'),
       
    38         mediasegmenttemplate = _.template('<div class="media-segments-list"><div class="media-segment">'
    35         mediasegmenttemplate = _.template('<div class="media-segments-list"><div class="media-segment">'
    39             + '<div class="media-section media-segment-section" style="left:<%= left %>px; width:<%= width %>px; background:<%= annotation.getMedia().color %>"></div>'
    36             + '<div class="media-section media-segment-section" style="left:<%= left %>px; width:<%= width %>px; background:<%= annotation.color %>"></div>'
    40             + '<div class="media-section media-current-section" style="left:<%= currentleft %>px; width:<%= currentwidth %>px;"><div class="media-current-section-inner"></div></div>'
    37             + '<div class="media-section media-current-section" style="left:<%= currentleft %>px; width:<%= currentwidth %>px;"><div class="media-current-section-inner"></div></div>'
    41             + '<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">'
    38             + '<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">'
    42             + '<h3><%= annotation.title %></h3><a href="#" class="button reprendre-segment" data-segment-id="<%= annotation.id %>"><%= IriSP.hc_messages.clone_segment %></a>'
    39             + '<h3><%= annotation.title %></h3><a href="#" class="button reprendre-segment" data-segment-id="<%= annotation.id %>"><%= IriSP.hc_messages.clone_segment %></a>'
    43             + '<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>'
    40             + '<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>'
    44             + '</div></div></div></div>');
    41             + '</div></div></div></div>'),
       
    42         addMode, currentMedia, currentSegment;
       
    43     
       
    44     IriSP.mashupcore(project, mashup);
    45     
    45     
    46     /* Validation of segments and mashup */
    46     /* Validation of segments and mashup */
    47     
    47     
    48     var segmentcritical = [
    48     var segmentcritical = [
    49         {
    49         {
    50             validate: function(_s) {
    50             validate: function(_s) {
    51                 var _d = _s.getDuration();
    51                 return (_s.getDuration() >= 1000);
    52                 return (_d > 1000 && _d < 180000);
    52             },
    53             },
    53             message: "Le segment doit durer au moins une seconde"
    54             message: "La durée du segment doit être comprise entre 1 seconde et trois minutes"
    54         },
       
    55         {
       
    56             validate: function(_s) {
       
    57                 return (_s.getDuration() < 180000);
       
    58             },
       
    59             message: "Le segment doit durer moins de trois minutes"
    55         },
    60         },
    56         {
    61         {
    57             validate: function(_s) {
    62             validate: function(_s) {
    58                 return (!!_s.title && _s.title !== IriSP.hc_messages.segment_title_placeholder);
    63                 return (!!_s.title && _s.title !== IriSP.hc_messages.segment_title_placeholder);
    59             },
    64             },
    60             message: "Un titre doit être donné au segment"
    65             message: "Le segment doit avoir un titre"
    61         }
    66         }
    62     ];
    67     ];
    63     var segmentwarning = [
    68     var segmentwarning = [
    64         {
    69         {
    65             validate: function(_s) {
    70             validate: function(_s) {
    66                 return (_s.description);
    71                 return (!!_s.description);
    67             },
    72             },
    68             message: "Il est recommandé de donner une description au segment"
    73             message: "Il est recommandé de donner une description au segment"
       
    74         },
       
    75         {
       
    76             validate: function(_s) {
       
    77                 return (!!_s.keywords.length);
       
    78             },
       
    79             message: "Il est recommandé de tagguer le segment"
    69         }
    80         }
    70     ];
    81     ];
    71     
    82     
    72     var mashupcritical = [
    83     var mashupcritical = [
    73         {
    84         {
   127             }
   138             }
   128         })
   139         })
   129     });
   140     });
   130     
   141     
   131     /* Fill right column when mashup is updated */
   142     /* Fill right column when mashup is updated */
   132    
       
   133     function setPointerToCurrentAnnotation() {
       
   134         if (mashupCurrentAnnotation) {
       
   135             var p = (mashupCurrentAnnotation.begin + mashupCurrentAnnotation.end) / (2 * mashup.duration);
       
   136             $(".mashup-description .pointer").css("left", (100 * p) + "%");
       
   137         }
       
   138     }
       
   139     
   143     
   140     function updateMashupUI() {
   144     function updateMashupUI() {
   141         var listhtml = '', vizhtml = '', t = 0, k = mashup.duration ? (100 / mashup.duration) : 0;
   145         var listhtml = '', critical = false, warning = false, messages = [];
   142         var critical = false, warning = false, messages = [];
       
   143         mashup.segments.forEach(function(_s) {
   146         mashup.segments.forEach(function(_s) {
   144             listhtml += segmenttemplate(_s);
   147             listhtml += segmenttemplate(_s);
   145             var vizdata = {
       
   146                 left: k * t,
       
   147                 width: k * _s.duration,
       
   148                 color: _s.getMedia().color,
       
   149                 segmentid: _s.annotation.id
       
   150             }
       
   151             vizhtml += viztemplate(vizdata);
       
   152             if (_s.annotation.status === "critical") {
   148             if (_s.annotation.status === "critical") {
   153                 critical = true;
   149                 critical = true;
   154             }
   150             }
   155             t += _s.duration.milliseconds;
       
   156         });
   151         });
   157         if (critical) {
   152         if (critical) {
   158             messages.push("Certains segments ne sont pas valides");
   153             messages.push("Certains segments ne sont pas valides");
   159         }
   154         }
   160         
   155         
   177         $(".publier-button").toggleClass("disable", critical);
   172         $(".publier-button").toggleClass("disable", critical);
   178         
   173         
   179         $(".liste-segment .validate").removeClass("critical warning valid").addClass(mashup.status);
   174         $(".liste-segment .validate").removeClass("critical warning valid").addClass(mashup.status);
   180         $(".liste-segment .validate-tooltip").html("<ul><li>" + messages.join("</li><li>")+"</li></ul>");
   175         $(".liste-segment .validate-tooltip").html("<ul><li>" + messages.join("</li><li>")+"</li></ul>");
   181         
   176         
   182         var intervals = [ 1000, 2000, 5000, 10000, 30000, 60000, 120000, 300000, 600000, 900000, 1800000, 3600000, 7200000 ];
   177         $(".col-right .list-video").html(listhtml).find(".item-video:last-child .bottom, .item-video:first-child .top").addClass("disable");
   183         
   178         
   184         function createIntervals(maxn) {
   179         project.trigger("mouseout-annotation");
   185             for (var i = 0; i < intervals.length; i++) {
   180     }
   186                 if (mashup.duration / intervals[i] <= maxn) {
   181     
   187                     var html = '';
   182     mashup.on("setcurrent", function() {
   188                     for (var j = intervals[i]; j < mashup.duration; j += intervals[i]) {
   183         currentMedia = mashup;
   189                         html += intervaltemplate({ left: k * j, time: new IriSP.Model.Time(j) });
   184     });
   190                     }
       
   191                     return html;
       
   192                 }
       
   193             }
       
   194             return "";
       
   195         }
       
   196         
       
   197         $(".col-right .list-video").html(listhtml).find(".item-video:last-child .bottom, .item-video:first-child .top").addClass("disable");
       
   198         $(".mashup-total-duration").text(mashup.duration.toString());
       
   199         $(".frise-segments").html(vizhtml);
       
   200         $(".col-right .frise-indications").html(createIntervals(4));
       
   201         $(".bloc-pvw .frise-indications").html(createIntervals(8));
       
   202         highlightCurrentSegment();
       
   203         if (currentMedia === mashup) {
       
   204             $(".Ldt-Ctrl-Time-Total").text(currentMedia.duration.toString());
       
   205             if (mashupTimecode > mashup.duration) {
       
   206                 mashup.setCurrentTime(mashup.duration);
       
   207             }
       
   208             changeCurrentAnnotation();
       
   209             setPointerToCurrentAnnotation();
       
   210         }
       
   211     }
       
   212     
   185     
   213     mashup.on("change",updateMashupUI);
   186     mashup.on("change",updateMashupUI);
   214     
       
   215     /* Slider */
       
   216    
       
   217     var timeSlider = $(".Ldt-Slider"),
       
   218         timeSliderContainer = $(".Ldt-Slider-Container"),
       
   219         slidersRange = 920;
       
   220     timeSlider.slider({
       
   221         range: "min",
       
   222         value: 0,
       
   223         min: 0,
       
   224         max: slidersRange,
       
   225         slide: function(event, ui) {
       
   226             if (currentMedia) {
       
   227                 var t = currentMedia.duration * ui.value / slidersRange;
       
   228                 currentMedia.setCurrentTime(t);
       
   229             }
       
   230         }
       
   231     });
       
   232     
       
   233     var timeSliderHandle = timeSlider.find('.ui-slider-handle'),
       
   234         timeSliderMaximized = false,
       
   235         timeSliderTimeoutId = false,
       
   236         timeSliderMinimizedHeight = 4,
       
   237         timeSliderMaximizedHeight = 10,
       
   238         timeSliderTimeoutDuration = 1500,
       
   239         timeTooltip = $(".Ldt-Slider-Time");
       
   240     
       
   241     timeSliderContainer.css(calculateSliderCss(timeSliderMinimizedHeight));
       
   242     timeSliderHandle.css(calculateHandleCss(timeSliderMinimizedHeight));
       
   243     
       
   244     function timeSliderMouseOver() {
       
   245         if (timeSliderTimeoutId) {
       
   246             window.clearTimeout(timeSliderTimeoutId);
       
   247             timeSliderTimeoutId = false;
       
   248         }
       
   249         if (!timeSliderMaximized) {
       
   250            timeSliderAnimateToHeight(timeSliderMaximizedHeight);
       
   251            timeSliderMaximized = true;
       
   252         }
       
   253     }
       
   254     
       
   255     function timeSliderMouseOut() {
       
   256         timeTooltip.hide();
       
   257         if (timeSliderTimeoutId) {
       
   258             clearTimeout(timeSliderTimeoutId);
       
   259             timeSliderTimeoutId = false;
       
   260         }
       
   261         timeSliderTimeoutId = setTimeout(function() {
       
   262             if (timeSliderMaximized) {
       
   263                 timeSliderAnimateToHeight(timeSliderMinimizedHeight);
       
   264                 timeSliderMaximized = false;
       
   265             }
       
   266             timeSliderTimeoutId = false;
       
   267         }, timeSliderTimeoutDuration);
       
   268     }
       
   269     
       
   270     timeSliderContainer
       
   271         .mouseover(function() {
       
   272             timeTooltip.show();
       
   273             timeSliderMouseOver();
       
   274         })
       
   275         .mouseout(timeSliderMouseOut);
       
   276     timeSlider.mousemove(function(_e) {
       
   277             var _x = _e.pageX - timeSlider.offset().left,
       
   278                 _t = new IriSP.Model.Time(
       
   279                 );
       
   280             timeTooltip.text(_t.toString()).css("left",_x);
       
   281         });
       
   282     
       
   283     $(".Ldt-Ctrl").mouseover(timeSliderMouseOver).mouseout(timeSliderMouseOut);
       
   284     
       
   285     function timeSliderAnimateToHeight(_height) {
       
   286         timeSliderContainer.stop().animate(
       
   287             calculateSliderCss(_height),
       
   288             500,
       
   289             function() {
       
   290                 IriSP.jQuery(this).css("overflow","visible");
       
   291             });
       
   292         timeSliderHandle.stop().animate(
       
   293             calculateHandleCss(_height),
       
   294             500,
       
   295             function() {
       
   296                 IriSP.jQuery(this).css("overflow","visible");
       
   297             });
       
   298     }
       
   299 
       
   300     function calculateSliderCss(_size) {
       
   301         return {
       
   302             height: _size + "px",
       
   303             "margin-top": (timeSliderMinimizedHeight - _size) + "px"
       
   304         };
       
   305     }
       
   306 
       
   307     function calculateHandleCss(_size) {
       
   308         return {
       
   309             height: (2 + _size) + "px",
       
   310             width: (2 + _size) + "px",
       
   311             "margin-left": -Math.ceil(2 + _size / 2) + "px" 
       
   312         }
       
   313     }
       
   314     
       
   315     /* Controller Widget */
       
   316    
       
   317     var volBlock = $(".Ldt-Ctrl-Volume-Control");
       
   318     
       
   319     $('.Ldt-Ctrl-Sound')
       
   320         .click(function() {
       
   321             if (currentMedia) {
       
   322                 currentMedia.setMuted(!currentMedia.getMuted());
       
   323             }
       
   324         })
       
   325         .mouseover(function() {
       
   326             volBlock.show();
       
   327         })
       
   328         .mouseout(function() {
       
   329             volBlock.hide();
       
   330         });
       
   331     volBlock.mouseover(function() {
       
   332         volBlock.show();
       
   333     }).mouseout(function() {
       
   334         volBlock.hide();
       
   335     });
       
   336     
       
   337     var volBar = $(".Ldt-Ctrl-Volume-Bar");
       
   338     
       
   339     function ctrlVolumeUpdater() {
       
   340         if (currentMedia) {
       
   341             var _muted = currentMedia.getMuted(),
       
   342                 _vol = currentMedia.getVolume();
       
   343             if (_vol === false) {
       
   344                 _vol = .5;
       
   345             }
       
   346             var _soundCtl = $(".Ldt-Ctrl-Sound");
       
   347             _soundCtl.removeClass("Ldt-Ctrl-Sound-Mute Ldt-Ctrl-Sound-Half Ldt-Ctrl-Sound-Full");
       
   348             if (_muted) {        
       
   349                 _soundCtl.attr("title", "Activer le son")
       
   350                     .addClass("Ldt-Ctrl-Sound-Mute");    
       
   351             } else {
       
   352                 _soundCtl.attr("title", "Couper le son")
       
   353                     .addClass(_vol < .5 ? "Ldt-Ctrl-Sound-Half" : "Ldt-Ctrl-Sound-Full" )
       
   354             }
       
   355             volBar.slider("value", _muted ? 0 : 100 * _vol);
       
   356             volBar.attr("title",'Volume : ' + Math.floor(100 * _vol) + '%');
       
   357         }
       
   358     }
       
   359     
       
   360     volBar.slider({
       
   361         slide: function(event, ui) {
       
   362             if (currentMedia) {
       
   363                 currentMedia.setVolume(ui.value / 100);
       
   364             }
       
   365         }
       
   366     });
       
   367     
       
   368     $(".Ldt-Ctrl-Play").click(function() {
       
   369         if (currentMedia) {
       
   370             if (currentMedia.getPaused()) {        
       
   371                 currentMedia.play();
       
   372             } else {
       
   373                 currentMedia.pause();
       
   374             }
       
   375         }
       
   376     });
       
   377     
       
   378     $(".Ldt-Ctrl-SetIn").click(function() {
       
   379         if (currentMedia && currentSegment) {
       
   380             currentSegment.setBegin(currentMedia.getCurrentTime());
       
   381         }
       
   382     });
       
   383     $(".Ldt-Ctrl-SetOut").click(function() {
       
   384         if (currentMedia && currentSegment) {
       
   385             currentSegment.setEnd(currentMedia.getCurrentTime());
       
   386         }
       
   387     });
       
   388     
   187     
   389     /* Slice Widget */
   188     /* Slice Widget */
   390    
   189    
   391     var sliceSlider = $(".Ldt-Slice"),
   190     var sliceSlider = $(".Ldt-Slice"),
   392         sliceStartTime;
   191         sliceStartTime,
       
   192         slidersRange = 920;
   393     
   193     
   394     sliceSlider.slider({
   194     sliceSlider.slider({
   395         range: true,
   195         range: true,
   396         values: [0, slidersRange],
   196         values: [0, slidersRange],
   397         min: 0,
   197         min: 0,
   428             if (currentMedia && currentSegment) {
   228             if (currentMedia && currentSegment) {
   429                 currentMedia.setCurrentTime(currentSegment.end);
   229                 currentMedia.setCurrentTime(currentSegment.end);
   430             }
   230             }
   431         });
   231         });
   432     
   232     
   433     /* UI Events */
   233     
   434 
   234     /* Update Segment UI */
   435     function onCurrentMediaPlay() {
       
   436         $(".Ldt-Ctrl-Play")
       
   437             .attr("title", "Pause")
       
   438             .removeClass("Ldt-Ctrl-Play-PlayState")
       
   439             .addClass("Ldt-Ctrl-Play-PauseState")
       
   440     }
       
   441     
       
   442     function onCurrentMediaPause() {
       
   443         $(".Ldt-Ctrl-Play")
       
   444             .attr("title", "Lecture")
       
   445             .removeClass("Ldt-Ctrl-Play-PauseState")
       
   446             .addClass("Ldt-Ctrl-Play-PlayState")
       
   447     }
       
   448     
       
   449     function onCurrentMediaTimeupdate(_time) {
       
   450         $(".Ldt-Ctrl-Time-Elapsed").text(_time.toString());
       
   451         timeSlider.slider("value",slidersRange * _time / currentMedia.duration);
       
   452     }
       
   453     
       
   454     /* Mashup Player */
       
   455 
       
   456     var mashupCurrentMedia = null,
       
   457         mashupCurrentAnnotation = null,
       
   458         mashupSegmentBegin,
       
   459         mashupSegmentEnd,
       
   460         mashupTimecode = 0,
       
   461         mashupSeeking = false,
       
   462         seekdiv = $(".video-wait"),
       
   463         mashupTimedelta;
       
   464     
       
   465     function showSeek() {
       
   466         if (mashupSeeking) {
       
   467             seekdiv.show();
       
   468         }
       
   469     }
       
   470     
       
   471     function changeCurrentAnnotation() {
       
   472         if (mashupTimecode >= mashup.duration) {
       
   473             if (!mashup.paused) {
       
   474                 mashup.paused = true;
       
   475                 mashup.trigger("pause");
       
   476             }
       
   477             mashupTimecode = 0;
       
   478         }
       
   479         var _annotation = mashup.getAnnotationAtTime( mashupTimecode );
       
   480         if (typeof _annotation === "undefined") {
       
   481             if (mashupCurrentMedia) {
       
   482                 mashupCurrentMedia.pause();
       
   483                 if (!mashup.paused) {
       
   484                     mashup.paused = true;
       
   485                     mashup.trigger("pause");
       
   486                 }
       
   487             }
       
   488             return;
       
   489         }
       
   490         mashupCurrentAnnotation = _annotation;
       
   491         mashupSegmentBegin = mashupCurrentAnnotation.annotation.begin.milliseconds;
       
   492         mashupSegmentEnd = mashupCurrentAnnotation.annotation.end.milliseconds;
       
   493         mashupTimedelta = mashupSegmentBegin - mashupCurrentAnnotation.begin.milliseconds;
       
   494         mashupCurrentMedia = mashupCurrentAnnotation.getMedia();
       
   495         
       
   496         project.getMedias().forEach(function(_media) {
       
   497             if (_media !== mashupCurrentMedia) {
       
   498                 _media.hide();
       
   499                 _media.pause();
       
   500             } else {
       
   501                 _media.show();
       
   502             }
       
   503         });
       
   504         
       
   505         mashupCurrentMedia.setCurrentTime( mashupTimecode + mashupTimedelta);
       
   506         mashupCurrentMedia.seeking = true;
       
   507         
       
   508         if (!mashup.paused) {
       
   509             mashupCurrentMedia.play();
       
   510             mashupSeeking = true;
       
   511             setTimeout(showSeek,200);
       
   512         }
       
   513         mashup.trigger("timeupdate", new IriSP.Model.Time(mashupTimecode));
       
   514 
       
   515     }
       
   516     
       
   517     /* Set current Media */
       
   518    
       
   519     var currentMedia, currentSegment;
       
   520     
   235     
   521     function updateSegmentUI() {
   236     function updateSegmentUI() {
   522         if (currentMedia && currentSegment) {
   237         if (currentMedia && currentSegment) {
   523             var start = currentSegment.begin,
   238             var start = currentSegment.begin,
   524                 end = currentSegment.end,
   239                 end = currentSegment.end,
   562             $(".segmentation .validate").removeClass("critical warning valid").addClass(currentSegment.status);
   277             $(".segmentation .validate").removeClass("critical warning valid").addClass(currentSegment.status);
   563             $(".segmentation .validate-tooltip").html("<ul><li>" + currentSegment.status_messages.join("</li><li>")+"</li></ul>");
   278             $(".segmentation .validate-tooltip").html("<ul><li>" + currentSegment.status_messages.join("</li><li>")+"</li></ul>");
   564         }
   279         }
   565     }
   280     }
   566     
   281     
   567     var addMode;
       
   568     
       
   569     function setMedia(media) {
   282     function setMedia(media) {
   570         if (currentMedia) {
   283         if (currentMedia) {
   571             currentMedia.pause();
   284             currentMedia.pause();
   572         }
   285         }
   573         currentMedia = media;
   286         currentMedia = media;
       
   287         project.trigger("set-current", media);
   574         if (currentMedia.elementType == "media") {
   288         if (currentMedia.elementType == "media") {
   575             $("video").hide();
       
   576             showSegmentation();
   289             showSegmentation();
   577             if (!currentMedia.loaded) {
       
   578                 seekdiv.show();
       
   579             }
       
   580             var currentvideo = $('#video_' + currentMedia.id);
       
   581             if (!currentvideo.length) {
       
   582                 addMediaPlayer(currentMedia);
       
   583             }
       
   584             $(".tab-media-title").text(currentMedia.title);
   290             $(".tab-media-title").text(currentMedia.title);
   585             
   291             
   586             addMode = !(currentSegment && mashup.hasAnnotation(currentSegment));
   292             addMode = !(currentSegment && mashup.hasAnnotation(currentSegment));
   587             
   293             
   588             if (!currentSegment) {
   294             if (!currentSegment) {
   589                 currentSegment = new IriSP.Model.Annotation(false, project);
   295                 currentSegment = new IriSP.Model.Annotation(false, project);
   590                 currentSegment.setMedia(currentMedia.id);
   296                 currentSegment.setMedia(currentMedia.id);
   591                 currentSegment.setBegin(0);
   297                 currentSegment.setBegin(0);
   592                 currentSegment.setEnd(currentMedia.duration);
   298                 currentSegment.setEnd(currentMedia.duration);
   593                 currentSegment.title = IriSP.hc_messages.segment_title_placeholder;
   299                 currentSegment.title = IriSP.hc_messages.segment_title_placeholder;
       
   300                 currentSegment.color = currentMedia.color;
       
   301                 currentSegment.keywords = [];
   594                 currentSegment.description = "";
   302                 currentSegment.description = "";
   595                 currentSegment.on("change-begin", function() {
   303                 currentSegment.on("change-begin", function() {
   596                     if (currentMedia && currentSegment === this) {
   304                     if (currentMedia && currentSegment === this) {
   597                         currentMedia.setCurrentTime(this.begin);
   305                         currentMedia.setCurrentTime(this.begin);
   598                         updateSegmentUI();
   306                         updateSegmentUI();
   602                     if (currentMedia && currentSegment === this) {
   310                     if (currentMedia && currentSegment === this) {
   603                         currentMedia.setCurrentTime(this.end);
   311                         currentMedia.setCurrentTime(this.end);
   604                         updateSegmentUI();
   312                         updateSegmentUI();
   605                     }
   313                     }
   606                 });
   314                 });
   607                 currentSegment.on("enter", function() {
       
   608                     if (currentMedia === mashup) {
       
   609                         $(".annotation-title").text(this.title);
       
   610                         $(".annotation-begin").text(this.begin.toString());
       
   611                         $(".annotation-end").text(this.end.toString());
       
   612                         $(".annotation-media-title").text(this.getMedia().title);
       
   613                         $(".annotation-description").text(this.description);
       
   614                         setPointerToCurrentAnnotation();
       
   615                         highlightCurrentSegment();
       
   616                     }
       
   617                 });
       
   618             }
   315             }
   619             if (currentMedia.loaded) {
   316             if (currentMedia.loaded) {
   620                 currentMedia.setCurrentTime(currentSegment.begin);
   317                 currentMedia.setCurrentTime(currentSegment.begin);
   621             }
   318             }
   622             $(".add-segment").val(addMode ? "Ajouter au Hashcut" : "Sauvegarder");
   319             $(".add-segment").val(addMode ? "Ajouter au Hashcut" : "Sauvegarder");
   623             $(".create-or-edit").text(addMode ? "Créer un nouveau segment" : "Modifier le segment");
   320             $(".create-or-edit").text(addMode ? "Créer un nouveau segment" : "Modifier le segment");
   624             updateSegmentUI();
       
   625             media.show();
   321             media.show();
   626             $("#segment-title").val(currentSegment.title);
   322             $("#segment-title").val(currentSegment.title);
   627             $("#segment-description").val(currentSegment.description);
   323             $("#segment-description").val(currentSegment.description);
   628             $("#segment-tags").val("");
   324             var segment_tags = $("#segment-tags");
       
   325             segment_tags.tagit("option","onTagRemoved",function(){});
       
   326             segment_tags.tagit("option","onTagAdded",function(){});
       
   327             segment_tags.tagit("removeAll");
       
   328             _(currentSegment.keywords).each(function(tag) {
       
   329                 segment_tags.tagit("createTag",tag);
       
   330             });
       
   331             segment_tags.tagit("option","onTagRemoved",updateSegmentTags);
       
   332             segment_tags.tagit("option","onTagAdded",updateSegmentTags);
       
   333             updateSegmentUI();
   629             var relatedSegments = mashup.segments.filter(function(_s) {
   334             var relatedSegments = mashup.segments.filter(function(_s) {
   630                 return _s.getMedia() === currentMedia && _s.annotation !== currentSegment;
   335                 return _s.getMedia() === currentMedia && _s.annotation !== currentSegment;
   631             });
   336             });
   632             var html = "";
   337             var html = "";
   633             if (relatedSegments.length) {
   338             if (relatedSegments.length) {
   650                 });
   355                 });
   651                 $(".self-media-segments").show();
   356                 $(".self-media-segments").show();
   652             } else {
   357             } else {
   653                 $(".self-media-segments").hide();
   358                 $(".self-media-segments").hide();
   654             }
   359             }
       
   360             
       
   361             //TODO: Show Related Segments from http://capsicum/pf/ldtplatform/api/ldt/1.0/segments/bytimecode/f72aa2f4-29bb-11e2-a193-08002791f1b7/0/674000?format=json
       
   362             
   655         }
   363         }
   656         $(".self-media-segments .media-segments-list").html(html);
   364         $(".self-media-segments .media-segments-list").html(html);
   657         if (currentMedia.elementType === "mashup") {
   365         if (currentMedia.elementType === "mashup") {
   658             showPreview();
   366             showPreview();
   659             mashup.checkLoaded();
   367         }
   660         }
   368     }
   661         $(".Ldt-Ctrl-Time-Total").text(currentMedia.duration.toString());
       
   662         // TODO: Do something with the tags !
       
   663         onCurrentMediaTimeupdate(currentMedia.getCurrentTime());
       
   664         onCurrentMediaPause();
       
   665         highlightCurrentSegment();
       
   666     }
       
   667     
       
   668     function addMediaPlayer(media) {
       
   669         var videoid = "video_" + media.id,
       
   670             videoEl = $('<video>'),
       
   671             width = $(".video").width(),
       
   672             height = $(".video").height(),
       
   673             mp4_file = media.video.replace(/\.webm$/i,'.mp4'),
       
   674             webm_file = media.video.replace(/\.mp4$/i,'.webm'),
       
   675             mp4_src = $('<source>'),
       
   676             webm_src = $('<source>');
       
   677         mp4_src.attr({
       
   678             src: mp4_file,
       
   679             type: "video/mp4"
       
   680         });
       
   681         webm_src.attr({
       
   682             src: webm_file,
       
   683             type: "video/webm"
       
   684         });
       
   685         videoEl.attr({
       
   686             id : videoid,
       
   687             width : width,
       
   688             height : height
       
   689         }).css({
       
   690             position : "absolute",
       
   691             left: 0,
       
   692             top: 0,
       
   693             width : width,
       
   694             height : height
       
   695         });
       
   696         videoEl.append(mp4_src).append(webm_src);
       
   697         $(".video").append(videoEl);
       
   698         
       
   699         media.show = function() {
       
   700             videoEl.show();
       
   701         }
       
   702         media.hide = function() {
       
   703             videoEl.hide();
       
   704         }
       
   705         
       
   706         var popcorn = Popcorn("#" + videoid);
       
   707         
       
   708         // Binding functions to Popcorn
       
   709         
       
   710         media.on("setcurrenttime", function(_milliseconds) {
       
   711             if (media.loaded) {
       
   712                 popcorn.currentTime(_milliseconds / 1000);
       
   713             }
       
   714         });
       
   715         
       
   716         media.on("setvolume", function(_vol) {
       
   717             media.volume = _vol;
       
   718             if (media.loaded) {
       
   719                 popcorn.volume(_vol);
       
   720             }
       
   721         });
       
   722         
       
   723         media.on("setmuted", function(_muted) {
       
   724             media.muted = _muted;
       
   725             if (media.loaded) {
       
   726                 popcorn.muted(_muted);
       
   727             }
       
   728         });
       
   729         
       
   730         media.on("setplay", function() {
       
   731             if (media.loaded) {
       
   732                 popcorn.play();
       
   733             }
       
   734         });
       
   735         
       
   736         media.on("setpause", function() {
       
   737             if (media.loaded) {
       
   738                 popcorn.pause();
       
   739             }
       
   740         });
       
   741         
       
   742         // Binding Popcorn events to media
       
   743         
       
   744         function getVolume() {
       
   745             media.muted = popcorn.muted();
       
   746             media.volume = popcorn.volume();
       
   747         }
       
   748         
       
   749         popcorn.on("loadedmetadata", function() {
       
   750             getVolume();
       
   751             media.loaded = true;
       
   752             media.trigger("loadedmetadata");
       
   753             media.trigger("volumechange");
       
   754         })
       
   755         
       
   756         popcorn.on("timeupdate", function() {
       
   757             media.trigger("timeupdate", new IriSP.Model.Time(1000*popcorn.currentTime()));
       
   758         });
       
   759         
       
   760         popcorn.on("volumechange", function() {
       
   761             getVolume();
       
   762             media.trigger("volumechange");
       
   763         })
       
   764         
       
   765         popcorn.on("play", function() {
       
   766             media.trigger("play");
       
   767         });
       
   768         
       
   769         popcorn.on("pause", function() {
       
   770             media.trigger("pause");
       
   771         });
       
   772         
       
   773         popcorn.on("seeked", function() {
       
   774             media.trigger("seeked");
       
   775         });
       
   776         
       
   777         // Binding UI Events and Mashup Playing to Media
       
   778         
       
   779         media.on("loadedmetadata", function() {
       
   780             if (media === currentMedia) {
       
   781                 seekdiv.hide();
       
   782             }
       
   783             mashup.checkLoaded();
       
   784         });
       
   785         
       
   786         media.on("play", function() {
       
   787             if (media === currentMedia) {
       
   788                 onCurrentMediaPlay();
       
   789             }
       
   790             if (mashup === currentMedia && media === mashupCurrentMedia) {
       
   791                 mashup.trigger("play");
       
   792             }
       
   793         });
       
   794         
       
   795         media.on("pause", function() {
       
   796             if (media === currentMedia) {
       
   797                 onCurrentMediaPause();
       
   798             }
       
   799             if (mashup === currentMedia && media === mashupCurrentMedia) {
       
   800                 mashup.trigger("pause");
       
   801             }
       
   802         });
       
   803         
       
   804         media.on("timeupdate", function(_time) {
       
   805             if (media === currentMedia) {
       
   806                 onCurrentMediaTimeupdate(_time);
       
   807             }
       
   808             if (mashup === currentMedia && !mashup.paused && media === mashupCurrentMedia && !media.seeking) {
       
   809                 if ( _time < mashupSegmentEnd ) {
       
   810                     if ( _time >= mashupSegmentBegin ) {
       
   811                         mashupTimecode = _time - mashupTimedelta;
       
   812                     } else {
       
   813                         mashupTimecode = mashupSegmentBegin - mashupTimedelta;
       
   814                         media.setCurrentTime(mashupSegmentBegin);
       
   815                     }
       
   816                 } else {
       
   817                     mashupTimecode = mashupSegmentEnd - mashupTimedelta;
       
   818                     media.pause();
       
   819                     changeCurrentAnnotation();
       
   820                 }
       
   821                 mashup.trigger("timeupdate", new IriSP.Model.Time(mashupTimecode));
       
   822             }
       
   823         });
       
   824         
       
   825         media.on("seeked", function() {
       
   826             media.seeking = false;
       
   827             if (mashup === currentMedia && media === mashupCurrentMedia && mashupSeeking) {
       
   828                 mashupSeeking = false;
       
   829                 seekdiv.hide();
       
   830             }
       
   831         });
       
   832         
       
   833         media.on("volumechange", function() {
       
   834             if (media === currentMedia) {
       
   835                 ctrlVolumeUpdater();
       
   836             }
       
   837             mashup.muted = media.muted;
       
   838             mashup.volume = media.volume;
       
   839             mashup.trigger("volumechange");
       
   840         })
       
   841         
       
   842     }
       
   843 
       
   844     // Mashup Events
       
   845     
       
   846     mashup.on("setcurrenttime", function(_milliseconds) {
       
   847         mashupTimecode = _milliseconds;
       
   848         changeCurrentAnnotation();
       
   849     });
       
   850     
       
   851     mashup.on("setvolume", function(_vol) {
       
   852         mashup.getMedias().forEach(function(_media) {
       
   853             _media.setVolume(_vol);
       
   854         });
       
   855         mashup.volume = _vol;
       
   856     });
       
   857     
       
   858     mashup.on("setmuted", function(_muted) {
       
   859         mashup.getMedias().forEach(function(_media) {
       
   860             _media.setMuted(_muted);
       
   861         });
       
   862         mashup.muted = _muted;
       
   863     });
       
   864     
       
   865     mashup.on("setplay", function() {
       
   866         mashup.paused = false;
       
   867         changeCurrentAnnotation();
       
   868     });
       
   869     
       
   870     mashup.on("setpause", function() {
       
   871         mashup.paused = true;
       
   872         if (mashupCurrentMedia) {
       
   873             mashupCurrentMedia.pause();
       
   874         }
       
   875     });
       
   876     
       
   877     mashup.on("loadedmetadata", function() {
       
   878         if (mashup === currentMedia) {
       
   879             changeCurrentAnnotation();
       
   880         }
       
   881     });
       
   882     
       
   883     /* Mashup Events to UI */
       
   884    
       
   885     mashup.on("play", function() {
       
   886         if (mashup === currentMedia) {
       
   887             onCurrentMediaPlay();
       
   888         }
       
   889     });
       
   890     
       
   891     mashup.on("pause", function() {
       
   892         if (mashup === currentMedia) {
       
   893             onCurrentMediaPause();
       
   894         }
       
   895     });
       
   896     
       
   897     mashup.on("timeupdate", function(_time) {
       
   898         if (mashup === currentMedia) {
       
   899             $(".frise-position").css("left",(100*_time/mashup.duration)+"%");
       
   900             onCurrentMediaTimeupdate(_time);
       
   901         }
       
   902     });
       
   903         
   369         
   904     /* Segment Form interaction */
   370     /* Segment Form interaction */
   905    
   371    
   906     $("#segment-title").on("keyup change input paste", function() {
   372     $("#segment-title").on("keyup change input paste", function() {
   907         if (currentMedia && currentSegment) {
   373         if (currentMedia && currentSegment) {
   908             currentSegment.title = $(this).val();
   374             currentSegment.title = $(this).val();
   909             updateSegmentUI();
   375             updateSegmentUI();
   910             updateMashupUI();
   376             mashup.trigger("change");
   911         }
   377         }
   912     });
   378     });
   913     $("#segment-description").on("keyup change input paste", function() {
   379     $("#segment-description").on("keyup change input paste", function() {
   914         if (currentMedia && currentSegment) {
   380         if (currentMedia && currentSegment) {
   915             currentSegment.description = $(this).val();
   381             currentSegment.description = $(this).val();
   916             updateSegmentUI();
   382             mashup.trigger("change");
   917         }
   383         }
   918     });
   384     });
   919     $("#segment-form").submit(function() {
   385     $("#segment-form").submit(function() {
   920         if (addMode) {
   386         if (addMode) {
   921             mashup.addAnnotation(currentSegment);
   387             mashup.addAnnotation(currentSegment);
   922         } else {
   388         } else {
   923             updateMashupUI();
   389             mashup.trigger("change");
   924         }
   390         }
   925         var segment = mashup.getAnnotation(currentSegment);
   391         var segment = mashup.getAnnotation(currentSegment);
   926         currentSegment = undefined;
   392         currentSegment = undefined;
   927         setMedia(mashup);
   393         setMedia(mashup);
   928         if (segment) {
   394         if (segment) {
   929             mashup.setCurrentTime(segment.begin);
   395             mashup.setCurrentTime(segment.begin);
   930         }
   396             mashup.trigger("enter-annotation",segment);
   931         return false;
   397         }
   932     })
   398         return false;
       
   399     });
       
   400     
       
   401     $("#segment-tags").tagit();
       
   402     
       
   403     
       
   404     /* We have to defer this function because the tagit events
       
   405      * are triggered before the data are updated */
       
   406     function updateSegmentTags() {
       
   407         window.setTimeout(function() {
       
   408             if (currentMedia && currentSegment) {
       
   409                 currentSegment.keywords = $("#segment-tags").tagit("assignedTags");
       
   410             }
       
   411         }, 0);
       
   412     }
   933     
   413     
   934     /* Click on media items */
   414     /* Click on media items */
   935    
   415    
   936     $(".col-left").on("click", ".item-video", function() {
   416     $(".col-left").on("click", ".item-video", function() {
   937         currentSegment = undefined;
   417         currentSegment = undefined;
   965     function reorganizeMashup() {
   445     function reorganizeMashup() {
   966         var ids = $(".organize-segments .item-video").map(function(){return $(this).attr("data-segment-id")});
   446         var ids = $(".organize-segments .item-video").map(function(){return $(this).attr("data-segment-id")});
   967         mashup.setAnnotationsById(ids);
   447         mashup.setAnnotationsById(ids);
   968     }
   448     }
   969     
   449     
   970     function highlightCurrentSegment() {
   450     project.on("mouseover-annotation", function(annotation) {
   971         $(".organize-segments .item-video, .col-left .item-video, .frise-segment").removeClass("active");
   451         var mediaid = annotation.getMedia().id;
   972         var segmentid = undefined;
   452         $(".media").removeClass("active");
   973         if (currentMedia && currentSegment) {
   453         $(".media[data-media-id='" + mediaid + "']").addClass("active");
   974             segmentid = currentSegment.id;
   454     });
   975         }
   455     
   976         if (currentMedia === mashup && mashupCurrentAnnotation) {
   456     project.on("mouseout-annotation", function(annotation) {
   977             segmentid = mashupCurrentAnnotation.annotation.id;
   457         $(".media").removeClass("active");
   978         }
       
   979         $(".item-video[data-segment-id='" + segmentid + "']").addClass("active");
       
   980         var mediaid = undefined;
   458         var mediaid = undefined;
   981         if (currentMedia) {
   459         if (currentMedia && currentMedia.elementType === "media") {
   982             mediaid = currentMedia.id;
   460             mediaid = currentMedia.id;
   983         }
   461             if (currentSegment) {
   984         if (currentMedia === mashup && mashupCurrentMedia) {
   462                 $(".annotation").removeClass("active");
   985             mediaid = mashupCurrentMedia.id
   463                 $(".annotation[data-segment-id='" + currentSegment.id + "']").addClass("active");
   986         }
   464             }
   987         $(".col-left .item-video[data-media-id='" + mediaid + "']").addClass("active");
   465         }
   988     }
   466         if (currentMedia === mashup && mashup.currentMedia) {
   989     
   467             mediaid = mashup.currentMedia.id
   990     function hoverSegment() {
   468         }
   991         var segmentid = $(this).attr("data-segment-id");
   469         $(".media[data-media-id='" + mediaid + "']").addClass("active");
   992         $(".organize-segments .item-video, .frise-segment").removeClass("active");
   470     });
   993         $(".item-video[data-segment-id='" + segmentid + "'], .frise-segment[data-segment-id='" + segmentid + "']").addClass("active");
       
   994     }
       
   995     
       
   996     $(".frise")
       
   997     .on("mouseover", ".frise-segment", hoverSegment)
       
   998     .on("mouseout", ".frise-segment", highlightCurrentSegment)
       
   999     
   471     
  1000     $(".organize-segments")
   472     $(".organize-segments")
  1001     .sortable({
   473     .sortable({
  1002         stop : reorganizeMashup
   474         stop : reorganizeMashup
  1003     })
   475     })
  1004     .on("mouseover", ".item-video", hoverSegment)
   476     .on("mouseover", ".item-video", function() {
  1005     .on("mouseout", ".item-video", highlightCurrentSegment)
   477         project.trigger("mouseover-annotation", project.getElement($(this).attr("data-segment-id")));
  1006     .on("click", ".item-video", function(e) {
   478     })
  1007         var el = $(this),
   479     .on("mouseout", ".item-video", function() {
  1008             segment = mashup.getAnnotationById(el.attr("data-segment-id"));
   480         project.trigger("mouseout-annotation");
  1009         setMedia(mashup);
   481     })
  1010         if (segment) {
   482     .on("click", ".item-video", function() {
  1011             mashup.setCurrentTime(segment.begin);
   483         project.trigger("click-annotation", project.getElement($(this).attr("data-segment-id")));
  1012         }
       
  1013         return false;
       
  1014     })
   484     })
  1015     .on("click", ".edit", function(e) {
   485     .on("click", ".edit", function(e) {
  1016         var currentItem = $(this).parents(".item-video"),
   486         var currentItem = $(this).parents(".item-video"),
  1017             media = project.getElement(currentItem.attr("data-media-id")),
   487             media = project.getElement(currentItem.attr("data-media-id")),
  1018             segment = project.getElement(currentItem.attr("data-segment-id"));
   488             segment = project.getElement(currentItem.attr("data-segment-id"));
  1128         return false;
   598         return false;
  1129     });
   599     });
  1130     
   600     
  1131     /* Changing Hashcut Title and description */
   601     /* Changing Hashcut Title and description */
  1132     
   602     
       
   603     $("#hashcut-tags").tagit({
       
   604         onTagRemoved: updateSegmentTags,
       
   605         onTagAdded: updateSegmentTags
       
   606     });
       
   607     
  1133     mashup.title = IriSP.hc_messages.mashup_title_placeholder;
   608     mashup.title = IriSP.hc_messages.mashup_title_placeholder;
  1134     $(".title-video-wrap a").text(mashup.title);
   609     $(".title-video-wrap a").text(mashup.title);
  1135     $("#hashcut-title").val(mashup.title);
   610     $("#hashcut-title").val(mashup.title);
  1136     
   611     
  1137     $("#hashcut-title").on("keyup change input paste", function() {
   612     $("#hashcut-title").on("keyup change input paste", function() {
  1138         mashup.title = $(this).val();
   613         mashup.title = $(this).val();
  1139         $(".title-video-wrap a").text(mashup.title);
   614         $(".title-video-wrap a").text(mashup.title);
  1140         updateMashupUI();
   615         mashup.trigger("change");
  1141     });
   616     });
  1142     
   617     
  1143     $("#hashcut-description").on("keyup change input paste", function() {
   618     $("#hashcut-description").on("keyup change input paste", function() {
  1144         mashup.description = $(this).val();
   619         mashup.description = $(this).val();
  1145         updateMashupUI();
   620         mashup.trigger("change");
  1146     });
   621     });
  1147     
   622     
  1148     updateMashupUI();
   623     function updateMashupTags() {
       
   624         window.setTimeout(function() {
       
   625             mashup.keywords = $("#segment-tags").tagit("assignedTags");
       
   626         }, 0);
       
   627     }
       
   628     
       
   629     mashup.trigger("change");
  1149 }
   630 }