diff --git a/src/kibana/apps/dashboard/directives/grid.js b/src/kibana/apps/dashboard/directives/grid.js index a2f606fecd77..a78a2a5c435f 100644 --- a/src/kibana/apps/dashboard/directives/grid.js +++ b/src/kibana/apps/dashboard/directives/grid.js @@ -11,89 +11,177 @@ define(function (require) { return { restrict: 'A', scope : { - grid: '=', - control: '=' + panels: '=' }, - link: function ($scope, elem) { - var width = elem.width(); + link: function ($scope, $el) { + var $container = $el.parent(); + var $window = $(window); + var $body = $(document.body); var gridster; // defined in init() - $scope.control = $scope.control || {}; + // number of columns to render + var COLS = 12; + // number of pixed between each column/row + var SPACER = 10; - $scope.$watch('grid', function (grid) { - if (grid === void 0) return; // wait until we have something + var init = function () { + $el.addClass('gridster'); - init(); - $scope.control.unserializeGrid(grid); - }); - - var init = _.once(function () { - elem.addClass('gridster'); - - elem.on('click', 'li i.remove', function (event) { - var target = event.target.parentNode.parentNode; - gridster.remove_widget(target); - }); - - gridster = elem.gridster({ + gridster = $el.gridster({ + max_cols: COLS, + min_cols: COLS, autogenerate_stylesheet: false, - widget_margins: [5, 5], - widget_base_dimensions: [((width - 100) / 12), 100], - min_cols: 12, - max_cols: 12, resize: { - enabled: true + enabled: true, + stop: readGridsterChangeHandler }, - serialize_params: function (el, wgd) { - return { - col: wgd.col, - row: wgd.row, - size_x: wgd.size_x, - size_y: wgd.size_y, - params: el.data('params') - }; + draggable: { + stop: readGridsterChangeHandler } }).data('gridster'); - gridster.generate_stylesheet({namespace: '.gridster'}); - }); - $scope.control.clearGrid = function (cb) { - gridster.remove_all_widgets(); - }; + layout(); + var safeLayout = _.debounce(layout, 200); + $window.on('resize', safeLayout); + + $scope.$watchCollection('panels', function (panels) { + var currentPanels = gridster.$widgets.toArray().map(function (el) { + return getPanelFor(el); + }); + + // panels that are now missing from the panels array + var removed = _.difference(currentPanels, panels); + + // panels that have been added + var added = _.difference(panels, currentPanels); + + if (removed.length) removed.forEach(removePanel); + if (added.length) added.forEach(addPanel); + + // ensure that every panel can be serialized now that we are done + $scope.panels.forEach(makePanelSerializeable); - $scope.control.unserializeGrid = function (grid) { - if (typeof grid === 'string') { - grid = JSON.stringify(grid); - } - gridster.remove_all_widgets(); - grid.forEach(function (panel) { - $scope.control.addWidget(panel); + // alert interested parties that we have finished processing changes to the panels + if (added.length || removed.length) $scope.$root.$broadcast('change:vis'); + }); + + $scope.$on('$destroy', function () { + $window.off('destroy', safeLayout); + + if (!gridster) return; + gridster.$widgets.each(function (el) { + removePanel(getPanelFor(el)); + }); }); }; - $scope.control.serializeGrid = function () { - return gridster.serialize(); + // return the panel object for an element. + // + // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + // ALWAYS CALL makePanelSerializeable AFTER YOU ARE DONE WITH IT + // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + var getPanelFor = function (el) { + var $el = el.jquery ? el : $(el); + var panel = $el.data('panel'); + + panel.$el = $el; + panel.$scope = $el.data('$scope'); + + return panel; }; - $scope.control.addWidget = function (panel) { - panel = panel || {}; + // since the $el and $scope are circular structures, they need to be + // removed from panel before it can be serialized (we also wouldn't + // want them to show up in the url) + var makePanelSerializeable = function (panel) { + delete panel.$el; + delete panel.$scope; + }; + + // tell gridster to remove the panel, and cleanup our metadata + var removePanel = function (panel) { + gridster.remove_widget(panel.$el); + panel.$scope.$destroy(); + + panel.$el.removeData('panel'); + panel.$el.removeData('panel$scope'); + }; + + // tell gridster to add the panel, and create additional meatadata like $scope + var addPanel = function (panel) { _.defaults(panel, { size_x: 3, - size_y: 2, - params: { - type: 'new' - } + size_y: 2 + }); + + // ignore panels that don't have vis id's + if (!panel.visId) throw new Error('missing visId on panel'); + + panel.$scope = $scope.$new(); + panel.$scope.panel = panel; + + panel.$el = $compile('
{{ data | json }}', - scope: { - vis: '=' - }, - link: function ($scope, $el) { - var vis = $scope.vis; - var notify = createNotifier({ - location: vis.type + ' visualization' - }); - - vis.searchSource.onResults().then(function onResults(resp) { - notify.event('render visualization'); - $scope.data = vis.buildChartDataFromResponse(resp); - notify.event('render visualization', true); - - window.canvasVisSource = vis.searchSource; - return vis.searchSource.onResults().then(onResults); - }).catch(notify.fatal); - } - }; - } - - require('modules').get('kibana/directive') - .directive('visualization', VisualizationDirective); -}); \ No newline at end of file diff --git a/src/kibana/apps/visualize/directives/visualize.js b/src/kibana/apps/visualize/directives/visualize.js new file mode 100644 index 000000000000..cc6566fd9e09 --- /dev/null +++ b/src/kibana/apps/visualize/directives/visualize.js @@ -0,0 +1,32 @@ +define(function (require) { + var k4d3 = require('k4d3'); + var $ = require('jquery'); + + require('css!../styles/visualization.css'); + + var module = require('modules').get('kibana/directive'); + + module.directive('visualize', function (createNotifier) { + return { + restrict: 'E', + link: function ($scope, $el) { + $scope.$watch('vis', function (vis, prevVis) { + if (prevVis) prevVis.destroy(); + if (!vis) return; + + var notify = createNotifier({ + location: vis.type + ' visualization' + }); + + vis.searchSource.onResults(function onResults(resp) { + var $disp = $('
'); + $disp.text(JSON.stringify(vis.buildChartDataFromResponse(resp), null, ' ')); + $el.html($disp); + }); + + $scope.$root.$broadcast('ready:vis'); + }); + } + }; + }); +}); \ No newline at end of file diff --git a/src/kibana/apps/visualize/editor.html b/src/kibana/apps/visualize/editor.html index 5c298c64c4d5..e2e432842c2a 100644 --- a/src/kibana/apps/visualize/editor.html +++ b/src/kibana/apps/visualize/editor.html @@ -35,7 +35,7 @@-diff --git a/src/kibana/apps/visualize/index.js b/src/kibana/apps/visualize/index.js index 618f537748aa..61ccc5d498fb 100644 --- a/src/kibana/apps/visualize/index.js +++ b/src/kibana/apps/visualize/index.js @@ -5,7 +5,7 @@ define(function (require) { require('./controllers/wizard'); require('./directives/canvas'); - require('./directives/visualization'); + require('./directives/visualize'); require('./directives/config_category'); require('routes') diff --git a/src/kibana/apps/visualize/styles/main.css b/src/kibana/apps/visualize/styles/main.css index 49b03a83b888..df3f33017712 100644 --- a/src/kibana/apps/visualize/styles/main.css +++ b/src/kibana/apps/visualize/styles/main.css @@ -112,7 +112,7 @@ .vis-editor-content .vis-config-panel .agg-config-interval td:first-child { padding-left: 0px; } -.vis-editor-content .vis-canvas visualization { +.vis-editor-content vis-canvas { display: block; background: black; height: 800px; diff --git a/src/kibana/apps/visualize/styles/main.less b/src/kibana/apps/visualize/styles/main.less index 21734a13c1de..f8f708c62480 100644 --- a/src/kibana/apps/visualize/styles/main.less +++ b/src/kibana/apps/visualize/styles/main.less @@ -58,15 +58,13 @@ } } - .vis-canvas { - visualization { - display: block; - background: black; - height: 800px; - width: 95%; - margin: 0 auto; - overflow: auto; - } + vis-canvas { + display: block; + background: black; + height: 800px; + width: 95%; + margin: 0 auto; + overflow: auto; } } diff --git a/src/kibana/apps/visualize/styles/visualization.css b/src/kibana/apps/visualize/styles/visualization.css new file mode 100644 index 000000000000..bf2f70b51fd8 --- /dev/null +++ b/src/kibana/apps/visualize/styles/visualization.css @@ -0,0 +1,6 @@ +visualize { + display: block; + height: 100%; + width: 100%; + overflow: auto; +} diff --git a/src/kibana/apps/visualize/styles/visualization.less b/src/kibana/apps/visualize/styles/visualization.less new file mode 100644 index 000000000000..49e007f0d457 --- /dev/null +++ b/src/kibana/apps/visualize/styles/visualization.less @@ -0,0 +1,6 @@ +visualize { + display: block; + height: 100%; + width: 100%; + overflow: auto; +} \ No newline at end of file diff --git a/src/kibana/components/courier/data_source/abstract.js b/src/kibana/components/courier/data_source/abstract.js index 3c9260d5a231..3ef22e2bbba5 100644 --- a/src/kibana/components/courier/data_source/abstract.js +++ b/src/kibana/components/courier/data_source/abstract.js @@ -129,14 +129,14 @@ define(function (require) { * be fetched on the next run of the courier * @return {Promise} */ - SourceAbstract.prototype.onResults = function () { + SourceAbstract.prototype.onResults = function (handler) { var source = this; return new Promise.emitter(function (resolve, reject, defer) { source._courier._pendingRequests.push({ source: source, defer: defer }); - }); + }, handler); }; /** diff --git a/src/kibana/components/saved_object/saved_object.js b/src/kibana/components/saved_object/saved_object.js index 34fe83a5b92d..bc903dfca46d 100644 --- a/src/kibana/components/saved_object/saved_object.js +++ b/src/kibana/components/saved_object/saved_object.js @@ -176,6 +176,7 @@ define(function (require) { */ this.destroy = function () { docSource.cancelPending(); + if (obj.searchSource) obj.searchSource.cancelPending(); }; } diff --git a/src/kibana/styles/_mixins.less b/src/kibana/styles/_mixins.less new file mode 100644 index 000000000000..ed9ca4f17c38 --- /dev/null +++ b/src/kibana/styles/_mixins.less @@ -0,0 +1,138 @@ +.text-shadow (@string: 0 1px 3px rgba(0, 0, 0, 0.25)) { + text-shadow: @string; +} +.box-shadow (@string) { + -webkit-box-shadow: @string; + -moz-box-shadow: @string; + box-shadow: @string; +} +.drop-shadow (@x: 0, @y: 1px, @blur: 2px, @spread: 0, @alpha: 0.25) { + -webkit-box-shadow: @x @y @blur @spread rgba(0, 0, 0, @alpha); + -moz-box-shadow: @x @y @blur @spread rgba(0, 0, 0, @alpha); + box-shadow: @x @y @blur @spread rgba(0, 0, 0, @alpha); +} +.inner-shadow (@x: 0, @y: 1px, @blur: 2px, @spread: 0, @alpha: 0.25) { + -webkit-box-shadow: inset @x @y @blur @spread rgba(0, 0, 0, @alpha); + -moz-box-shadow: inset @x @y @blur @spread rgba(0, 0, 0, @alpha); + box-shadow: inset @x @y @blur @spread rgba(0, 0, 0, @alpha); +} + +.box-sizing (@type: border-box) { + -webkit-box-sizing: @type; + -moz-box-sizing: @type; + box-sizing: @type; +} + +.border-radius (@radius: 5px) { + -webkit-border-radius: @radius; + -moz-border-radius: @radius; + border-radius: @radius; + + -moz-background-clip: padding; + -webkit-background-clip: padding-box; + background-clip: padding-box; +} +.border-radiuses (@topright: 0, @bottomright: 0, @bottomleft: 0, @topleft: 0) { + -webkit-border-top-right-radius: @topright; + -webkit-border-bottom-right-radius: @bottomright; + -webkit-border-bottom-left-radius: @bottomleft; + -webkit-border-top-left-radius: @topleft; + + -moz-border-radius-topright: @topright; + -moz-border-radius-bottomright: @bottomright; + -moz-border-radius-bottomleft: @bottomleft; + -moz-border-radius-topleft: @topleft; + + border-top-right-radius: @topright; + border-bottom-right-radius: @bottomright; + border-bottom-left-radius: @bottomleft; + border-top-left-radius: @topleft; + + -moz-background-clip: padding; + -webkit-background-clip: padding-box; + background-clip: padding-box; +} + +.opacity (@opacity: 0.5) { + -webkit-opacity: @opacity; + -moz-opacity: @opacity; + opacity: @opacity; +} + +.gradient (@startColor: #eee, @endColor: white) { + background-color: @startColor; + background: -webkit-gradient(linear, left top, left bottom, from(@startColor), to(@endColor)); + background: -webkit-linear-gradient(top, @startColor, @endColor); + background: -moz-linear-gradient(top, @startColor, @endColor); + background: -ms-linear-gradient(top, @startColor, @endColor); + background: -o-linear-gradient(top, @startColor, @endColor); +} +.horizontal-gradient (@startColor: #eee, @endColor: white) { + background-color: @startColor; + background-image: -webkit-gradient(linear, left top, right top, from(@startColor), to(@endColor)); + background-image: -webkit-linear-gradient(left, @startColor, @endColor); + background-image: -moz-linear-gradient(left, @startColor, @endColor); + background-image: -ms-linear-gradient(left, @startColor, @endColor); + background-image: -o-linear-gradient(left, @startColor, @endColor); +} + +.animation (@name, @duration: 300ms, @delay: 0, @ease: ease) { + -webkit-animation: @name @duration @delay @ease; + -moz-animation: @name @duration @delay @ease; + -ms-animation: @name @duration @delay @ease; +} + +.transition (@transition) { + -webkit-transition: @transition; + -moz-transition: @transition; + -ms-transition: @transition; + -o-transition: @transition; +} +.transform(@string){ + -webkit-transform: @string; + -moz-transform: @string; + -ms-transform: @string; + -o-transform: @string; +} +.scale (@factor) { + -webkit-transform: scale(@factor); + -moz-transform: scale(@factor); + -ms-transform: scale(@factor); + -o-transform: scale(@factor); +} +.rotate (@deg) { + -webkit-transform: rotate(@deg); + -moz-transform: rotate(@deg); + -ms-transform: rotate(@deg); + -o-transform: rotate(@deg); +} +.skew (@deg, @deg2) { + -webkit-transform: skew(@deg, @deg2); + -moz-transform: skew(@deg, @deg2); + -ms-transform: skew(@deg, @deg2); + -o-transform: skew(@deg, @deg2); +} +.translate (@x, @y:0) { + -webkit-transform: translate(@x, @y); + -moz-transform: translate(@x, @y); + -ms-transform: translate(@x, @y); + -o-transform: translate(@x, @y); +} +.translate3d (@x, @y: 0, @z: 0) { + -webkit-transform: translate3d(@x, @y, @z); + -moz-transform: translate3d(@x, @y, @z); + -ms-transform: translate3d(@x, @y, @z); + -o-transform: translate3d(@x, @y, @z); +} +.perspective (@value: 1000) { + -webkit-perspective: @value; + -moz-perspective: @value; + -ms-perspective: @value; + perspective: @value; +} +.transform-origin (@x:center, @y:center) { + -webkit-transform-origin: @x @y; + -moz-transform-origin: @x @y; + -ms-transform-origin: @x @y; + -o-transform-origin: @x @y; +} \ No newline at end of file diff --git a/src/kibana/styles/main.css b/src/kibana/styles/main.css index 28d6f1eb0dab..b49926634d62 100644 --- a/src/kibana/styles/main.css +++ b/src/kibana/styles/main.css @@ -6540,9 +6540,6 @@ notifications { .navbar-nav li a { cursor: pointer; } -.container-default { - margin-top: 10px; -} .navbar { margin-bottom: 0px!important; } diff --git a/src/kibana/styles/main.less b/src/kibana/styles/main.less index 218fc3900764..0dd1b204a2f5 100644 --- a/src/kibana/styles/main.less +++ b/src/kibana/styles/main.less @@ -47,10 +47,6 @@ notifications { cursor: pointer; } -.container-default { - margin-top: 10px; -} - .navbar { margin-bottom: 0px!important; } diff --git a/src/kibana/utils/config_template.js b/src/kibana/utils/config_template.js index 44cec3cc0221..4bae945aabf5 100644 --- a/src/kibana/utils/config_template.js +++ b/src/kibana/utils/config_template.js @@ -1,28 +1,29 @@ define(function (require) { function ConfigTemplate(templates) { - this.current = null; + var template = this; + template.current = null; - this.toggle = function (name) { + template.toggle = function (name) { var toSwitch = templates[name]; - if (this.current === toSwitch) { - this.current = null; + if (template.current === toSwitch) { + template.current = null; return false; } else { - this.current = toSwitch; + template.current = toSwitch; return true; } }; - this.close = function (name) { + template.close = function (name) { var toClose = templates[name]; - if (this.current === toClose) { - this.current = null; + if (template.current === toClose) { + template.current = null; } }; - this.toString = function () { - return this.current; + template.toString = function () { + return template.current; }; } diff --git a/src/kibana/utils/rison.js b/src/kibana/utils/rison.js index c4b68dd7dfba..819c57ec60e2 100644 --- a/src/kibana/utils/rison.js +++ b/src/kibana/utils/rison.js @@ -190,7 +190,8 @@ rison.quote = function(x) { return "'" + x + "'"; }, undefined: function (x) { - throw new Error("rison can't encode the undefined value"); + // ignore undefined just like JSON + // throw new Error("rison can't encode the undefined value"); } };+