Browse Source

HUE-7565 [dashboard] Allow dragging to dashboard to create a new widget

Enrico Berti 8 years ago
parent
commit
fe147dfdb4

File diff suppressed because it is too large
+ 0 - 0
desktop/core/src/desktop/static/desktop/css/hue.css


File diff suppressed because it is too large
+ 0 - 0
desktop/core/src/desktop/static/desktop/css/hue3-extra.css


+ 0 - 7
desktop/core/src/desktop/static/desktop/ext/js/gridster-knockout.min.js

@@ -1,7 +0,0 @@
-/*!
- * gridster-knockout v0.0.1
- * (c) Justin Kohlhepp - 
- * License: MIT (http://www.opensource.org/licenses/mit-license.php)
- */
-
-ko.bindingHandlers.gridster={init:function(a,b,c,d,e){for(var f=$(a).data("gridster"),g=ko.unwrap(b)(),h=ko.unwrap(g.items),i=ko.unwrap(g.template),j=$("#"+i).html(),k=1,l=0;l<h.length;l++){var m=h[l];m.widgetId&&m.widgetId()>=k&&(k=parseInt(m.widgetId())+1)}for(var n=function(a){var c=void 0!==a.col?parseInt(a.col()):null,d=void 0!==a.row?parseInt(a.row()):null,g=parseInt(a.sizex()),h=parseInt(a.sizey()),i=f.add_widget(j,g,h,c,d);void 0===a.col&&(a.col=ko.observable(i.attr("data-col"))),void 0===a.row&&(a.row=ko.observable(i.attr("data-row"))),void 0===a.widgetId&&(a.widgetId=ko.observable(),a.widgetId(k++)),i.attr("data-widgetId",a.widgetId()),a.gridsterElement=i.get(0);var l=e.createChildContext(a,null,function(a){ko.utils.extend(a,b())});return ko.applyBindingsToDescendants(l,i.get(0)),i},o=function(a){for(var b=0;b<h.length;b++){var c=h[b];if(c.widgetId()==a)return c}return null},l=0;l<h.length;l++)m=h[l],n(m);g.items.subscribe(function(a){a.forEach(function(a){switch(a.status){case"added":{n(a.value)}break;case"deleted":f.remove_widget(a.value.gridsterElement);break;default:throw new Error("Unexpected change.status")}})},null,"arrayChange");var p=function(){for(var a=0;a<h.length;a++){var b=h[a],c=$(b.gridsterElement).attr("data-col"),d=$(b.gridsterElement).attr("data-row");b.col(c),b.row(d)}},q=f.options.resize.stop;f.options.resize.stop=function(a,b,c){var d=c.attr("data-widgetId"),e=c.attr("data-sizex"),f=c.attr("data-sizey"),g=o(d);g.sizex(e),g.sizey(f),p(),void 0!==q&&q(a,b,c)};var r=f.options.draggable.stop;return f.options.draggable.stop=function(a,b){p(),void 0!==r&&r(a,b)},{controlsDescendantBindings:!0}}};

+ 155 - 0
desktop/core/src/desktop/static/desktop/js/gridster-knockout.js

