diff --git a/capsule-prototype/css/mosaic.css b/capsule-prototype/css/mosaic.css index 02f0b81c9f65033da7cd8ad3165e683bb8b46ff2..4a8a8ccd8f78604ca9177cc0c78559976b3b0992 100644 --- a/capsule-prototype/css/mosaic.css +++ b/capsule-prototype/css/mosaic.css @@ -27,7 +27,7 @@ flex-wrap: wrap; } -.mosaic_filter_item { +.mosaic_filter_item, .mosaic_label_filter { width: var(--filter-width); height: var(--filter-height); margin: var(--base-spacing); @@ -40,6 +40,10 @@ align-items: center; } +.mosaic_label_filter_enabled { + background-color: rgba(255, 255, 255, .5); +} + .mosaic_category { padding: var(--base-spacing); margin: var(--base-spacing); diff --git a/capsule-prototype/css/popup.css b/capsule-prototype/css/popup.css index b8baa3e33acc276e6cfe75feb0fc1746987a091e..b8f80ff6251cbf3a4a017b4bcca1abf41aa5f3ce 100644 --- a/capsule-prototype/css/popup.css +++ b/capsule-prototype/css/popup.css @@ -1,4 +1,27 @@ +#popupLabelsInput { + padding: 0; +} + +#popupLabelsInput label { + cursor: pointer; + width: fit-content; + padding: 1px; + margin: 1px; +} + +#popupLabelsInput input:checked + label { + background-color: rgba(255, 255, 255, .5); +} + +#new_annotation_label { + color: white; + background-color: rgba(0, 0, 0, .5); + border: 0; + margin: 0; + margin-bottom: 2px; + font-size: 100%; +} #popupSpace, #popupAlertSpace, #popupAddLinkSpace, #popupSettingsSpace { width: 100%; diff --git a/capsule-prototype/index.html b/capsule-prototype/index.html index fbe9317f4b4c7947720fa010b85c513144165f6c..5f56bad57f2d74dea3569fdb98a54d8afa861639 100644 --- a/capsule-prototype/index.html +++ b/capsule-prototype/index.html @@ -184,6 +184,14 @@ <div class="popupRightItem" id="popupSpeed" title="Speed"></div> <input type='range' min='0' max='4' class='popupInput' id='popupSpeedInput' /> + + <div class="popupRightItem" id="popupLabels" title="Labels"></div> + <!-- templates --> + <div id='popupLabelsInput' class='popupInput flex-col'> + <input type='text' id='new_annotation_label' placeholder='Add a new tag' class='editmode' /> + <input class='annotation_labels_template' type='checkbox' id='' value='' style='display: none;' /> + <label class='annotation_labels_template' for='' style='display: none;'></label> + </div> </td> </tr> </table> diff --git a/capsule-prototype/js/online-rekall/Project.js b/capsule-prototype/js/online-rekall/Project.js index ebc832ca4e60938692fcc1c5943dff7cd465252a..6698c77fbccdc4cea39cda5b5c18a0085ad3607f 100644 --- a/capsule-prototype/js/online-rekall/Project.js +++ b/capsule-prototype/js/online-rekall/Project.js @@ -211,6 +211,8 @@ Project.prototype.timelineUpdate = function() { Project.prototype.analyse = function() { $('#flattentimeline').html("<div id='flattentimeline_highlight'></div>"); + var parsed_labels = $('input.annotation_labels').toArray().map(i => i.value); + //Analyse Tags.flattenTimelineTags = []; var filtredTags = new Array(); @@ -223,6 +225,34 @@ Project.prototype.analyse = function() { rekall.sortings["horizontal"].analyseAdd(tag); rekall.sortings["colors"] .analyseAdd(tag); Tags.flattenTimelineTags.push(tag); + var labels = tag.getMetadata('Rekall->Labels'); + if (labels && labels != '') { + var input_template = $('input.annotation_labels_template').first(); + var html_label_template = $('label.annotation_labels_template').first(); + var _labels = labels.split(';'); + for (var i in _labels) { + var label = _labels[i]; + if (label == '') continue; + if (parsed_labels.indexOf(label) != -1) + continue; + parsed_labels.push(label); + var inp = input_template.clone(); + var id = 'annotation_label_' + parsed_labels.indexOf(label); + inp.attr('id', id); + inp.attr('value', label); + + var html_label = html_label_template.clone(); + html_label.attr('for', id); + html_label.html(label); + + html_label.show(); + html_label.addClass('annotation_labels').removeClass('annotation_labels_template'); + inp.addClass('annotation_labels').removeClass('annotation_labels_template'); + + $('#popupLabelsInput').append(inp); + $('#popupLabelsInput').append(html_label); + } + } } } } diff --git a/capsule-prototype/js/online-script.js b/capsule-prototype/js/online-script.js index cf840c2f874b019e0445f1c964d5ae7481856245..afd9f67b8e4412ed7cd0213854288ff0472a7fc4 100644 --- a/capsule-prototype/js/online-script.js +++ b/capsule-prototype/js/online-script.js @@ -152,6 +152,34 @@ $(document).ready(function() { }); function setEditionControls() { + + $('#new_annotation_label').keypress(function(event) { + var keycode = (event.keyCode ? event.keyCode : event.which); + if (keycode != 13) { + return; + } + var label = $('#new_annotation_label').val(); + var input = $('input.annotation_labels_template').first().clone(); + var html_label = $('label.annotation_labels_template').first().clone(); + var id = 'annotation_label_' + Math.floor((Math.random()*1000)); + + input.attr('name', 'annotation_labels'); + input.attr('checked', 'true'); + input.attr('id', id); + input.attr('value', label); + + html_label.attr('for', id); + html_label.html(label); + + html_label.show(); + html_label.addClass('annotation_labels').removeClass('annotation_labels_template'); + input.addClass('annotation_labels').removeClass('annotation_labels_template'); + + $('#popupLabelsInput').append(input); + $('#popupLabelsInput').append(html_label); + $('#new_annotation_label').val(''); + }) + //Drag&drop files $(document).on({ dragenter: function(event) { @@ -465,6 +493,13 @@ function setEditionControls() { closeInputs(); }); + $("#popupLabels").click(function(event){ + event.stopPropagation(); + closeInputs(); + $(this).hide(); + $("#popupLabelsInput").show().focus(); + }); + $("#popupSpeed").click(function(event){ event.stopPropagation(); closeInputs(); @@ -782,6 +817,21 @@ function closeInputs() { if(newSpeed!="") $("#popupSpeed").html(get_str_for_speed(newSpeed)).removeClass("empty"); else $("#popupSpeed").html("+ Set video speed during this annotation ").addClass("empty"); + } else if($(this).attr("id")=="popupLabelsInput") { + + var keyDoc = $(this).parent().attr("keydoc"); + var newLabels = ''; + $('input.annotation_labels:checked') + .toArray().forEach(e => { + newLabels += e.value + ';'; + }) + setMetaFromDom(keyDoc, "Rekall->Labels", newLabels); + + if (newLabels === '') { + $('#popupLabels').html('+ Set annotation labels ').addClass('empty'); + $('#popupLabelsInput').hide(); + } + } else if($(this).attr("id")=="popupAuthorInput") { var keyDoc = $(this).parent().attr("keydoc"); @@ -803,7 +853,7 @@ function closeInputs() { setMetaFromDom(keyDoc, "Rekall->Link", newLink); if(newLink!="") { - if(rekall_common.owner.canEdit) $("#popupLink").html(newLink).removeClass("empty"); + if(rekall_common.owner.canEdit) $("#popupLink").html(newLink).removeClass("empty"); else $("#popupLink").html("<a href='"+newLink+"' target='_blank'>"+newLink+"</a>").removeClass("empty"); } else $("#popupLink").html("+ Add a link").addClass("empty"); @@ -873,6 +923,7 @@ function openMosaic() { let div=$('<div/>').addClass('mosaic_item').on('click', function() {tagOrDoc.openPopupEdit();}); div.append($('<img/>').attr('src', url).attr('onerror', "this.src='../shared/css/images/img-document.png';")); div.append($('<span/>').addClass('caption').text(name)); + div.attr('data-rekall-labels', tagOrDoc.getMetadata('Rekall->Labels')); return div; } function getFilterElement(text, color, callback, css_class) { @@ -903,6 +954,7 @@ function openMosaic() { 'mosaic_filter_item_all' )); container.append(filterdiv); + var labels = new Set(); // TODO is there a better way to iterate over tags or documents ? for ( let [k, v] of Object.entries(rekall.sortings.colors.categories)) { let categoryName = rekall.sortings.colors.getCategoryName(k) @@ -924,9 +976,42 @@ function openMosaic() { for (let i in v.tags){ grid.append(getMosaicItem(v.tags[i])); + var current_labels = v.tags[i].getMetadata('Rekall->Labels'); + if (current_labels && current_labels != '') { + current_labels.split(';').forEach(l => {if (l != '') labels.add(l)}); + } } container.append(category); } + labels.forEach(l => { + var button = $('<p/>').html(l); + button.my_state = undefined; + button.addClass('mosaic_label_filter'); + button.click(ev => { + if (typeof(ev.target.my_state) === 'undefined') ev.target.my_state = true; + else ev.target.my_state = !ev.target.my_state; + if (ev.target.my_state) $(ev.target).addClass('mosaic_label_filter_enabled'); + else $(ev.target).removeClass('mosaic_label_filter_enabled'); + var labels = new Set(); + $('.mosaic_label_filter').toArray().forEach(f => { + if (f.my_state == true) + labels.add(f.innerHTML); + }); + if (!labels.size) { + $('.mosaic_label_filter').toArray().forEach(f => { + f.my_state = undefined; + $(f).removeClass('mosaic_label_filter_enabled'); + }); + } + $('.mosaic_item').toArray().forEach(t => { + if (!labels.size || Array.from(labels).map(l => t.dataset.rekallLabels.indexOf(l) != -1).includes(true)) + $(t).show(); + else + $(t).hide(); + }); + }) + filterdiv.append(button); + }); } function getTagGradientColor(tag) { @@ -1014,6 +1099,19 @@ bgColorLeft = getTagGradientColor(tag); if((speed)&&(speed!="")) $("#popupSpeed").html(get_str_for_speed(speed)).removeClass("empty"); else $("#popupSpeed").html("+ Set video speed during this annotation").addClass("empty"); + var labels = tag.getMetadata("Rekall->Labels"); + $('input.annotation_labels').attr('checked', false); + if (labels && labels != '') { + var _labels = labels.split(';'); + $('input.annotation_labels').toArray().forEach(e => { + e.checked = (_labels.indexOf(e.value) != -1); + }) + $('#popupLabelsInput').show(); + } else { + $('#popupLabelsInput').hide(); + $('#popupLabels').html('+ Add labels'); + } + var comments = tag.getMetadata("Rekall->Comments"); if((comments)&&(comments!="")) $("#popupLegende").html(comments).removeClass("empty"); else $("#popupLegende").html("+ Add a comment").addClass("empty");