diff --git a/capsule-prototype/index.html b/capsule-prototype/index.html
index 8caea838f292055c6eb52b4b1c8e6fc9cab53aad..4d5a2759effb4d67c6e9bed8cada2cd46cb4cdb8 100644
--- a/capsule-prototype/index.html
+++ b/capsule-prototype/index.html
@@ -25,6 +25,8 @@
 	<script language="javascript" type='text/javascript' src='../shared/js/libs/sha1.js'></script>
 	<script language="javascript" type='text/javascript' src='../shared/js/libs/fastdom.js'></script>
 	<script language="javascript" type='text/javascript' src='../shared/js/libs/moment-with-langs.min.js'></script>
+	<script language="javascript" type="text/javascript" src='../shared/js/shared.js'></script>
+	<script language="javascript" type="text/javascript" src='../shared/js/MosaicPanelWidget.js'></script>
 
 	<script language="javascript" type='text/javascript' src="../shared/php/rekallApp.js.php"></script>
 
diff --git a/capsule-prototype/js/MosaicPanelWidget.js b/capsule-prototype/js/MosaicPanelWidget.js
new file mode 100644
index 0000000000000000000000000000000000000000..e4382afd524d587f274c1c0ded421f6500035709
--- /dev/null
+++ b/capsule-prototype/js/MosaicPanelWidget.js
@@ -0,0 +1,164 @@
+function MosaicPanelWidget(opts){
+
+    const defaultOptions = {
+        htmlElement: $('#mosaic_tab'),
+        rekall: window.app.rekall.Rekall('rekall'),
+        getTagGradientColor: function(tag) {
+            var isOpera = !!window.opera || navigator.userAgent.indexOf(' OPR/') >= 0;  // Opera 8.0+ (UA detection to detect Blink/v8-powered Opera)
+            var isFirefox = typeof InstallTrigger !== 'undefined';   // Firefox 1.0+
+            var isSafari = Object.prototype.toString.call(window.HTMLElement).indexOf('Constructor') > 0;   // At least Safari 3+: "[object HTMLElementConstructor]"
+            var isChrome = !!window.chrome && !isOpera;              // Chrome 1+
+            var isIE = /*@cc_on!@*/false || !!document.documentMode; // At least IE6
+
+            if(isOpera) {
+                return  "-o-linear-gradient(right bottom,  rgba(20,46,51,1) 0%, "+tag.color+" 100%)";
+            }
+            if(isFirefox) {
+                return "-moz-linear-gradient(right bottom,  rgba(20,46,51,1) 0%, "+tag.color+" 100%)";
+            }
+            if((isSafari)||(isChrome)){
+                return "-webkit-linear-gradient(right bottom,  rgba(20,46,51,1) 0%, "+tag.color+" 100%)";
+            }
+        },
+        getPreviewPath: function (tagOrDoc){
+            Utils.getPreviewPath(tagOrDoc);
+        },
+        isOpen: false
+    };
+
+    const localOptions = $.extend({}, defaultOptions, opts);
+
+    const getUrl = function(tagOrDoc, path){
+        if ('undefined' === typeof path) {
+            let [reg, type] = tagOrDoc.getMetadata("Rekall->Type").split('/');
+            if (reg === 'rekall') {
+                if (type === 'marker') type = 'note';
+            }
+            return "../shared/css/images/img-"+type+".png";
+        } else {
+            return path;
+        }
+    }
+
+    const createHtmlElementForTagOrDoc = function (tagOrDoc) {
+        let path = localOptions.getPreviewPath(tagOrDoc);
+        let name = tagOrDoc.getMetadata("Rekall->Name");
+        let url = getUrl(tagOrDoc, path);
+
+        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', '');
+        div.attr('data-rekall-labels', tagOrDoc.getMetadata('Rekall->Labels'));
+        return div;
+    }
+
+    const getFilterElement = function (text, color, callback, css_class) {
+        return $('<div/>')
+            .addClass('mosaic_filter_item')
+            .addClass(css_class)
+            .on('click', callback)
+            .css('background-color', color)
+            .append($('<h2/>').text(text));
+    }
+
+    const emptyPanel = function(){
+        localOptions.htmlElement.html('');
+    }
+
+    const fillPanel = function(){
+        let filterdiv = $('<div/>').addClass('mosaic_filter');
+        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.htmlElement.append(filterdiv);
+        let labels = new Set();
+        // TODO is there a better way to iterate over tags or documents ?
+        for ( let [k, v] of  Object.entries(localOptions.rekall.sortings.colors.categories)) {
+            let categoryName = localOptions.rekall.sortings.colors.getCategoryName(k)
+            let category = $('<div/>').addClass('mosaic_category').css('background', localOptions.getTagGradientColor(v));
+
+            let grid = $('<div/>').addClass('mosaic_category_grid');
+
+            category.append($('<h2/>').text(categoryName));
+            category.append(grid);
+
+            filterdiv.append(getFilterElement(
+                categoryName,
+                v.color,function() {
+                    category.toggle();
+                    $(this).toggleClass('mosaic_filter_disabled');
+                },
+                ''
+            ));
+
+            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)});
+                }
+            }
+            localOptions.htmlElement.append(category);
+        }
+
+        labels.forEach(l => {
+            let 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');
+                let 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');
+                    });
+                }
+                console.debug('filters: ', labels);
+                $('.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);
+        });
+    }
+
+    return {
+        show: function(){
+            if (localOptions.isOpen)
+                return;
+
+            localOptions.rekall.timeline.pause();
+            fillPanel();
+            localOptions.htmlElement.show();
+            localOptions.updateObserver = window.app.rekall.Rekall('pubSub').subscribe('popupEdit.updated', this.refresh)
+        },
+        hide: function (){
+            window.app.rekall.Rekall('pubSub').unsubscribe(localOptions.updateObserver);
+            localOptions.updateObserver = undefined;
+            localOptions.htmlElement.hide();
+            emptyPanel();
+        },
+        refresh: function (){
+            emptyPanel();
+            fillPanel();
+        }
+    };
+}
\ No newline at end of file
diff --git a/capsule-prototype/js/online-rekall/RekallApplication.js b/capsule-prototype/js/online-rekall/RekallApplication.js
index c214d4e070efd711ed12dd41534e6798ff399253..24b763baff26059b8d84014aeb7e3be198ab3c86 100644
--- a/capsule-prototype/js/online-rekall/RekallApplication.js
+++ b/capsule-prototype/js/online-rekall/RekallApplication.js
@@ -8,7 +8,8 @@
             'videoPlayer',
             'rekall',
             'getUrl',