@@ -0,0 +1,155 @@
+// Licensed to Cloudera, Inc. under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  Cloudera, Inc. licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+/*!
+ * gridster-knockout v0.0.1
+ * (c) Justin Kohlhepp -
+ * License: MIT (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+// It's not maintained anymore so we modified it to get extra things in
+
+ko.bindingHandlers.gridster = {
+  init: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
+    var gridster = $(element).data('gridster');
+    var model = ko.unwrap(valueAccessor)();
+    var itemsArray = ko.unwrap(model.items);
+    var template = ko.unwrap(model.template);
+    var templateContents = $('#' + template).html();
+
+    // Some of the items may already have widget IDs, so we have
+    // to initialize the counter to be higher than that
+    var idCounter = 1;
+    for (var i = 0; i < itemsArray.length; i++) {
+      var item = itemsArray[i];
+      if (item.widgetId && item.widgetId() >= idCounter)
+        idCounter = parseInt(item.widgetId()) + 1;
+    }
+
+    var addWidget = function (widget) {
+      var col = (widget.col !== undefined) ? parseInt(ko.unwrap(widget.col)) : null;
+      var row = (widget.row !== undefined) ? parseInt(ko.unwrap(widget.row)) : null;
+      var sizex = parseInt(ko.unwrap(widget.size_x));
+      var sizey = parseInt(ko.unwrap(widget.size_y));
+      var addedWidget = gridster.add_widget(templateContents, sizex, sizey, col, row);
+
+      // Update the col and row based on it being added
+      if (widget.col === undefined)
+        widget.col = ko.observable(addedWidget.attr('data-col'));
+      if (widget.row === undefined)
+        widget.row = ko.observable(addedWidget.attr('data-row'));
+
+      // Keep an id for each widget so we can keep our sanity
+      if (widget.widgetId === undefined) {
+        widget.widgetId = ko.observable();
+        widget.widgetId(idCounter++);
+      }
+      addedWidget.attr('data-widgetId', widget.widgetId());
+
+      // Keep track of the element that was created with the widget
+      widget.gridsterElement = addedWidget.get(0);
+
+      // http://knockoutjs.com/documentation/custom-bindings-controlling-descendant-bindings.html
+      var childBindingContext = bindingContext.createChildContext(
+        widget,
+        null, // Optionally, pass a string here as an alias for the data item in descendant contexts
+        function (context) {
+          ko.utils.extend(context, valueAccessor());
+        });
+      ko.applyBindingsToDescendants(childBindingContext, addedWidget.get(0));
+
+      return addedWidget;
+    };
+
+    var getWidgetModelById = function (widgetId) {
+      for (var i = 0; i < itemsArray.length; i++) {
+        var item = itemsArray[i];
+        if (item.widgetId() == widgetId)
+          return item;
+      }
+      return null;
+    };
+
+    for (var i = 0; i < itemsArray.length; i++) {
+      item = itemsArray[i];
+      addWidget(item);
+    }
+
+    model.items.subscribe(function (changes) {
+      changes.forEach(function (change) {
+        switch (change.status) {
+          case 'added':
+            var addedWidget = addWidget(change.value);
+            if (change.value.callback) {
+              ko.unwrap(change.value.callback)(addedWidget);
+            }
+            break;
+          case 'deleted':
+            gridster.remove_widget(change.value.gridsterElement);
+            break;
+          default:
+            throw new Error('Unexpected change.status');
+            break;
+        }
+      });
+    }, null, "arrayChange");
+
+    var syncPositionsToModel = function () {
+      // Loop through all model items
+      for (var i = 0; i < itemsArray.length; i++) {
+        var item = itemsArray[i];
+        var eleColValue = $(item.gridsterElement).attr('data-col');
+        var eleRowValue = $(item.gridsterElement).attr('data-row');
+        item.col(eleColValue);
+        item.row(eleRowValue);
+      }
+    };
+
+    // Just in case the consumer set up their own resize handler, we need to chain the calls
+    var oldOnResize = gridster.options.resize.stop;
+    gridster.options.resize.stop = function (event, ui, $widget) {
+      var widgetId = $widget.attr('data-widgetId');
+      var newSizeX = $widget.attr('data-sizex');
+      var newSizeY = $widget.attr('data-sizey');
+
+      var widgetModel = getWidgetModelById(widgetId);
+      widgetModel.size_x(newSizeX);
+      widgetModel.size_y(newSizeY);
+
+      // Move a widget can change the positions of all other widgets
+      syncPositionsToModel();
+
+      if (oldOnResize !== undefined)
+        oldOnResize(event, ui, $widget);
+    };
+
+    var oldOnMove = gridster.options.draggable.stop;
+    gridster.options.draggable.stop = function (event, ui) {
+      // Moving a widget can change the positions of all other widgets
+      syncPositionsToModel();
+
+      if (oldOnMove !== undefined)
+        oldOnMove(event, ui);
+    };
+
+    // We are controlling sub-bindings, so don't have KO do it
+    return {controlsDescendantBindings: true};
+  },
+
+  //update: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
+  //    alert('update called');
+  //},
+};

+ 1 - 0
desktop/core/src/desktop/static/desktop/less/components/hue-gridster.less

