diff --git a/capsule-prototype/index.html b/capsule-prototype/index.html index 74e3481c0374b510c4ad6ab122df2224f6b87666..7ad8498a28c865808909621e4d94c9849c581c55 100644 --- a/capsule-prototype/index.html +++ b/capsule-prototype/index.html @@ -29,6 +29,7 @@ <script language="javascript" type='text/javascript' src="../shared/php/rekallApp.js.php"></script> + <script language="javascript" type="text/javascript" src="../shared/js/online-rekall/NotificationEntityChanged.js" ></script> <script language="javascript" type='text/javascript' src='../shared/js/rekall/Utils.js'></script> <script language="javascript" type='text/javascript' src='../shared/js/online-rekall/Rekall.js'></script> <script language="javascript" type='text/javascript' src='../shared/js/rekall/Source.js'></script> diff --git a/capsule-prototype/js/MosaicPanelWidget.js b/capsule-prototype/js/MosaicPanelWidget.js index d9c8d41e287e9a813908362197d318cd6ddd686e..3b51e18a8a48fba1d4301d947360518cdcd0d387 100644 --- a/capsule-prototype/js/MosaicPanelWidget.js +++ b/capsule-prototype/js/MosaicPanelWidget.js @@ -28,11 +28,37 @@ }, rekall: function() { return app.rekall.Rekall('rekall'); - } + }, + _filters_to_hide: [], + _followedItems: [] }; const localOptions = $.extend({}, defaultOptions); + // eslint-disable-next-line no-unused-vars + localOptions._resetFilters = function(event) { + $('.mosaic_filter_item') + .removeClass('mosaic_filter_disabled'); + $('.mosaic_label_filter_enabled').click(); + localOptions._filters_to_hide = []; + $('.mosaic_category').show(); + } + + localOptions._destroyFollowedElements = function () { + let elementsToDestroy = localOptions._followedItems; + localOptions._followedItems = []; + elementsToDestroy.forEach(e => e.remove()); + } + + function toggleSelectedFilter(filterName){ + let filters = localOptions._filters_to_hide; + if (filters.includes(filterName)){ + filters.splice(filters.indexOf(filterName), 1); + } else { + filters.push(filterName); + } + } + const getUrl = function (tagOrDoc, path) { if ('undefined' === typeof path) { let [reg, type] = tagOrDoc.getMetadata("Rekall->Type").split('/'); @@ -45,6 +71,14 @@ } } + const computeLabelShowing = function(div, tag) { + if (tag.getLabels().filter(value => localOptions._filters_to_hide.includes(value.toLowerCase())).length){ + div.hide(); + } else { + div.show() + } + } + const createHtmlElementForTagOrDoc = function (tagOrDoc) { let path = localOptions.getPreviewPath(tagOrDoc); let name = tagOrDoc.getMetadata("Rekall->Name"); @@ -64,20 +98,49 @@ div.append($('<span/>').addClass('caption').text(name)); div.attr('data-rekall-labels', ''); div.attr('data-rekall-labels', labels.toLowerCase()); + + let divUpdater = { + div: div, + tag: tagOrDoc + }; + divUpdater.update = function(tag){ + div.attr('data-rekall-labels', tag.getMetadata('Rekall->Labels') || '') + computeLabelShowing(div, tag); + } + divUpdater.remove = function () { + tagOrDoc.removeObserver(divUpdater); + } + divUpdater.computeVisibility = function (){ + computeLabelShowing(divUpdater.div, divUpdater.tag); + } + tagOrDoc.addObserver(divUpdater); + localOptions._followedItems.push(divUpdater); + + computeLabelShowing(div, tagOrDoc); + return div; } - const getFilterElement = function (text, color, callback, css_class) { - return $('<div/>') + const getFilterElement = function (text, color, callback, css_class, isToggled) { + let result = $('<div/>') .addClass('mosaic_filter_item mosaic_button') .addClass(css_class) .on('click', callback) .css('background-color', color) .append($('<h2/>').text(text)); + + + if(isToggled){ + result.toggle(); + result.toggleClass('mosaic_filter_disabled'); + } + + return result; } const emptyPanel = function () { localOptions.htmlElement().html(''); + localOptions._destroyFollowedElements(); } const fillPanel = function () { @@ -85,11 +148,9 @@ filterdiv.append(getFilterElement( 'Reset filters', 'rgb(100,100,100)', - function () { - $('.mosaic_filter_item').removeClass('mosaic_filter_disabled'); - $('.mosaic_category').show(); - }, - 'mosaic_filter_item_all' + localOptions._resetFilters, + 'mosaic_filter_item_all', + false )); localOptions.htmlElement().append(filterdiv); let labels = new Set(); @@ -108,54 +169,50 @@ v.color, function () { category.toggle(); $(this).toggleClass('mosaic_filter_disabled'); + localOptions.toggle_filter(categoryName) }, - '' + '', + localOptions._filters_to_hide.includes(categoryName) )); for (let i in v.tags) { grid.append(createHtmlElementForTagOrDoc(v.tags[i])); - let current_labels = v.tags[i].getMetadata('Rekall->Labels'); - if (current_labels && current_labels != '') { - current_labels.split(';').forEach(l => { - if (l != '') labels.add(l) - }); - } + v.tags[i].getLabels().forEach(l => labels.add(l)); } localOptions.htmlElement().append(category); } labels.forEach(l => { let button = $('<div/>').html(l); - button.my_state = undefined; + button.my_state = localOptions._filters_to_hide.includes(l.toLowerCase()); button.addClass('mosaic_filter_item mosaic_button mosaic_label_filter'); + if(button.my_state) { + button.toggleClass('mosaic_label_filter_enabled'); + } 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'); + ev.target.my_state = !ev.target.my_state; + localOptions.toggle_filter(l.toLowerCase()); + $(ev.target).toggleClass('mosaic_label_filter_enabled'); let labels = new Set(); $('.mosaic_label_filter').toArray().forEach(f => { - if (f.my_state == true) + if (true === f.my_state) labels.add(f.innerText.toLowerCase()); }); if (!labels.size) { $('.mosaic_label_filter').toArray().forEach(f => { - f.my_state = undefined; + f.my_state = false; $(f).removeClass('mosaic_label_filter_enabled'); }); } console.debug('filters: ', labels); - $('.mosaic_item').toArray().forEach(t => { - if (!labels.size || Array.from(labels).map(l => t.dataset.rekallLabels.indexOf(l.toLowerCase()) != -1).includes(true)) - $(t).show(); - else - $(t).hide(); - }); + localOptions._followedItems.forEach(value => value.computeVisibility()) }) filterdiv.append(button); }); } + localOptions.toggle_filter = toggleSelectedFilter; + return { show: function () { if (localOptions.isOpen) @@ -176,6 +233,8 @@ localOptions.htmlElement().hide(); emptyPanel(); localOptions.isOpen = false; + localOptions._destroyFollowedElements(); + localOptions._filters_to_hide = []; }, refresh: function () { emptyPanel(); diff --git a/capsule-prototype/js/PopupPanelWidget.js b/capsule-prototype/js/PopupPanelWidget.js index f73e5c1dd0e9271e0fa0decadfee371fc7ebff4d..680065eac293cac7677673b25ef19c5f8ef604dc 100644 --- a/capsule-prototype/js/PopupPanelWidget.js +++ b/capsule-prototype/js/PopupPanelWidget.js @@ -655,12 +655,8 @@ AnnotationTagPanelEdition.prototype = Object.create(PanelEdition.prototype); AnnotationTagPanelEdition.prototype.appendInputEditorDiv = function (parentDiv) { let that = this; - function getLabelArray(tag) { - let labels = tag.document.project.labels; - if (!labels) - return []; - - return labels; + function getProjectLabels(tag) { + return tag.document.getLabels(); } function appendTagInputWithLabel(panel, currentLabel, panelContainsLabel) { @@ -719,7 +715,7 @@ AnnotationTagPanelEdition.prototype.appendInputEditorDiv = function (parentDiv) that.tagCreationDiv.css('border', 'initial'); let labelToAdd = $(this).val(); - let labels = getLabelArray(that.tag); + let labels = getProjectLabels(that.tag); if (labels.indexOf(labelToAdd) === -1) { that.tag.document.project.labels.push(labelToAdd); appendTagInputWithLabel(that, labelToAdd); @@ -736,9 +732,8 @@ AnnotationTagPanelEdition.prototype.appendInputEditorDiv = function (parentDiv) .addClass('flex-col popupInput popupInputNoHide') .attr('id', 'popupLabelsInput'); - let _labels = getLabelArray(this.tag); - let tagLabels = that.tag.getMetadata('Rekall->Labels'); - tagLabels = tagLabels && tagLabels.trim().length ? tagLabels.slice(';') : []; + let _labels = getProjectLabels(this.tag); + let tagLabels = that.tag.getLabels(); for (let currentLabel of _labels) { if (!this.canEdit && !tagLabels.includes(currentLabel)) continue; diff --git a/capsule-prototype/js/online-rekall/NotificationEntityChanged.js b/capsule-prototype/js/online-rekall/NotificationEntityChanged.js new file mode 100644 index 0000000000000000000000000000000000000000..3585de3ab74febd82507b070caab408bd274c6a5 --- /dev/null +++ b/capsule-prototype/js/online-rekall/NotificationEntityChanged.js @@ -0,0 +1,27 @@ +function NotificationEntityChanged(data) { + this.__observers = []; +} + +NotificationEntityChanged.prototype.notifyChange = function() { + let observers = this.__observers; + observers.forEach(o => { if (o.update && typeof o.update === 'function') { o.update(this) } }); +}; + +NotificationEntityChanged.prototype.addObserver = function (observer) { + if (!this.__observers.includes(observer)) { + this.__observers.push(observer) + } +}; + +NotificationEntityChanged.prototype.removeObserver = function (observer) { + if (!this.__observers.includes(observer)) { + this.__observers.slice(this.__observers.indexOf(observer), 1); + } +}; + + +NotificationEntityChanged.prototype.getObserver = function (index) { + if (index > -1 && index < this.__observers.length) { + return this.__observers[index]; + } +}; \ No newline at end of file diff --git a/capsule-prototype/js/online-rekall/Project.js b/capsule-prototype/js/online-rekall/Project.js index 7ec93b4ec09d6e1316f416bedc0900be41177ae5..1dca2682fe864b71c7fbae8de227d94124b02ab3 100644 --- a/capsule-prototype/js/online-rekall/Project.js +++ b/capsule-prototype/js/online-rekall/Project.js @@ -144,17 +144,11 @@ Project.prototype.analyse = function () { this.rekall.sortings["horizontal"].analyseAdd(tag); this.rekall.sortings["colors"].analyseAdd(tag); Tags.push(tag); - let labels = tag.getMetadata('Rekall->Labels'); - if (labels && labels != '') { - let _labels = labels.split(';'); - for (let i in _labels) { - let label = _labels[i]; - if (label.length === 0) continue; - if (parsed_labels.indexOf(label) != -1) - continue; - parsed_labels.push(label); + tag.getLabels().forEach(l => { + if (parsed_labels.indexOf(l) === -1) { + parsed_labels.push(l); } - } + }) } } } diff --git a/capsule-prototype/js/online-rekall/Tag.js b/capsule-prototype/js/online-rekall/Tag.js index 155367fce439d57a4deb41231ea6d15d1fc85368..5d0ac8cc30b2abc7162b49663b2458c76ddde113 100644 --- a/capsule-prototype/js/online-rekall/Tag.js +++ b/capsule-prototype/js/online-rekall/Tag.js @@ -23,6 +23,7 @@ */ function Tag(document) { + NotificationEntityChanged.call(this, {}); this.document = document; this.documentOriginal = this.document; this.version = document.currentVersion; @@ -34,13 +35,18 @@ function Tag(document) { this.thumbnail = undefined; } +Tag.prototype = Object.create(NotificationEntityChanged.prototype); + + Tag.keyToOpenAfterLoading = undefined; Tag.prototype.getMetadata = function (metadataKey, metadataValue) { return this.document.getMetadata(metadataKey, this.version); } Tag.prototype.setMetadata = function (metadataKey, metadataValue) { - return this.document.setMetadata(metadataKey, metadataValue, this.version); + let result = this.document.setMetadata(metadataKey, metadataValue, this.version); + this.notifyChange(); + return result; } Tag.prototype.getMetadatas = function () { return this.document.getMetadatas(this.version); @@ -88,12 +94,17 @@ Tag.prototype.getTimeEnd = function () { return this.timeEnd; } Tag.prototype.setTimeStart = function (val) { - if (!isNaN(val)) + if (!isNaN(val)) { this.timeStart = val; + this.notifyChange(); + } } Tag.prototype.setTimeEnd = function (val) { - if (!isNaN(val)) + if (!isNaN(val)) { this.timeEnd = val; + this.notifyChange(); + } + } Tag.prototype.isGoodVersion = function () { return this.version == this.document.goodVersion; @@ -176,6 +187,21 @@ Tag.prototype.createTimelineDiv = function () { return this.flattenTimelineDom; } +Tag.prototype.getLabels = function () { + let labels = this.getMetadata('Rekall->Labels'); + if (!labels) + return []; + + let isStringNullOrWhitspace = function (s) { + if (typeof s !== "string" || !s) { + return false; + } + + return !s.trim(); + } + + return labels.split(';').filter(s => !isStringNullOrWhitspace(s)); +} function HighLightedTag(data) { Tag.call(this, data.Document); diff --git a/capsule-prototype/js/rekall/Document.js b/capsule-prototype/js/rekall/Document.js index 926063491c1f0a9906183669d84c30994cbc021a..cb4a3d73cf87af6d07efa22660cd078f05ac9a3b 100644 --- a/capsule-prototype/js/rekall/Document.js +++ b/capsule-prototype/js/rekall/Document.js @@ -22,6 +22,7 @@ */ function Document(data) { + NotificationEntityChanged.call(this, {}); this.tags = new Array(); this.metadatas = new Array(); this.currentVersion = 0; @@ -30,6 +31,9 @@ function Document(data) { this.setMetadata("Rekall->Visibility", ""); this.project = data.project; } + +Document.prototype = Object.create(NotificationEntityChanged.prototype); + Document.prototype.addTag = function(tag) { this.tags.push(tag); } @@ -48,6 +52,7 @@ Document.prototype.getMetadatas = function(version) { Document.prototype.setMetadatas = function(metadatas, version) { version = this.initMetadatas(); this.metadatas[version] = metadatas; + this.notifyChange(); } Document.prototype.isVideoOrAudio = function(version) { return this.isAudio(version) || this.isVideo(version); @@ -95,7 +100,13 @@ Document.prototype.getFileName = function() { var path = Utils.getLocalFilePath(this, "file"); return path.replace(/.*r=(.*)/, (f, m) => m); } +Document.prototype.getLabels = function() { + let labels = this.project.labels; + if (!labels) + return []; + return labels; +} Document.availableMetadataKeys = new Object(); @@ -121,6 +132,9 @@ Document.prototype.setMetadata = function(metadataKey, metadataValue, version) { Document.availableMetadataKeys[metadataKey] = {count: 0}; Document.availableMetadataKeys[metadataKey].count++; } + if(currentValue != metadataValue) { + this.notifyChange(); + } return (currentValue != metadataValue); }