# HG changeset patch # User veltr # Date 1351619085 -3600 # Node ID b2d068afdbd872f046f1d4b2c2a25f51056299bd # Parent 7c394ea40f28aa35087d6a5e1529642defa3c156 Mashup editing and preview diff -r 7c394ea40f28 -r b2d068afdbd8 integration/css/edition.css --- a/integration/css/edition.css Mon Oct 29 18:11:09 2012 +0100 +++ b/integration/css/edition.css Tue Oct 30 18:44:45 2012 +0100 @@ -33,7 +33,7 @@ color: #333333; font-size: 14px; } -.title-video-wrap p.time-length span{ +.mashup-total-duration { color: #de2500; } /* popin - update-title */ @@ -122,32 +122,22 @@ display: none; } +/* frise */ .frise{ width: 458px; - position: relative; height: 22px; overflow: hidden; -} -.frise .segment{ - height: 22px; -} -.frise span.indication{ - height: 22px; - line-height: 22px; -} -/* frise */ -.frise{ position: relative; border: 1px solid #333333; } -.segments,.indications{ +.frise-segments,.frise-indications{ width: 100%; height: 100%; position: absolute; top: 0; left: 0; } -.indications{ +.frise-indications{ z-index: 2; } .aucun-segment{ @@ -157,8 +147,10 @@ line-height: 20px; width: 100%; z-index:0; + background: #CCCCCC; } -.frise .segment{ +.frise-segment{ + height: 22px; position: absolute; background-image: url(../img/border-right-segment.png); background-repeat: repeat-y; @@ -167,15 +159,17 @@ -webkit-box-shadow: inset 0px 2px 2px 0px #333; box-shadow: inset 0px 2px 2px 0px #333; } -.frise .segment:first-child{ +.frise-segment:first-child{ -moz-box-shadow: inset 2px 2px 2px 0px #333; -webkit-box-shadow: inset 2px 2px 2px 0px #333; box-shadow: inset 2px 2px 2px 0px #333; } -.frise .segment:last-child{ +.frise-segment:last-child{ background-image: none; } -.frise span.indication{ +.frise-indication{ + height: 22px; + line-height: 22px; display: inline-block; position: absolute; color: #FFF; @@ -187,6 +181,9 @@ background: url(../img/bg-indication.png) 15px 0 no-repeat; margin-left: -15px; } +.frise-position { + width: 1px; margin-left: -0.5px; position: absolute; height: 100%; background: #FF00FC; +} /* col-middle - bloc-segmentation */ .bloc-segmentation{ position: relative; @@ -417,10 +414,10 @@ position: relative; height: 20px; } -.col-right .frise .segment{ +.col-right .frise-segment{ height: 20px; } -.col-right .frise .indication{ +.col-right .frise-indication{ height: 20px; line-height: 20px; } @@ -435,11 +432,14 @@ .mashup-description img.pointer{ right: 36px; } -.mashup-description h2{ +.annotation-title { color: #de2500; font-size: 14px; font-weight: bold; } +.annotation-time { + color: #7628DF; +} .mashup-description table{ font-size: 12px; } @@ -532,6 +532,8 @@ .item-video img{ float: left; margin-right: 4px; + max-width: 80px; + max-height: 60px; box-shadow: 2px 2px 2px #333333; } .item-video .video-info{ @@ -602,6 +604,7 @@ background: url(../img/publier-button-sprite.png) 0 0 no-repeat; left: 382px; top: 10px; + z-index: 4; } a.publier-button:hover{ background-position: 0 -67px; diff -r 7c394ea40f28 -r b2d068afdbd8 integration/data/bpidata.json --- a/integration/data/bpidata.json Mon Oct 29 18:11:09 2012 +0100 +++ b/integration/data/bpidata.json Tue Oct 30 18:44:45 2012 +0100 @@ -69,5 +69,19 @@ "author": "Harun Farocki", "thumbnail": "thumbnails/comparaison-80.jpg", "duration": 3707000 + }, { + "id": "fabcc0ae-2287-11e2-a537-00145ea4a2be", + "video": "http://media.iri.centrepompidou.fr/video/mashup/bpimashup_lesfiguiersdebarbarieontilsuneame.mp4", + "title": "Les figuiers de Barbarie ont-ils une âme ?", + "author": "Rachel Mizrahi, Gilles Dinnematin", + "thumbnail": "thumbnails/figuiers-80.jpg", + "duration": 3356000 + }, { + "id": "75de2d46-2287-11e2-aaec-00145ea4a2be", + "video": "http://media.iri.centrepompidou.fr/video/mashup/bpimashup_heureduberger.mp4", + "title": "L'heure du berger", + "author": "Pierre Creton", + "thumbnail": "thumbnails/berger-80.jpg", + "duration": 2333000 } ] \ No newline at end of file diff -r 7c394ea40f28 -r b2d068afdbd8 integration/data/moon.json --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/integration/data/moon.json Tue Oct 30 18:44:45 2012 +0100 @@ -0,0 +1,24 @@ +[ + { + "id": "melies", + "video": "http://www.iri.centrepompidou.fr/dev/~veltr/mashup/moon/melies.mp4", + "title": "Le voyage à la lune", + "author": "Georges Méliès", + "thumbnail": "http://www.iri.centrepompidou.fr/dev/~veltr/mashup/moon/melies.jpg", + "duration": 674000 + }, { + "id": "apollo", + "video": "http://www.iri.centrepompidou.fr/dev/~veltr/mashup/moon/apollo.mp4", + "title": "Apollo 11 Overview", + "author": "NASA", + "thumbnail": "http://www.iri.centrepompidou.fr/dev/~veltr/mashup/moon/apollo.jpg", + "duration": 137000 + }, { + "id": "juno", + "video": "http://www.iri.centrepompidou.fr/dev/~veltr/mashup/moon/juno.mp4", + "title": "Launch of Juno!", + "author": "NASA", + "thumbnail": "http://www.iri.centrepompidou.fr/dev/~veltr/mashup/moon/atlas.jpg", + "duration": 130000 + } +] \ No newline at end of file diff -r 7c394ea40f28 -r b2d068afdbd8 integration/edition.html --- a/integration/edition.html Mon Oct 29 18:11:09 2012 +0100 +++ b/integration/edition.html Tue Oct 30 18:44:45 2012 +0100 @@ -26,11 +26,11 @@