@@ -22,6 +22,7 @@
     list-style-type: none;
     & > li {
       border: 1px solid @cui-gray-200;
+      background: @cui-gray-050 !important;
     }
   }
   .card-widget {

+ 6 - 0
desktop/core/src/desktop/templates/common_dashboard.mako

@@ -169,6 +169,12 @@
   <div class="container-fluid">
   %if USE_GRIDSTER.get():
 
+    <div class="container-fluid" data-bind="visible: $root.isEditing() && columns().length > 0">
+      <div class="add-row-gridster" data-bind="droppable: { data: showAddFacetDemiModal, options:{ greedy:true }}">
+        ${ _('Drag a widget here') }
+      </div>
+    </div>
+
     <div class="gridster">
       <!-- ko if: typeof gridItems !== 'undefined' -->
       <ul class="unstyled" data-bind="gridster: { items: gridItems, template: 'widget-template-gridster${ suffix }' }"></ul>

File diff suppressed because it is too large
+ 0 - 0
desktop/libs/dashboard/src/dashboard/static/dashboard/css/common_dashboard.css


+ 3 - 2
desktop/libs/dashboard/src/dashboard/static/dashboard/js/search.ko.js

@@ -62,8 +62,8 @@ function layoutToGridster(vm) {
         layout.push({
           col: 1,
           row: 1 + rowCnt * defaultWidgetHeight,
-          sizex: numCols,
-          sizey: defaultWidgetHeight,
+          size_x: numCols,
+          size_y: defaultWidgetHeight,
           widget: vm.getWidgetById(widget.id())
         });
         rowCnt++;
@@ -2593,6 +2593,7 @@ var SearchViewModel = function (collection_json, query_json, initial_json) {
     self.asyncSearchesCounter([]);
     self.isSyncingCollections(false);
     self.isPlayerMode(false);
+    self.gridItems([]);
 
     if (window.location.search.indexOf("collection") > -1) {
       hueUtils.changeURL((IS_HUE_4 ? '/hue' : '') + '/dashboard/new_search');

+ 8 - 0
desktop/libs/dashboard/src/dashboard/static/dashboard/less/common_dashboard.less

@@ -250,6 +250,14 @@
     overflow: auto !important;
   }
 
+  .add-row-gridster {
+    background-color: @cui-gray-050;
+    border: 1px solid @cui-gray-200;
+    text-align: center;
+    padding: 13px;
+    color: @cui-gray-500;
+  }
+
   .add-row {
     background-color: @cui-gray-050;
     min-height: 36px;

+ 115 - 49
desktop/libs/dashboard/src/dashboard/templates/common_search.mako

@@ -196,9 +196,7 @@ from dashboard.conf import USE_GRIDSTER, HAS_REPORT_ENABLED
 <%dashboard:layout_toolbar>
   <%def name="results()">
     <div data-bind="css: { 'draggable-widget': true, 'disabled': !availableDraggableResultset() },
-                    draggable: {data: draggableResultset(), isEnabled: availableDraggableResultset,
-                    options: {'start': function(event, ui){lastWindowScrollPosition = $(window).scrollTop();$('.card-body').slideUp('fast');},
-                              'stop': function(event, ui){$('.card-body').slideDown('fast'); $root.collection.template.isGridLayout(true); checkResultHighlightingAvailability(); }}}"
+                    draggable: {data: draggableResultset(), isEnabled: availableDraggableResultset, options: getDraggableOptions({ stop: function() { $root.collection.template.isGridLayout(true); checkResultHighlightingAvailability(); } }) }"
          title="${_('Grid')}" rel="tooltip" data-placement="top">
          <a data-bind="style: { cursor: $root.availableDraggableResultset() ? 'move' : 'default' }">
                        <i class="fa fa-table"></i>
@@ -208,14 +206,7 @@ from dashboard.conf import USE_GRIDSTER, HAS_REPORT_ENABLED
     <div data-bind="css: { 'draggable-widget': true, 'disabled': !availableDraggableResultset() },
                     draggable: {data: draggableHtmlResultset(),
                     isEnabled: availableDraggableResultset,
-                    options: {'start': function(event, ui){lastWindowScrollPosition = $(window).scrollTop();$('.card-body').slideUp('fast');},
-                              'stop': function(event, ui){
-                                  $('.card-body').slideDown('fast', function(){$(window).scrollTop(lastWindowScrollPosition)});
-                                  $root.collection.template.isGridLayout(false);
-                                  checkResultHighlightingAvailability();
-                               }
-                             }
-                    }"
+                    options: getDraggableOptions({ stop: function(){ $root.collection.template.isGridLayout(false); checkResultHighlightingAvailability(); } }) }"
          title="${_('HTML')}" rel="tooltip" data-placement="top">
          <a data-bind="style: { cursor: $root.availableDraggableResultset() ? 'move' : 'default' }">
                        <i class="fa fa-code"></i>
@@ -224,8 +215,7 @@ from dashboard.conf import USE_GRIDSTER, HAS_REPORT_ENABLED
 
     <div data-bind="css: { 'draggable-widget': true, 'disabled': !availableDraggableFilter() },
                     draggable: {data: draggableFilter(), isEnabled: availableDraggableFilter,
-                    options: {'start': function(event, ui){lastWindowScrollPosition = $(window).scrollTop();$('.card-body').slideUp('fast');},
-                              'stop': function(event, ui){$('.card-body').slideDown('fast', function(){$(window).scrollTop(lastWindowScrollPosition)});}}}"
+                    options: getDraggableOptions() }"
          title="${_('Filter Bar')}" rel="tooltip" data-placement="top">
          <a data-bind="style: { cursor: $root.availableDraggableFilter() ? 'move' : 'default' }">
                        <i class="fa fa-filter"></i>
@@ -234,8 +224,7 @@ from dashboard.conf import USE_GRIDSTER, HAS_REPORT_ENABLED
 
     <div data-bind="css: { 'draggable-widget': true, 'disabled': !availableDraggableLeaflet()},
                     draggable: {data: draggableLeafletMap(), isEnabled: availableDraggableLeaflet,
-                    options: {'start': function(event, ui){lastWindowScrollPosition = $(window).scrollTop();$('.card-body').slideUp('fast');},
-                              'stop': function(event, ui){$('.card-body').slideDown('fast', function(){$(window).scrollTop(lastWindowScrollPosition)});}}}"
+                    options: getDraggableOptions() }"
          title="${_('Marker Map')}" rel="tooltip" data-placement="top">
          <a data-bind="style: { cursor: 'move' }">
              <i class="fa fa-map-marker"></i>
@@ -245,8 +234,7 @@ from dashboard.conf import USE_GRIDSTER, HAS_REPORT_ENABLED
     <div data-bind="visible: $root.collection.supportAnalytics,
                     css: { 'draggable-widget': true, 'disabled': !hasAvailableFields() },
                     draggable: {data: draggableCounter(), isEnabled: hasAvailableFields,
-                    options: {'start': function(event, ui){lastWindowScrollPosition = $(window).scrollTop();$('.card-body').slideUp('fast');},
-                              'stop': function(event, ui){$('.card-body').slideDown('fast', function(){$(window).scrollTop(lastWindowScrollPosition)});}}}"
+                    options: getDraggableOptions() }"
          title="${_('Counter')}" rel="tooltip" data-placement="top">
          <a data-bind="style: { cursor: $root.hasAvailableFields() ? 'move' : 'default' }">
                        <i class="fa fa-superscript" style="font-size: 110%"></i>