-            'projectName'
+            'projectName',
+            'mosaicWidget'
         ];
 
         let localOptions = {};
@@ -25,6 +26,14 @@
                     $.extend(localOptions, opts);
                 }
             },
+            mosaicWidget : function (){
+                if (!arguments.length){
+                    return localOptions.MosaicWidget;
+                }
+                if ('object' === typeof arguments[0] && null !== arguments[0]) {
+                    localOptions.MosaicWidget = arguments[0];
+                }
+            },
             openUrl: function () {
                 if (!arguments)
                     return;
diff --git a/capsule-prototype/js/online-script.js b/capsule-prototype/js/online-script.js
index 34d52382b1b0832faf8d114b7c12f38e1b0054b5..cd11dd403f800636188b851a8eab32e690761677 100644
--- a/capsule-prototype/js/online-script.js
+++ b/capsule-prototype/js/online-script.js
@@ -11,7 +11,8 @@ window.onload = function() {
 	pubSub.subscribe('video', openVideo);
 	pubSub.subscribe('open_paste_modal', openPasteModal);
 	pubSub.subscribe('close_paste_modal', closePasteModal);
-	pubSub.subscribe('updated.project.name', onUpdatedProjectName)
+	pubSub.subscribe('updated.project.name', onUpdatedProjectName);
+	window.app.rekall.Rekall('mosaicWidget', new MosaicPanelWidget())
 };
 
 $(document).ready(function() {
@@ -828,140 +829,19 @@ function closeEdit() {
 	if(isPaused=="false") rekall.timeline.play();
 	$("#popupSpace").hide();
 	$("#popupEdit").hide();
+	window.app.rekall.Rekall('pubSub').publish('popupEdit.updated', {});
 }
 
 function openVideo() {
-		$('#mosaic_tab').hide();
-		$('#video_tab').show();
-		$('#left_menu_controls').show();
+	window.app.rekall.Rekall('mosaicWidget').hide();
+	$('#video_tab').show();
+	$('#left_menu_controls').show();
 }
 
 function openMosaic() {
-    function getMosaicItem(tagOrDoc) {
-        let path = Utils.getPreviewPath(tagOrDoc);
-        let name = tagOrDoc.getMetadata("Rekall->Name");
-        let url ='';
-        if ('undefined' === typeof path) {
-          let [reg, type] = tagOrDoc.getMetadata("Rekall->Type").split('/');
-          if (reg === 'rekall') {
-            if (type === 'marker') type = 'note';
-          }
-          url = "../shared/css/images/img-"+type+".png";
-        } else {
-            url = path;
-        }
-        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', '');
-        div.attr('data-rekall-labels', tagOrDoc.getMetadata('Rekall->Labels'));
-        return div;
-    }
-    function getFilterElement(text, color, callback, css_class) {
-        return $('<div/>')
-            .addClass('mosaic_filter_item')
-            .addClass(css_class)
-            .on('click', callback)
-            .css('background-color', color)
-            .append($('<h2/>').text(text));
-    }
-
-
-		rekall.timeline.pause();
-		$('#mosaic_tab').show();
-		$('#video_tab').hide();
-		$('#left_menu_controls').hide();
-
-    let container = $('#mosaic_tab');
-    container.html('');
-    let filterdiv = $('<div/>').addClass('mosaic_filter');
-    filterdiv.append(getFilterElement(
-        'Reset filters',
-        'rgb(100,100,100)',
-        function () {
-            $('.mosaic_filter_item').removeClass('mosaic_filter_disabled');
-            $('.mosaic_category').show();
-        },
-        '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)
-        let category = $('<div/>').addClass('mosaic_category').css('background', getTagGradientColor(v));
-
-        let grid = $('<div/>').addClass('mosaic_category_grid');
-
-        category.append($('<h2/>').text(categoryName));
-        category.append(grid);
-
-        filterdiv.append(getFilterElement(
-            categoryName,
-            v.color,function() {
-                category.toggle();
-                $(this).toggleClass('mosaic_filter_disabled');
-            },
-            ''
-        ));
-
-        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');
-          });
-        }
-        console.debug('filters: ', labels);
-        $('.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) {
-	var isOpera = !!window.opera || navigator.userAgent.indexOf(' OPR/') >= 0;  // Opera 8.0+ (UA detection to detect Blink/v8-powered Opera)
-	var isFirefox = typeof InstallTrigger !== 'undefined';   // Firefox 1.0+
-	var isSafari = Object.prototype.toString.call(window.HTMLElement).indexOf('Constructor') > 0;   // At least Safari 3+: "[object HTMLElementConstructor]"
-	var isChrome = !!window.chrome && !isOpera;              // Chrome 1+
-	var isIE = /*@cc_on!@*/false || !!document.documentMode; // At least IE6
-
-    if(isOpera) {
-        return  "-o-linear-gradient(right bottom,  rgba(20,46,51,1) 0%, "+tag.color+" 100%)";
-    }
-    if(isFirefox) {
-        return "-moz-linear-gradient(right bottom,  rgba(20,46,51,1) 0%, "+tag.color+" 100%)";
-    }
-    if((isSafari)||(isChrome)){
-        return "-webkit-linear-gradient(right bottom,  rgba(20,46,51,1) 0%, "+tag.color+" 100%)";
-    }
+	$('#video_tab').hide();
+	$('#left_menu_controls').hide();
+    window.app.rekall.Rekall('mosaicWidget').show();
 }
 
 function fillPopupEdit(tag) {
@@ -1251,7 +1131,7 @@ function uploadFiles(files, additionnal_post_data={}) {
 		formData.append("author",       rekall_common.owner.author);
 		formData.append("locationGps",  rekall_common.owner.locationGps);
 		formData.append("locationName", rekall_common.owner.locationName);
-    Object.keys(additionnal_post_data).forEach(k => formData.append(k, additionnal_post_data[k]));
+        Object.keys(additionnal_post_data).forEach(k => formData.append(k, additionnal_post_data[k]));
 
 		if(formData != undefined) {
 			filesToUpload.push({
diff --git a/capsule-prototype/js/shared.js b/capsule-prototype/js/shared.js
new file mode 100644
index 0000000000000000000000000000000000000000..bb434ceb51e5245850616d856cc33b81429d2a50
--- /dev/null
+++ b/capsule-prototype/js/shared.js
@@ -0,0 +1,17 @@
+function getTagGradientColor(tag) {
+    var isOpera = !!window.opera || navigator.userAgent.indexOf(' OPR/') >= 0;  // Opera 8.0+ (UA detection to detect Blink/v8-powered Opera)
+    var isFirefox = typeof InstallTrigger !== 'undefined';   // Firefox 1.0+
+    var isSafari = Object.prototype.toString.call(window.HTMLElement).indexOf('Constructor') > 0;   // At least Safari 3+: "[object HTMLElementConstructor]"
+    var isChrome = !!window.chrome && !isOpera;              // Chrome 1+
+    var isIE = /*@cc_on!@*/false || !!document.documentMode; // At least IE6
+
+    if (isOpera) {
+        return "-o-linear-gradient(right bottom,  rgba(20,46,51,1) 0%, " + tag.color + " 100%)";
+    }
+    if (isFirefox) {
+        return "-moz-linear-gradient(right bottom,  rgba(20,46,51,1) 0%, " + tag.color + " 100%)";
+    }
+    if ((isSafari) || (isChrome)) {
+        return "-webkit-linear-gradient(right bottom,  rgba(20,46,51,1) 0%, " + tag.color + " 100%)";
+    }
+}
\ No newline at end of file