- +

- +

@@ -112,7 +112,7 @@

Hashcut sans titre -

Durée: 00:00

+

Durée: 00:00

Tous les Hashcuts @@ -257,21 +257,11 @@
- -
- 00:30 - 00:30 - 00:30 - 00:30 +
- -
-
-
-
-
-
+
+
@@ -279,26 +269,23 @@
    -
  • -
  • -
  • -
  • +
-

Chargement de la capsule lunaire

+

- + - + - +
Extrait de :Le Voyage dans la Lune (05:01 - 05:24) ( - )
Description :Lors d'un colloque d'astronomie, le professeur Barbenfouillis crée l'événement en faisant part à l'assemblée de son projet de voyage dans la Lune. Il organise ensuite la visite à ses confrères de l'atelier où l'obus spatial est en chantier. Il sera propulsé en direction de la Lune au moyen d'un canon géant.
Tags :astronomie, lune, fiction, histoire du cinéma, canon, projectile(fonctionnalité à venir)
@@ -313,16 +300,9 @@

Liste des segments

Aucun segment

-
- 00:30 - 00:30 +
-
-
-
-
-
-
+
@@ -350,7 +330,6 @@ - @@ -359,5 +338,10 @@ + diff -r 7c394ea40f28 -r b2d068afdbd8 integration/js/editor.js --- a/integration/js/editor.js Mon Oct 29 18:11:09 2012 +0100 +++ b/integration/js/editor.js Tue Oct 30 18:44:45 2012 +0100 @@ -1,28 +1,31 @@ -IriSP.Hashcut = function() { +IriSP.Hashcut = function(options) { /* Load Media List */ var directory = new IriSP.Model.Directory(), project = directory.remoteSource({ - url: "data/bpidata.json", + url: options.url, serializer: IriSP.serializers.medialist }), mashup = new IriSP.Model.Mashup(false, project), - mediatemplate = '
  • {{title}}' - + '{{title}}{{description}}' - + 'Durée : {{duration}}
  • ', - segmenttemplate = '
  • {{media_title}}' - + '{{media_title}}' - + '{{title}}{{begin}} - {{end}} ({{duration}})' - + '
    • ' - + '
  • '; + mediatemplate = _.template('
  • <%= title %>' + + '<%= title %><%= description %>' + + 'Durée : <%= duration.toString() %>
  • '), + segmenttemplate = _.template('
  • ' + + '<%= annotation.getMedia().title %>' + + '<%= annotation.getMedia().title %>' + + '<%= annotation.title %><%= annotation.begin.toString() %> - <%= annotation.end.toString() %> (<%= annotation.getDuration().toString() %>)' + + '
    • ' + + '
  • '), + viztemplate = _.template('
    '), + intervaltemplate = _.template('<%= time.toString() %>'); /* Fill left column with Media List */ project.onLoad(function() { var html = ''; project.getMedias().forEach(function(_m) { - html += Mustache.to_html(mediatemplate, _m); + html += mediatemplate(_m); }); $(".col-left .list-video").html(html); }); @@ -55,15 +58,58 @@ /* Fill right column when mashup is updated */ - function fillRightColumn() { - var html = ''; - mashup.segments.forEach(function(_s) { - html += Mustache.to_html(segmenttemplate, _s); - }); - $(".col-right .list-video").html(html); + function setPointerToCurrentAnnotation() { + if (mashupCurrentAnnotation) { + var p = (mashupCurrentAnnotation.begin + mashupCurrentAnnotation.end) / (2 * mashup.duration); + $(".mashup-description .pointer").css("left", (100 * p) + "%"); + } } - mashup.on("add-segments",fillRightColumn); + function updateMashupUI() { + var listhtml = '', vizhtml = '', t = 0, k = mashup.duration ? (100 / mashup.duration) : 0; + mashup.segments.forEach(function(_s) { + listhtml += segmenttemplate(_s); + var vizdata = { + left: k * t, + width: k * _s.duration, + color: _s.getMedia().color + } + vizhtml += viztemplate(vizdata); + t += _s.duration.milliseconds; + }); + + var intervals = [ 1000, 2000, 5000, 10000, 30000, 60000, 120000, 300000, 600000, 900000, 1800000, 3600000, 7200000 ]; + + function createIntervals(maxn) { + for (var i = 0; i < intervals.length; i++) { + if (mashup.duration / intervals[i] <= maxn) { + var html = ''; + for (var j = intervals[i]; j < mashup.duration; j += intervals[i]) { + html += intervaltemplate({ left: k * j, time: new IriSP.Model.Time(j) }); + } + return html; + } + } + return ""; + } + + $(".col-right .list-video").html(listhtml).find(".item-video:last-child .bottom, .item-video:first-child .top").addClass("disable"); + $(".mashup-total-duration").text(mashup.duration.toString()); + $(".frise-segments").html(vizhtml); + $(".col-right .frise-indications").html(createIntervals(4)); + $(".bloc-pvw .frise-indications").html(createIntervals(8)); + highlightCurrentSegment(); + if (currentMedia === mashup) { + $(".Ldt-Ctrl-Time-Total").text(currentMedia.duration.toString()); + if (mashupTimecode > mashup.duration) { + mashup.setCurrentTime(mashup.duration); + } + changeCurrentAnnotation(); + setPointerToCurrentAnnotation(); + } + } + + mashup.on("change",updateMashupUI); /* Slider */ @@ -229,13 +275,13 @@ }); $(".Ldt-Ctrl-SetIn").click(function() { - if (currentMedia && currentMedia.currentSegment) { - currentMedia.currentSegment.setBegin(currentMedia.getCurrentTime()); + if (currentMedia && currentSegment) { + currentSegment.setBegin(currentMedia.getCurrentTime()); } }); $(".Ldt-Ctrl-SetOut").click(function() { - if (currentMedia && currentMedia.currentSegment) { - currentMedia.currentSegment.setEnd(currentMedia.getCurrentTime()); + if (currentMedia && currentSegment) { + currentSegment.setEnd(currentMedia.getCurrentTime()); } }); @@ -257,12 +303,12 @@ } }, slide: function(event, ui) { - if (currentMedia && currentMedia.currentSegment) { + if (currentMedia && currentSegment) { var t = currentMedia.duration * ui.value / slidersRange; if (ui.value === ui.values[0]) { - currentMedia.currentSegment.setBegin(t); + currentSegment.setBegin(t); } else { - currentMedia.currentSegment.setEnd(t); + currentSegment.setEnd(t); } } } @@ -271,15 +317,15 @@ sliceSlider.find(".ui-slider-handle:first") .addClass("Ldt-Slice-left-handle") .click(function() { - if (currentMedia && currentMedia.currentSegment) { - currentMedia.setCurrentTime(currentMedia.currentSegment.begin); + if (currentMedia && currentSegment) { + currentMedia.setCurrentTime(currentSegment.begin); } }); sliceSlider.find(".ui-slider-handle:last") .addClass("Ldt-Slice-right-handle") .click(function() { - if (currentMedia && currentMedia.currentSegment) { - currentMedia.setCurrentTime(currentMedia.currentSegment.end); + if (currentMedia && currentSegment) { + currentMedia.setCurrentTime(currentSegment.end); } }); @@ -304,71 +350,155 @@ timeSlider.slider("value",slidersRange * _time / currentMedia.duration); } + /* Mashup Player */ + + var mashupCurrentMedia = null, + mashupCurrentAnnotation = null, + mashupSegmentBegin, + mashupSegmentEnd, + mashupTimecode = 0, + mashupSeeking = false, + mashupSeekdiv, + mashupTimedelta; + + function changeCurrentAnnotation() { + if (mashupTimecode >= mashup.duration) { + if (!mashup.paused) { + mashup.paused = true; + mashup.trigger("pause"); + } + mashupTimecode = 0; + } + var _annotation = mashup.getAnnotationAtTime( mashupTimecode ); + if (typeof _annotation === "undefined") { + if (mashupCurrentMedia) { + mashupCurrentMedia.pause(); + if (!mashup.paused) { + mashup.paused = true; + mashup.trigger("pause"); + } + } + return; + } + mashupCurrentAnnotation = _annotation; + mashupSegmentBegin = mashupCurrentAnnotation.annotation.begin.milliseconds; + mashupSegmentEnd = mashupCurrentAnnotation.annotation.end.milliseconds; + mashupTimedelta = mashupSegmentBegin - mashupCurrentAnnotation.begin.milliseconds; + mashupCurrentMedia = mashupCurrentAnnotation.getMedia(); + + project.getMedias().forEach(function(_media) { + if (_media !== mashupCurrentMedia) { + _media.hide(); + _media.pause(); + } else { + _media.show(); + } + }); + + mashupCurrentMedia.setCurrentTime( mashupTimecode + mashupTimedelta); + mashupCurrentMedia.seeking = true; + + if (!mashup.paused) { + mashupCurrentMedia.play(); + mashupSeeking = true; +//TODO: _seekdiv.show(); + } + mashup.trigger("timeupdate", new IriSP.Model.Time(mashupTimecode)); + + } + /* Set current Media */ - var currentMedia; + var currentMedia, currentSegment; function updateSliderAndTangles() { - if (currentMedia && currentMedia.currentSegment) { - var start = currentMedia.currentSegment.begin, - end = currentMedia.currentSegment.end, - dur = currentMedia.currentSegment.getDuration(), - f = slidersRange / currentMedia.duration; + 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"); sliceSlider.slider( "values", [ f * start, f * end ] ); - $(".tangle-start").text(start.toString()).attr("data-milliseconds",start.milliseconds); - $(".tangle-end").text(end.toString()).attr("data-milliseconds",end.milliseconds); - $(".tangle-duration").text(dur.toString()).attr("data-milliseconds",dur.milliseconds); + 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); $(".segment-info .pointer").css("left",(parseFloat($(".Ldt-Slice-left-handle").css("left")) + parseFloat($(".Ldt-Slice-right-handle").css("left")))/2) } } - function setMedia(mediaid) { + var addMode; + + function setMedia(media) { $(".col-left .item-video").removeClass("active"); $(".tutorial").hide(); - $("video").hide(); if (currentMedia) { currentMedia.pause(); } - currentMedia = project.getElement(mediaid); + currentMedia = media; if (currentMedia.elementType == "media") { - $(".col-left .item-video[data-media-id='" + mediaid + "']").addClass("active"); + $("video").hide(); + $(".col-left .item-video[data-media-id='" + currentMedia.id + "']").addClass("active"); showSegmentation(); - var currentvideo = $('#video_' + mediaid); + var currentvideo = $('#video_' + currentMedia.id); if (!currentvideo.length) { addMediaPlayer(currentMedia); - currentvideo = $('#video_' + mediaid); } $(".tab-media-title").text(currentMedia.title); - if (!currentMedia.currentSegment) { - currentMedia.currentSegment = new IriSP.Model.Annotation(false, project); - currentMedia.currentSegment.setMedia(currentMedia.id); - currentMedia.currentSegment.setBegin(0); - currentMedia.currentSegment.setEnd(currentMedia.duration); - currentMedia.currentSegment.thumbnail = currentMedia.thumbnail; - currentMedia.currentSegment.title = "Segment sans titre"; - currentMedia.currentSegment.description = "Extrait de « " + currentMedia.title + " »"; - currentMedia.currentSegment.on("change-begin", function() { - if (currentMedia && currentMedia.currentSegment === this) { + + 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 = "Segment sans titre"; + currentSegment.description = "Extrait de « " + currentMedia.title + " »"; + currentSegment.on("change-begin", function() { + if (currentMedia && currentSegment === this) { currentMedia.setCurrentTime(this.begin); updateSliderAndTangles(); } }); - currentMedia.currentSegment.on("change-end", function() { - if (currentMedia && currentMedia.currentSegment === this) { + currentSegment.on("change-end", function() { + if (currentMedia && currentSegment === this) { currentMedia.setCurrentTime(this.end); updateSliderAndTangles(); } }); + currentSegment.on("enter", function() { + if (currentMedia === mashup) { + $(".annotation-title").text(this.title); + $(".annotation-begin").text(this.begin.toString()); + $(".annotation-end").text(this.end.toString()); + $(".annotation-media-title").text(this.getMedia().title); + $(".annotation-description").text(this.description); + setPointerToCurrentAnnotation(); + } + }) } + 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"); updateSliderAndTangles(); + media.show(); + $("#segment-title").val(currentSegment.title); + $("#segment-description").val(currentSegment.description); + $("#segment-tags").val(""); } - currentvideo.show(); + if (currentMedia.elementType === "mashup") { + showPreview(); + mashup.checkLoaded(); + } $(".Ldt-Ctrl-Time-Total").text(currentMedia.duration.toString()); - $("#segment-title").val(currentMedia.currentSegment.title); - $("#segment-description").text(currentMedia.currentSegment.description); // TODO: Do something with the tags ! onCurrentMediaTimeupdate(currentMedia.getCurrentTime()); onCurrentMediaPause(); + highlightCurrentSegment(); } function addMediaPlayer(media) { @@ -401,31 +531,48 @@ }); videoEl.append(mp4_src).append(webm_src); $(".video").append(videoEl); - + + media.show = function() { + videoEl.show(); + } + media.hide = function() { + videoEl.hide(); + } + var popcorn = Popcorn("#" + videoid); // Binding functions to Popcorn media.on("setcurrenttime", function(_milliseconds) { - popcorn.currentTime(_milliseconds / 1000); + if (media.loaded) { + popcorn.currentTime(_milliseconds / 1000); + } }); media.on("setvolume", function(_vol) { - popcorn.volume(_vol); media.volume = _vol; + if (media.loaded) { + popcorn.volume(_vol); + } }); media.on("setmuted", function(_muted) { - popcorn.muted(_muted); media.muted = _muted; + if (media.loaded) { + popcorn.muted(_muted); + } }); media.on("setplay", function() { - popcorn.play(); + if (media.loaded) { + popcorn.play(); + } }); media.on("setpause", function() { - popcorn.pause(); + if (media.loaded) { + popcorn.pause(); + } }); // Binding Popcorn events to media @@ -437,6 +584,7 @@ popcorn.on("loadedmetadata", function() { getVolume(); + media.loaded = true; media.trigger("loadedmetadata"); media.trigger("volumechange"); }) @@ -462,55 +610,162 @@ media.trigger("seeked"); }); - // Binding UI Events to Media + // Binding UI Events and Mashup Playing to Media + + media.on("loadedmetadata", function() { + mashup.checkLoaded(); + }); media.on("play", function() { if (media === currentMedia) { onCurrentMediaPlay(); } + if (mashup === currentMedia && media === mashupCurrentMedia) { + mashup.trigger("play"); + } }); media.on("pause", function() { if (media === currentMedia) { onCurrentMediaPause(); } + if (mashup === currentMedia && media === mashupCurrentMedia) { + mashup.trigger("pause"); + } }); media.on("timeupdate", function(_time) { if (media === currentMedia) { onCurrentMediaTimeupdate(_time); } + if (mashup === currentMedia && !mashup.paused && media === mashupCurrentMedia && !media.seeking) { + if ( _time < mashupSegmentEnd ) { + if ( _time >= mashupSegmentBegin ) { + mashupTimecode = _time - mashupTimedelta; + } else { + mashupTimecode = mashupSegmentBegin - mashupTimedelta; + media.setCurrentTime(mashupSegmentBegin); + } + } else { + mashupTimecode = mashupSegmentEnd - mashupTimedelta; + media.pause(); + changeCurrentAnnotation(); + } + mashup.trigger("timeupdate", new IriSP.Model.Time(mashupTimecode)); + } + }); + + media.on("seeked", function() { + media.seeking = false; + if (mashup === currentMedia && media === mashupCurrentMedia && mashupSeeking) { + mashupSeeking = false; +//TODO: _seekdiv.hide(); + } }); media.on("volumechange", function() { if (media === currentMedia) { ctrlVolumeUpdater(); } + mashup.muted = media.muted; + mashup.volume = media.volume; + mashup.trigger("volumechange"); }) } + + // Mashup Events + mashup.on("setcurrenttime", function(_milliseconds) { + mashupTimecode = _milliseconds; + changeCurrentAnnotation(); + }); + + mashup.on("setvolume", function(_vol) { + mashup.getMedias().forEach(function(_media) { + _media.setVolume(_vol); + }); + mashup.volume = _vol; + }); + + mashup.on("setmuted", function(_muted) { + mashup.getMedias().forEach(function(_media) { + _media.setMuted(_muted); + }); + mashup.muted = _muted; + }); + + mashup.on("setplay", function() { + mashup.paused = false; + changeCurrentAnnotation(); + }); + + mashup.on("setpause", function() { + mashup.paused = true; + if (mashupCurrentMedia) { + mashupCurrentMedia.pause(); + } + }); + + mashup.on("loadedmetadata", function() { + if (mashup === currentMedia) { + changeCurrentAnnotation(); + } + }); + + /* Mashup Events to UI */ + + mashup.on("play", function() { + if (mashup === currentMedia) { + onCurrentMediaPlay(); + } + }); + + mashup.on("pause", function() { + if (mashup === currentMedia) { + onCurrentMediaPause(); + } + }); + + mashup.on("timeupdate", function(_time) { + if (mashup === currentMedia) { + $(".frise-position").css("left",(100*_time/mashup.duration)+"%"); + onCurrentMediaTimeupdate(_time); + } + }); + /* Segment Form interaction */ $("#segment-title").on("keyup change input paste", function() { - if (currentMedia && currentMedia.currentSegment) { - currentMedia.currentSegment.title = $(this).val(); + if (currentMedia && currentSegment) { + currentSegment.title = $(this).val(); + updateMashupUI(); } }); $("#segment-description").on("keyup change input paste", function() { - if (currentMedia && currentMedia.currentSegment) { - currentMedia.currentSegment.title = $(this).val(); + if (currentMedia && currentSegment) { + currentSegment.description = $(this).val(); } }); $("#segment-form").submit(function() { - mashup.addSegment(currentMedia.currentSegment); - currentMedia.currentSegment = undefined; + if (addMode) { + mashup.addAnnotation(currentSegment); + } else { + updateMashupUI(); + } + var segment = mashup.getAnnotation(currentSegment); + currentSegment = undefined; + setMedia(mashup); + if (segment) { + mashup.setCurrentTime(segment.begin); + } }) /* Click on media items */ $(".col-left").on("click", ".item-video", function() { - setMedia($(this).attr("data-media-id")); + currentSegment = undefined; + setMedia(project.getElement($(this).attr("data-media-id"))); }); /* Click on Tabs */ @@ -524,30 +779,67 @@ return false; } - $(".tab-pvw").click(showPreview); - - function disableMoveItemVideo() { - $(".organize-segments .top, .organize-segments .bottom").removeClass("disable"); - $(".organize-segments .item-video:last-child .bottom, .organize-segments .item-video:first-child .top").addClass("disable"); - } - disableMoveItemVideo(); - - $(".organize-segments").sortable({ - stop : function(){ - disableMoveItemVideo(); + $(".tab-pvw").click(function() { + if (mashup.segments.length) { + setMedia(mashup); } }); - $(".organize-segments .top").click(function(e){ + /* Click on segments */ + + function reorganizeMashup() { + var ids = $(".organize-segments .item-video").map(function(){return $(this).attr("data-segment-id")}); + mashup.setAnnotationsById(ids); + } + + function highlightCurrentSegment() { + $(".organize-segments .item-video").removeClass("active"); + if (currentMedia && currentSegment) { + $(".item-video[data-segment-id='" + currentSegment.id + "']").addClass("active"); + } + } + + $(".organize-segments").sortable({ + stop : reorganizeMashup + }); + + $(".organize-segments").on("click", ".item-video", function(e) { + var el = $(this), + segment = mashup.getAnnotationById(el.attr("data-segment-id")); + setMedia(mashup); + if (segment) { + mashup.setCurrentTime(segment.begin); + } + return false; + }); + + $(".organize-segments").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; + }); + + $(".organize-segments").on("click", ".top", function(e){ var currentItem = $(this).parents(".item-video"); currentItem.insertBefore(currentItem.prev()); - disableMoveItemVideo(); + reorganizeMashup(); + return false; }); - $(".organize-segments .bottom").click(function(e){ + $(".organize-segments").on("click", ".bottom", function(e){ var currentItem = $(this).parents(".item-video"); currentItem.insertAfter(currentItem.next()); - disableMoveItemVideo(); + reorganizeMashup(); + return false; + }); + + $(".organize-segments").on("click", ".delete", function(e){ + var id = $(this).parents(".item-video").attr("data-segment-id"); + mashup.removeAnnotationById(id); + return false; }); /* Tangles */ @@ -577,6 +869,7 @@ }) .mouseup(function() { if (activeTangle) { + activeTangle.text(activeTangle.text().replace(/\.\d+$/,'')); $(".time-tangle").removeClass("active deactivate"); activeTangle = undefined; } @@ -584,33 +877,36 @@ $(".tangle-start") .mouseup(function(evt) { - if (!tangleHasMoved && currentMedia && currentMedia.currentSegment) { - currentMedia.setCurrentTime(currentMedia.currentSegment.begin); + if (!tangleHasMoved && currentMedia && currentSegment) { + currentMedia.setCurrentTime(currentSegment.begin); } }) .on("valuechange", function(evt, val) { - if (currentMedia && currentMedia.currentSegment) { - currentMedia.currentSegment.setBegin(val); + if (currentMedia && currentSegment) { + currentSegment.setBegin(val); } }); $(".tangle-end") .mouseup(function(evt) { - if (!tangleHasMoved && currentMedia && currentMedia.currentSegment) { - currentMedia.setCurrentTime(currentMedia.currentSegment.end); + if (!tangleHasMoved && currentMedia && currentSegment) { + currentMedia.setCurrentTime(currentSegment.end); } }) .on("valuechange", function(evt, val) { - if (currentMedia && currentMedia.currentSegment) { - currentMedia.currentSegment.setEnd(val); + if (currentMedia && currentSegment) { + currentSegment.setEnd(val); } }); $(".tangle-duration").on("valuechange", function(evt, val) { - if (currentMedia && currentMedia.currentSegment) { - currentMedia.currentSegment.setDuration(val); + if (currentMedia && currentSegment) { + currentSegment.setDuration(val); } }); + + $(".mashup-description .edit").click(function() { + if (mashupCurrentAnnotation) { + currentSegment = mashupCurrentAnnotation.annotation; + setMedia(mashupCurrentAnnotation.getMedia()); + } + }) } - -$(function() { - var hashcut = new IriSP.Hashcut(); -}); diff -r 7c394ea40f28 -r b2d068afdbd8 integration/js/medialist-serializer.js --- a/integration/js/medialist-serializer.js Mon Oct 29 18:11:09 2012 +0100 +++ b/integration/js/medialist-serializer.js Tue Oct 30 18:44:45 2012 +0100 @@ -3,14 +3,17 @@ IriSP.jQuery.getJSON(_url, _callback); }, deSerialize : function(_data, _source) { + var colors = ["#1f77b4","#ff7f0e","#2ca02c","#d62728","#9467bd","#8c564b","#e377c2","#7f7f7f","#bcbd22","#17becf"]; var _medialist = new IriSP.Model.List(_source.directory); - IriSP._(_data).each(function(_m) { + _data = IriSP._(_data).shuffle(); + IriSP._(_data).each(function(_m, i) { var _media = new IriSP.Model.Media(_m.id, _source); _media.video = _m.video; _media.title = _m.title; _media.description = "par " + _m.author; _media.setDuration(_m.duration); _media.thumbnail = _m.thumbnail; + _media.color = colors[i % colors.length]; _medialist.push(_media); }); _source.addList("media", _medialist); diff -r 7c394ea40f28 -r b2d068afdbd8 integration/js/model.js --- a/integration/js/model.js Mon Oct 29 18:11:09 2012 +0100 +++ b/integration/js/model.js Tue Oct 30 18:44:45 2012 +0100 @@ -301,16 +301,16 @@ } Model.Time.prototype.setMilliseconds = function(_milliseconds) { - var _ante = _milliseconds; + var _ante = this.milliseconds; switch(typeof _milliseconds) { case "string": - this.milliseconds = parseFloat(_milliseconds); + this.milliseconds = parseInt(_milliseconds); break; case "number": - this.milliseconds = _milliseconds; + this.milliseconds = Math.floor(_milliseconds); break; case "object": - this.milliseconds = parseFloat(_milliseconds.valueOf()); + this.milliseconds = parseInt(_milliseconds.valueOf()); break; default: this.milliseconds = 0; @@ -333,7 +333,8 @@ return { hours : Math.floor(_totalSeconds / 3600), minutes : (Math.floor(_totalSeconds / 60) % 60), - seconds : _totalSeconds % 60 + seconds : _totalSeconds % 60, + milliseconds: this.milliseconds % 1000 } } @@ -345,7 +346,7 @@ return this.milliseconds; } -Model.Time.prototype.toString = function() { +Model.Time.prototype.toString = function(showCs) { function pad(_n) { var _res = _n.toString(); while (_res.length < 2) { @@ -359,6 +360,9 @@ _res += _hms.hours + ':' } _res += pad(_hms.minutes) + ':' + pad(_hms.seconds); + if (showCs) { + _res += "." + Math.round(_hms.milliseconds / 100) + } return _res; } @@ -526,6 +530,9 @@ this.trigger("setpause"); } +Model.Playable.prototype.show = function() {} + +Model.Playable.prototype.hide = function() {} /* */ @@ -680,13 +687,18 @@ this.end = new Model.Time(); this.duration = new Model.Time(); this.title = this.annotation.title; - this.media_title = this.getMedia().title; this.description = this.annotation.description; this.color = this.annotation.color; var _this = this; this.on("click", function() { _mashup.setCurrentTime(_this.begin); }); + this.on("enter", function() { + _this.annotation.trigger("enter"); + }); + this.on("leave", function() { + _this.annotation.trigger("leave"); + }); } Model.MashedAnnotation.prototype = new Model.Element(null); @@ -724,16 +736,17 @@ this.elementType = 'mashup'; this.duration = new Model.Time(); this.segments = new Model.List(_source.directory); + this.loaded = false; var _currentMedia = null; var _this = this; this.on("timeupdate", function(_time) { - _this.getAnnotations().filter(function(_a) { + _this.getSegments().filter(function(_a) { return (_a.end <= _time || _a.begin > _time) && _a.playing }).forEach(function(_a) { _a.playing = false; _a.trigger("leave"); }); - _this.getAnnotations().filter(function(_a) { + _this.getSegments().filter(function(_a) { return _a.begin <= _time && _a.end > _time && !_a.playing }).forEach(function(_a) { _a.playing = true; @@ -750,13 +763,25 @@ }); this._updateTimes = function() { _this.updateTimes(); + _this.trigger("change"); } - this.on("add-segments", this._updateTimes); - this.on("remove-segments", this._updateTimes); + this.on("add", this._updateTimes); + this.on("remove", this._updateTimes); } Model.Mashup.prototype = new Model.Playable(); +Model.Mashup.prototype.checkLoaded = function() { + var loaded = !!this.segments.length; + this.getMedias().forEach(function(_m) { + loaded = loaded && _m.loaded; + }); + this.loaded = loaded; + if (loaded) { + this.trigger("loadedmetadata"); + } +} + Model.Mashup.prototype.updateTimes = function() { var _time = 0; this.segments.forEach(function(_segment) { @@ -766,70 +791,102 @@ this.duration.setMilliseconds(_time); } -Model.Mashup.prototype.addSegment = function(_annotation, _defer) { +Model.Mashup.prototype.addAnnotation = function(_annotation, _defer) { var _mashedAnnotation = new Model.MashedAnnotation(this, _annotation), _defer = _defer || false; this.segments.push(_mashedAnnotation); _annotation.on("change-begin", this._updateTimes); _annotation.on("change-end", this._updateTimes); if (!_defer) { - this.trigger("add-segments"); + this.trigger("add"); + } +} + +Model.Mashup.prototype.addAnnotationById = function(_elId, _defer) { + var _annotation = this.source.getElement(_elId), + _defer = _defer || false; + if (typeof _annotation !== "undefined") { + this.addAnnotation(_annotation, _defer); } } -Model.Mashup.prototype.addSegmentById = function(_elId, _defer) { - var _annotation = this.source.getElement(_elId), - _defer = _defer || false; - if (typeof _annotation !== "undefined") { - this.addSegment(_annotation, _defer); +Model.Mashup.prototype.addAnnotations = function(_segments) { + var _this = this; + ns._(_segments).forEach(function(_segment) { + _this.addAnnotation(_segment, true); + }); + this.trigger("add"); +} + +Model.Mashup.prototype.addAnnotationsById = function(_segments) { + var _this = this; + ns._(_segments).forEach(function(_segment) { + _this.addAnnotationById(_segment, true); + }); + this.trigger("add"); +} + +Model.Mashup.prototype.removeAnnotation = function(_annotation, _defer) { + var _defer = _defer || false; + _annotation.off("change-begin", this._updateTimes); + _annotation.off("change-end", this._updateTimes); + this.segments.removeId(this.id + "_" + _annotation.id); + if (!_defer) { + this.trigger("remove"); } } -Model.Mashup.prototype.addSegments = function(_segments) { - var _this = this; - ns._(_segments).forEach(function(_segment) { - _this.addSegment(_segment, true); - }); - this.trigger("add-segments"); -} +Model.Mashup.prototype.removeAnnotationById = function(_annId, _defer) { + var _defer = _defer || false; + var _annotation = this.source.getElement(_annId); -Model.Mashup.prototype.addSegmentsById = function(_segments) { - var _this = this; - ns._(_segments).forEach(function(_segment) { - _this.addSegmentById(_segment, true); - }); - this.trigger("add-segments"); -} - -Model.Mashup.prototype.removeSegment = function(_annotation, _defer) { - var _defer = _defer || false; - _annotation.off("change-begin", this._updateTimes); - _annotation.off("change-end", this._updateTimes); - this.segments.removeElement(_annotation); + if (_annotation) { + this.removeAnnotation(_annotation, _defer); + } if (!_defer) { - this.trigger("remove-segments"); + this.trigger("remove"); } } -Model.Mashup.prototype.removeSegmentById = function(_annId, _defer) { - var _defer = _defer || false; - var _annotation = this.source.getElementById(_annId); - if (_annotation) { - this.removeSegment(_annotation, _defer); +Model.Mashup.prototype.setAnnotations = function(_segments) { + while (this.segments.length) { + this.removeAnnotation(this.segments[0].annotation, true); } - this.segments.removeElement(_annotation); - if (!_defer) { - this.trigger("remove-segments"); + this.addAnnotations(_segments); +} + +Model.Mashup.prototype.setAnnotationsById = function(_segments) { + while (this.segments.length) { + this.removeAnnotation(this.segments[0].annotation, true); } + this.addAnnotationsById(_segments); } -Model.Mashup.prototype.getAnnotations = function() { +Model.Mashup.prototype.hasAnnotation = function(_annotation) { + return !!ns._(this.segments).find(function(_s) { + return _s.annotation === _annotation + }); +} + +Model.Mashup.prototype.getAnnotation = function(_annotation) { + return ns._(this.segments).find(function(_s) { + return _s.annotation === _annotation + }); +} + +Model.Mashup.prototype.getAnnotationById = function(_id) { + return ns._(this.segments).find(function(_s) { + return _s.annotation.id === _id + }); +} + +Model.Mashup.prototype.getSegments = function() { return this.segments; } Model.Mashup.prototype.getMedias = function() { - var medias = new Model.List(_source.directory); - this.segments.each(function(_annotation) { + var medias = new Model.List(this.source.directory); + this.segments.forEach(function(_annotation) { medias.push(_annotation.getMedia()) }) return medias; diff -r 7c394ea40f28 -r b2d068afdbd8 integration/lib/mustache.js --- a/integration/lib/mustache.js Mon Oct 29 18:11:09 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,436 +0,0 @@ -/* - mustache.js — Logic-less templates in JavaScript - - See http://mustache.github.com/ for more info. -*/ - -var Mustache = function () { - var _toString = Object.prototype.toString; - - Array.isArray = Array.isArray || function (obj) { - return _toString.call(obj) == "[object Array]"; - } - - var _trim = String.prototype.trim, trim; - - if (_trim) { - trim = function (text) { - return text == null ? "" : _trim.call(text); - } - } else { - var trimLeft, trimRight; - - // IE doesn't match non-breaking spaces with \s. - if ((/\S/).test("\xA0")) { - trimLeft = /^[\s\xA0]+/; - trimRight = /[\s\xA0]+$/; - } else { - trimLeft = /^\s+/; - trimRight = /\s+$/; - } - - trim = function (text) { - return text == null ? "" : - text.toString().replace(trimLeft, "").replace(trimRight, ""); - } - } - - var escapeMap = { - "&": "&", - "<": "<", - ">": ">", - '"': '"', - "'": ''' - }; - - function escapeHTML(string) { - return String(string).replace(/&(?!\w+;)|[<>"']/g, function (s) { - return escapeMap[s] || s; - }); - } - - var regexCache = {}; - var Renderer = function () {}; - - Renderer.prototype = { - otag: "{{", - ctag: "}}", - pragmas: {}, - buffer: [], - pragmas_implemented: { - "IMPLICIT-ITERATOR": true - }, - context: {}, - - render: function (template, context, partials, in_recursion) { - // reset buffer & set context - if (!in_recursion) { - this.context = context; - this.buffer = []; // TODO: make this non-lazy - } - - // fail fast - if (!this.includes("", template)) { - if (in_recursion) { - return template; - } else { - this.send(template); - return; - } - } - - // get the pragmas together - template = this.render_pragmas(template); - - // render the template - var html = this.render_section(template, context, partials); - - // render_section did not find any sections, we still need to render the tags - if (html === false) { - html = this.render_tags(template, context, partials, in_recursion); - } - - if (in_recursion) { - return html; - } else { - this.sendLines(html); - } - }, - - /* - Sends parsed lines - */ - send: function (line) { - if (line !== "") { - this.buffer.push(line); - } - }, - - sendLines: function (text) { - if (text) { - var lines = text.split("\n"); - for (var i = 0; i < lines.length; i++) { - this.send(lines[i]); - } - } - }, - - /* - Looks for %PRAGMAS - */ - render_pragmas: function (template) { - // no pragmas - if (!this.includes("%", template)) { - return template; - } - - var that = this; - var regex = this.getCachedRegex("render_pragmas", function (otag, ctag) { - return new RegExp(otag + "%([\\w-]+) ?([\\w]+=[\\w]+)?" + ctag, "g"); - }); - - return template.replace(regex, function (match, pragma, options) { - if (!that.pragmas_implemented[pragma]) { - throw({message: - "This implementation of mustache doesn't understand the '" + - pragma + "' pragma"}); - } - that.pragmas[pragma] = {}; - if (options) { - var opts = options.split("="); - that.pragmas[pragma][opts[0]] = opts[1]; - } - return ""; - // ignore unknown pragmas silently - }); - }, - - /* - Tries to find a partial in the curent scope and render it - */ - render_partial: function (name, context, partials) { - name = trim(name); - if (!partials || partials[name] === undefined) { - throw({message: "unknown_partial '" + name + "'"}); - } - if (!context || typeof context[name] != "object") { - return this.render(partials[name], context, partials, true); - } - return this.render(partials[name], context[name], partials, true); - }, - - /* - Renders inverted (^) and normal (#) sections - */ - render_section: function (template, context, partials) { - if (!this.includes("#", template) && !this.includes("^", template)) { - // did not render anything, there were no sections - return false; - } - - var that = this; - - var regex = this.getCachedRegex("render_section", function (otag, ctag) { - // This regex matches _the first_ section ({{#foo}}{{/foo}}), and captures the remainder - return new RegExp( - "^([\\s\\S]*?)" + // all the crap at the beginning that is not {{*}} ($1) - - otag + // {{ - "(\\^|\\#)\\s*(.+)\\s*" + // #foo (# == $2, foo == $3) - ctag + // }} - - "\n*([\\s\\S]*?)" + // between the tag ($2). leading newlines are dropped - - otag + // {{ - "\\/\\s*\\3\\s*" + // /foo (backreference to the opening tag). - ctag + // }} - - "\\s*([\\s\\S]*)$", // everything else in the string ($4). leading whitespace is dropped. - - "g"); - }); - - - // for each {{#foo}}{{/foo}} section do... - return template.replace(regex, function (match, before, type, name, content, after) { - // before contains only tags, no sections - var renderedBefore = before ? that.render_tags(before, context, partials, true) : "", - - // after may contain both sections and tags, so use full rendering function - renderedAfter = after ? that.render(after, context, partials, true) : "", - - // will be computed below - renderedContent, - - value = that.find(name, context); - - if (type === "^") { // inverted section - if (!value || Array.isArray(value) && value.length === 0) { - // false or empty list, render it - renderedContent = that.render(content, context, partials, true); - } else { - renderedContent = ""; - } - } else if (type === "#") { // normal section - if (Array.isArray(value)) { // Enumerable, Let's loop! - renderedContent = that.map(value, function (row) { - return that.render(content, that.create_context(row), partials, true); - }).join(""); - } else if (that.is_object(value)) { // Object, Use it as subcontext! - renderedContent = that.render(content, that.create_context(value), - partials, true); - } else if (typeof value == "function") { - // higher order section - renderedContent = value.call(context, content, function (text) { - return that.render(text, context, partials, true); - }); - } else if (value) { // boolean section - renderedContent = that.render(content, context, partials, true); - } else { - renderedContent = ""; - } - } - - return renderedBefore + renderedContent + renderedAfter; - }); - }, - - /* - Replace {{foo}} and friends with values from our view - */ - render_tags: function (template, context, partials, in_recursion) { - // tit for tat - var that = this; - - var new_regex = function () { - return that.getCachedRegex("render_tags", function (otag, ctag) { - return new RegExp(otag + "(=|!|>|&|\\{|%)?([^#\\^]+?)\\1?" + ctag + "+", "g"); - }); - }; - - var regex = new_regex(); - var tag_replace_callback = function (match, operator, name) { - switch(operator) { - case "!": // ignore comments - return ""; - case "=": // set new delimiters, rebuild the replace regexp - that.set_delimiters(name); - regex = new_regex(); - return ""; - case ">": // render partial - return that.render_partial(name, context, partials); - case "{": // the triple mustache is unescaped - case "&": // & operator is an alternative unescape method - return that.find(name, context); - default: // escape the value - return escapeHTML(that.find(name, context)); - } - }; - var lines = template.split("\n"); - for(var i = 0; i < lines.length; i++) { - lines[i] = lines[i].replace(regex, tag_replace_callback, this); - if (!in_recursion) { - this.send(lines[i]); - } - } - - if (in_recursion) { - return lines.join("\n"); - } - }, - - set_delimiters: function (delimiters) { - var dels = delimiters.split(" "); - this.otag = this.escape_regex(dels[0]); - this.ctag = this.escape_regex(dels[1]); - }, - - escape_regex: function (text) { - // thank you Simon Willison - if (!arguments.callee.sRE) { - var specials = [ - '/', '.', '*', '+', '?', '|', - '(', ')', '[', ']', '{', '}', '\\' - ]; - arguments.callee.sRE = new RegExp( - '(\\' + specials.join('|\\') + ')', 'g' - ); - } - return text.replace(arguments.callee.sRE, '\\$1'); - }, - - /* - find `name` in current `context`. That is find me a value - from the view object - */ - find: function (name, context) { - name = trim(name); - - // Checks whether a value is thruthy or false or 0 - function is_kinda_truthy(bool) { - return bool === false || bool === 0 || bool; - } - - var value; - - // check for dot notation eg. foo.bar - if (name.match(/([a-z_]+)\./ig)) { - var childValue = this.walk_context(name, context); - if (is_kinda_truthy(childValue)) { - value = childValue; - } - } else { - if (is_kinda_truthy(context[name])) { - value = context[name]; - } else if (is_kinda_truthy(this.context[name])) { - value = this.context[name]; - } - } - - if (typeof value == "function") { - return value.apply(context); - } - if (value !== undefined) { - return value; - } - // silently ignore unkown variables - return ""; - }, - - walk_context: function (name, context) { - var path = name.split('.'); - // if the var doesn't exist in current context, check the top level context - var value_context = (context[path[0]] != undefined) ? context : this.context; - var value = value_context[path.shift()]; - while (value != undefined && path.length > 0) { - value_context = value; - value = value[path.shift()]; - } - // if the value is a function, call it, binding the correct context - if (typeof value == "function") { - return value.apply(value_context); - } - return value; - }, - - // Utility methods - - /* includes tag */ - includes: function (needle, haystack) { - return haystack.indexOf(this.otag + needle) != -1; - }, - - // by @langalex, support for arrays of strings - create_context: function (_context) { - if (this.is_object(_context)) { - return _context; - } else { - var iterator = "."; - if (this.pragmas["IMPLICIT-ITERATOR"]) { - iterator = this.pragmas["IMPLICIT-ITERATOR"].iterator; - } - var ctx = {}; - ctx[iterator] = _context; - return ctx; - } - }, - - is_object: function (a) { - return a && typeof a == "object"; - }, - - /* - Why, why, why? Because IE. Cry, cry cry. - */ - map: function (array, fn) { - if (typeof array.map == "function") { - return array.map(fn); - } else { - var r = []; - var l = array.length; - for(var i = 0; i < l; i++) { - r.push(fn(array[i])); - } - return r; - } - }, - - getCachedRegex: function (name, generator) { - var byOtag = regexCache[this.otag]; - if (!byOtag) { - byOtag = regexCache[this.otag] = {}; - } - - var byCtag = byOtag[this.ctag]; - if (!byCtag) { - byCtag = byOtag[this.ctag] = {}; - } - - var regex = byCtag[name]; - if (!regex) { - regex = byCtag[name] = generator(this.otag, this.ctag); - } - - return regex; - } - }; - - return({ - name: "mustache.js", - version: "0.5.0-dev", - - /* - Turns a template and view into HTML - */ - to_html: function (template, view, partials, send_fun) { - var renderer = new Renderer(); - if (send_fun) { - renderer.send = send_fun; - } - renderer.render(template, view || {}, partials); - if (!send_fun) { - return renderer.buffer.join("\n"); - } - } - }); -}();