@@ -257,8 +245,7 @@ from dashboard.conf import USE_GRIDSTER, HAS_REPORT_ENABLED
       <%def name="widgets()">
     <div data-bind="visible: ! $root.collection.supportAnalytics(), css: { 'draggable-widget': true, 'disabled': !availableDraggableChart() },
                     draggable: {data: draggableFacet(), isEnabled: availableDraggableChart,
-                    options: {'start': function(event, ui){lastWindowScrollPosition = $(window).scrollTop();$('.card-body').slideUp('fast');},
-                              'stop': function(event, ui){$('.card-body').slideDown('fast', function(){$(window).scrollTop(lastWindowScrollPosition)});}}}"
+                    options: getDraggableOptions() }"
          title="${_('Text Facet')}" rel="tooltip" data-placement="top">
          <a data-bind="style: { cursor: $root.availableDraggableChart() ? 'move' : 'default' }">
                        <i class="fa fa-sort-amount-asc"></i>
@@ -266,8 +253,7 @@ from dashboard.conf import USE_GRIDSTER, HAS_REPORT_ENABLED
     </div>
     <div data-bind="visible: $root.collection.supportAnalytics(), css: { 'draggable-widget': true, 'disabled': !availableDraggableChart() },
                     draggable: {data: draggableTextFacet(), isEnabled: availableDraggableChart,
-                    options: {'start': function(event, ui){lastWindowScrollPosition = $(window).scrollTop();$('.card-body').slideUp('fast');},
-                              'stop': function(event, ui){$('.card-body').slideDown('fast', function(){$(window).scrollTop(lastWindowScrollPosition)});}}}"
+                    options: getDraggableOptions() }"
          title="${_('Text Facet')}" rel="tooltip" data-placement="top">
          <a data-bind="style: { cursor: $root.availableDraggableChart() ? 'move' : 'default' }">
                        <i class="fa fa-sort-amount-asc"></i>
