diff -r 20e00017dd60 -r 10974bff4dae web/res/metadataplayer/CreateAnnotation.js --- a/web/res/metadataplayer/CreateAnnotation.js Fri Dec 11 18:11:13 2015 +0100 +++ b/web/res/metadataplayer/CreateAnnotation.js Tue Dec 29 13:25:14 2015 +0100 @@ -1,13 +1,11 @@ /* TODO: Add Social Network Sharing */ IriSP.Widgets.CreateAnnotation = function(player, config) { - var _this = this; IriSP.Widgets.Widget.call(this, player, config); - if (_this.api_method == 'local' && window.localStorage[_this.api_endpoint_template]) { + if (_this.editable_storage != '' && window.localStorage[_this.editable_storage]) { this.source.onLoad(function () { - var _export = _this.player.sourceManager.newLocalSource({serializer: IriSP.serializers[_this.api_serializer]}); - _export.deSerialize(window.localStorage[_this.api_endpoint_template]); - console.log("Loaded personal annotations", _export); + var _export = _this.player.sourceManager.newLocalSource({serializer: IriSP.serializers['ldt_localstorage']}); + _export.deSerialize(window.localStorage[_this.editable_storage]); _this.source.merge(_export); }); }; @@ -18,13 +16,14 @@ IriSP.Widgets.CreateAnnotation.prototype.defaults = { show_title_field : true, show_creator_field : true, + creator_field_readonly : false, start_visible : true, always_visible : false, show_slice : true, - show_controls: false, show_arrow : true, show_mic_record: false, show_mic_play: false, + show_time: true, minimize_annotation_widget : true, creator_name : "", creator_avatar : "", @@ -51,13 +50,22 @@ }], slice_annotation_type: "chap", annotation_type: "Contributions", + post_at_segment_time: false, + segment_annotation_type: "chap", api_serializer: "ldt_annotate", api_endpoint_template: "", api_method: "POST", + // Id that will be used as localStorage key + editable_storage: "", + project_id: "", after_send_timeout: 0, close_after_send: false, tag_prefix: "#", - slice_widget: null + pause_when_displaying: false, + custom_send_button: false, + custom_cancel_button: false, + custom_description_placeholder: false, + preview_mode: false, }; IriSP.Widgets.CreateAnnotation.prototype.messages = { @@ -66,12 +74,14 @@ to_time: "to", at_time: "at", submit: "Submit", + preview_submit: "You cannot submit annotations in preview mode", + cancel: "Cancel", add_keywords_: "Add keywords:", add_polemic_keywords_: "Add polemic attributes :", your_name_: "Your name:", - annotate_video: "Annotate this video", + annotate_video: "New note", type_title: "Annotation title", - type_description: "Type the full contents of your annotation here.", + type_description: "Enter a new note...", wait_while_processing: "Please wait while your annotation is being processed...", error_while_contacting: "An error happened while contacting the server. Your annotation has not been saved.", annotation_saved: "Thank you, your annotation has been saved.", @@ -80,19 +90,24 @@ "polemic++": "Agree", "polemic--": "Disagree", "polemic??": "Question", - "polemic==": "Reference" + "polemic==": "Reference", + "in_tooltip": "Set begin time to current player time", + "out_tooltip": "Set begin time to current player time", + "play_tooltip": "Play the fragment" }, fr: { from_time: "de", to_time: "à", at_time: "à", submit: "Envoyer", + preview_submit: "Vous ne pouvez pas envoyer d'annotation en mode aperçu", + cancel: "Annuler", add_keywords_: "Ajouter des mots-clés\u00a0:", add_polemic_keywords_: "Ajouter des attributs polémiques\u00a0:", your_name_: "Votre nom\u00a0:", - annotate_video: "Annoter cette vidéo", + annotate_video: "Entrez une nouvelle note...", type_title: "Titre de l'annotation", - type_description: "Rédigez ici le contenu de votre annotation.", + type_description: "Prenez vos notes...", wait_while_processing: "Veuillez patienter pendant le traitement de votre annotation...", error_while_contacting: "Une erreur s'est produite en contactant le serveur. Votre annotation n'a pas été enregistrée.", annotation_saved: "Merci, votre annotation a été enregistrée.", @@ -101,28 +116,36 @@ "polemic++": "Accord", "polemic--": "Désaccord", "polemic??": "Question", - "polemic==": "Référence" + "polemic==": "Référence", + "in_tooltip": "Utiliser le temps courant comme début", + "out_tooltip": "Utiliser le temps courant comme fin", + "play_tooltip": "Jouer le fragment" } }; IriSP.Widgets.CreateAnnotation.prototype.template = - '{{#show_slice}}
{{/show_slice}}' + '{{#show_slice}}
{{/show_slice}}' + '{{^show_slice}}{{#show_arrow}}
{{/show_arrow}}{{/show_slice}}' + '
' + '
' + '

{{l10n.annotate_video}}{{#show_title_field}}

' + '

{{/show_title_field}}' - + ' {{#show_slice}}{{l10n.from_time}} {{/show_slice}}{{^show_slice}}{{l10n.at_time}} {{/show_slice}} 00:00' + + '{{#show_time}} {{#show_slice}}{{l10n.from_time}} {{/show_slice}}{{^show_slice}}{{l10n.at_time}} {{/show_slice}} 00:00{{/show_time}}' + '{{#show_slice}} {{l10n.to_time}} {{end}}{{/show_slice}}' - + '{{#show_creator_field}}{{l10n.your_name_}} {{/show_creator_field}}

' + + '{{#show_creator_field}}{{l10n.your_name_}} {{/show_creator_field}}' + '{{#show_controls}}
' - + 'IN' - + 'OUT' - + 'Play' + + 'In' + + 'Out' + + 'Play' + '
{{/show_controls}}' - + '' + + '' + '{{#show_creator_field}}
{{/show_creator_field}}' - + '' + + '
' + + '{{#preview_mode}}{{/preview_mode}}' + + '{{^preview_mode}}{{/preview_mode}}' + + '' + + '
00:00
' + + '
' + '{{#show_mic_record}}
Add voice annotation
' + ' ' + ' ' @@ -146,7 +169,7 @@ + ' pluginspage="http://www.macromedia.com/go/getflashplayer">' + ' ' + ' ' - + '
{{/show_mic_record}}' + + '
{{/show_mic_record}}' + '{{#tags.length}}
{{l10n.add_keywords_}}
{{/tags.length}}' + '{{#polemics.length}}
{{l10n.add_polemic_keywords_}}
'; - + IriSP.Widgets.CreateAnnotation.prototype.draw = function() { var _this = this; - + this.begin = new IriSP.Model.Time(); this.end = this.source.getDuration(); - + this.tag_prefix = this.tag_prefix || ""; - + if (this.tag_titles && !this.tags) { if(!(this.tag_titles.length==1 && this.tag_titles[0]=="")){ this.tags = IriSP._(this.tag_titles).map(function(_tag_title) { @@ -200,23 +223,20 @@ this.renderTemplate(); if (this.show_mic_record) { this.recorder = this.$.find("embed")[0]; - + window.setAudioUrl = function(_url) { _this.audio_url = _url; - }; + } } if (this.show_slice) { - this.slice_widget = this.insertSubwidget( + this.insertSubwidget( this.$.find(".Ldt-CreateAnnotation-Slice"), { type: "Slice", show_arrow: this.show_arrow, annotation_type: this.slice_annotation_type, onBoundsChanged: function(_from, _to) { - _this.begin = new IriSP.Model.Time(_from || 0); - _this.end = new IriSP.Model.Time(_to || 0); - _this.$.find(".Ldt-CreateAnnotation-Begin").html(_this.begin.toString()); - _this.$.find(".Ldt-CreateAnnotation-End").html(_this.end.toString()); + _this.setBeginEnd(_from, _to); } }, "slice" @@ -226,17 +246,21 @@ this.insertSubwidget(this.$.find(".Ldt-CreateAnnotation-Arrow"), {type: "Arrow"},"arrow"); } this.onMediaEvent("timeupdate", function(_time) { - _this.begin = new IriSP.Model.Time(_time || 0); - _this.end = new IriSP.Model.Time(_time || 0); - _this.$.find(".Ldt-CreateAnnotation-Begin").html(_this.begin.toString()); - if (_this.arrow) { - _this.arrow.moveToTime(_time); - } + // Do not update timecode if description is not empty + if (_this.$.find(".Ldt-CreateAnnotation-Description").val().trim() == "") { + _this.setBeginEnd(_time, _time); + if (_this.arrow) { + _this.arrow.moveToTime(_time); + } + }; }); } + this.$.find(".Ldt-CreateAnnotation-Cancel").click(function() { + _this.player.trigger("CreateAnnotation.hide"); + }); this.$.find(".Ldt-CreateAnnotation-Close").click(function() { _this.close_after_send - ? _this.hide() + ? _this.player.trigger("CreateAnnotation.hide") : _this.showScreen("Main"); return false; }); @@ -264,17 +288,15 @@ switch (action) { case "In": // Set In bound to current player time - _this.begin = new IriSP.Model.Time(_this.media.getCurrentTime() || 0); - _this.$.find(".Ldt-CreateAnnotation-Begin").html(_this.begin.toString()); + this.setBegin(_this.media.getCurrentTime()); break; case "Out": // Set In bound to current player time - _this.end = new IriSP.Model.Time(_this.media.getCurrentTime() || _this.media.duration); - _this.$.find(".Ldt-CreateAnnotation-End").html(_this.end.toString()); + this.setEnd(_this.media.getCurrentTime() || _this.media.duration); break; case "Play": - _this.media.setCurrentTime(_this.begin); - _this.media.play() + this.media.setCurrentTime(_this.begin); + this.media.play(); break; } return false; @@ -286,63 +308,86 @@ this.$.hide(); this.hide(); } - + this.onMdpEvent("CreateAnnotation.toggle","toggle"); + this.onMdpEvent("CreateAnnotation.hide", "hide"); + this.onMdpEvent("CreateAnnotation.show", "show"); this.$.find("form").submit(this.functionWrapper("onSubmit")); }; +IriSP.Widgets.CreateAnnotation.prototype.setBegin = function (t) { + this.begin = new IriSP.Model.Time(t || 0); + this.$.find(".Ldt-CreateAnnotation-Begin").html(this.begin.toString()); +}; + +IriSP.Widgets.CreateAnnotation.prototype.setEnd = function (t) { + this.end = new IriSP.Model.Time(t || 0); + this.$.find(".Ldt-CreateAnnotation-End").html(this.end.toString()); +}; + +IriSP.Widgets.CreateAnnotation.prototype.setBeginEnd = function (begin, end) { + this.setBegin(begin); + this.setEnd(end); +}; + IriSP.Widgets.CreateAnnotation.prototype.showScreen = function(_screenName) { this.$.find('.Ldt-CreateAnnotation-' + _screenName).show() .siblings().hide(); -} +}; IriSP.Widgets.CreateAnnotation.prototype.show = function() { - this.visible = true; - this.showScreen('Main'); - this.$.find(".Ldt-CreateAnnotation-Description").val("").css("border-color", "#666666").addClass("empty"); - if (this.show_title_field) { - this.$.find(".Ldt-CreateAnnotation-Title").val("").css("border-color", "#666666").addClass("empty"); - } - if (this.show_creator_field) { - this.$.find(".Ldt-CreateAnnotation-Creator").val(this.creator_name).css("border-color", "#666666"); - if (!this.creator_name) { - this.$.find(".Ldt-CreateAnnotation-Creator").addClass("empty"); + if (!this.visible){ + this.visible = true; + if (this.pause_when_displaying){ + this.media.pause(); + } + this.showScreen('Main'); + this.$.find(".Ldt-CreateAnnotation-Description").val("").css("border-color", "#666666").addClass("empty"); + if (this.show_title_field) { + this.$.find(".Ldt-CreateAnnotation-Title").val("").css("border-color", "#666666").addClass("empty"); } - } - this.$.find(".Ldt-CreateAnnotation-TagLi, .Ldt-CreateAnnotation-PolemicLi").removeClass("selected"); - this.$.slideDown(); - if (this.minimize_annotation_widget) { - this.player.trigger("Annotation.minimize"); + if (this.show_creator_field) { + this.$.find(".Ldt-CreateAnnotation-Creator").val(this.creator_name).css("border-color", "#666666"); + if (!this.creator_name) { + this.$.find(".Ldt-CreateAnnotation-Creator").addClass("empty"); + } + } + this.$.find(".Ldt-CreateAnnotation-TagLi, .Ldt-CreateAnnotation-PolemicLi").removeClass("selected"); + this.$.slideDown(); + if (this.minimize_annotation_widget) { + this.player.trigger("Annotation.minimize"); + } } }; IriSP.Widgets.CreateAnnotation.prototype.hide = function() { - if (this.recorder) { - this.recorder.stopRecord(); - } - if (!this.always_visible) { - this.visible = false; - this.$.slideUp(); - if (this.minimize_annotation_widget) { - this.player.trigger("Annotation.maximize"); + if (this.visible){ + if (this.recorder) { + this.recorder.stopRecord(); + } + if (!this.always_visible) { + this.visible = false; + this.$.slideUp(); + if (this.minimize_annotation_widget) { + this.player.trigger("Annotation.maximize"); + } } } }; IriSP.Widgets.CreateAnnotation.prototype.toggle = function() { - var _this = this; if (!this.always_visible) { if (this.visible) { this.hide(); } else { - _this.begin = new IriSP.Model.Time(_this.media.getCurrentTime() || 0); - _this.end = new IriSP.Model.Time(_this.media.getCurrentTime() || 0); - _this.$.find(".Ldt-CreateAnnotation-Begin").html(_this.begin.toString()); - _this.$.find(".Ldt-CreateAnnotation-End").html(_this.end.toString()); - if (_this.slice_widget) { - _this.slice_widget.setBounds(_this.begin, _this.end); + var t = this.media.getCurrentTime() || 0; + this.setBeginEnd(t, t); + if (this.slice_widget) { + this.slice_widget.setBounds(this.begin, this.end); } this.show(); + // Set focus on textarea + this.$.find(".Ldt-CreateAnnotation-Description").focus(); } } }; @@ -365,7 +410,12 @@ } }; -IriSP.Widgets.CreateAnnotation.prototype.onDescriptionChange = function() { +IriSP.Widgets.CreateAnnotation.prototype.onDescriptionChange = function(e) { + if (e !== undefined && e.keyCode == 13 && !e.shiftKey) { + // Return: submit. Use shift-Return to insert a LF + this.onSubmit(); + return true; + } var _field = this.$.find(".Ldt-CreateAnnotation-Description"), _contents = _field.val(); _field.css("border-color", !!_contents ? "#666666" : "#ff0000"); @@ -413,56 +463,73 @@ return !!_contents; }; -/* Fonction effectuant l'envoi des annotations */ IriSP.Widgets.CreateAnnotation.prototype.onSubmit = function() { - /* Si les champs obligatoires sont vides, on annule l'envoi */ + /* If mandatory fields are empty, we cancel the sending */ if (!this.onDescriptionChange() || (this.show_title_field && !this.onTitleChange()) || (this.show_creator_field && !this.onCreatorChange())) { return false; } - + if (this.recorder) { this.recorder.stopRecord(); } - + var _this = this, - _exportedAnnotations = new IriSP.Model.List(this.player.sourceManager), /* Création d'une liste d'annotations contenant une annotation afin de l'envoyer au serveur */ - _export = this.player.sourceManager.newLocalSource({serializer: IriSP.serializers[this.api_serializer]}), /* Création d'un objet source utilisant un sérialiseur spécifique pour l'export */ - _annotation = new IriSP.Model.Annotation(false, _export), /* Création d'une annotation dans cette source avec un ID généré à la volée (param. false) */ - _annotationTypes = this.source.getAnnotationTypes().searchByTitle(this.annotation_type, true), /* Récupération du type d'annotation dans lequel l'annotation doit être ajoutée */ - _annotationType = (_annotationTypes.length ? _annotationTypes[0] : new IriSP.Model.AnnotationType(false, _export)), /* Si le Type d'Annotation n'existe pas, il est créé à la volée */ - _url = Mustache.to_html(this.api_endpoint_template, {id: this.source.projectId}); /* Génération de l'URL à laquelle l'annotation doit être envoyée, qui doit inclure l'ID du projet */ - - /* Si nous avons dû générer un ID d'annotationType à la volée... */ + _exportedAnnotations = new IriSP.Model.List(this.player.sourceManager), /* We create a List to send to the server that will contains the annotation */ + _export = this.player.sourceManager.newLocalSource({serializer: IriSP.serializers[this.api_serializer]}), /* We create a source object using a specific serializer for export */ + _local_export = this.player.sourceManager.newLocalSource({serializer: IriSP.serializers['ldt_localstorage']}), /* Source object using a specific serializer for local export */ + _annotation = new IriSP.Model.Annotation(false, _export), /* We create an annotation in the source with a generated ID (param. false) */ + _annotationTypes = this.source.getAnnotationTypes().searchByTitle(this.annotation_type, true), /* We get the AnnotationType in which the annotation will be added */ + _annotationType = (_annotationTypes.length ? _annotationTypes[0] : new IriSP.Model.AnnotationType(false, _export)), /* If it doesn't already exists, we create it */ + _url = Mustache.to_html(this.api_endpoint_template, {id: this.source.projectId}); /* We make the url to send the request to, must include project id */ + + /* If we created an AnnotationType on the spot ... */ if (!_annotationTypes.length) { - /* Il ne faudra pas envoyer l'ID généré au serveur */ + /* ... We must not send its id to the server ... */ _annotationType.dont_send_id = true; - /* Il faut inclure le titre dans le type d'annotation */ + /* ... And we must include its title. */ _annotationType.title = this.annotation_type; } - + /* - * Nous remplissons les données de l'annotation générée à la volée - * ATTENTION: Si nous sommes sur un MASHUP, ces éléments doivent se référer AU MEDIA D'ORIGINE + * Will fill the generated annotation object's data + * WARNING: If we're on a MASHUP, these datas must refer the ORIGINAL MEDIA * */ - _annotation.setMedia(this.source.currentMedia.id); /* Id du média annoté */ - _annotation.setBegin(this.begin); /*Timecode de début */ - _annotation.setEnd(this.end); /* Timecode de fin */ - _annotation.created = new Date(); /* Date de création de l'annotation */ - - _annotation.setAnnotationType(_annotationType.id); /* Id du type d'annotation */ - _annotation.description = this.$.find(".Ldt-CreateAnnotation-Description").val(); /* Champ description */ + _annotation.setMedia(this.source.currentMedia.id); /* Annotated media ID */ + + if (this.post_at_segment_time){ + var _currentTime = this.media.getCurrentTime() + var _segmentsAnnotations = this.source.getAnnotationsByTypeTitle(this.segments_annotation_type) + var _currentSegments = _segmentsAnnotations.filter(function(_segment){ + return (_currentTime >= _segment.begin && _currentTime <= _segment.end) + }); + if (_currentSegments.length == 0){ + _annotation.setBegin(this.begin); /* Widget starting timecode */ + _annotation.setEnd(this.end); /* Widget end timecode */ + } + else { + _annotation.setBegin(_currentSegments[0].begin); /* Segment starting timecode */ + _annotation.setEnd(_currentSegments[0].end); /* Segment end timecode */ + } + } + else { + _annotation.setBeginEnd(this.begin, this.end); /* Widget end/start timecodes */ + } + _annotation.setAnnotationType(_annotationType.id); /* Annotation type ID */ if (this.show_title_field) { - /* Champ titre, seulement s'il est visible */ + /* Title field, only if it's visible */ _annotation.title = this.$.find(".Ldt-CreateAnnotation-Title").val(); - } else { - _annotation.title = _annotation.description; + }if (this.project_id != ""){ + /* Project id, only if it's been specifiec in the config */ + _annotation.project_id = this.project_id; } - + _annotation.created = new Date(); /* Annotation creation date */ + _annotation.description = this.$.find(".Ldt-CreateAnnotation-Description").val(); /* Description field */ + var tagIds = Array.prototype.map.call( this.$.find(".Ldt-CreateAnnotation-TagLi.selected"), - function(el) { return IriSP.jQuery(el).attr("tag-id")} + function(el) { return IriSP.jQuery(el).attr("tag-id"); } ); - + IriSP._(_annotation.description.match(/#[^\s#.,;]+/g)).each(function(_tt) { var _tag, _tag_title = _tt.replace(/^#/,''), @@ -477,10 +544,8 @@ if (tagIds.indexOf(_tag.id) === -1) { tagIds.push(_tag.id); } - - }) - - _annotation.setTags(IriSP._(tagIds).uniq()); /*Liste des ids de tags */ + }); + _annotation.setTags(IriSP._(tagIds).uniq()); /* Tag ids list */ if (this.audio_url) { _annotation.audio = { src: "mic", @@ -494,52 +559,53 @@ _annotation.creator = this.creator_name; } _exportedAnnotations.push(_annotation); /* Ajout de l'annotation à la liste à exporter */ + + if (this.editable_storage != '') { + // Append to localStorage annotations - if (this.api_method == 'local') { - // Append to existing localStorage annotations // FIXME: handle movie ids - /* Use localStorage */ - /* Data will be serialized in the localStore property designated by api_endpoint_template */ - _export.addList("annotation", _exportedAnnotations); /* Ajout de la liste à exporter à l'objet Source */ - _this.source.merge(_export); /* On ajoute la nouvelle annotation au recueil original */ + _local_export.addList("annotation", _exportedAnnotations); /* Ajout de la liste à exporter à l'objet Source */ + _this.source.merge(_local_export); /* On ajoute la nouvelle annotation au recueil original */ // Import previously saved local annotations - if (window.localStorage[_this.api_endpoint_template]) { - _export.deSerialize(window.localStorage[_this.api_endpoint_template]); + if (window.localStorage[this.editable_storage]) { + _local_export.deSerialize(window.localStorage[this.editable_storage]); } // Save everything back - window.localStorage[_this.api_endpoint_template] = _export.serialize(); - if (_this.pause_on_write && _this.media.getPaused()) { - _this.media.play(); - } + window.localStorage[_this.editable_storage] = _local_export.serialize(); _this.player.trigger("AnnotationsList.refresh"); /* On force le rafraîchissement du widget AnnotationsList */ + _this.player.trigger("Annotation.create", _annotation); _this.$.find(".Ldt-CreateAnnotation-Description").val(""); - } else { - _export.addList("annotation",_exportedAnnotations); /* Ajout de la liste à exporter à l'objet Source */ - /* Envoi de l'annotation via AJAX au serveur ! */ + } + + if (_url !== "") { + _exportedAnnotations.push(_annotation); /* We add the annotation in the list to export */ + _export.addList("annotation",_exportedAnnotations); /* We add the list to the source object */ + var _this = this; + /* We send the AJAX request to the server ! */ IriSP.jQuery.ajax({ url: _url, type: this.api_method, contentType: 'application/json', - data: _export.serialize(), /* L'objet Source est sérialisé */ + data: _export.serialize(), /* Source is serialized */ success: function(_data) { - _this.showScreen('Saved'); /* Si l'appel a fonctionné, on affiche l'écran "Annotation enregistrée" */ - if (_this.after_send_timeout) { /* Selon les options de configuration, on revient à l'écran principal ou on ferme le widget, ou rien */ + _this.showScreen('Saved'); + if (_this.after_send_timeout) { window.setTimeout( function() { _this.close_after_send - ? _this.hide() - : _this.show(); + ? _this.player.trigger("CreateAnnotation.hide") + : _this.player.trigger("CreateAnnotation.show"); }, _this.after_send_timeout ); } - _export.getAnnotations().removeElement(_annotation, true); /* Pour éviter les doublons, on supprime l'annotation qui a été envoyée */ - _export.deSerialize(_data); /* On désérialise les données reçues pour les réinjecter */ - _this.source.merge(_export); /* On récupère les données réimportées dans l'espace global des données */ + _export.getAnnotations().removeElement(_annotation, true); /* We delete the sent annotation to avoid redundancy */ + _export.deSerialize(_data); /* Data deserialization */ + _this.source.merge(_export); /* We merge the deserialized data with the current source data */ if (_this.pause_on_write && _this.media.getPaused()) { _this.media.play(); } - _this.player.trigger("AnnotationsList.refresh"); /* On force le rafraîchissement du widget AnnotationsList */ + _this.player.trigger("AnnotationsList.refresh"); }, error: function(_xhr, _error, _thrown) { IriSP.log("Error when sending annotation", _thrown); @@ -552,7 +618,7 @@ } }); this.showScreen('Wait'); - } - + }; return false; }; +