[TIC-Web] add context menu and filter 03/120603/4
authorChangHyun Lee <leechwin.lee@samsung.com>
Thu, 23 Mar 2017 09:08:36 +0000 (18:08 +0900)
committerChangHyun Lee <leechwin.lee@samsung.com>
Fri, 24 Mar 2017 02:16:54 +0000 (11:16 +0900)
- add 'uncheck all' in contenxt menu
- add package filter

Change-Id: Ie79891b0650675aed7d1a70cc0345f95976ed945
Signed-off-by: ChangHyun Lee <leechwin.lee@samsung.com>
bower.json
gulpfile.js
public/src/index.html
public/src/js/config.js
public/src/js/page/package.js
public/src/lib/bootstrap-contextmenu/bootstrap-contextmenu.js [new file with mode: 0644]

index 4206099..2711708 100644 (file)
@@ -29,6 +29,7 @@
     "bootstrap": ">= 3.0.0",
     "bootstrap-treeview": "https://github.com/skateman/bootstrap-treeview.git#master",
     "components-font-awesome": "~4.7.0",
-    "bootstrap-validator": "~0.11.9"
+    "bootstrap-validator": "~0.11.9",
+    "bootstrap-contextmenu": "https://github.com/sydcanem/bootstrap-contextmenu.git#master"
   }
 }
index 47b68c7..5dc5949 100644 (file)
@@ -17,9 +17,10 @@ gulp.task('copy', ['clean'], function() {
     var bootstrap = gulp.src('bower_components/bootstrap/dist/**/*').pipe(gulp.dest('public/src/lib/bootstrap'));
     var bootstrapTreeview = gulp.src('bower_components/bootstrap-treeview/dist/**/*').pipe(gulp.dest('public/src/lib/bootstrap-treeview'));
     var bootstrapValidator = gulp.src('bower_components/bootstrap-validator/dist/**/*').pipe(gulp.dest('public/src/lib/bootstrap-validator'));
+    var bootstrapContextMenu = gulp.src('bower_components/bootstrap-contextmenu/bootstrap-contextmenu.js').pipe(gulp.dest('public/src/lib/bootstrap-contextmenu'));
     var componentsFontAwesomeCss = gulp.src('bower_components/components-font-awesome/css/**/*').pipe(gulp.dest('public/src/lib/components-font-awesome/css'));
     var componentsFontAwesomeFont = gulp.src('bower_components/components-font-awesome/fonts/**/*').pipe(gulp.dest('public/src/lib/components-font-awesome/fonts'));
-    return merge(requirejs, jsnlog, lodash, jquery, jquerySortable, bootstrap, bootstrapTreeview, bootstrapValidator, componentsFontAwesomeCss, componentsFontAwesomeFont);
+    return merge(requirejs, jsnlog, lodash, jquery, jquerySortable, bootstrap, bootstrapTreeview, bootstrapValidator, bootstrapContextMenu, componentsFontAwesomeCss, componentsFontAwesomeFont);
 });
 
 // watch
index 81257f0..cd610f4 100644 (file)
                                                 <i class="fa fa-navicon"></i>
                                             </button>
                                             <ul class="dropdown-menu dropdown-menu-right">
-                                                <li><a id="tic-package-toolbar-checkall">Check All</a></li>
-                                                <li><a id="tic-package-toolbar-uncheckall">Uncheck All</a></li>
+                                                <li><a id="tic-package-toolbar-all">All</a></li>
+                                                <li><a id="tic-package-toolbar-debug">Debug</a></li>
+                                                <li><a id="tic-package-toolbar-devel">Devel</a></li>
                                             </ul>
                                             <button type="button" class="btn btn-default" id="tic-package-left-col-tree-toolbar-collapse-all">
                                                 <i class="fa fa-minus"></i>
                 <a type="button" id="log-view-content-download" class="fa fa-download log-view-content-download"> Download</a>
             </div>
             <div id="log-view-content-body" class="log-view-content-body"></div>
-        </div>
-        <!-- /End Log View -->
+        </div><!-- /End Log View -->
+
+        <!-- Context menu in package tree -->
+        <div id="tic-package-context-menu">
+            <ul class="dropdown-menu" role="menu">
+                <li><a id="tic-package-context-menu-uncheck-all" tabindex="-1">Uncheck All</a></li>
+                <!--
+                <li class="divider"></li>
+                -->
+            </ul>
+        </div><!-- /Context menu in package tree -->
 
         <!-- library loading -->
         <script src="/socket.io/socket.io.js"></script>