@@ -275,8 +261,7 @@ from dashboard.conf import USE_GRIDSTER, HAS_REPORT_ENABLED
     </div>
     <div data-bind="visible: ! $root.collection.supportAnalytics(), css: { 'draggable-widget': true, 'disabled': !availableDraggableChart() },
                     draggable: {data: draggablePie(), isEnabled: availableDraggableChart,
-                    options: {'start': function(event, ui){lastWindowScrollPosition = $(window).scrollTop();$('.card-body').slideUp('fast');},
-                              'stop': function(event, ui){$('.card-body').slideDown('fast', function(){$(window).scrollTop(lastWindowScrollPosition)});}}}"
+                    options: getDraggableOptions() }"
          title="${_('Pie Chart')}" rel="tooltip" data-placement="top">
          <a data-bind="style: { cursor: $root.availableDraggableChart() ? 'move' : 'default' }">
                        <i class="hcha hcha-pie-chart"></i>
@@ -284,8 +269,7 @@ from dashboard.conf import USE_GRIDSTER, HAS_REPORT_ENABLED
     </div>
     <div data-bind="visible: $root.collection.supportAnalytics(), css: { 'draggable-widget': true, 'disabled': !availableDraggableChart() },
                     draggable: {data: draggablePie2(), isEnabled: availableDraggableChart,
-                    options: {'start': function(event, ui){lastWindowScrollPosition = $(window).scrollTop();$('.card-body').slideUp('fast');},
-                              'stop': function(event, ui){$('.card-body').slideDown('fast', function(){$(window).scrollTop(lastWindowScrollPosition)});}}}"
+                    options: getDraggableOptions() }"
          title="${_('Pie Chart')}" rel="tooltip" data-placement="top">
          <a data-bind="style: { cursor: $root.availableDraggableChart() ? 'move' : 'default' }">
                        <i class="fa fa-pie-chart"></i>
@@ -294,8 +278,7 @@ from dashboard.conf import USE_GRIDSTER, HAS_REPORT_ENABLED
     <div data-bind="visible: ! $root.collection.supportAnalytics(),
                     css: { 'draggable-widget': true, 'disabled': !availableDraggableChart() },
                     draggable: {data: draggableBar(), isEnabled: availableDraggableChart,
-                    options: {'start': function(event, ui){lastWindowScrollPosition = $(window).scrollTop();$('.card-body').slideUp('fast');},
-                              'stop': function(event, ui){$('.card-body').slideDown('fast', function(){$(window).scrollTop(lastWindowScrollPosition)});}}}"
+                    options: getDraggableOptions() }"
          title="${_('Bar Chart')}" rel="tooltip" data-placement="top">
          <a data-bind="style: { cursor: $root.availableDraggableChart() ? 'move' : 'default' }">
                        <i class="hcha hcha-bar-chart"></i>
@@ -304,8 +287,7 @@ from dashboard.conf import USE_GRIDSTER, HAS_REPORT_ENABLED
     <div data-bind="visible: $root.collection.supportAnalytics(),
                     css: { 'draggable-widget': true, 'disabled': ! availableDraggableChart() },
                     draggable: {data: draggableBucket(), isEnabled: availableDraggableChart,
-                    options: {'start': function(event, ui){lastWindowScrollPosition = $(window).scrollTop();$('.card-body').slideUp('fast');},
-                              'stop': function(event, ui){$('.card-body').slideDown('fast', function(){$(window).scrollTop(lastWindowScrollPosition)});}}}"
+                    options: getDraggableOptions() }"
          title="${_('Chart')}" rel="tooltip" data-placement="top">
          <a data-bind="style: { cursor: $root.availableDraggableChart() ? 'move' : 'default' }">
                        <i class="fa fa-bar-chart"></i>
@@ -314,8 +296,7 @@ from dashboard.conf import USE_GRIDSTER, HAS_REPORT_ENABLED
     <div data-bind="visible: ! $root.collection.supportAnalytics(),
                     css: { 'draggable-widget': true, 'disabled': !availableDraggableNumbers() },
                     draggable: {data: draggableLine(), isEnabled: availableDraggableNumbers,
-                    options: {'start': function(event, ui){lastWindowScrollPosition = $(window).scrollTop();$('.card-body').slideUp('fast');},
-                              'stop': function(event, ui){$('.card-body').slideDown('fast', function(){$(window).scrollTop(lastWindowScrollPosition)});}}}"
+                    options: getDraggableOptions() }"
          title="${_('Line Chart')}" rel="tooltip" data-placement="top">
          <a data-bind="style: { cursor: $root.availableDraggableNumbers() ? 'move' : 'default' }">
                        <i class="hcha hcha-line-chart"></i>
@@ -323,8 +304,7 @@ from dashboard.conf import USE_GRIDSTER, HAS_REPORT_ENABLED
     </div>
     <div data-bind="visible: ! $root.collection.supportAnalytics(), css: { 'draggable-widget': true, 'disabled': false },
                     draggable: {data: draggableTree(), isEnabled: true,
-                    options: {'start': function(event, ui){lastWindowScrollPosition = $(window).scrollTop();$('.card-body').slideUp('fast');},
-                              'stop': function(event, ui){$('.card-body').slideDown('fast', function(){$(window).scrollTop(lastWindowScrollPosition)});}}}"
+                    options: getDraggableOptions() }"
          title="${_('Tree')}" rel="tooltip" data-placement="top">
          <a data-bind="style: { cursor: $root.availableDraggableChart() ? 'move' : 'default' }">
                        <i class="fa fa-sitemap fa-rotate-270"></i>
@@ -332,8 +312,7 @@ from dashboard.conf import USE_GRIDSTER, HAS_REPORT_ENABLED
     </div>
     <div data-bind="visible: $root.collection.supportAnalytics(), css: { 'draggable-widget': true, 'disabled': false },
                     draggable: {data: draggableTree2(), isEnabled: true,
-                    options: {'start': function(event, ui){lastWindowScrollPosition = $(window).scrollTop();$('.card-body').slideUp('fast');},
-                              'stop': function(event, ui){$('.card-body').slideDown('fast', function(){$(window).scrollTop(lastWindowScrollPosition)});}}}"
+                    options: getDraggableOptions() }"
          title="${_('Tree')}" rel="tooltip" data-placement="top">
          <a data-bind="style: { cursor: $root.availableDraggableChart() ? 'move' : 'default' }">
                        <i class="fa fa-sitemap fa-rotate-270"></i>
@@ -342,8 +321,7 @@ from dashboard.conf import USE_GRIDSTER, HAS_REPORT_ENABLED
     <div data-bind="visible: ! $root.collection.supportAnalytics(),
                     css: { 'draggable-widget': true, 'disabled': false },
                     draggable: {data: draggableHeatmap(), isEnabled: true,
-                    options: {'start': function(event, ui){lastWindowScrollPosition = $(window).scrollTop();$('.card-body').slideUp('fast');},
-                              'stop': function(event, ui){$('.card-body').slideDown('fast', function(){$(window).scrollTop(lastWindowScrollPosition)});}}}"
+                    options: getDraggableOptions() }"
          title="${_('Heatmap')}" rel="tooltip" data-placement="top">
          <a data-bind="style: { cursor: $root.availableDraggableChart() ? 'move' : 'default' }">
                        <i class="fa fa-th"></i>
@@ -351,8 +329,7 @@ from dashboard.conf import USE_GRIDSTER, HAS_REPORT_ENABLED
     </div>
     <div data-bind="visible: ! $root.collection.supportAnalytics(), css: { 'draggable-widget': true, 'disabled': ! availableDraggableHistogram() },
                     draggable: {data: draggableHistogram(), isEnabled: availableDraggableHistogram,
-                    options: {'start': function(event, ui){lastWindowScrollPosition = $(window).scrollTop();$('.card-body').slideUp('fast');},
-                              'stop': function(event, ui){$('.card-body').slideDown('fast', function(){$(window).scrollTop(lastWindowScrollPosition)});}}}"
+                    options: getDraggableOptions() }"
          title="${_('Timeline')}" rel="tooltip" data-placement="top">
          <a data-bind="style: { cursor: $root.availableDraggableHistogram() ? 'move' : 'default' }">
                        <i class="hcha hcha-timeline-chart"></i>