index 5ea97a5..3726104 100644 (file)
@@ -7,6 +7,7 @@ require = {
             'bootstrap': 'lib/bootstrap/js/bootstrap',
             'bootstrap-treeview': 'lib/bootstrap-treeview/bootstrap-treeview.min',
             'bootstrap-validator': 'lib/bootstrap-validator/validator',
+            'bootstrap-contextmenu': 'lib/bootstrap-contextmenu/bootstrap-contextmenu',
             'jsnlog': 'lib/jsnlog/jsnlog'
     },
     shim: {
index d3219b0..df2dbf4 100644 (file)
@@ -1,6 +1,7 @@
 define([
     'jquery',
     'lodash',
+    'bootstrap-contextmenu',
     'js/util',
     'js/page/settings',
     'js/logger',
@@ -9,6 +10,7 @@ define([
 ], function (
     $,
     _,
+    bootstrapContextmenu,
     Util,
     Settings,
     Logger,
@@ -1101,9 +1103,8 @@ define([
         // button - image creation
         $('#tic-package-create').on('click', onClickHandlerForImgCreationBtn);
 
-        function _filter() {
-            var filterText = $('#tic-package-toolbar-input').val();
-            var matchNodes = $tree.treeview('search', [ filterText, {
+        function _filter(text) {
+            var matchNodes = $tree.treeview('search', [ text, {
                 ignoreCase: true,     // case insensitive
                 exactMatch: false,    // like or equals
                 revealResults: true,  // reveal matching nodes
@@ -1117,78 +1118,72 @@ define([
                     node.$el.show();
                 });
             } else {
-                if (_.isEmpty(filterText)) {
+                if (_.isEmpty(text)) {
                     _.forEach(packages, function (node) {
                         node.$el.show();
                     });
                 }
             }
+        }
+
+        function _filterInput() {
+            var filterText = $('#tic-package-toolbar-input').val();
+            _filter(filterText);
             $('#tic-package-toolbar-input-clear').toggleClass('hidden', _.isEmpty(filterText));
         }
-        $('#tic-package-toolbar-input').on('input change', _filter);
+        $('#tic-package-toolbar-input').on('keyup', _.debounce(_filterInput, 500));
+
+        function _filterAll() {
+            _filter('');
+        }
+        $('#tic-package-toolbar-all').on('click', _filterAll);
+        function _filterDebug() {
+            _filter('debug');
+        }
+        $('#tic-package-toolbar-debug').on('click', _filterDebug);
+        function _filterDevel() {
+            _filter('devel');
+        }
+        $('#tic-package-toolbar-devel').on('click', _filterDevel);
 
         function _inputClearBtnHandler() {
-            $('#tic-package-toolbar-input').val('').trigger('change').focus();
+            $('#tic-package-toolbar-input').val('').focus();
+            _filter('');
             $(this).toggleClass('hidden', true);
         }
         $('#tic-package-toolbar-input-clear').on('click', _inputClearBtnHandler);
 
-        function _checkAllBtnHandler() {
-            var startTS = performance.now();
-            var depPkg = {};
-            _.forEach(pkgInfo, function(value, key) {
-                if (!value.checked) {
-                    var dependents = _analyzeInstallDependency(value);
-                    if (dependents.result === true) {
-                        _.forEach(dependents.data, function(pkg, pkgName) {
-                            pkg.checked = true;
-                            depPkg[pkgName] = pkg;
-                        });
-                    }
-                }
-            });
-            var analyzeTS = performance.now();
-            var toggleNode = [];
-            if (!_.isEmpty(depPkg)) {
-                _.forEach(depPkg, function(value, key) {
-                    // update treeview data
+        function _contextMenuHandler() {
+            function _uncheckAllBtnHandler() {
+                // INFO: bug for state.checked = false in treeview objects
+                $tree.treeview('checkAll', { silent: true });
+                $tree.treeview('uncheckAll', { silent: true });
+                _.forEach(pkgInfo, function(value, key) {
+                    value.checked = false;
+                    value.selfChecked = false;
+                    value.forward = null;
+                    value.backward = null;
+                    value.group = null;
+
+                    // INFO: bug for state.checked = false in treeview objects
                     _.forEach(value.view, function(node) {
-                        if (node.state.checked === false) {
-                            toggleNode.push(node);
-                        }
+                        node.state.checked = false;
                     });
                 });
-
-                $tree.treeview('checkNode', [toggleNode, { silent: true }]);
+                groups = {};
+                conflicts = {};
                 _updateSummary();
             }
-            var endTS = performance.now();
-            logger.info('[All-Check] Total time: ' + (endTS - startTS) + 'ms');
-            logger.info('[All-Check] Analyze dep. time: ' + (analyzeTS - startTS) + 'ms');
-            logger.info('[All-Check] Update view time: ' + (endTS - analyzeTS) + 'ms');
-        }
-        $('#tic-package-toolbar-checkall').on('click', _checkAllBtnHandler);
-
-        function _uncheckAllBtnHandler() {
-            // FIXME: bug for state.checked = false in treeview objects
-            $tree.treeview('checkAll', { silent: true });
-            $tree.treeview('uncheckAll', { silent: true });
-            _.forEach(pkgInfo, function(value, key) {
-                value.checked = false;
-                value.selfChecked = false;
-                value.forward = null;
-                value.backward = null;
-                value.group = null;
-                // FIXME: bug for state.checked = false in treeview objects
-                _.forEach(value.view, function(node) {
-                    node.state.checked = false;
-                });
+            $tree.contextmenu({
+                target: '#tic-package-context-menu',
+                onItem: function (row, e) {
+                    if (e.target.id === 'tic-package-context-menu-uncheck-all') {
+                        _uncheckAllBtnHandler();
+                    }
+                }
             });
-            groups = {};
-            conflicts = {};
-            _updateSummary();
         }
-        $('#tic-package-toolbar-uncheckall').on('click', _uncheckAllBtnHandler);
+        _contextMenuHandler();
 
         function _collapseAll() {
             $tree.treeview('collapseAll');
diff --git a/public/src/lib/bootstrap-contextmenu/bootstrap-contextmenu.js b/public/src/lib/bootstrap-contextmenu/bootstrap-contextmenu.js
new file mode 100644 (file)
index 0000000..9a5027b
--- /dev/null
@@ -0,0 +1,207 @@
+/*!
+ * Bootstrap Context Menu
+ * Author: @sydcanem
+ * https://github.com/sydcanem/bootstrap-contextmenu
+ *
+ * Inspired by Bootstrap's dropdown plugin.
+ * Bootstrap (http://getbootstrap.com).
+ *
+ * Licensed under MIT
+ * ========================================================= */
+
+;(function($) {
+
+       'use strict';
+
+       /* CONTEXTMENU CLASS DEFINITION
+        * ============================ */
+       var toggle = '[data-toggle="context"]';
+
+       var ContextMenu = function (element, options) {
+               this.$element = $(element);
+
+               this.before = options.before || this.before;
+               this.onItem = options.onItem || this.onItem;
+               this.scopes = options.scopes || null;
+
+               if (options.target) {
+                       this.$element.data('target', options.target);
+               }
+
+               this.listen();
+       };
+
+       ContextMenu.prototype = {
+
+               constructor: ContextMenu
+               ,show: function(e) {
+
+                       var $menu
+                               , evt
+                               , tp
+                               , items
+                               , relatedTarget = { relatedTarget: this, target: e.currentTarget };
+
+                       if (this.isDisabled()) return;
+
+                       this.closemenu();
+
+                       if (this.before.call(this,e,$(e.currentTarget)) === false) return;
+
+                       $menu = this.getMenu();
+                       $menu.trigger(evt = $.Event('show.bs.context', relatedTarget));
+
+                       tp = this.getPosition(e, $menu);
+                       items = 'li:not(.divider)';
+                       $menu.attr('style', '')
+                               .css(tp)
+                               .addClass('open')
+                               .on('click.context.data-api', items, $.proxy(this.onItem, this, $(e.currentTarget)))
+                               .trigger('shown.bs.context', relatedTarget);
+
+                       // Delegating the `closemenu` only on the currently opened menu.
+                       // This prevents other opened menus from closing.
+                       $('html')
+                               .on('click.context.data-api', $menu.selector, $.proxy(this.closemenu, this));
+
+                       return false;
+               }
+
+               ,closemenu: function(e) {
+                       var $menu
+                               , evt
+                               , items
+                               , relatedTarget;
+
+                       $menu = this.getMenu();
+
+                       if(!$menu.hasClass('open')) return;
+
+                       relatedTarget = { relatedTarget: this };
+                       $menu.trigger(evt = $.Event('hide.bs.context', relatedTarget));
+
+                       items = 'li:not(.divider)';
+                       $menu.removeClass('open')
+                               .off('click.context.data-api', items)
+                               .trigger('hidden.bs.context', relatedTarget);
+
+                       $('html')
+                               .off('click.context.data-api', $menu.selector);
+                       // Don't propagate click event so other currently
+                       // opened menus won't close.
+                       if (e) {
+                               e.stopPropagation();
+                       }
+               }
+
+               ,keydown: function(e) {
+                       if (e.which == 27) this.closemenu(e);
+               }
+
+               ,before: function(e) {
+                       return true;
+               }
+
+               ,onItem: function(e) {
+                       return true;
+               }
+
+               ,listen: function () {
+                       this.$element.on('contextmenu.context.data-api', this.scopes, $.proxy(this.show, this));
+                       $('html').on('click.context.data-api', $.proxy(this.closemenu, this));
+                       $('html').on('keydown.context.data-api', $.proxy(this.keydown, this));
+               }
+
+               ,destroy: function() {
+                       this.$element.off('.context.data-api').removeData('context');
+                       $('html').off('.context.data-api');
+               }
+
+               ,isDisabled: function() {
+                       return this.$element.hasClass('disabled') || 
+                                       this.$element.attr('disabled');
+               }
+
+               ,getMenu: function () {
+                       var selector = this.$element.data('target')
+                               , $menu;
+
+                       if (!selector) {
+                               selector = this.$element.attr('href');
+                               selector = selector && selector.replace(/.*(?=#[^\s]*$)/, ''); //strip for ie7
+                       }
+
+                       $menu = $(selector);
+
+                       return $menu && $menu.length ? $menu : this.$element.find(selector);
+               }
+
+               ,getPosition: function(e, $menu) {
+                       var mouseX = e.clientX
+                               , mouseY = e.clientY
+                               , boundsX = $(window).width()
+                               , boundsY = $(window).height()
+                               , menuWidth = $menu.find('.dropdown-menu').outerWidth()
+                               , menuHeight = $menu.find('.dropdown-menu').outerHeight()
+                               , tp = {"position":"absolute","z-index":9999}
+                               , Y, X, parentOffset;
+
+                       if (mouseY + menuHeight > boundsY) {
+                               Y = {"top": mouseY - menuHeight + $(window).scrollTop()};
+                       } else {
+                               Y = {"top": mouseY + $(window).scrollTop()};
+                       }
+
+                       if ((mouseX + menuWidth > boundsX) && ((mouseX - menuWidth) > 0)) {
+                               X = {"left": mouseX - menuWidth + $(window).scrollLeft()};
+                       } else {
+                               X = {"left": mouseX + $(window).scrollLeft()};
+                       }
+
+                       // If context-menu's parent is positioned using absolute or relative positioning,
+                       // the calculated mouse position will be incorrect.
+                       // Adjust the position of the menu by its offset parent position.
+                       parentOffset = $menu.offsetParent().offset();
+                       X.left = X.left - parentOffset.left;
+                       Y.top = Y.top - parentOffset.top;
+                       return $.extend(tp, Y, X);
+               }
+
+       };
+
+       /* CONTEXT MENU PLUGIN DEFINITION
+        * ========================== */
+
+       $.fn.contextmenu = function (option,e) {
+               return this.each(function () {
+                       var $this = $(this)
+                               , data = $this.data('context')
+                               , options = (typeof option == 'object') && option;
+
+                       if (!data) $this.data('context', (data = new ContextMenu($this, options)));
+                       if (typeof option == 'string') data[option].call(data, e);
+               });
+       };
+
+       $.fn.contextmenu.Constructor = ContextMenu;
+
+       /* APPLY TO STANDARD CONTEXT MENU ELEMENTS
+        * =================================== */
+
+       $(document)
+          .on('contextmenu.context.data-api', function() {
+                       $(toggle).each(function () {
+                               var data = $(this).data('context');
+                               if (!data) return;
+                               data.closemenu();
+                       });
+               })
+               .on('contextmenu.context.data-api', toggle, function(e) {
+                       $(this).contextmenu('show', e);
+
+                       e.preventDefault();
+                       e.stopPropagation();
+               });
+               
+}(jQuery));