@@ -360,8 +337,7 @@ from dashboard.conf import USE_GRIDSTER, HAS_REPORT_ENABLED
     </div>
     <div data-bind="visible: $root.collection.supportAnalytics(), css: { 'draggable-widget': true, 'disabled': ! availableTimeline() },
                     draggable: {data: draggableTimeline(), isEnabled: availableTimeline,
-                    options: {'start': function(event, ui){lastWindowScrollPosition = $(window).scrollTop();$('.card-body').slideUp('fast');},
-                              'stop': function(event, ui){$('.card-body').slideDown('fast', function(){$(window).scrollTop(lastWindowScrollPosition)});}}}"
+                    options: getDraggableOptions() }"
          title="${_('Timeline')}" rel="tooltip" data-placement="top">
          <a data-bind="style: { cursor: $root.availableTimeline() ? 'move' : 'default' }">
                        <i class="fa fa-line-chart"></i>
@@ -369,8 +345,7 @@ from dashboard.conf import USE_GRIDSTER, HAS_REPORT_ENABLED
     </div>
     <div data-bind="visible: ! $root.collection.supportAnalytics(), css: { 'draggable-widget': true, 'disabled': ! availableDraggableMap() },
                     draggable: {data: draggableMap(), isEnabled: availableDraggableMap,
-                    options: {'start': function(event, ui){lastWindowScrollPosition = $(window).scrollTop();$('.card-body').slideUp('fast');},
-                              'stop': function(event, ui){$('.card-body').slideDown('fast', function(){$(window).scrollTop(lastWindowScrollPosition)});}}}"
+                    options: getDraggableOptions() }"
          title="${_('Gradient Map')}" rel="tooltip" data-placement="top">
          <a data-bind="style: { cursor: $root.availableDraggableMap() ? 'move' : 'default' }">
                        <i class="hcha hcha-map-chart"></i>
@@ -378,8 +353,7 @@ from dashboard.conf import USE_GRIDSTER, HAS_REPORT_ENABLED
     </div>
     <div data-bind="visible: $root.collection.supportAnalytics(), css: { 'draggable-widget': true, 'disabled': ! availableDraggableMap() },
                     draggable: {data: draggableGradienMap(), isEnabled: availableDraggableMap,
-                    options: {'start': function(event, ui){lastWindowScrollPosition = $(window).scrollTop();$('.card-body').slideUp('fast');},
-                              'stop': function(event, ui){$('.card-body').slideDown('fast', function(){$(window).scrollTop(lastWindowScrollPosition)});}}}"
+                    options: getDraggableOptions() }"
          title="${_('Gradient Map')}" rel="tooltip" data-placement="top">
          <a data-bind="style: { cursor: $root.availableDraggableMap() ? 'move' : 'default' }">
                        <i class="hcha hcha-map-chart"></i>
@@ -389,8 +363,7 @@ from dashboard.conf import USE_GRIDSTER, HAS_REPORT_ENABLED
     <div data-bind="visible: $root.collection.supportAnalytics(),
                     css: { 'draggable-widget': true, 'disabled': false },
                     draggable: {data: draggableDocument(), isEnabled: true,
-                    options: {'start': function(event, ui){lastWindowScrollPosition = $(window).scrollTop();$('.card-body').slideUp('fast');},
-                              'stop': function(event, ui){$('.card-body').slideDown('fast', function(){$(window).scrollTop(lastWindowScrollPosition)});}}}"
+                    options: getDraggableOptions() }"
          title="${_('Document')}" rel="tooltip" data-placement="top">
          <a data-bind="style: { cursor: true ? 'move' : 'default' }">
                        <i class="fa fa-file-o"></i>
@@ -2776,7 +2749,7 @@ ${ dashboard.import_layout(True) }
 
 %if USE_GRIDSTER.get():
 <script src="${ static('desktop/ext/js/jquery/plugins/jquery.gridster.with-extras.min.js') }"></script>
-<script src="${ static('desktop/ext/js/gridster-knockout.min.js') }"></script>
+<script src="${ static('desktop/js/gridster-knockout.js') }"></script>
 %endif
 
 ${ dashboard.import_bindings() }
@@ -2954,6 +2927,35 @@ function getFormat(format, minMaxDiff, widget) {
 
 
 var lastWindowScrollPosition = 0;
+%if USE_GRIDSTER.get():
+var getDraggableOptions = function() {
+  return {
+    'start': function (event, ui) {
+      $(ui.helper).css('z-index','999999');
+    }
+  };
+};
+%else:
+  var getDraggableOptions = function (options) {
+    return {
+      'start': function (event, ui) {
+        lastWindowScrollPosition = $(window).scrollTop();
+        $('.card-body').slideUp('fast');
+        if (options && options.start) {
+          options.start();
+        }
+      },
+      'stop': function (event, ui) {
+        $('.card-body').slideDown('fast', function () {
+          $(window).scrollTop(lastWindowScrollPosition)
+        });
+        if (options && options.stop) {
+          options.stop();
+        }
+      }
+    }
+  }
+%endif
 
 function pieChartDataTransformer(data) {
   var _data = [];
@@ -3599,7 +3601,7 @@ $(document).ready(function () {
     widget_base_dimensions: ['auto', WIDGET_BASE_HEIGHT],
     avoid_overlapped_widgets: true,
     max_cols: 12,
-    max_rows: 60,
+    max_rows: 6000,
     resize: {
       enabled: true,
       stop: function (event, ui, $widget) {
@@ -3650,6 +3652,20 @@ $(document).ready(function () {
 
   huePubSub.subscribe('gridster.remove.widget', function (widgetId) {
     $(".gridster>ul").data('gridster').remove_widget($("#wdg_" + widgetId).parents('li.gs-w'));
+  }, 'dashboard')
+
+  huePubSub.subscribe('gridster.add.widget', function (widgetId){
+    var newPosition = $('.gridster ul').data('gridster').next_position(12, 12);
+    searchViewModel.gridItems.push(ko.mapping.fromJS({
+      col: newPosition.col,
+      row: newPosition.row,
+      size_x: 12,
+      size_y: 12,
+      widget: searchViewModel.getWidgetById(widgetId),
+      callback: function(el){
+        $('.gridster ul').data('gridster').move_widget(el, 1, 1);
+      }
+    }));
   }, 'dashboard');
 
 %endif
@@ -3854,6 +3870,55 @@ $(document).ready(function () {
 
   var selectedWidget = null;
   var selectedRow = null;
+  %if USE_GRIDSTER.get():
+  function showAddFacetDemiModal(widget) {
+    var fakeRow = searchViewModel.columns()[0].addEmptyRow(true);
+    fakeRow.addWidget(widget)
+
+    if (["resultset-widget", "html-resultset-widget", "filter-widget", "leafletmap-widget"].indexOf(widget.widgetType()) == -1) {
+      searchViewModel.collection.template.fieldsModalFilter("");
+      searchViewModel.collection.template.fieldsModalType(widget.widgetType());
+
+      selectedWidget = widget;
+
+      if (searchViewModel.collection.template.availableWidgetFields().length == 1){
+        addFacetDemiModalFieldPreview(searchViewModel.collection.template.availableWidgetFields()[0]);
+      }
+      else {
+        $('#addFacetInput').typeahead({
+          'source': searchViewModel.collection.template.availableWidgetFieldsNames(),
+          'updater': function (item) {
+            addFacetDemiModalFieldPreview({'name': function () {
+              return item
+            }});
+            return item;
+          }
+        });
+        $("#addFacetDemiModal").modal("show");
+        $("#addFacetDemiModal input[type='text']").focus();
+      }
+    }
+    else {
+      huePubSub.publish('gridster.add.widget', widget.id());
+    }
+  }
+
+
+  function addFacetDemiModalFieldPreview(field) {
+    var _existingFacet = searchViewModel.collection.getFacetById(selectedWidget.id());
+    if (selectedWidget != null) {
+      selectedWidget.hasBeenSelected = true;
+      selectedWidget.isLoading(true);
+      searchViewModel.collection.addFacet({'name': field.name(), 'widget_id': selectedWidget.id(), 'widgetType': selectedWidget.widgetType()});
+      if (_existingFacet != null) {
+        _existingFacet.label(field.name());
+        _existingFacet.field(field.name());
+      }
+      $("#addFacetDemiModal").modal("hide");
+      huePubSub.publish('gridster.add.widget', selectedWidget.id());
+    }
+  }
+  %else:
   function showAddFacetDemiModal(widget, row) {
     if (["resultset-widget", "html-resultset-widget", "filter-widget", "leafletmap-widget"].indexOf(widget.widgetType()) == -1) {
       searchViewModel.collection.template.fieldsModalFilter("");
@@ -3901,6 +3966,7 @@ $(document).ready(function () {
       }
     }
   }
+  %endif
 
   function addFacetDemiModalFieldCancel() {
     searchViewModel.removeWidget(selectedWidget);

Some files were not shown because too many files changed in this diff