mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
Merge remote-tracking branch 'upstream/vislib/refactor' into vislib/refactor
Conflicts: src/kibana/components/vislib/lib/handler.js src/kibana/components/vislib/lib/legend.js
This commit is contained in:
commit
bcd03c3f5a
136 changed files with 2803 additions and 712 deletions
4
.bowerrc
4
.bowerrc
|
@ -1,3 +1,3 @@
|
|||
{
|
||||
"directory": "./src/bower_components"
|
||||
}
|
||||
"directory": "./src/kibana/bower_components"
|
||||
}
|
||||
|
|
5
.gitignore
vendored
5
.gitignore
vendored
|
@ -1,7 +1,8 @@
|
|||
.DS_Store
|
||||
node_modules
|
||||
src/bower_components
|
||||
bower_components
|
||||
**/*.css
|
||||
trash
|
||||
build
|
||||
target
|
||||
target
|
||||
.jruby
|
1
.ruby-version
Normal file
1
.ruby-version
Normal file
|
@ -0,0 +1 @@
|
|||
1.9.3-p547
|
|
@ -12,9 +12,12 @@ module.exports = function (grunt) {
|
|||
target: __dirname + '/target', // location of the compressed build targets
|
||||
buildApp: __dirname + '/build/kibana', // build directory for the app
|
||||
|
||||
jrubyVersion: '1.7.14',
|
||||
jrubyPath: __dirname + '/.jruby',
|
||||
|
||||
unitTestDir: __dirname + '/test/unit',
|
||||
testUtilsDir: __dirname + '/test/utils',
|
||||
bowerComponentsDir: __dirname + '/src/bower_components',
|
||||
bowerComponentsDir: __dirname + '/src/kibana/bower_components',
|
||||
|
||||
meta: {
|
||||
banner: '/*! <%= package.name %> - v<%= package.version %> - ' +
|
||||
|
@ -36,4 +39,4 @@ module.exports = function (grunt) {
|
|||
|
||||
// load task definitions
|
||||
grunt.loadTasks('tasks');
|
||||
};
|
||||
};
|
||||
|
|
7
LICENSE.md
Normal file
7
LICENSE.md
Normal file
|
@ -0,0 +1,7 @@
|
|||
Copyright 2012-2014 Elasticsearch BV
|
||||
|
||||
Licensed 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.
|
2
TODOS.md
2
TODOS.md
|
@ -14,6 +14,8 @@
|
|||
- Change index to be the resolved in some way, last three months, last hour, last year, whatever
|
||||
- **[src/kibana/components/vislib/vis.js](https://github.com/elasticsearch/kibana4/blob/master/src/kibana/components/vislib/vis.js)**
|
||||
- need to come up with a solution for resizing when no data is available
|
||||
- **[src/kibana/components/vislib/visualizations/column_chart.js](https://github.com/elasticsearch/kibana4/blob/master/src/kibana/components/vislib/visualizations/column_chart.js)**
|
||||
- refactor so that this is called from the data module
|
||||
- **[src/kibana/components/visualize/visualize.js](https://github.com/elasticsearch/kibana4/blob/master/src/kibana/components/visualize/visualize.js)**
|
||||
- we need to have some way to clean up result requests
|
||||
- **[src/kibana/directives/rows.js](https://github.com/elasticsearch/kibana4/blob/master/src/kibana/directives/rows.js)**
|
||||
|
|
42
bower.json
42
bower.json
|
@ -20,32 +20,32 @@
|
|||
"tests"
|
||||
],
|
||||
"dependencies": {
|
||||
"requirejs": "~2.1.10",
|
||||
"angular": "~1.2.14",
|
||||
"lodash": "~2.4.1",
|
||||
"d3": "~3.4.8",
|
||||
"angular-route": "~1.2.14",
|
||||
"gridster": "~0.5.0",
|
||||
"angular-mocks": "~1.2.14",
|
||||
"font-awesome": "~4.0.3",
|
||||
"requirejs-text": "~2.0.10",
|
||||
"async": "~0.2.10",
|
||||
"bootstrap": "~3.1.1",
|
||||
"jquery": "~2.1.0",
|
||||
"moment": "~2.5.1",
|
||||
"require-css": "~0.1.2",
|
||||
"angular-bootstrap": "~0.10.0",
|
||||
"jsonpath": "*",
|
||||
"moment-timezone": "~0.0.3",
|
||||
"angular-bindonce": "~0.3.1",
|
||||
"angular-ui-ace": "bower",
|
||||
"angular-bootstrap": "~0.10.0",
|
||||
"angular-elastic": "~2.3.3",
|
||||
"inflection": "~1.3.5",
|
||||
"FileSaver": "*",
|
||||
"elasticsearch": "*",
|
||||
"angular-mocks": "~1.2.14",
|
||||
"angular-route": "~1.2.14",
|
||||
"angular-ui-ace": "bower",
|
||||
"async": "~0.2.10",
|
||||
"bluebird": "~2.1.3",
|
||||
"bootstrap": "~3.1.1",
|
||||
"d3": "~3.4.8",
|
||||
"elasticsearch": "*",
|
||||
"Faker": "~1.1.0",
|
||||
"FileSaver": "*",
|
||||
"font-awesome": "~4.0.3",
|
||||
"gridster": "~0.5.0",
|
||||
"inflection": "~1.3.5",
|
||||
"jquery": "~2.1.0",
|
||||
"jsonpath": "*",
|
||||
"lesshat": "~3.0.2",
|
||||
"Faker": "~1.1.0"
|
||||
"lodash": "~2.4.1",
|
||||
"moment": "~2.5.1",
|
||||
"moment-timezone": "~0.0.3",
|
||||
"require-css": "~0.1.2",
|
||||
"requirejs": "~2.1.10",
|
||||
"requirejs-text": "~2.0.10"
|
||||
},
|
||||
"devDependencies": {}
|
||||
}
|
||||
|
|
10
package.json
10
package.json
|
@ -9,7 +9,7 @@
|
|||
"bluebird": "~2.0.7",
|
||||
"connect": "~2.19.5",
|
||||
"event-stream": "~3.1.5",
|
||||
"expect.js": "~0.2.0",
|
||||
"expect.js": "~0.3.1",
|
||||
"grunt": "~0.4.5",
|
||||
"grunt-contrib-clean": "~0.5.0",
|
||||
"grunt-contrib-compress": "~0.9.1",
|
||||
|
@ -20,15 +20,21 @@
|
|||
"grunt-contrib-requirejs": "~0.4.4",
|
||||
"grunt-contrib-watch": "~0.5.3",
|
||||
"grunt-mocha": "~0.4.10",
|
||||
"grunt-replace": "^0.7.9",
|
||||
"grunt-run": "^0.2.3",
|
||||
"http-proxy": "~1.1.4",
|
||||
"husky": "~0.6.0",
|
||||
"istanbul": "~0.2.4",
|
||||
"load-grunt-config": "~0.7.0",
|
||||
"lodash": "~2.4.1",
|
||||
"mkdirp": "^0.5.0",
|
||||
"mocha": "~1.20.1",
|
||||
"path-browserify": "0.0.0",
|
||||
"progress": "^1.1.8",
|
||||
"request": "^2.40.0",
|
||||
"requirejs": "~2.1.14",
|
||||
"rjs-build-analysis": "0.0.3"
|
||||
"rjs-build-analysis": "0.0.3",
|
||||
"tar": "^1.0.1"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "grunt test",
|
||||
|
|
|
@ -1,30 +0,0 @@
|
|||
require "java"
|
||||
require "warbler"
|
||||
|
||||
HERE = File.expand_path(File.dirname(__FILE__))
|
||||
|
||||
task "default" => "jar:run"
|
||||
|
||||
namespace "jar" do
|
||||
desc "Run the project jar file"
|
||||
task "run" => "jar" do
|
||||
exec("cd #{HERE} && rm -rf /tmp/kibana* && cp kibana.jar /tmp && cd /tmp && unzip -o kibana.jar 'kibana/public/*' && unzip -o kibana.jar kibana/config/web.ru && env PUBLIC_FOLDER=/tmp/kibana/public java -server -jar kibana.jar kibana/config/web.ru")
|
||||
end
|
||||
end
|
||||
|
||||
desc "Create the project jar file"
|
||||
task "jar" do
|
||||
system("cd #{HERE} && jruby -S warble")
|
||||
end
|
||||
|
||||
# desc "Watch for changes"
|
||||
# task "watch" => "vendor/fswatch" do
|
||||
# system("killall fswatch")
|
||||
# system("fswatch #{HERE}/lib \"bash -c \\\"kill -SIGUSR2 \\\`ps u|grep [o]rg.jruby.Main|grep bin/puma|awk {\'print \\\$2\'}\\\`\\\"\" &")
|
||||
# end
|
||||
|
||||
desc "Run the project from jruby"
|
||||
# task "run" => "watch" do
|
||||
task "run" do
|
||||
exec("cd #{HERE} && jruby -S bundle exec jruby -S bin/kibana config/web.ru")
|
||||
end
|
|
@ -1,38 +0,0 @@
|
|||
#!/usr/bin/env ruby
|
||||
#
|
||||
# This file was generated by RubyGems.
|
||||
#
|
||||
# The application 'puma' is installed as part of a gem, and
|
||||
# this file is here to facilitate running it.
|
||||
#
|
||||
|
||||
require 'rubygems'
|
||||
|
||||
version = ">= 0"
|
||||
|
||||
HERE = File.expand_path(File.dirname(__FILE__))
|
||||
|
||||
if ARGV.first
|
||||
str = ARGV.first
|
||||
str = str.dup.force_encoding("BINARY") if str.respond_to? :force_encoding
|
||||
if str =~ /\A_(.*)_\z/
|
||||
version = $1
|
||||
ARGV.shift
|
||||
end
|
||||
end
|
||||
|
||||
# Include the puma config unless it's been overriden
|
||||
unless ARGV.include?('-C')
|
||||
ARGV << '-C'
|
||||
ARGV << "#{HERE}/../config/puma.rb"
|
||||
end
|
||||
|
||||
# Include the rack config if it hasn't been included
|
||||
if (ARGV.grep(/config\/web\.ru/)).empty?
|
||||
ARGV << "#{HERE}/../config/web.ru"
|
||||
end
|
||||
|
||||
print ARGV, "\n"
|
||||
|
||||
gem 'puma', version
|
||||
load Gem.bin_path('puma', 'puma', version)
|
|
@ -1,2 +0,0 @@
|
|||
port 8000
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
# Add the libs directory to the load path
|
||||
ROOT = File.expand_path("#{File.dirname(__FILE__)}/../")
|
||||
$LOAD_PATH.unshift(ROOT)
|
||||
|
||||
require "rubygems"
|
||||
require "bundler/setup"
|
||||
|
||||
# Require the application
|
||||
require "#{ROOT}/lib/app"
|
||||
|
||||
# Run the application
|
||||
run Kibana::App
|
|
@ -1,27 +0,0 @@
|
|||
# Add the root of the project to the $LOAD_PATH, For some reason it seems
|
||||
# to be getting lost when we use warble to make the jar. This fixes it :D
|
||||
$LOAD_PATH.unshift(ROOT)
|
||||
|
||||
require "rack/reverse_proxy"
|
||||
require "routes/home"
|
||||
require "routes/api"
|
||||
|
||||
module Kibana
|
||||
class App < Sinatra::Base
|
||||
|
||||
configure do
|
||||
set :root, ROOT
|
||||
set :public_folder, "#{ROOT}/public"
|
||||
set :httponly, true
|
||||
end
|
||||
|
||||
# Rack middleware goes here
|
||||
use Rack::ReverseProxy do
|
||||
reverse_proxy /^\/elasticsearch(.*)$/, 'http://localhost:9200$1'
|
||||
end
|
||||
|
||||
# Routes go here
|
||||
use Routes::Home
|
||||
use Routes::Api
|
||||
end
|
||||
end
|
|
@ -1,22 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Welcome to the Future Home of Kibana</title>
|
||||
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-12 text-center" style="padding-top: 40px;">
|
||||
<h1>Welcome to the Future Home of Kibana</h1>
|
||||
<p>This is the server component of Kibana. It's just a quick prototype of the things we need.</p>
|
||||
<p><a href="/test.html">Static Server</a></p>
|
||||
<p><a href="/api/foo">Server Side APIs</a></p>
|
||||
<p><a href="/elasticsearch">Elasticsearch Proxy</a></p>
|
||||
<h3>Much more coming soon...</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -1,9 +0,0 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>This is a test file</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>This is a test</h1>
|
||||
<p>This should work outside of anything else.</p>
|
||||
</body>
|
||||
</html>
|
|
@ -1,16 +0,0 @@
|
|||
require "routes/base"
|
||||
require "lib/helpers"
|
||||
|
||||
module Kibana
|
||||
module Routes
|
||||
class Api < Base
|
||||
|
||||
helpers Kibana::Helpers
|
||||
|
||||
get "/api/foo" do
|
||||
json :foo => doSomething()
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,15 +0,0 @@
|
|||
require "sinatra/base"
|
||||
require "sinatra/json"
|
||||
|
||||
module Kibana
|
||||
module Routes
|
||||
class Base < Sinatra::Base
|
||||
helpers Sinatra::JSON
|
||||
|
||||
configure do
|
||||
# Confirgure stuffs here
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,13 +0,0 @@
|
|||
require "routes/base"
|
||||
|
||||
module Kibana
|
||||
module Routes
|
||||
class Home < Base
|
||||
|
||||
get "/" do
|
||||
File.read(File.join(ROOT, 'public', 'index.html'))
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
|
@ -25,7 +25,7 @@ define(function (require) {
|
|||
|
||||
require('routes')
|
||||
.when('/dashboard', {
|
||||
templateUrl: 'kibana/apps/dashboard/index.html',
|
||||
template: require('text!apps/dashboard/index.html'),
|
||||
resolve: {
|
||||
dash: function (savedDashboards) {
|
||||
return savedDashboards.get();
|
||||
|
@ -33,7 +33,7 @@ define(function (require) {
|
|||
}
|
||||
})
|
||||
.when('/dashboard/:id', {
|
||||
templateUrl: 'kibana/apps/dashboard/index.html',
|
||||
template: require('text!apps/dashboard/index.html'),
|
||||
resolve: {
|
||||
dash: function (savedDashboards, Notifier, $route, $location, courier) {
|
||||
return savedDashboards.get($route.current.params.id)
|
||||
|
@ -42,7 +42,7 @@ define(function (require) {
|
|||
}
|
||||
});
|
||||
|
||||
app.directive('dashboardApp', function (Notifier, courier, savedVisualizations, appStateFactory, timefilter) {
|
||||
app.directive('dashboardApp', function (Notifier, courier, savedVisualizations, appStateFactory, timefilter, kbnUrl) {
|
||||
return {
|
||||
controller: function ($scope, $route, $routeParams, $location, configFile) {
|
||||
var notify = new Notifier({
|
||||
|
@ -113,7 +113,7 @@ define(function (require) {
|
|||
.then(function () {
|
||||
notify.info('Saved Dashboard as "' + dash.title + '"');
|
||||
if (dash.id !== $routeParams.id) {
|
||||
$location.url('/dashboard/' + encodeURIComponent(dash.id));
|
||||
kbnUrl.change('/dashboard/{{id}}', {id: dash.id});
|
||||
}
|
||||
})
|
||||
.catch(notify.fatal);
|
||||
|
|
|
@ -13,7 +13,7 @@ define(function (require) {
|
|||
});
|
||||
|
||||
// This is the only thing that gets injected into controllers
|
||||
module.service('savedDashboards', function (Promise, SavedDashboard, config, es) {
|
||||
module.service('savedDashboards', function (Promise, SavedDashboard, config, es, kbnUrl) {
|
||||
|
||||
// Returns a single dashboard by ID, should be the name of the dashboard
|
||||
this.get = function (id) {
|
||||
|
@ -23,7 +23,7 @@ define(function (require) {
|
|||
};
|
||||
|
||||
this.urlFor = function (id) {
|
||||
return '#/dashboard/' + encodeURIComponent(id);
|
||||
return kbnUrl.eval('#/dashboard/{{id}}', {id: id});
|
||||
};
|
||||
|
||||
this.delete = function (ids) {
|
||||
|
|
|
@ -46,7 +46,7 @@ define(function (require) {
|
|||
});
|
||||
|
||||
app.controller('discover', function ($scope, config, courier, $route, $window, savedSearches, savedVisualizations,
|
||||
Notifier, $location, globalState, appStateFactory, timefilter, Promise, Private) {
|
||||
Notifier, $location, globalState, appStateFactory, timefilter, Promise, Private, kbnUrl) {
|
||||
|
||||
var Vis = Private(require('components/vis/vis'));
|
||||
var SegmentedFetch = Private(require('apps/discover/_segmented_fetch'));
|
||||
|
@ -110,7 +110,8 @@ define(function (require) {
|
|||
$state.index = config.get('defaultIndex');
|
||||
} else {
|
||||
notify.warning(reason + 'Please set a default index to continue.');
|
||||
$location.url('/settings/indices');
|
||||
kbnUrl.change('/settings/indices');
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -227,7 +228,7 @@ define(function (require) {
|
|||
.then(function () {
|
||||
notify.info('Saved Data Source "' + savedSearch.title + '"');
|
||||
if (savedSearch.id !== $route.current.params.id) {
|
||||
$location.url(globalState.writeToUrl('/discover/' + encodeURIComponent(savedSearch.id)));
|
||||
kbnUrl.change('/discover/{{id}}', { id: savedSearch.id });
|
||||
}
|
||||
});
|
||||
})
|
||||
|
@ -388,7 +389,7 @@ define(function (require) {
|
|||
};
|
||||
|
||||
$scope.newQuery = function () {
|
||||
$location.url('/discover');
|
||||
kbnUrl.change('/discover');
|
||||
};
|
||||
|
||||
$scope.updateDataSource = function () {
|
||||
|
|
|
@ -19,10 +19,8 @@ define(function (require) {
|
|||
scope: {
|
||||
fields: '=',
|
||||
toggle: '=',
|
||||
refresh: '=',
|
||||
data: '=',
|
||||
state: '=',
|
||||
updateFilterInQuery: '=filter',
|
||||
searchSource: '='
|
||||
},
|
||||
template: html,
|
||||
|
@ -165,8 +163,7 @@ define(function (require) {
|
|||
count: 5,
|
||||
grouped: false
|
||||
});
|
||||
var indexPattern = $scope.searchSource.get('index');
|
||||
indexPattern.popularizeField(field.name, 1);
|
||||
$scope.increaseFieldCounter(field, 1);
|
||||
} else {
|
||||
delete field.details;
|
||||
}
|
||||
|
|
|
@ -29,23 +29,28 @@ define(function (require) {
|
|||
if ($scope.mapping[column] && !$scope.mapping[column].indexed) return;
|
||||
|
||||
var sorting = $scope.sorting;
|
||||
var defaultClass = ['fa', 'fa-sort', 'table-header-sortchange'];
|
||||
|
||||
if (!sorting) return [];
|
||||
if (!sorting) return defaultClass;
|
||||
|
||||
if (column === sorting[0]) {
|
||||
return ['fa', sorting[1] === 'asc' ? 'fa-sort-up' : 'fa-sort-down'];
|
||||
} else {
|
||||
return ['fa', 'fa-sort', 'table-header-sortchange'];
|
||||
return defaultClass;
|
||||
}
|
||||
};
|
||||
|
||||
$scope.moveLeft = function (column) {
|
||||
var index = _.indexOf($scope.columns, column);
|
||||
if (index === 0) return;
|
||||
|
||||
_.move($scope.columns, index, --index);
|
||||
};
|
||||
|
||||
$scope.moveRight = function (column) {
|
||||
var index = _.indexOf($scope.columns, column);
|
||||
if (index === $scope.columns.length - 1) return;
|
||||
|
||||
_.move($scope.columns, index, ++index);
|
||||
};
|
||||
|
||||
|
@ -112,7 +117,6 @@ define(function (require) {
|
|||
return {
|
||||
restrict: 'A',
|
||||
scope: {
|
||||
fields: '=',
|
||||
columns: '=',
|
||||
filtering: '=',
|
||||
mapping: '=',
|
||||
|
@ -139,27 +143,14 @@ define(function (require) {
|
|||
$scope.maxLength = 250;
|
||||
}
|
||||
|
||||
|
||||
// for now, rows are "tracked" by their index, but this could eventually
|
||||
// be configured so that changing the order of the rows won't prevent
|
||||
// them from staying open on update
|
||||
function rowId(row) {
|
||||
var id = $scope.rows.indexOf(row);
|
||||
return ~id ? id : null;
|
||||
}
|
||||
|
||||
// inverse of rowId()
|
||||
function rowForId(id) {
|
||||
return $scope.rows[id];
|
||||
}
|
||||
|
||||
// toggle display of the rows details, a full list of the fields from each row
|
||||
$scope.toggleRow = function (row, event) {
|
||||
$scope.toggleRow = function () {
|
||||
var row = $scope.row;
|
||||
var id = row._id;
|
||||
|
||||
$scope.open = !$scope.open;
|
||||
|
||||
var $tr = $(event.delegateTarget.parentElement);
|
||||
var $tr = element;
|
||||
var $detailsTr = $tr.next();
|
||||
|
||||
///
|
||||
|
@ -169,7 +160,7 @@ define(function (require) {
|
|||
$detailsTr.toggle($scope.open);
|
||||
|
||||
// Change the caret icon
|
||||
var $toggleIcon = $($(event.delegateTarget).children('i')[0]);
|
||||
var $toggleIcon = $(element.children().first().find('i')[0]);
|
||||
$toggleIcon.toggleClass('fa-caret-down');
|
||||
$toggleIcon.toggleClass('fa-caret-right');
|
||||
|
||||
|
@ -207,7 +198,12 @@ define(function (require) {
|
|||
$scope.filtering(field, row._source[field] || row[field], operation);
|
||||
};
|
||||
|
||||
$scope.$watch('columns', function () {
|
||||
$scope.$watch('columns', function (columns) {
|
||||
element.empty();
|
||||
createSummaryRow($scope.row, $scope.row._id);
|
||||
});
|
||||
|
||||
$scope.$watch('timefield', function (timefield) {
|
||||
element.empty();
|
||||
createSummaryRow($scope.row, $scope.row._id);
|
||||
});
|
||||
|
@ -216,7 +212,7 @@ define(function (require) {
|
|||
function createSummaryRow(row, id) {
|
||||
|
||||
var expandTd = $('<td>').html('<i class="fa fa-caret-right"></span>')
|
||||
.attr('ng-click', 'toggleRow(row, $event)');
|
||||
.attr('ng-click', 'toggleRow()');
|
||||
$compile(expandTd)($scope);
|
||||
element.append(expandTd);
|
||||
|
||||
|
@ -240,11 +236,7 @@ define(function (require) {
|
|||
*/
|
||||
function _displayField(el, row, field, truncate) {
|
||||
var val = _getValForField(row, field, truncate);
|
||||
if (val instanceof DOMNode) {
|
||||
el.append(val);
|
||||
} else {
|
||||
el.text(val);
|
||||
}
|
||||
el.text(val);
|
||||
return el;
|
||||
}
|
||||
|
||||
|
@ -266,17 +258,9 @@ define(function (require) {
|
|||
// undefined and null should just be an empty string
|
||||
val = (val == null) ? '' : val;
|
||||
|
||||
// truncate
|
||||
// truncate the column text, not the details
|
||||
if (typeof val === 'string' && val.length > $scope.maxLength) {
|
||||
if (untruncate) {
|
||||
var complete = val;
|
||||
val = document.createElement('kbn-truncated');
|
||||
val.setAttribute('orig', complete);
|
||||
val.setAttribute('length', $scope.maxLength);
|
||||
val = $compile(val)($scope)[0];// return the actual element
|
||||
} else {
|
||||
val = val.substring(0, $scope.maxLength) + '...';
|
||||
}
|
||||
val = val.substring(0, $scope.maxLength) + '...';
|
||||
}
|
||||
|
||||
return val;
|
||||
|
|
|
@ -3,7 +3,8 @@
|
|||
<tbody>
|
||||
<tr ng-repeat="row in rows |limitTo:limit track by row._index+row._id"
|
||||
kbn-table-row="row"
|
||||
columns="columns" mapping="mapping" sorting="sorting" timefield="timefield" max-length="maxLength" filtering="filtering"></tr>
|
||||
columns="columns" mapping="mapping" sorting="sorting" timefield="timefield" max-length="maxLength" filtering="filtering"
|
||||
class="discover-table-row"></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<kbn-infinite-scroll more="addRows"></kbn-infinite-scroll>
|
|
@ -3,7 +3,7 @@
|
|||
<span ng-click="sort(timefield)">Time <i ng-class="headerClass(timefield)"></i></span>
|
||||
</th>
|
||||
<th ng-repeat="name in columns">
|
||||
<span ng-click="sort(name)">
|
||||
<span ng-click="sort(name)" class="table-header-name">
|
||||
{{name}} <i ng-class="headerClass(name)"></i>
|
||||
</span>
|
||||
<span class="table-header-move">
|
||||
|
|
|
@ -15,7 +15,7 @@ define(function (require) {
|
|||
title: 'searches'
|
||||
});
|
||||
|
||||
module.service('savedSearches', function (Promise, config, configFile, es, createNotifier, SavedSearch) {
|
||||
module.service('savedSearches', function (Promise, config, configFile, es, createNotifier, SavedSearch, kbnUrl) {
|
||||
|
||||
|
||||
var notify = createNotifier({
|
||||
|
@ -27,7 +27,7 @@ define(function (require) {
|
|||
};
|
||||
|
||||
this.urlFor = function (id) {
|
||||
return '#/discover/' + encodeURIComponent(id);
|
||||
return kbnUrl.eval('#/discover/{{id}}', {id: id});
|
||||
};
|
||||
|
||||
this.delete = function (ids) {
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
<nav class="navbar navbar-default navbar-static-top subnav">
|
||||
<div class="container-fluid">
|
||||
<ul class="nav navbar-nav">
|
||||
<li ng-repeat="s in sections" ng-class="s.class">
|
||||
<a class="navbar-link" ng-href="{{s.url}}">{{s.display}}</a>
|
||||
<li ng-repeat="section in sections" ng-class="section.class">
|
||||
<a class="navbar-link" ng-href="{{section.url}}">{{section.display}}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
|
|
@ -14,7 +14,7 @@ define(function (require) {
|
|||
|
||||
// wrapper directive, which sets some global stuff up like the left nav
|
||||
require('modules').get('apps/settings')
|
||||
.directive('kbnSettingsIndices', function ($route, config) {
|
||||
.directive('kbnSettingsIndices', function ($route, config, kbnUrl) {
|
||||
return {
|
||||
restrict: 'E',
|
||||
transclude: true,
|
||||
|
@ -31,7 +31,7 @@ define(function (require) {
|
|||
.map(function (id) {
|
||||
return {
|
||||
id: id,
|
||||
url: '#/settings/indices/' + encodeURIComponent(id),
|
||||
url: kbnUrl.eval('#/settings/indices/{{id}}', {id: id}),
|
||||
class: 'sidebar-item-title ' + ($scope.edittingId === id ? 'active' : ''),
|
||||
default: $scope.defaultIndex === id
|
||||
};
|
||||
|
|
|
@ -41,7 +41,7 @@ define(function (require) {
|
|||
'kibana/notify',
|
||||
'kibana/courier'
|
||||
])
|
||||
.controller('VisEditor', function ($scope, $route, timefilter, appStateFactory, $location, globalState, $timeout) {
|
||||
.controller('VisEditor', function ($scope, $route, timefilter, appStateFactory, $location, kbnUrl, $timeout) {
|
||||
|
||||
var _ = require('lodash');
|
||||
var angular = require('angular');
|
||||
|
@ -157,11 +157,7 @@ define(function (require) {
|
|||
|
||||
if (savedVis.id === $route.current.params.id) return;
|
||||
|
||||
$location.url(
|
||||
globalState.writeToUrl(
|
||||
'/visualize/edit/' + encodeURIComponent(savedVis.id)
|
||||
)
|
||||
);
|
||||
kbnUrl.change('/visualize/edit/{{id}}', {id: savedVis.id});
|
||||
}, notify.fatal);
|
||||
};
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ define(function (require) {
|
|||
title: 'visualizations'
|
||||
});
|
||||
|
||||
app.service('savedVisualizations', function (Promise, es, config, SavedVis, Private, Notifier) {
|
||||
app.service('savedVisualizations', function (Promise, es, config, SavedVis, Private, Notifier, kbnUrl) {
|
||||
var visTypes = Private(require('components/vis_types/index'));
|
||||
var notify = new Notifier({
|
||||
location: 'saved visualization service'
|
||||
|
@ -22,7 +22,7 @@ define(function (require) {
|
|||
};
|
||||
|
||||
this.urlFor = function (id) {
|
||||
return '#/visualize/edit/' + encodeURIComponent(id);
|
||||
return kbnUrl.eval('#/visualize/edit/{{id}}', {id: id});
|
||||
};
|
||||
|
||||
this.delete = function (ids) {
|
||||
|
|
|
@ -24,9 +24,9 @@ define(function (require) {
|
|||
}
|
||||
});
|
||||
|
||||
module.controller('VisualizeWizardStep1', function ($route, $scope, $location, timefilter) {
|
||||
module.controller('VisualizeWizardStep1', function ($route, $scope, $location, timefilter, kbnUrl) {
|
||||
$scope.step2WithSearchUrl = function (hit) {
|
||||
return '#/visualize/step/2?savedSearchId=' + encodeURIComponent(hit.id);
|
||||
return kbnUrl.eval('#/visualize/step/2?savedSearchId={{id}}', {id: hit.id});
|
||||
};
|
||||
|
||||
timefilter.enabled = false;
|
||||
|
@ -38,7 +38,7 @@ define(function (require) {
|
|||
|
||||
$scope.$watch('indexPattern.selection', function (pattern) {
|
||||
if (!pattern) return;
|
||||
$location.url('/visualize/step/2?indexPattern=' + encodeURIComponent(pattern));
|
||||
kbnUrl.change('/visualize/step/2?indexPattern={{pattern}}', {pattern: pattern});
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -8,7 +8,6 @@ define(function (require) {
|
|||
if (query.query_string && query.query_string.query) {
|
||||
return query.query_string.query;
|
||||
}
|
||||
|
||||
return JSON.stringify(query);
|
||||
}
|
||||
|
||||
|
@ -32,4 +31,4 @@ define(function (require) {
|
|||
]
|
||||
});
|
||||
};
|
||||
});
|
||||
});
|
||||
|
|
|
@ -3,7 +3,7 @@ define(function (require) {
|
|||
'kibana/notify'
|
||||
]);
|
||||
|
||||
var configFile = require('config_file');
|
||||
var configFile = JSON.parse(require('text!config'));
|
||||
// allow the rest of the app to get the configFile easily
|
||||
module.constant('configFile', configFile);
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
define(function (require) {
|
||||
var errors = require('errors');
|
||||
|
||||
return function RedirectWhenMissingFn($location, $route, globalState, Notifier) {
|
||||
return function RedirectWhenMissingFn($location, kbnUrl, globalState, Notifier) {
|
||||
var SavedObjectNotFound = errors.SavedObjectNotFound;
|
||||
|
||||
var notify = new Notifier();
|
||||
|
@ -27,7 +27,7 @@ define(function (require) {
|
|||
if (!url) url = '/';
|
||||
|
||||
notify.error(err);
|
||||
$route.changeUrl(globalState.writeToUrl(url));
|
||||
kbnUrl.change(url);
|
||||
return;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
define(function (require) {
|
||||
return function EnsureSomeIndexPatternsFn(Private, Notifier, $location, $route) {
|
||||
return function EnsureSomeIndexPatternsFn(Private, Notifier, $location, kbnUrl) {
|
||||
var errors = require('errors');
|
||||
var notify = new Notifier();
|
||||
|
||||
|
@ -7,7 +7,7 @@ define(function (require) {
|
|||
return function promiseHandler(patterns) {
|
||||
if (!patterns || patterns.length === 0) {
|
||||
// notify.warning(new errors.NoDefinedIndexPatterns());
|
||||
$route.change('/settings/indices');
|
||||
kbnUrl.changePath('/settings/indices');
|
||||
}
|
||||
|
||||
return patterns;
|
||||
|
|
68
src/kibana/components/reflow_watcher.js
Normal file
68
src/kibana/components/reflow_watcher.js
Normal file
|
@ -0,0 +1,68 @@
|
|||
define(function (require) {
|
||||
return function ReflowWatcherService(Private, $rootScope, $http) {
|
||||
var angular = require('angular');
|
||||
var $ = require('jquery');
|
||||
var _ = require('lodash');
|
||||
|
||||
var EventEmitter = Private(require('factories/events'));
|
||||
var $body = $(document.body);
|
||||
var $window = $(window);
|
||||
|
||||
var MOUSE_EVENTS = 'mouseup';
|
||||
var WINDOW_EVENTS = 'resize';
|
||||
|
||||
_(ReflowWatcher).inherits(EventEmitter);
|
||||
/**
|
||||
* Watches global activity which might hint at a change in the content, which
|
||||
* in turn provides a hint to resizers that they should check their size
|
||||
*/
|
||||
function ReflowWatcher() {
|
||||
ReflowWatcher.Super.call(this);
|
||||
|
||||
// bound version of trigger that can be used as a handler
|
||||
this.trigger = _.bind(this.trigger, this);
|
||||
this._emitReflow = _.bind(this._emitReflow, this);
|
||||
|
||||
// list of functions to call that will unbind our watchers
|
||||
this._unwatchers = [
|
||||
$rootScope.$watchCollection(function () {
|
||||
return $http.pendingRequests;
|
||||
}, this.trigger)
|
||||
];
|
||||
|
||||
$body.on(MOUSE_EVENTS, this.trigger);
|
||||
$window.on(WINDOW_EVENTS, this.trigger);
|
||||
}
|
||||
|
||||
/**
|
||||
* Simply emit reflow, but in a way that can be bound and passed to
|
||||
* other functions. Using _.bind caused extra arguments to be added, and
|
||||
* then emitted to other places. No Bueno
|
||||
*
|
||||
* @return {void}
|
||||
*/
|
||||
ReflowWatcher.prototype._emitReflow = function () {
|
||||
this.emit('reflow');
|
||||
};
|
||||
|
||||
/**
|
||||
* Emit the "reflow" event in the next tick of the digest cycle
|
||||
* @return {void}
|
||||
*/
|
||||
ReflowWatcher.prototype.trigger = function () {
|
||||
$rootScope.$evalAsync(this._emitReflow);
|
||||
};
|
||||
|
||||
/**
|
||||
* Signal to the ReflowWatcher that it should clean up it's listeners
|
||||
* @return {void}
|
||||
*/
|
||||
ReflowWatcher.prototype.destroy = function () {
|
||||
$body.off(MOUSE_EVENTS, this.trigger);
|
||||
$window.off(WINDOW_EVENTS, this.trigger);
|
||||
_.callEach(this._unwatchers);
|
||||
};
|
||||
|
||||
return new ReflowWatcher();
|
||||
};
|
||||
});
|
|
@ -21,8 +21,8 @@ define(function (require) {
|
|||
template: html,
|
||||
controller: function ($scope) {
|
||||
var init = function () {
|
||||
$scope.formatRelative();
|
||||
$scope.setMode($scope.mode);
|
||||
$scope.formatRelative();
|
||||
};
|
||||
|
||||
$scope.format = 'MMMM Do YYYY, HH:mm:ss.SSS';
|
||||
|
@ -136,4 +136,4 @@ define(function (require) {
|
|||
};
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
|
97
src/kibana/components/url/url.js
Normal file
97
src/kibana/components/url/url.js
Normal file
|
@ -0,0 +1,97 @@
|
|||
define(function (require) {
|
||||
require('filters/uriescape');
|
||||
require('filters/rison');
|
||||
var _ = require('lodash');
|
||||
var rison = require('utils/rison');
|
||||
var location = require('modules').get('kibana/url');
|
||||
|
||||
location.service('kbnUrl', function ($route, $location, $rootScope, globalState, $parse) {
|
||||
var self = this;
|
||||
self.reloading = false;
|
||||
|
||||
self.change = function (url, paramObj, forceReload) {
|
||||
self._changeLocation('url', url, paramObj, forceReload);
|
||||
};
|
||||
|
||||
self.changePath = function (url, paramObj, forceReload) {
|
||||
self._changeLocation('path', url, paramObj, forceReload);
|
||||
};
|
||||
|
||||
self._changeLocation = function (type, url, paramObj, forceReload) {
|
||||
var doReload = false;
|
||||
|
||||
if (_.isBoolean(paramObj)) {
|
||||
forceReload = paramObj;
|
||||
paramObj = undefined;
|
||||
}
|
||||
|
||||
url = self.eval(url, paramObj);
|
||||
|
||||
// path change
|
||||
if (type === 'path') {
|
||||
if (url !== $location.path()) {
|
||||
$location.path(globalState.writeToUrl(url));
|
||||
doReload = (!self.matches(url));
|
||||
}
|
||||
// default to url change
|
||||
} else {
|
||||
if (url !== $location.url()) {
|
||||
$location.url(globalState.writeToUrl(url));
|
||||
doReload = (!self.matches(url));
|
||||
}
|
||||
}
|
||||
|
||||
if (forceReload || doReload) {
|
||||
self.reload();
|
||||
}
|
||||
};
|
||||
|
||||
self.eval = function (url, paramObj) {
|
||||
paramObj = paramObj || {};
|
||||
|
||||
return parseUrlPrams(url, paramObj);
|
||||
};
|
||||
|
||||
self.matches = function (url) {
|
||||
var route = $route.current.$$route;
|
||||
if (!route || !route.regexp) return false;
|
||||
return route.regexp.test(url);
|
||||
};
|
||||
|
||||
$rootScope.$on('$routeUpdate', reloadingComplete);
|
||||
$rootScope.$on('$routeChangeStart', reloadingComplete);
|
||||
|
||||
function parseUrlPrams(url, paramObj) {
|
||||
return url.replace(/\{\{([^\}]+)\}\}/g, function (match, expr) {
|
||||
// remove filters
|
||||
var key = expr.split('|')[0].trim();
|
||||
|
||||
// verify that the expression can be evaluated
|
||||
var p = $parse(key)(paramObj);
|
||||
|
||||
// if evaluation can't be made, throw
|
||||
if (_.isUndefined(p)) {
|
||||
throw new Error('Replacement failed, unresolved expression: ' + expr);
|
||||
}
|
||||
|
||||
// append uriescape filter if not included
|
||||
if (expr.indexOf('uriescape') === -1) {
|
||||
expr += '|uriescape';
|
||||
}
|
||||
|
||||
return $parse(expr)(paramObj);
|
||||
});
|
||||
}
|
||||
|
||||
self.reload = function () {
|
||||
if (!self.reloading) {
|
||||
$route.reload();
|
||||
self.reloading = true;
|
||||
}
|
||||
};
|
||||
|
||||
function reloadingComplete() {
|
||||
self.reloading = false;
|
||||
}
|
||||
});
|
||||
});
|
|
@ -10,7 +10,7 @@ define(function (require) {
|
|||
shareYAxis: true,
|
||||
addTooltip: true,
|
||||
addLegend: true,
|
||||
addEvents: true
|
||||
addBrushing: true
|
||||
},
|
||||
schemas: new Schemas([
|
||||
{
|
||||
|
|
|
@ -9,9 +9,10 @@ define(function (require) {
|
|||
/*
|
||||
* Specifies the visualization layout for column charts.
|
||||
*
|
||||
* This is done using an array of objects. Each object has
|
||||
* a `parent` DOM element, a DOM `type` (e.g. div, svg, etc),
|
||||
* and a `class`. These are required attributes.
|
||||
* This is done using an array of objects. The first object has
|
||||
* a `parent` DOM element, a DOM `type` (e.g. div, svg, etc),
|
||||
* and a `class` (required). Each child can omit the parent object,
|
||||
* but must include a type and class.
|
||||
*
|
||||
* Optionally, you can specify `datum` to be bound to the DOM
|
||||
* element, a `splits` function that divides the selected element
|
||||
|
@ -21,6 +22,7 @@ define(function (require) {
|
|||
* Objects in children arrays are children of the current object and return
|
||||
* DOM elements which are children of their respective parent element.
|
||||
*/
|
||||
|
||||
return function (el, data) {
|
||||
if (!el || !data) {
|
||||
throw new Error('Both an el and data need to be specified');
|
||||
|
@ -34,28 +36,23 @@ define(function (require) {
|
|||
datum: data,
|
||||
children: [
|
||||
{
|
||||
parent: 'vis-wrapper',
|
||||
type: 'div',
|
||||
class: 'y-axis-col-wrapper',
|
||||
children: [
|
||||
{
|
||||
parent: 'y-axis-col-wrapper',
|
||||
type: 'div',
|
||||
class: 'y-axis-col',
|
||||
children: [
|
||||
{
|
||||
parent: 'y-axis-col',
|
||||
type: 'div',
|
||||
class: 'y-axis-title'
|
||||
},
|
||||
{
|
||||
parent: 'y-axis-col',
|
||||
type: 'div',
|
||||
class: 'y-axis-chart-title',
|
||||
splits: chartTitleSplit
|
||||
},
|
||||
{
|
||||
parent: 'y-axis-col',
|
||||
type: 'div',
|
||||
class: 'y-axis-div-wrapper',
|
||||
splits: yAxisSplit
|
||||
|
@ -63,42 +60,35 @@ define(function (require) {
|
|||
]
|
||||
},
|
||||
{
|
||||
parent: 'y-axis-col-wrapper',
|
||||
type: 'div',
|
||||
class: 'y-axis-spacer-block'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
parent: 'vis-wrapper',
|
||||
type: 'div',
|
||||
class: 'vis-col-wrapper',
|
||||
children: [
|
||||
{
|
||||
parent: 'vis-col-wrapper',
|
||||
type: 'div',
|
||||
class: 'chart-wrapper',
|
||||
splits: chartSplit
|
||||
},
|
||||
{
|
||||
parent: 'vis-col-wrapper',
|
||||
type: 'div',
|
||||
class: 'x-axis-wrapper',
|
||||
children: [
|
||||
{
|
||||
parent: 'x-axis-wrapper',
|
||||
type: 'div',
|
||||
class: 'x-axis-div-wrapper',
|
||||
splits: xAxisSplit
|
||||
},
|
||||
{
|
||||
parent: 'x-axis-wrapper',
|
||||
type: 'div',
|
||||
class: 'x-axis-chart-title',
|
||||
splits: chartTitleSplit
|
||||
},
|
||||
{
|
||||
parent: 'x-axis-wrapper',
|
||||
type: 'div',
|
||||
class: 'x-axis-title'
|
||||
}
|
||||
|
@ -107,12 +97,10 @@ define(function (require) {
|
|||
]
|
||||
},
|
||||
{
|
||||
parent: 'vis-wrapper',
|
||||
type: 'div',
|
||||
class: 'legend-col-wrapper'
|
||||
},
|
||||
{
|
||||
parent: 'vis-wrapper',
|
||||
type: 'div',
|
||||
class: 'k4tip'
|
||||
}
|
||||
|
|
|
@ -12,12 +12,21 @@ define(function (require) {
|
|||
* arguments:
|
||||
* data => Provided data object
|
||||
*/
|
||||
function Data(data) {
|
||||
function Data(data, attr) {
|
||||
if (!(this instanceof Data)) {
|
||||
return new Data(data);
|
||||
return new Data(data, attr);
|
||||
}
|
||||
|
||||
this.data = data;
|
||||
this._attr = attr;
|
||||
// d3 stack function
|
||||
this._attr = _.defaults(attr || {}, {
|
||||
offset: 'zero',
|
||||
stack: d3.layout.stack()
|
||||
.x(function (d) { return d.x; })
|
||||
.y(function (d) { return d.y; })
|
||||
.offset(this._attr.offset)
|
||||
});
|
||||
}
|
||||
|
||||
// Return the actual x and y data values
|
||||
|
@ -53,6 +62,51 @@ define(function (require) {
|
|||
return values;
|
||||
};
|
||||
|
||||
Data.prototype.shouldBeStacked = function (series) {
|
||||
// Series should be an array
|
||||
if (series.length > 1) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
// Calculate the max y value from this.dataArray
|
||||
Data.prototype.getYMaxValue = function () {
|
||||
var self = this;
|
||||
var arr = [];
|
||||
|
||||
// for each object in the dataArray,
|
||||
// push the calculated y value to the initialized array (arr)
|
||||
_.forEach(this.flatten(), function (series) {
|
||||
arr.push(self.getYStackMax(series));
|
||||
});
|
||||
|
||||
// return the largest value from the array
|
||||
return _.max(arr);
|
||||
};
|
||||
|
||||
Data.prototype.stackData = function (series) {
|
||||
// Determine if the data should be stacked
|
||||
if (this.shouldBeStacked(series)) {
|
||||
// if true, stack data
|
||||
return this._attr.stack(series);
|
||||
}
|
||||
return series;
|
||||
};
|
||||
|
||||
Data.prototype.getYStackMax = function (series) {
|
||||
// Return the calculated y value
|
||||
return d3.max(this.stackData(series), function (data) {
|
||||
return d3.max(data, function (d) {
|
||||
// if stacked, need to add d.y0 + d.y for the y value
|
||||
if (d.y0) {
|
||||
return d.y0 + d.y;
|
||||
}
|
||||
return d.y;
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// Inject zeros into the data
|
||||
Data.prototype.injectZeros = function () {
|
||||
return injectZeros(this.data);
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
define(function (require) {
|
||||
var _ = require('lodash');
|
||||
var $ = require('jquery');
|
||||
|
||||
return function HandlerBaseClass(d3, Private) {
|
||||
var Data = Private(require('components/vislib/lib/data'));
|
||||
|
@ -25,7 +24,7 @@ define(function (require) {
|
|||
return new Handler(vis);
|
||||
}
|
||||
|
||||
this.data = new Data(vis.data);
|
||||
this.data = new Data(vis.data, vis._attr);
|
||||
this.vis = vis;
|
||||
this.el = vis.el;
|
||||
this.ChartClass = vis.ChartClass;
|
||||
|
@ -34,7 +33,6 @@ define(function (require) {
|
|||
});
|
||||
|
||||
// Visualization constructors
|
||||
|
||||
// Add the visualization layout
|
||||
this.layout = new Layout(this.el, this.data.injectZeros(), this._attr.type);
|
||||
|
||||
|
@ -60,8 +58,7 @@ define(function (require) {
|
|||
// add a y axis
|
||||
this.yAxis = new YAxis({
|
||||
el: this.el,
|
||||
chartData: this.data.chartData(),
|
||||
dataArray: this.data.flatten(),
|
||||
yMax: this.data.getYMaxValue(),
|
||||
_attr: this._attr
|
||||
});
|
||||
|
||||
|
@ -72,7 +69,7 @@ define(function (require) {
|
|||
this.chartTitle = new ChartTitle(this.el);
|
||||
|
||||
// Array of objects to render to the visualization
|
||||
this.renderArray = [
|
||||
this.renderArray = _.filter([
|
||||
this.layout,
|
||||
this.legend,
|
||||
this.tooltip,
|
||||
|
@ -80,7 +77,7 @@ define(function (require) {
|
|||
this.chartTitle,
|
||||
this.yAxis,
|
||||
this.xAxis
|
||||
];
|
||||
], Boolean);
|
||||
}
|
||||
|
||||
// Render the visualization
|
||||
|
@ -91,7 +88,7 @@ define(function (require) {
|
|||
|
||||
// Render objects in the render array
|
||||
_.forEach(this.renderArray, function (property) {
|
||||
if (property && typeof property.render === 'function') {
|
||||
if (typeof property.render === 'function') {
|
||||
property.render();
|
||||
}
|
||||
});
|
||||
|
@ -137,8 +134,7 @@ define(function (require) {
|
|||
this.removeAll(this.el);
|
||||
|
||||
// Return an error wrapper DOM element
|
||||
return d3.select(this.el)
|
||||
.append('div')
|
||||
return d3.select(this.el).append('div')
|
||||
// class name needs `chart` in it for the polling checkSize function
|
||||
// to continuously call render on resize
|
||||
.attr('class', 'chart error')
|
||||
|
|
|
@ -61,11 +61,12 @@ define(function (require) {
|
|||
obj.parent = '.' + obj.parent;
|
||||
}
|
||||
|
||||
var el = this.appendElem(obj.parent, obj.type, obj.class);
|
||||
// append child
|
||||
var childEl = this.appendElem(obj.parent, obj.type, obj.class);
|
||||
|
||||
if (obj.datum) {
|
||||
// Bind datum to the element
|
||||
el.datum(obj.datum);
|
||||
childEl.datum(obj.datum);
|
||||
}
|
||||
|
||||
if (obj.splits) {
|
||||
|
@ -74,11 +75,20 @@ define(function (require) {
|
|||
}
|
||||
|
||||
if (obj.children) {
|
||||
// Recursively pass object to createLayout
|
||||
// Creating the parent elem for the child nodes
|
||||
var newParent = d3.select(this.el).select('.' + obj.class)[0][0];
|
||||
|
||||
_.forEach(obj.children, function (obj) {
|
||||
if (!obj.parent) {
|
||||
obj.parent = newParent;
|
||||
}
|
||||
});
|
||||
|
||||
// Recursively pass children to createLayout
|
||||
this.createLayout(obj.children);
|
||||
}
|
||||
|
||||
return el;
|
||||
return childEl;
|
||||
};
|
||||
|
||||
// Appends a `type` of DOM element to `el` and gives it a class name attribute `className`
|
||||
|
|
|
@ -66,7 +66,7 @@ define(function (require) {
|
|||
.append('li')
|
||||
.attr('class', function (d) {
|
||||
// class names reflect the color assigned to the labels
|
||||
return 'color ' + self.classify(args.color(d));
|
||||
return 'color ' + self.colorToClass(args.color(d));
|
||||
})
|
||||
.html(function (d) {
|
||||
// return the appropriate color for each dot
|
||||
|
@ -75,7 +75,7 @@ define(function (require) {
|
|||
};
|
||||
|
||||
// Create a class name based on the colors assigned to each label
|
||||
Legend.prototype.classify = function (name) {
|
||||
Legend.prototype.colorToClass = function (name) {
|
||||
return 'c' + name.replace(/[#]/g, '');
|
||||
};
|
||||
|
||||
|
@ -112,15 +112,12 @@ define(function (require) {
|
|||
|
||||
visEl.selectAll('.color')
|
||||
.on('mouseover', function (d) {
|
||||
var liClass = '.' + self.classify(self.color(d));
|
||||
var liClass = '.' + self.colorToClass(self.color(d));
|
||||
visEl.selectAll('.color').style('opacity', self._attr.blurredOpacity);
|
||||
|
||||
// select series on chart
|
||||
visEl.selectAll(liClass).style('opacity', self._attr.focusOpacity);
|
||||
|
||||
});
|
||||
|
||||
visEl.selectAll('.color')
|
||||
})
|
||||
.on('mouseout', function () {
|
||||
visEl.selectAll('.color').style('opacity', self._attr.defaultOpacity);
|
||||
});
|
||||
|
|
209
src/kibana/components/vislib/lib/resize_checker.js
Normal file
209
src/kibana/components/vislib/lib/resize_checker.js
Normal file
|
@ -0,0 +1,209 @@
|
|||
define(function (require) {
|
||||
return function ResizeCheckerFactory(Private, Notifier) {
|
||||
var $ = require('jquery');
|
||||
var _ = require('lodash');
|
||||
|
||||
var EventEmitter = Private(require('factories/events'));
|
||||
var reflowWatcher = Private(require('components/reflow_watcher'));
|
||||
var sequencer = require('utils/sequencer');
|
||||
|
||||
var SCHEDULE_LONG = ResizeChecker.SCHEDULE_LONG = sequencer.createEaseOut(
|
||||
250, // shortest delay
|
||||
10000, // longest delay
|
||||
150 // tick count
|
||||
);
|
||||
|
||||
var SCHEDULE_SHORT = ResizeChecker.SCHEDULE_SHORT = sequencer.createEaseIn(
|
||||
5, // shortest delay
|
||||
500, // longest delay
|
||||
100 // tick count
|
||||
);
|
||||
|
||||
// maximum ms that we can delay emitting 'resize'. This is only used
|
||||
// to debounce resizes when the size of the element is constantly changing
|
||||
var MS_MAX_RESIZE_DELAY = ResizeChecker.MS_MAX_RESIZE_DELAY = 500;
|
||||
|
||||
/**
|
||||
* Checks the size of an element on a regular basis. Provides
|
||||
* an event that is emited when the element has changed size.
|
||||
*
|
||||
* @class ResizeChecker
|
||||
* @param {HtmlElement} el - the element to track the size of
|
||||
*/
|
||||
_(ResizeChecker).inherits(EventEmitter);
|
||||
function ResizeChecker(el) {
|
||||
ResizeChecker.Super.call(this);
|
||||
|
||||
this.$el = $(el);
|
||||
this.notify = new Notifier({ location: 'Vislib ResizeChecker ' + _.uniqueId() });
|
||||
|
||||
this.saveSize();
|
||||
|
||||
this.check = _.bind(this.check, this);
|
||||
this.check();
|
||||
|
||||
this.onReflow = _.bind(this.onReflow, this);
|
||||
reflowWatcher.on('reflow', this.onReflow);
|
||||
}
|
||||
|
||||
ResizeChecker.prototype.onReflow = function () {
|
||||
this.startSchedule(SCHEDULE_LONG);
|
||||
};
|
||||
|
||||
/**
|
||||
* Read the size of the element
|
||||
*
|
||||
* @method read
|
||||
* @return {object} - an object with keys `w` (width) and `h` (height)
|
||||
*/
|
||||
ResizeChecker.prototype.read = function () {
|
||||
return {
|
||||
w: this.$el.width(),
|
||||
h: this.$el.height()
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Save the element size, preventing it from being considered as an
|
||||
* update.
|
||||
*
|
||||
* @method save
|
||||
* @param {object} [size] - optional size to save, otherwise #read() is called
|
||||
* @return {boolean} - true if their was a change in the new
|
||||
*/
|
||||
ResizeChecker.prototype.saveSize = function (size) {
|
||||
if (!size) size = this.read();
|
||||
|
||||
if (this._equalsSavedSize(size)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this._savedSize = size;
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Determine if a given size matches the currently saved size.
|
||||
*
|
||||
* @private
|
||||
* @method _equalsSavedSize
|
||||
* @param {object} a - an object that matches the return value of #read()
|
||||
* @return {boolean} - true if the passed in value matches the saved size
|
||||
*/
|
||||
ResizeChecker.prototype._equalsSavedSize = function (a) {
|
||||
var b = this._savedSize || {};
|
||||
return a.w === b.w && a.h === b.h;
|
||||
};
|
||||
|
||||
/**
|
||||
* Read the time that the dirty state last changed.
|
||||
*
|
||||
* @method lastDirtyChange
|
||||
* @return {timestamp} - the unix timestamp (in ms) of the last update
|
||||
* to the dirty state
|
||||
*/
|
||||
ResizeChecker.prototype.lastDirtyChange = function () {
|
||||
return this._dirtyChangeStamp;
|
||||
};
|
||||
|
||||
/**
|
||||
* Record the dirty state
|
||||
*
|
||||
* @method saveDirty
|
||||
* @param {boolean} val
|
||||
* @return {boolean} - true if the dirty state changed by this save
|
||||
*/
|
||||
ResizeChecker.prototype.saveDirty = function (val) {
|
||||
val = !!val;
|
||||
|
||||
if (val === this._isDirty) return false;
|
||||
|
||||
this._isDirty = val;
|
||||
this._dirtyChangeStamp = Date.now();
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* The check routine that executes regularly and will reschedule itself
|
||||
* to run again in the future. It determines the state of the elements
|
||||
* size and decides when to emit the "update" event.
|
||||
*
|
||||
* @method check
|
||||
* @return {void}
|
||||
*/
|
||||
ResizeChecker.prototype.check = function () {
|
||||
var newSize = this.read();
|
||||
var dirty = this.saveSize(newSize);
|
||||
var dirtyChanged = this.saveDirty(dirty);
|
||||
|
||||
var doneDirty = !dirty && dirtyChanged;
|
||||
var muchDirty = dirty && (this.lastDirtyChange() - Date.now() > MS_MAX_RESIZE_DELAY);
|
||||
if (doneDirty || muchDirty) {
|
||||
this.emit('resize', newSize);
|
||||
}
|
||||
|
||||
// if the dirty state is unchanged, continue using the previous schedule
|
||||
if (!dirtyChanged) {
|
||||
return this.continueSchedule();
|
||||
}
|
||||
|
||||
// when the state changes start a new schedule. Use a schedule that quickly
|
||||
// slows down if it is unknown wether there are will be additional changes
|
||||
return this.startSchedule(dirty ? SCHEDULE_SHORT : SCHEDULE_LONG);
|
||||
};
|
||||
|
||||
/**
|
||||
* Start running a new schedule, using one of the SCHEDULE_* constants.
|
||||
*
|
||||
* @method startSchedule
|
||||
* @param {integer[]} schedule - an array of millisecond times that should
|
||||
* be used to schedule calls to #check();
|
||||
* @return {integer} - the id of the next timer
|
||||
*/
|
||||
ResizeChecker.prototype.startSchedule = function (schedule) {
|
||||
this._tick = -1;
|
||||
this._currentSchedule = schedule;
|
||||
return this.continueSchedule();
|
||||
};
|
||||
|
||||
/**
|
||||
* Continue running the current schedule. MUST BE CALLED AFTER #startSchedule()
|
||||
*
|
||||
* @method continueSchedule
|
||||
* @return {integer} - the id of the next timer
|
||||
*/
|
||||
ResizeChecker.prototype.continueSchedule = function () {
|
||||
clearTimeout(this._timerId);
|
||||
|
||||
if (this._tick < this._currentSchedule.length - 1) {
|
||||
// at the end of the schedule, don't progress any further but repeat the last value
|
||||
this._tick += 1;
|
||||
}
|
||||
|
||||
var check = this.check; // already bound
|
||||
var tick = this._tick;
|
||||
var notify = this.notify;
|
||||
var ms = this._currentSchedule[this._tick];
|
||||
return (this._timerId = setTimeout(function () {
|
||||
check();
|
||||
}, ms));
|
||||
};
|
||||
|
||||
/**
|
||||
* Signal that the ResizeChecker should shutdown.
|
||||
*
|
||||
* Cleans up it's listeners and timers.
|
||||
*
|
||||
* @method destroy
|
||||
* @return {void}
|
||||
*/
|
||||
ResizeChecker.prototype.destroy = function () {
|
||||
reflowWatcher.off('reflow', this.check);
|
||||
clearTimeout(this._timerId);
|
||||
};
|
||||
|
||||
return ResizeChecker;
|
||||
};
|
||||
});
|
|
@ -52,60 +52,15 @@ define(function (require) {
|
|||
// if time, return a time domain
|
||||
if (ordered && ordered.date) {
|
||||
// Calculate the min date, max date, and time interval;
|
||||
return this.getTimeDomain(scale, this.xValues, ordered);
|
||||
return this.getTimeDomain(scale, ordered);
|
||||
}
|
||||
// return a nominal domain, i.e. array of x values
|
||||
return this.getOrdinalDomain(scale, this.xValues);
|
||||
};
|
||||
|
||||
// Returns a time domain
|
||||
XAxis.prototype.getTimeDomain = function (scale, xValues, ordered) {
|
||||
var maxXValue = d3.max(xValues);
|
||||
var timeInterval = ordered.interval;
|
||||
// Take the min of the xValues or the min date sent on the ordered object
|
||||
var minDate = Math.min(d3.min(xValues), ordered.min);
|
||||
// Take the max of the xValues or the max date that sent on the ordered object
|
||||
var maxDate = +maxXValue <= ordered.max ?
|
||||
this.calculateMaxDate(ordered.max, +maxXValue, timeInterval) : +maxXValue + timeInterval;
|
||||
|
||||
// Add the domain to the scale
|
||||
scale.domain([minDate, maxDate]);
|
||||
|
||||
return scale;
|
||||
};
|
||||
|
||||
// Returns an accurate maxDate
|
||||
XAxis.prototype.calculateMaxDate = function (orderedDate, maxXValue, interval) {
|
||||
/*
|
||||
* Elasticsearch returns bucketed data.
|
||||
*
|
||||
* Buckets have a beginning (the start time), an end (the end time),
|
||||
* and an interval, the width of the bar minus padding.
|
||||
*
|
||||
* We need to create an x axis that ends at the end (or end time) of the
|
||||
* last bucket.
|
||||
*
|
||||
* The time stamp values from the maxXValue represent the beginning
|
||||
* of each bucket. We cannot guarantee that the values passed from
|
||||
* the ordered.max field represents the end of a bucket.
|
||||
*
|
||||
* So, if we were to render either as the cutoff date, then the last bar
|
||||
* on the far right side of the axis may be partially cut off.
|
||||
* Therefore, we need to calculate the end time of the last bucket.
|
||||
*/
|
||||
|
||||
// Difference between the ordered.max value and the max x value
|
||||
var diff = orderedDate - maxXValue;
|
||||
|
||||
// if diff is smaller than the interval, but not zero, add the missing
|
||||
// percentage of the interval back to the ordered.max date
|
||||
if (diff !== 0 && diff < interval) {
|
||||
// calculates the appropriate end time
|
||||
return +orderedDate + ((1 - diff / interval) * interval);
|
||||
}
|
||||
|
||||
// if diff is > than the interval or equals 0 return the ordered.max value
|
||||
return orderedDate;
|
||||
XAxis.prototype.getTimeDomain = function (scale, ordered) {
|
||||
return scale.domain([ordered.min, ordered.max]);
|
||||
};
|
||||
|
||||
// Return a nominal(d3 ordinal) domain
|
||||
|
@ -118,12 +73,10 @@ define(function (require) {
|
|||
XAxis.prototype.getRange = function (scale, ordered, width) {
|
||||
// if time, return a normal range
|
||||
if (ordered && ordered.date) {
|
||||
scale.range([0, width]);
|
||||
return scale;
|
||||
return scale.range([0, width]);
|
||||
}
|
||||
// if nominal, return rangeBands with a default (0.1) spacer specified
|
||||
scale.rangeBands([0, width], 0.1);
|
||||
return scale;
|
||||
return scale.rangeBands([0, width], 0.1);
|
||||
};
|
||||
|
||||
// Return the x axis scale
|
||||
|
|
|
@ -9,20 +9,12 @@ define(function (require) {
|
|||
* Append a y axis to the visualization
|
||||
* arguments:
|
||||
* el => reference to DOM element
|
||||
* chartData => array(s) of x and y value objects
|
||||
* dataArray => flattened array of all value (x, y) objects
|
||||
* _attr => visualization attributes
|
||||
*/
|
||||
function YAxis(args) {
|
||||
this.el = args.el;
|
||||
this.chartData = args.chartData;
|
||||
this.dataArray = args.dataArray;
|
||||
this._attr = _.defaults(args._attr || {}, {
|
||||
// d3 stack function
|
||||
stack: d3.layout.stack()
|
||||
.x(function (d) { return d.x; })
|
||||
.y(function (d) { return d.y; })
|
||||
});
|
||||
this.yMax = args.yMax;
|
||||
this._attr = _.defaults(args._attr || {}, {});
|
||||
}
|
||||
|
||||
_(YAxis.prototype).extend(ErrorHandler.prototype);
|
||||
|
@ -32,61 +24,8 @@ define(function (require) {
|
|||
d3.select(this.el).selectAll('.y-axis-div').call(this.draw());
|
||||
};
|
||||
|
||||
// Determine if data should be stacked
|
||||
YAxis.prototype.isStacked = function () {
|
||||
var data = this.chartData;
|
||||
|
||||
// if the length of the series array is > 1, stack is true
|
||||
for (var i = 0; i < data.length; i++) {
|
||||
if (data[i].series.length > 1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
// Calculate the max y value from this.dataArray
|
||||
YAxis.prototype.getYMaxValue = function () {
|
||||
var self = this;
|
||||
var arr = [];
|
||||
|
||||
// for each object in the dataArray,
|
||||
// push the calculated y value to the initialized array (arr)
|
||||
_.forEach(this.dataArray, function (series) {
|
||||
arr.push(self.getYStackMax(series));
|
||||
});
|
||||
|
||||
// return the largest value from the array
|
||||
return _.max(arr);
|
||||
};
|
||||
|
||||
// Calculate the y value from the value object
|
||||
YAxis.prototype.getYStackMax = function (series) {
|
||||
var self = this;
|
||||
|
||||
// Determine if the data should be stacked
|
||||
if (this.isStacked()) {
|
||||
// if true, stack data
|
||||
series = this._attr.stack(series);
|
||||
}
|
||||
|
||||
// Return the calculated y value
|
||||
return d3.max(series, function (data) {
|
||||
return d3.max(data, function (d) {
|
||||
// if stacked, need to add d.y0 + d.y for the y value
|
||||
if (self.isStacked()) {
|
||||
return d.y0 + d.y;
|
||||
}
|
||||
return d.y;
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// Return the d3 y scale
|
||||
YAxis.prototype.getYScale = function (height) {
|
||||
// save reference to max y value
|
||||
this.yMax = this.getYMaxValue();
|
||||
|
||||
// save reference to y scale
|
||||
this.yScale = d3.scale.linear()
|
||||
.domain([0, this.yMax])
|
||||
|
|
|
@ -4,6 +4,7 @@ define(function (require) {
|
|||
var _ = require('lodash');
|
||||
|
||||
var Handler = Private(require('components/vislib/lib/handler'));
|
||||
var ResizeChecker = Private(require('components/vislib/lib/resize_checker'));
|
||||
var Events = Private(require('factories/events'));
|
||||
var chartTypes = Private(require('components/vislib/vis_types'));
|
||||
|
||||
|
@ -24,6 +25,12 @@ define(function (require) {
|
|||
this.el = $el.get ? $el.get(0) : $el;
|
||||
this.ChartClass = chartTypes[config.type];
|
||||
this._attr = _.defaults(config || {}, {});
|
||||
|
||||
// bind the resize function so it can be used as an event handler
|
||||
this.resize = _.bind(this.resize, this);
|
||||
|
||||
this.resizeChecker = new ResizeChecker(this.el);
|
||||
this.resizeChecker.on('resize', this.resize);
|
||||
}
|
||||
|
||||
// Exposed API for rendering charts.
|
||||
|
@ -46,27 +53,11 @@ define(function (require) {
|
|||
error.message === 'The height and/or width of this container is too small for this chart.') {
|
||||
this.handler.error(error.message);
|
||||
} else {
|
||||
console.group(error.message);
|
||||
console.log(error.message);
|
||||
}
|
||||
}
|
||||
|
||||
this.checkSize();
|
||||
};
|
||||
|
||||
// Check for changes to the chart container height and width.
|
||||
Vis.prototype.checkSize = _.debounce(function () {
|
||||
if (arguments.length) { return; }
|
||||
|
||||
// enable auto-resize
|
||||
var size = $(this.el).find('.chart').width() + ':' + $(this.el).find('.chart').height();
|
||||
|
||||
if (this.prevSize !== size) {
|
||||
this.resize();
|
||||
}
|
||||
this.prevSize = size;
|
||||
setTimeout(this.checkSize(), 250);
|
||||
}, 250);
|
||||
|
||||
// Resize the chart
|
||||
Vis.prototype.resize = function () {
|
||||
if (!this.data) {
|
||||
|
@ -78,16 +69,14 @@ define(function (require) {
|
|||
|
||||
// Destroy the chart
|
||||
Vis.prototype.destroy = function () {
|
||||
// Turn off checkSize
|
||||
this.checkSize(false);
|
||||
|
||||
// Removing chart and all elements associated with it
|
||||
d3.select(this.el).selectAll('*').remove();
|
||||
|
||||
// Cleaning up event listeners
|
||||
this.off('click', null);
|
||||
this.off('hover', null);
|
||||
this.off('brush', null);
|
||||
// remove event listeners
|
||||
this.resizeChecker.off('resize', this.resize);
|
||||
|
||||
// pass destroy call down to owned objects
|
||||
this.resizeChecker.destroy();
|
||||
};
|
||||
|
||||
// Set attributes on the chart
|
||||
|
|
|
@ -21,11 +21,9 @@ define(function (require) {
|
|||
ColumnChart.Super.apply(this, arguments);
|
||||
// Column chart specific attributes
|
||||
this._attr = _.defaults(vis._attr || {}, {
|
||||
offset: 'zero',
|
||||
xValue: function (d, i) { return d.x; },
|
||||
yValue: function (d, i) { return d.y; },
|
||||
dispatch: d3.dispatch('brush', 'click', 'hover', 'mouseenter', 'mouseleave', 'mouseover', 'mouseout'),
|
||||
stack: this._attr.stack.offset(this.offset)
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -45,6 +43,7 @@ define(function (require) {
|
|||
};
|
||||
|
||||
// Stack data
|
||||
// TODO: refactor so that this is called from the data module
|
||||
ColumnChart.prototype.stackData = function (data) {
|
||||
var self = this;
|
||||
|
||||
|
@ -77,8 +76,8 @@ define(function (require) {
|
|||
});
|
||||
});
|
||||
|
||||
// if `addEvents` is true, add brush canvas
|
||||
if (self._attr.addEvents) {
|
||||
// if `addBrushing` is true, add brush canvas
|
||||
if (self._attr.addBrushing) {
|
||||
svg.append('g')
|
||||
.attr('class', 'brush')
|
||||
.call(brush)
|
||||
|
@ -116,7 +115,7 @@ define(function (require) {
|
|||
bars.enter()
|
||||
.append('rect')
|
||||
.attr('class', function (d) {
|
||||
return 'color ' + Legend.prototype.classify.call(this, color(d.label));
|
||||
return 'color ' + Legend.prototype.colorToClass.call(this, color(d.label));
|
||||
})
|
||||
.attr('fill', function (d) {
|
||||
return color(d.label);
|
||||
|
@ -160,26 +159,21 @@ define(function (require) {
|
|||
var self = this;
|
||||
var tooltip = this.vis.tooltip;
|
||||
var isTooltip = this._attr.addTooltip;
|
||||
var addEvents = this._attr.addEvents;
|
||||
var dispatch = this._attr.dispatch;
|
||||
|
||||
bars
|
||||
.on('mouseover.bar', function (d, i) {
|
||||
if (addEvents) {
|
||||
d3.select(this)
|
||||
.classed('hover', true)
|
||||
.style('stroke', '#333')
|
||||
.style('cursor', 'pointer');
|
||||
d3.select(this)
|
||||
.classed('hover', true)
|
||||
.style('stroke', '#333')
|
||||
.style('cursor', 'pointer');
|
||||
|
||||
dispatch.hover(self.eventResponse(d, i));
|
||||
d3.event.stopPropagation();
|
||||
}
|
||||
dispatch.hover(self.eventResponse(d, i));
|
||||
d3.event.stopPropagation();
|
||||
})
|
||||
.on('click.bar', function (d, i) {
|
||||
if (addEvents) {
|
||||
dispatch.click(self.eventResponse(d, i));
|
||||
d3.event.stopPropagation();
|
||||
}
|
||||
dispatch.click(self.eventResponse(d, i));
|
||||
d3.event.stopPropagation();
|
||||
})
|
||||
.on('mouseout.bar', function () {
|
||||
d3.select(this).classed('hover', false)
|
||||
|
|
|
@ -8,11 +8,12 @@ define(function (require) {
|
|||
|
||||
require('components/config/config');
|
||||
require('components/courier/courier');
|
||||
require('components/notify/notify');
|
||||
require('components/state_management/app_state_factory');
|
||||
require('components/filter_bar/filter_bar');
|
||||
require('components/storage/storage');
|
||||
require('components/notify/notify');
|
||||
require('components/persisted_log/persisted_log');
|
||||
require('components/state_management/app_state_factory');
|
||||
require('components/storage/storage');
|
||||
require('components/url/url');
|
||||
require('directives/click_focus');
|
||||
require('directives/info');
|
||||
require('directives/spinner');
|
||||
|
|
|
@ -4,7 +4,6 @@ define(function (require) {
|
|||
.directive('confirmClick', function () {
|
||||
return {
|
||||
restrict: 'A',
|
||||
scope: {},
|
||||
link: function ($scope, $elem, attrs) {
|
||||
$elem.bind('click', function () {
|
||||
var message = attrs.confirmation || 'Are you sure?';
|
||||
|
|
|
@ -25,7 +25,7 @@ define(function (require) {
|
|||
var stringify = function () {
|
||||
var text;
|
||||
// If both parts are date math, try to look up a reasonable string
|
||||
if (!moment.isMoment($scope.from) && !moment.isMoment($scope.to)) {
|
||||
if ($scope.from && $scope.to && !moment.isMoment($scope.from) && !moment.isMoment($scope.to)) {
|
||||
var tryLookup = lookupByRange[$scope.from.toString() + ' to ' + $scope.to.toString()];
|
||||
if (tryLookup) {
|
||||
$elem.text(tryLookup.display);
|
||||
|
|
|
@ -3,7 +3,7 @@ define(function (require) {
|
|||
var _ = require('lodash');
|
||||
var rison = require('utils/rison');
|
||||
|
||||
module.directive('savedObjectFinder', function (savedSearches, savedVisualizations, savedDashboards, $location, $route) {
|
||||
module.directive('savedObjectFinder', function (savedSearches, savedVisualizations, savedDashboards, $location, kbnUrl) {
|
||||
|
||||
var vars = {
|
||||
searches: {
|
||||
|
@ -85,12 +85,12 @@ define(function (require) {
|
|||
|
||||
// angular wants the '/path', not '#/path'
|
||||
var path = url.substr(1);
|
||||
if ($route.matches(path)) {
|
||||
if (kbnUrl.matches(path)) {
|
||||
$event.preventDefault();
|
||||
|
||||
// change works with paths, but we are only here because the paths
|
||||
// are the same, so we have to change the whole url to be the new path
|
||||
$route.changeUrl(path);
|
||||
kbnUrl.change(path);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
16
src/kibana/filters/rison.js
Normal file
16
src/kibana/filters/rison.js
Normal file
|
@ -0,0 +1,16 @@
|
|||
define(function (require) {
|
||||
var rison = require('utils/rison');
|
||||
var module = require('modules').get('kibana');
|
||||
|
||||
module.filter('rison', function () {
|
||||
return function (str) {
|
||||
return rison.encode(str);
|
||||
};
|
||||
});
|
||||
|
||||
module.filter('risonDecode', function () {
|
||||
return function (str) {
|
||||
return rison.decode(str);
|
||||
};
|
||||
});
|
||||
});
|
|
@ -4,7 +4,7 @@
|
|||
define(function (require) {
|
||||
var _ = require('lodash');
|
||||
require('modules')
|
||||
.get('kbn/filters')
|
||||
.get('kibana')
|
||||
.filter('shortDots', function (config) {
|
||||
return function (str) {
|
||||
if (!_.isString(str) || config.get('shortDots:enable') !== true) {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
define(function (require) {
|
||||
require('modules')
|
||||
.get('kbn/filters')
|
||||
.get('kibana')
|
||||
.filter('uriescape', function () {
|
||||
return function (str) {
|
||||
return encodeURIComponent(str);
|
||||
|
|
|
@ -7,11 +7,11 @@
|
|||
<meta name="viewport" content="width=device-width">
|
||||
<title>Kibana 4</title>
|
||||
<!-- load the root require context -->
|
||||
<script src="bower_components/requirejs/require.js"></script>
|
||||
<script src="/bower_components/requirejs/require.js"></script>
|
||||
|
||||
<script src="kibana/require.config.js"></script>
|
||||
<script src="/require.config.js"></script>
|
||||
<script>require(['kibana'], function (kibana) { kibana.init(); });</script>
|
||||
<link rel="stylesheet" href="kibana/styles/main.css" >
|
||||
<link rel="stylesheet" href="/styles/main.css" >
|
||||
</head>
|
||||
<body ng-controller="kibana" ng-class="'application-'+activeApp">
|
||||
<kbn-notifications list="notifList"></kbn-notifications>
|
|
@ -12,7 +12,7 @@ define(function (require) {
|
|||
require('angular-route');
|
||||
require('angular-bindonce');
|
||||
|
||||
var configFile = require('config_file');
|
||||
var configFile = JSON.parse(require('text!config'));
|
||||
|
||||
var kibana = modules.get('kibana', [
|
||||
// list external requirements here
|
||||
|
@ -21,7 +21,7 @@ define(function (require) {
|
|||
'ngRoute'
|
||||
]);
|
||||
|
||||
configFile.elasticsearch = configFile.elasticsearch || ('http://' + window.location.hostname + ':9200');
|
||||
configFile.elasticsearch = ('http://' + window.location.hostname + '/elasticsearch/');
|
||||
|
||||
kibana
|
||||
// This stores the Kibana revision number, @REV@ is replaced by grunt.
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
require.config({
|
||||
baseUrl: './kibana',
|
||||
baseUrl: './',
|
||||
paths: {
|
||||
kibana: './index',
|
||||
config_file: '../config',
|
||||
|
||||
kibana: 'index',
|
||||
// special utils
|
||||
routes: 'utils/routes/index',
|
||||
errors: 'components/errors',
|
||||
|
@ -11,27 +9,27 @@ require.config({
|
|||
lodash: 'utils/_mixins',
|
||||
|
||||
// bower_components
|
||||
'angular-bindonce': '../bower_components/angular-bindonce/bindonce',
|
||||
'angular-bootstrap': '../bower_components/angular-bootstrap/ui-bootstrap-tpls',
|
||||
'angular-elastic': '../bower_components/angular-elastic/elastic',
|
||||
'angular-route': '../bower_components/angular-route/angular-route',
|
||||
'angular-ui-ace': '../bower_components/angular-ui-ace/ui-ace',
|
||||
ace: '../bower_components/ace-builds/src-noconflict/ace',
|
||||
angular: '../bower_components/angular/angular',
|
||||
async: '../bower_components/async/lib/async',
|
||||
bower_components: '../bower_components',
|
||||
css: '../bower_components/require-css/css',
|
||||
d3: '../bower_components/d3/d3',
|
||||
elasticsearch: '../bower_components/elasticsearch/elasticsearch.angular',
|
||||
faker: '../bower_components/Faker/faker',
|
||||
file_saver: '../bower_components/FileSaver/FileSaver',
|
||||
gridster: '../bower_components/gridster/dist/jquery.gridster',
|
||||
inflection: '../bower_components/inflection/lib/inflection',
|
||||
jquery: '../bower_components/jquery/dist/jquery',
|
||||
jsonpath: '../bower_components/jsonpath/lib/jsonpath',
|
||||
lodash_src: '../bower_components/lodash/dist/lodash',
|
||||
moment: '../bower_components/moment/moment',
|
||||
text: '../bower_components/requirejs-text/text'
|
||||
'angular-bindonce': 'bower_components/angular-bindonce/bindonce',
|
||||
'angular-bootstrap': 'bower_components/angular-bootstrap/ui-bootstrap-tpls',
|
||||
'angular-elastic': 'bower_components/angular-elastic/elastic',
|
||||
'angular-route': 'bower_components/angular-route/angular-route',
|
||||
'angular-ui-ace': 'bower_components/angular-ui-ace/ui-ace',
|
||||
ace: 'bower_components/ace-builds/src-noconflict/ace',
|
||||
angular: 'bower_components/angular/angular',
|
||||
async: 'bower_components/async/lib/async',
|
||||
bower_components: 'bower_components',
|
||||
css: 'bower_components/require-css/css',
|
||||
d3: 'bower_components/d3/d3',
|
||||
elasticsearch: 'bower_components/elasticsearch/elasticsearch.angular',
|
||||
faker: 'bower_components/Faker/faker',
|
||||
file_saver: 'bower_components/FileSaver/FileSaver',
|
||||
gridster: 'bower_components/gridster/dist/jquery.gridster',
|
||||
inflection: 'bower_components/inflection/lib/inflection',
|
||||
jquery: 'bower_components/jquery/dist/jquery',
|
||||
jsonpath: 'bower_components/jsonpath/lib/jsonpath',
|
||||
lodash_src: 'bower_components/lodash/dist/lodash',
|
||||
moment: 'bower_components/moment/moment',
|
||||
text: 'bower_components/requirejs-text/text'
|
||||
},
|
||||
shim: {
|
||||
angular: {
|
||||
|
|
|
@ -14,4 +14,4 @@ define(function (require) {
|
|||
|
||||
return es;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -62,10 +62,6 @@ define(function (require) {
|
|||
}()));
|
||||
}
|
||||
|
||||
Timefilter.prototype.enabled = function (state) {
|
||||
this.enabled = !!state;
|
||||
};
|
||||
|
||||
Timefilter.prototype.get = function (indexPattern) {
|
||||
var filter;
|
||||
var timefield = indexPattern.timeFieldName && _.find(indexPattern.fields, {name: indexPattern.timeFieldName});
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
@bs-less-dir: "../../bower_components/bootstrap/less";
|
||||
@bs-less-dir: "../bower_components/bootstrap/less";
|
||||
|
||||
// Core variables and mixins
|
||||
@import "theme/_variables.less";
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
@import "../../bower_components/font-awesome/less/font-awesome.less";
|
||||
@import "../bower_components/font-awesome/less/font-awesome.less";
|
||||
@import (reference) "lesshat.less";
|
||||
|
||||
// generic mixins
|
||||
|
|
|
@ -150,6 +150,15 @@ define(function (require) {
|
|||
// always call flush, it might not do anything
|
||||
flush(this, args);
|
||||
};
|
||||
},
|
||||
chunk: function (arr, count) {
|
||||
var size = Math.ceil(arr.length / count);
|
||||
var chunks = new Array(count);
|
||||
for (var i = 0; i < count; i ++) {
|
||||
var start = i * size;
|
||||
chunks[i] = arr.slice(start, start + size);
|
||||
}
|
||||
return chunks;
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
define(function (require) {
|
||||
return function routeSetup(Promise, kbnSetup, config, $route, indexPatterns, Notifier) {
|
||||
return function routeSetup(Promise, kbnSetup, config, $route, kbnUrl, indexPatterns, Notifier) {
|
||||
|
||||
var errors = require('errors');
|
||||
var NoDefaultIndexPattern = errors.NoDefaultIndexPattern;
|
||||
|
@ -26,7 +26,7 @@ define(function (require) {
|
|||
if (err instanceof NoDefaultIndexPattern || err instanceof NoDefinedIndexPatterns) {
|
||||
// .change short circuits the routes by calling $route.refresh(). We can safely swallow this error
|
||||
// after reporting it to the user
|
||||
$route.change('/settings/indices');
|
||||
kbnUrl.change('/settings/indices');
|
||||
(new Notifier()).error(err);
|
||||
} else {
|
||||
return Promise.reject(err);
|
||||
|
@ -34,4 +34,4 @@ define(function (require) {
|
|||
}
|
||||
};
|
||||
};
|
||||
});
|
||||
});
|
||||
|
|
|
@ -5,42 +5,6 @@ define(function (require) {
|
|||
require('components/setup/setup');
|
||||
require('services/promises');
|
||||
|
||||
require('modules').get('kibana')
|
||||
.config(function ($provide) {
|
||||
// decorate the $route object to include a change and changeUrl method
|
||||
$provide.decorator('$route', function ($delegate, $location, $rootScope) {
|
||||
var reloading;
|
||||
var doneReloading = function () { reloading = false; };
|
||||
$rootScope.$on('$routeUpdate', doneReloading);
|
||||
$rootScope.$on('$routeChangeStart', doneReloading);
|
||||
|
||||
var reload = function () {
|
||||
if (!reloading) $delegate.reload();
|
||||
reloading = true;
|
||||
};
|
||||
|
||||
$delegate.change = function (path) {
|
||||
if (path !== $location.path()) {
|
||||
$location.path(path);
|
||||
reload();
|
||||
}
|
||||
};
|
||||
$delegate.changeUrl = function (url) {
|
||||
if (url !== $location.url()) {
|
||||
$location.url(url);
|
||||
reload();
|
||||
}
|
||||
};
|
||||
$delegate.matches = function (url) {
|
||||
var route = $delegate.current.$$route;
|
||||
if (!route || !route.regexp) return null;
|
||||
return route.regexp.test(url);
|
||||
};
|
||||
|
||||
return $delegate;
|
||||
});
|
||||
});
|
||||
|
||||
function RouteManager() {
|
||||
var when = [];
|
||||
var additions = [];
|
||||
|
|
95
src/kibana/utils/sequencer.js
Normal file
95
src/kibana/utils/sequencer.js
Normal file
|
@ -0,0 +1,95 @@
|
|||
define(function (require) {
|
||||
var _ = require('lodash');
|
||||
|
||||
function create(min, max, length, mod) {
|
||||
var seq = new Array(length);
|
||||
|
||||
var valueDist = max - min;
|
||||
|
||||
// range of values that the mod creates
|
||||
var modRange = [mod(0, length), mod(length - 1, length)];
|
||||
|
||||
// distance between
|
||||
var modRangeDist = modRange[1] - modRange[0];
|
||||
|
||||
_.times(length, function (i) {
|
||||
var modIPercent = (mod(i, length) - modRange[0]) / modRangeDist;
|
||||
|
||||
// percent applied to distance and added to min to
|
||||
// produce value
|
||||
seq[i] = min + (valueDist * modIPercent);
|
||||
});
|
||||
|
||||
seq.min = min;
|
||||
seq.max = max;
|
||||
|
||||
return seq;
|
||||
}
|
||||
|
||||
return {
|
||||
/**
|
||||
* Create an exponential sequence of numbers.
|
||||
*
|
||||
* Creates a curve resembling:
|
||||
*
|
||||
* ;
|
||||
* /
|
||||
* /
|
||||
* .-'
|
||||
* _.-"
|
||||
* _.-'"
|
||||
* _,.-'"
|
||||
* _,..-'"
|
||||
* _,..-'""
|
||||
* _,..-'""
|
||||
* ____,..--'""
|
||||
*
|
||||
* @param {number} min - the min value to produce
|
||||
* @param {number} max - the max value to produce
|
||||
* @param {number} length - the number of values to produce
|
||||
* @return {number[]} - an array containing the sequence
|
||||
*/
|
||||
createEaseIn: _.partialRight(create, function (i, length) {
|
||||
// generates numbers from 1 to +Infinity
|
||||
return i * Math.pow(i, 1.1111);
|
||||
}),
|
||||
|
||||
/**
|
||||
* Create an sequence of numbers using sine.
|
||||
*
|
||||
* Create a curve resembling:
|
||||
*
|
||||
* ____,..--'""
|
||||
* _,..-'""
|
||||
* _,..-'""
|
||||
* _,..-'"
|
||||
* _,.-'"
|
||||
* _.-'"
|
||||
* _.-"
|
||||
* .-'
|
||||
* /
|
||||
* /
|
||||
* ;
|
||||
*
|
||||
*
|
||||
* @param {number} min - the min value to produce
|
||||
* @param {number} max - the max value to produce
|
||||
* @param {number} length - the number of values to produce
|
||||
* @return {number[]} - an array containing the sequence
|
||||
*/
|
||||
createEaseOut: _.partialRight(create, function (i, length) {
|
||||
// adapted from output of http://www.timotheegroleau.com/Flash/experiments/easing_function_generator.htm
|
||||
// generates numbers from 0 to 100
|
||||
|
||||
var ts = (i /= length) * i;
|
||||
var tc = ts * i;
|
||||
return 100 * (
|
||||
0.5 * tc * ts +
|
||||
-3 * ts * ts +
|
||||
6.5 * tc +
|
||||
-7 * ts +
|
||||
4 * i
|
||||
);
|
||||
})
|
||||
};
|
||||
});
|
19
src/server/DIST_README.md
Normal file
19
src/server/DIST_README.md
Normal file
|
@ -0,0 +1,19 @@
|
|||
# Kibana @@version
|
||||
|
||||
Kibana is an open source (Apache Licensed), browser based analytics and search dashboard for Elasticsearch. Kibana is a snap to setup and start using. Kibana strives to be easy to get started with, while also being flexible and powerful, just like Elasticsearch.
|
||||
|
||||
## Installation
|
||||
|
||||
* Download: http://www.elasticsearch.org/overview/kibana/installation/
|
||||
* Run **bin/kibana** on unix, or **bin/kibana.bat** on Windows.
|
||||
* Visit http://localhost:5601
|
||||
|
||||
## Need Help?
|
||||
|
||||
Need help? Try #elasticsearch or #logstash on Freenode IRC. You can also find help on the elasticsearch-users@googlegroups.com or logstash-users@googlegroups.com mailing lists.
|
||||
|
||||
You can also find documentation at http://www.elasticsearch.com/guide/en/kibana/current
|
||||
|
||||
## Contributing
|
||||
|
||||
If you have a bugfix or new feature that you would like to contribute to Kibana, please find or open an issue about it first. Kibana is an open source project that is available on Github: https://github.com/elasticsearch/kibana
|
|
@ -1,5 +1,3 @@
|
|||
ruby "1.9.3", :engine => 'jruby', :engine_version => '1.7.13'
|
||||
|
||||
source "https://rubygems.org"
|
||||
|
||||
gem 'sinatra', :require => 'sinatra/base'
|
||||
|
@ -8,3 +6,4 @@ gem 'puma'
|
|||
gem 'warbler'
|
||||
gem 'elasticsearch'
|
||||
gem 'rack-reverse-proxy', :require => 'rack/reverse_proxy'
|
||||
gem 'colorize'
|
|
@ -2,6 +2,7 @@ GEM
|
|||
remote: https://rubygems.org/
|
||||
specs:
|
||||
backports (3.6.0)
|
||||
colorize (0.7.3)
|
||||
elasticsearch (1.0.4)
|
||||
elasticsearch-api (= 1.0.4)
|
||||
elasticsearch-transport (= 1.0.4)
|
||||
|
@ -52,6 +53,7 @@ PLATFORMS
|
|||
ruby
|
||||
|
||||
DEPENDENCIES
|
||||
colorize
|
||||
elasticsearch
|
||||
puma
|
||||
rack-reverse-proxy
|
83
src/server/bin/initialize
Executable file
83
src/server/bin/initialize
Executable file
|
@ -0,0 +1,83 @@
|
|||
#!/usr/bin/env ruby
|
||||
require "optparse"
|
||||
require 'rubygems'
|
||||
require 'puma/cli'
|
||||
require "yaml"
|
||||
|
||||
HERE = File.expand_path(File.dirname(__FILE__))
|
||||
|
||||
module Kibana
|
||||
def self.global_settings
|
||||
@settings ||= {}
|
||||
end
|
||||
end
|
||||
|
||||
# Defaults for the options
|
||||
options = {
|
||||
:config => File.expand_path("#{HERE}/../config/kibana.yml") || ENV["CONFIG_PATH"]
|
||||
}
|
||||
|
||||
# Create a new parser
|
||||
parser = OptionParser.new do |opts|
|
||||
opts.on('-e', '--elasticsearch URI', 'Elasticsearch instance') do |arg|
|
||||
options[:elasticsearch] = arg
|
||||
end
|
||||
opts.on('-c', '--config PATH', 'Path to config file') do |arg|
|
||||
options[:config] = arg
|
||||
end
|
||||
opts.on('-p', '--port PORT', 'Kibana port') do |arg|
|
||||
options[:port] = arg
|
||||
end
|
||||
opts.on('-H', '--host HOST', 'Kibana host') do |arg|
|
||||
options[:host] = arg
|
||||
end
|
||||
opts.on('-v', '--version', 'Display version') do |arg|
|
||||
puts ENV['KIBANA_VERSION'] || 'dev-build'
|
||||
exit
|
||||
end
|
||||
opts.on('-h', '--help', 'Display this screen') do
|
||||
puts opts
|
||||
exit
|
||||
end
|
||||
end
|
||||
|
||||
# Set the usage banner
|
||||
parser.banner = "Usage: kibana <options>\n\n"
|
||||
|
||||
# Parse the command line arguments
|
||||
parser.parse! ARGV
|
||||
|
||||
# Load the config from default
|
||||
config = YAML.load(IO.read(options[:config]))
|
||||
|
||||
# Set the override for the port
|
||||
port = (options[:port] || config['port'])
|
||||
|
||||
# Set the override for the host
|
||||
host = (options[:host] || config['host'])
|
||||
|
||||
# Set the override for Elasticsaerch
|
||||
elasticsearch = (options[:elasticsearch] || config['elasticsearch'])
|
||||
|
||||
# If the env isn't set we need to set it to development
|
||||
ENV["RACK_ENV"] = "development" if ENV["RACK_ENV"].nil?
|
||||
|
||||
# Set the global_settings that are shared across every app
|
||||
Kibana.global_settings[:port] = port || 5601
|
||||
Kibana.global_settings[:host] = host || '0.0.0.0'
|
||||
Kibana.global_settings[:config] = config
|
||||
Kibana.global_settings[:elasticsearch] = elasticsearch
|
||||
Kibana.global_settings[:root] = File.expand_path("#{File.dirname(__FILE__)}/../")
|
||||
|
||||
# Set the public folder based on whether we are running in production or not.
|
||||
if ENV['RACK_ENV'] == ('production')
|
||||
Kibana.global_settings[:public_folder] = File.expand_path("#{File.dirname(__FILE__)}/../public/")
|
||||
else
|
||||
Kibana.global_settings[:public_folder] = File.expand_path("#{File.dirname(__FILE__)}/../../kibana/")
|
||||
end
|
||||
|
||||
# Add the root of the project to the load path
|
||||
$LOAD_PATH.unshift(Kibana.global_settings[:root])
|
||||
|
||||
require "lib/server"
|
||||
Kibana::Server.run(Kibana.global_settings)
|
25
src/server/bin/kibana.bat
Normal file
25
src/server/bin/kibana.bat
Normal file
|
@ -0,0 +1,25 @@
|
|||
@echo off
|
||||
|
||||
SETLOCAL
|
||||
if not defined JAVA_HOME goto java_home_err
|
||||
|
||||
set SCRIPT_DIR=%~dp0
|
||||
for %%I in ("%SCRIPT_DIR%..") do set DIR=%%~dpfI
|
||||
|
||||
set RACK_ENV=production
|
||||
set CONFIG_PATH=%DIR%\config\kibana.yml
|
||||
set KIBANA_VERSION=@@version
|
||||
|
||||
|
||||
TITLE Kibana %KIBANA_VERSION%
|
||||
|
||||
"%JAVA_HOME%\bin\java" -jar "%DIR%\lib\kibana.jar" %*
|
||||
|
||||
:java_home_err
|
||||
echo JAVA_HOME enviroment variable must be set!
|
||||
pause
|
||||
goto finally
|
||||
|
||||
:finally
|
||||
|
||||
ENDLOCAL
|
30
src/server/bin/kibana.sh
Executable file
30
src/server/bin/kibana.sh
Executable file
|
@ -0,0 +1,30 @@
|
|||
#!/bin/sh
|
||||
|
||||
SCRIPT=$0
|
||||
|
||||
# SCRIPT may be an arbitrarily deep series of symlinks. Loop until we have the concrete path.
|
||||
while [ -h "$SCRIPT" ] ; do
|
||||
ls=`ls -ld "$SCRIPT"`
|
||||
# Drop everything prior to ->
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
SCRIPT="$link"
|
||||
else
|
||||
SCRIPT=`dirname "$SCRIPT"`/"$link"
|
||||
fi
|
||||
done
|
||||
|
||||
DIR=$(dirname "${SCRIPT}")
|
||||
|
||||
if [ -x "${JAVA_HOME}/bin/java" ]; then
|
||||
JAVA="${JAVA_HOME}/bin/java"
|
||||
else
|
||||
JAVA=`which java`
|
||||
fi
|
||||
|
||||
if [ ! -x "${JAVA}" ]; then
|
||||
echo "Could not find any executable Java binary. Please install Java in your PATH or set JAVA_HOME"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
KIBANA_VERSION=@@version CONFIG_PATH=${DIR}/../config/kibana.yml RACK_ENV=production exec "${JAVA}" -jar "${DIR}/../lib/kibana.jar" "$@"
|
20
src/server/config/kibana.yml
Normal file
20
src/server/config/kibana.yml
Normal file
|
@ -0,0 +1,20 @@
|
|||
# Kibana is served by a backend server. This controls which port to use.
|
||||
port: 5601
|
||||
|
||||
# The Elasticsearch instance to user for all your queries
|
||||
elasticsearch: "http://localhost:9200"
|
||||
|
||||
# Kibana uses and index in Elasticsearch to store saved searches, visualizations
|
||||
# and dashboard. It will create an new index if it doesn't already exist.
|
||||
kibanaIndex: "kibana-int"
|
||||
|
||||
# Applications loaded and included into Kibana. Use the settings below to
|
||||
# customize the applications and thier names.
|
||||
apps:
|
||||
- { id: "discover", name: "Discover" }
|
||||
- { id: "visualize", name: "Visualize" }
|
||||
- { id: "dashboard", name: "Dashboard" }
|
||||
- { id: "settings", name: "Settings" }
|
||||
|
||||
# The default application to laad.
|
||||
defaultAppId: "discover"
|
22
src/server/config/web.ru
Normal file
22
src/server/config/web.ru
Normal file
|
@ -0,0 +1,22 @@
|
|||
require "rubygems"
|
||||
require "bundler/setup"
|
||||
|
||||
ROOT = File.expand_path("#{File.dirname(__FILE__)}/../")
|
||||
|
||||
if ENV['RACK_ENV'] == ('development')
|
||||
PUBLIC_ROOT = File.expand_path("#{File.dirname(__FILE__)}/../../kibana/")
|
||||
CONFIG_PATH = File.expand_path("#{File.dirname(__FILE__)}/kibana.yml")
|
||||
end
|
||||
|
||||
if ENV['RACK_ENV'] == ('production')
|
||||
PUBLIC_ROOT = File.expand_path("#{File.dirname(__FILE__)}/../public/")
|
||||
CONFIG_PATH = ENV["CONFIG_PATH"]
|
||||
end
|
||||
|
||||
$LOAD_PATH.unshift(ROOT)
|
||||
|
||||
# Require the application
|
||||
require "#{ROOT}/lib/app"
|
||||
|
||||
# Run the application
|
||||
run Kibana::App
|
44
src/server/lib/ColorLogger.rb
Normal file
44
src/server/lib/ColorLogger.rb
Normal file
|
@ -0,0 +1,44 @@
|
|||
require "rack/commonlogger"
|
||||
require "colorize"
|
||||
|
||||
class ColorLogger < Rack::CommonLogger
|
||||
def log(env, status, header, begin_at)
|
||||
now = Time.now
|
||||
length = extract_content_length(header)
|
||||
|
||||
case status
|
||||
when 300..399
|
||||
statusColor = :yellow
|
||||
when 400..499
|
||||
statusColor = :red
|
||||
when 500..599
|
||||
statusColor = :magenta
|
||||
else
|
||||
statusColor = :green
|
||||
end
|
||||
|
||||
msg = (now.strftime('%b %d, %Y @ %H:%M:%S.%L')).light_black << ' '
|
||||
msg << env["REQUEST_METHOD"].light_blue << ' '
|
||||
msg << env["PATH_INFO"]
|
||||
msg << (env["QUERY_STRING"].empty? ? '' : "?#{env["QUERY_STRING"]}" ) << ' '
|
||||
msg << status.to_s.send(statusColor) << ' '
|
||||
msg << ((now - begin_at) * 1000).to_i.to_s << 'ms - ' << length
|
||||
msg << "\n"
|
||||
|
||||
# If there is an error then we need to append the stack
|
||||
if env['sinatra.error'] && status != 404
|
||||
error = env['sinatra.error']
|
||||
msg << "#{error.message}\n #{error.backtrace.join("\n ")}".send(statusColor)
|
||||
msg << "\n"
|
||||
end
|
||||
|
||||
logger = @logger || env['rack.errors']
|
||||
if logger.respond_to?(:write)
|
||||
logger.write(msg)
|
||||
else
|
||||
logger << msg
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
46
src/server/lib/JSONLogger.rb
Normal file
46
src/server/lib/JSONLogger.rb
Normal file
|
@ -0,0 +1,46 @@
|
|||
require "rack/commonlogger"
|
||||
|
||||
class JSONLogger < Rack::CommonLogger
|
||||
def log(env, status, header, begin_at)
|
||||
now = Time.now
|
||||
length = extract_content_length(header)
|
||||
|
||||
data = {
|
||||
"@timestamp" => now.iso8601,
|
||||
:status => status.to_s[0..3],
|
||||
:level => status < 399 ? "INFO" : 'ERROR',
|
||||
:name => "Kibana",
|
||||
:request_method => env["REQUEST_METHOD"],
|
||||
:request => env["PATH_INFO"] + (env["QUERY_STRING"].empty? ? "" : "#{env['QUERY_STRING']}"),
|
||||
:path => env["PATH_INFO"],
|
||||
:query_string => env["QUERY_STRING"],
|
||||
:remote_addr => env['HTTP_X_FORWARD_FOR'] || env["REMOTE_ADDR"],
|
||||
:remote_user => env["REMOTE_USER"],
|
||||
:http_version => env["HTTP_VERSION"],
|
||||
:content_length => length,
|
||||
:response_time => ((now - begin_at) * 1000).to_i # convert to milliseconds
|
||||
}
|
||||
|
||||
# If there is an error then we need to append the stack
|
||||
if env['sinatra.error']
|
||||
error = env['sinatra.error']
|
||||
data[:error] = {
|
||||
:name => error.class.to_s,
|
||||
:message => error.message,
|
||||
:stack => error.backtrace
|
||||
}
|
||||
end
|
||||
|
||||
data[:message] = "#{data[:request_method]} #{data[:path]+(data[:query_string].empty? ? '' : '?'+data[:query_string])} #{data[:status]} #{data[:response_time]}ms - #{data[:content_length].empty? ? '-' : data[:content_length]}"
|
||||
|
||||
logger = @logger || env['rack.errors']
|
||||
msg = data.to_json+"\n"
|
||||
|
||||
if logger.respond_to?(:write)
|
||||
logger.write(msg)
|
||||
else
|
||||
logger << msg
|
||||
end
|
||||
end
|
||||
end
|
||||
|
68
src/server/lib/app.rb
Normal file
68
src/server/lib/app.rb
Normal file
|
@ -0,0 +1,68 @@
|
|||
# Add the root of the project to the $LOAD_PATH, For some reason it seems
|
||||
# to be getting lost when we use warble to make the jar. This fixes it :D
|
||||
$LOAD_PATH.unshift(Kibana.global_settings[:root])
|
||||
|
||||
require "logger"
|
||||
require "json"
|
||||
require "lib/JSONLogger"
|
||||
require "lib/ColorLogger"
|
||||
require "routes/home"
|
||||
require "sinatra/json"
|
||||
require "routes/proxy"
|
||||
|
||||
class Logger
|
||||
alias_method :write, :<<
|
||||
end
|
||||
|
||||
module Kibana
|
||||
class App < Sinatra::Base
|
||||
|
||||
helpers Sinatra::JSON
|
||||
|
||||
configure do
|
||||
logger = Logger.new(STDOUT)
|
||||
logger.formatter = proc do |severity, datetime, progname, msg|
|
||||
data = {
|
||||
'@timestamp' => datetime.iso8601,
|
||||
:level => severity,
|
||||
:name => progname || "Kibana",
|
||||
:message => msg
|
||||
}
|
||||
data.to_json + "\n"
|
||||
end
|
||||
set :logger, logger
|
||||
disable :raise_errors
|
||||
disable :show_exceptions
|
||||
disable :dump_errors
|
||||
end
|
||||
|
||||
configure :production do
|
||||
use JSONLogger, settings.logger
|
||||
end
|
||||
|
||||
configure :development do
|
||||
use ColorLogger, settings.logger
|
||||
end
|
||||
|
||||
error do
|
||||
500
|
||||
end
|
||||
|
||||
error 400 do
|
||||
json :status => 500, :message => "Bad Request"
|
||||
end
|
||||
|
||||
error 500 do
|
||||
json :status => 500, :message => "Internal Server Error"
|
||||
end
|
||||
|
||||
not_found do
|
||||
json :status => 404, :message => "Not Found"
|
||||
end
|
||||
|
||||
# Routes go here
|
||||
use Routes::Home
|
||||
use Routes::Proxy
|
||||
|
||||
end
|
||||
end
|
60
src/server/lib/server.rb
Normal file
60
src/server/lib/server.rb
Normal file
|
@ -0,0 +1,60 @@
|
|||
require "rubygems"
|
||||
require "bundler/setup"
|
||||
require "puma"
|
||||
require "colorize"
|
||||
require "json"
|
||||
require "#{Kibana.global_settings[:root]}/lib/app"
|
||||
|
||||
# Require the application
|
||||
module Kibana
|
||||
module Server
|
||||
|
||||
DEFAULTS = {
|
||||
:host => '0.0.0.0',
|
||||
:port => 5601,
|
||||
:threads => '0:16',
|
||||
:verbose => false
|
||||
}
|
||||
|
||||
def self.log(msg)
|
||||
if ENV['RACK_ENV'] == 'production'
|
||||
data = {
|
||||
"@timestamp" => Time.now.iso8601,
|
||||
:level => 'INFO',
|
||||
:name => 'Kibana',
|
||||
:message => msg
|
||||
}
|
||||
puts data.to_json
|
||||
else
|
||||
message = (Time.now.strftime('%b %d, %Y @ %H:%M:%S.%L')).light_black << ' '
|
||||
message << msg.yellow
|
||||
puts message
|
||||
end
|
||||
end
|
||||
|
||||
def self.run(options = {})
|
||||
options = DEFAULTS.merge(options)
|
||||
min, max = options[:threads].split(':', 2)
|
||||
|
||||
app = Kibana::App.new()
|
||||
server = Puma::Server.new(app)
|
||||
|
||||
# Configure server
|
||||
server.add_tcp_listener(options[:host], options[:port])
|
||||
server.min_threads = min
|
||||
server.max_threads = max
|
||||
|
||||
begin
|
||||
log("Kibana server started on tcp://#{options[:host]}:#{options[:port]} in #{ENV['RACK_ENV']} mode.")
|
||||
server.run.join
|
||||
rescue Interrupt
|
||||
log("Kibana server gracefully stopping, waiting for requests to finish")
|
||||
server.stop(true)
|
||||
log("Kibana server stopped.")
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
21
src/server/routes/base.rb
Normal file
21
src/server/routes/base.rb
Normal file
|
@ -0,0 +1,21 @@
|
|||
require "sinatra/base"
|
||||
require "sinatra/json"
|
||||
require "yaml"
|
||||
|
||||
module Kibana
|
||||
module Routes
|
||||
class Base < Sinatra::Base
|
||||
helpers Sinatra::JSON
|
||||
configure do
|
||||
config = Kibana.global_settings[:config].clone()
|
||||
config['elasticsearch'] = Kibana.global_settings[:elasticsearch]
|
||||
config['port'] = Kibana.global_settings[:port].to_i
|
||||
|
||||
set :root, Kibana.global_settings[:root]
|
||||
set :public_folder, Kibana.global_settings[:public_folder]
|
||||
set :httponly, true
|
||||
set :config, config
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
21
src/server/routes/home.rb
Normal file
21
src/server/routes/home.rb
Normal file
|
@ -0,0 +1,21 @@
|
|||
require "routes/base"
|
||||
|
||||
module Kibana
|
||||
module Routes
|
||||
class Home < Base
|
||||
|
||||
get "/" do
|
||||
File.read(File.join(settings.public_folder, 'index.html'))
|
||||
end
|
||||
|
||||
get "/config" do
|
||||
# Clone the settings object and change the elasticsearch attribute
|
||||
# to the proxy for elasticsearch
|
||||
data = settings.config.clone()
|
||||
data['elasticsearch'] = "#{request.scheme}://#{request.host}:#{request.port}/elasticsearch"
|
||||
json data
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
14
src/server/routes/proxy.rb
Normal file
14
src/server/routes/proxy.rb
Normal file
|
@ -0,0 +1,14 @@
|
|||
require "routes/base"
|
||||
require "rack/reverse_proxy"
|
||||
|
||||
module Kibana
|
||||
module Routes
|
||||
class Proxy < Base
|
||||
# Rack middleware goes here
|
||||
config = settings.config
|
||||
use Rack::ReverseProxy do
|
||||
reverse_proxy(/^\/elasticsearch(.*)$/, "#{config["elasticsearch"]}$1")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -4,9 +4,18 @@ module.exports = function (grunt) {
|
|||
'clean:build',
|
||||
'require_css_deps:copy',
|
||||
'less',
|
||||
'copy:kibana_src',
|
||||
'touch_config',
|
||||
'requirejs',
|
||||
'clean:unneeded_source_in_build',
|
||||
'copy:server_src',
|
||||
'download_jruby',
|
||||
'install_gems',
|
||||
'warble',
|
||||
'replace:dist',
|
||||
'copy:dist',
|
||||
'chmod_kibana',
|
||||
'compress:build_zip',
|
||||
'compress:build_tarball'
|
||||
]);
|
||||
};
|
||||
};
|
||||
|
|
9
tasks/chmod_kibana.js
Normal file
9
tasks/chmod_kibana.js
Normal file
|
@ -0,0 +1,9 @@
|
|||
var fs = require('fs');
|
||||
var join = require('path').join;
|
||||
module.exports = function (grunt) {
|
||||
grunt.registerTask('chmod_kibana', 'Chmods bin/kibana', function () {
|
||||
var done = this.async();
|
||||
var path = join(grunt.config.get('build'), 'dist', 'bin', 'kibana');
|
||||
fs.chmod(path, 0755, done);
|
||||
});
|
||||
};
|
|
@ -6,20 +6,19 @@ module.exports = function (grunt) {
|
|||
unneeded_source_in_build: {
|
||||
src: [
|
||||
// select all top level folders in bower_components
|
||||
'<%= build %>/bower_components/*',
|
||||
'<%= build %>/kibana/public/bower_components/*',
|
||||
// exclude the following top level components
|
||||
'!<%= build %>/bower_components/' + notIncludedComponents,
|
||||
|
||||
'!<%= build %>/kibana/public/bower_components/' + notIncludedComponents,
|
||||
// remove the contents of K4D3, font-awesome, and requirejs except necessary files
|
||||
'<%= build %>/bower_components/' + notIncludedComponents + '/*',
|
||||
'!<%= build %>/bower_components/requirejs/require.js',
|
||||
'!<%= build %>/bower_components/font-awesome/fonts',
|
||||
|
||||
'<%= build %>/**/_empty_',
|
||||
'<%= build %>/**/*.less',
|
||||
'<%= appBuild %>/{css-builder,normalize}.js',
|
||||
'<%= app %>/{css-builder,normalize}.js',
|
||||
'<%= build %>/kibana/public/bower_components/' + notIncludedComponents + '/*',
|
||||
'!<%= build %>/kibana/public/bower_components/requirejs/require.js',
|
||||
'!<%= build %>/kibana/public/bower_components/font-awesome/fonts',
|
||||
'<%= build %>/kibana/public/**/_empty_',
|
||||
'<%= build %>/kibana/public/**/*.less',
|
||||
'<%= build %>/kibana/public/config',
|
||||
'<%= build %>/kibana/public/{css-builder,normalize}.js',
|
||||
'<%= app %>/public/{css-builder,normalize}.js',
|
||||
]
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
|
@ -6,7 +6,7 @@ module.exports = function (grunt) {
|
|||
|
||||
return _.mapValues({
|
||||
build_zip: archiveName() + '.zip',
|
||||
build_tarball: archiveName() + '.zip',
|
||||
build_tarball: archiveName() + '.tar.gz',
|
||||
plugin: archiveName(true) + '.tar.gz'
|
||||
}, function (filename, task) {
|
||||
return {
|
||||
|
@ -16,11 +16,11 @@ module.exports = function (grunt) {
|
|||
files: [
|
||||
{
|
||||
expand: true,
|
||||
cwd: '<%= build %>',
|
||||
cwd: '<%= build %>/dist',
|
||||
src: ['**/*'],
|
||||
dest: '<%= pkg.name %>' + (task === 'plugin' ? '/_site' : '')
|
||||
}
|
||||
]
|
||||
};
|
||||
});
|
||||
};
|
||||
};
|
||||
|
|
71
tasks/config/copy.js
Normal file
71
tasks/config/copy.js
Normal file
|
@ -0,0 +1,71 @@
|
|||
module.exports = function (grunt) {
|
||||
var config = {
|
||||
|
||||
kibana_src: {
|
||||
expand: true,
|
||||
cwd: '<%= app %>',
|
||||
src: '**',
|
||||
dest: '<%= build %>/src/'
|
||||
},
|
||||
|
||||
server_src: {
|
||||
files: [
|
||||
{
|
||||
src: '<%= src %>/server/Gemfile',
|
||||
dest: '<%= build %>/kibana/Gemfile'
|
||||
},
|
||||
{
|
||||
src: '<%= src %>/server/Gemfile.lock',
|
||||
dest: '<%= build %>/kibana/Gemfile.lock'
|
||||
},
|
||||
{
|
||||
src: '<%= src %>/server/bin/initialize',
|
||||
dest: '<%= build %>/kibana/bin/initialize'
|
||||
},
|
||||
{
|
||||
expand: true,
|
||||
cwd: '<%= src %>/server/config/',
|
||||
src: '**',
|
||||
dest: '<%= build %>/kibana/config'
|
||||
},
|
||||
{
|
||||
expand: true,
|
||||
cwd: '<%= src %>/server/lib/',
|
||||
src: '**',
|
||||
dest: '<%= build %>/kibana/lib'
|
||||
},
|
||||
{
|
||||
expand: true,
|
||||
cwd: '<%= src %>/server/routes/',
|
||||
src: '**',
|
||||
dest: '<%= build %>/kibana/routes'
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
dist: {
|
||||
options: { mode: true },
|
||||
files: [
|
||||
{
|
||||
src: '<%= root %>/LICENSE.md',
|
||||
dest: '<%= build %>/dist/LICENSE.md'
|
||||
},
|
||||
{
|
||||
expand: true,
|
||||
cwd: '<%= build %>/kibana/',
|
||||
src: '*.jar',
|
||||
dest: '<%= build %>/dist/lib/'
|
||||
},
|
||||
{
|
||||
expand: true,
|
||||
cwd: '<%= src %>/server/config/',
|
||||
src: 'kibana.yml',
|
||||
dest: '<%= build %>/dist/config/'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
return config;
|
||||
};
|
|
@ -17,8 +17,9 @@ module.exports = function (grunt) {
|
|||
ignores: [
|
||||
'node_modules/*',
|
||||
'dist/*',
|
||||
'sample/*'
|
||||
'sample/*',
|
||||
'<%= src %>/kibana/bower_components/**/*'
|
||||
]
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
var bc = require('path').join(__dirname, '../../src/bower_components');
|
||||
var bc = require('path').join(__dirname, '../../src/kibana/bower_components');
|
||||
|
||||
module.exports = {
|
||||
src: {
|
||||
|
|
31
tasks/config/replace.js
Normal file
31
tasks/config/replace.js
Normal file
|
@ -0,0 +1,31 @@
|
|||
var join = require('path').join;
|
||||
module.exports = function (grunt) {
|
||||
var pkg = grunt.config.get('pkg');
|
||||
var build = grunt.config.get('build');
|
||||
var src = grunt.config.get('src');
|
||||
var config = {
|
||||
dist: {
|
||||
options: {
|
||||
patterns: [
|
||||
{ match: 'version', replacement: pkg.version }
|
||||
]
|
||||
},
|
||||
files: [
|
||||
{
|
||||
src: [join(src, 'server', 'DIST_README.md')],
|
||||
dest: join(build, 'dist', 'README.md')
|
||||
},
|
||||
{
|
||||
src: [join(src, 'server', 'bin', 'kibana.sh')],
|
||||
dest: join(build, 'dist', 'bin', 'kibana')
|
||||
},
|
||||
{
|
||||
src: [join(src, 'server', 'bin', 'kibana.bat')],
|
||||
dest: join(build, 'dist', 'bin', 'kibana.bat')
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
return config;
|
||||
};
|
|
@ -2,14 +2,15 @@ module.exports = function (grunt) {
|
|||
var config = {
|
||||
build: {
|
||||
options: {
|
||||
appDir: '<%= src %>',
|
||||
dir: '<%= build %>',
|
||||
mainConfigFile: '<%= app %>/require.config.js',
|
||||
appDir: '<%= build %>/src',
|
||||
dir: '<%= build %>/kibana/public',
|
||||
mainConfigFile: '<%= build %>/src/require.config.js',
|
||||
modules: [
|
||||
{
|
||||
name: 'kibana',
|
||||
excludeShallow: [
|
||||
'../config',
|
||||
'text!config'
|
||||
],
|
||||
include: [
|
||||
'controllers/kibana'
|
||||
|
@ -60,10 +61,10 @@ module.exports = function (grunt) {
|
|||
|
||||
// include each app
|
||||
var main = config.build.options.modules[0];
|
||||
var configFile = require('requirejs')(grunt.config.get('src') + '/config.js');
|
||||
var configFile = require('requirejs')(grunt.config.get('app') + '/config.js');
|
||||
configFile.apps.forEach(function (app) {
|
||||
main.include.push('apps/' + app.id + '/index');
|
||||
});
|
||||
|
||||
return config;
|
||||
};
|
||||
};
|
||||
|
|
28
tasks/config/run.js
Normal file
28
tasks/config/run.js
Normal file
|
@ -0,0 +1,28 @@
|
|||
module.exports = function (grunt) {
|
||||
var jrubyPath = grunt.config.get('jrubyPath');
|
||||
var jruby = jrubyPath + '/bin/jruby';
|
||||
var cmd = grunt.config.get('src') + '/server/bin/initialize';
|
||||
|
||||
var config = {
|
||||
mri_server: {
|
||||
options: {
|
||||
wait: false
|
||||
// quiet: true
|
||||
},
|
||||
cmd: cmd
|
||||
},
|
||||
jruby_server: {
|
||||
options: {
|
||||
wait: false
|
||||
// quiet: true
|
||||
},
|
||||
cmd: jruby,
|
||||
args: [
|
||||
cmd
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
return config;
|
||||
|
||||
};
|
|
@ -1,4 +1,16 @@
|
|||
module.exports = function (grunt) {
|
||||
var kibana_server_tasks = [];
|
||||
if (grunt.option('use-mri')) {
|
||||
kibana_server_tasks = [
|
||||
'stop:mri_server',
|
||||
'run:mri_server'
|
||||
];
|
||||
} else {
|
||||
kibana_server_tasks = [
|
||||
'stop:jruby_server',
|
||||
'run:jruby_server'
|
||||
];
|
||||
}
|
||||
var config = {
|
||||
test: {
|
||||
files: [
|
||||
|
@ -26,6 +38,16 @@ module.exports = function (grunt) {
|
|||
'<%= testUtilsDir %>/istanbul_reporter/report.clientside.jade'
|
||||
],
|
||||
tasks: ['jade:clientside']
|
||||
},
|
||||
kibana_server: {
|
||||
files: [
|
||||
'src/server/**/*.rb',
|
||||
'src/server/**/*.yml'
|
||||
],
|
||||
tasks: kibana_server_tasks,
|
||||
options: {
|
||||
spawn: false
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
21
tasks/dev.js
21
tasks/dev.js
|
@ -1,8 +1,21 @@
|
|||
module.exports = function (grunt) {
|
||||
grunt.registerTask('dev', [
|
||||
var useJRuby = grunt.option('use-jruby');
|
||||
var tasks = [
|
||||
'less',
|
||||
'jade',
|
||||
'jade'
|
||||
];
|
||||
if (useJRuby) {
|
||||
tasks = tasks.concat([
|
||||
'download_jruby',
|
||||
'install_gems',
|
||||
'run:jruby_server',
|
||||
'wait_for_jruby'
|
||||
]);
|
||||
} else {
|
||||
tasks = tasks.concat(['run:mri_server']);
|
||||
}
|
||||
grunt.registerTask('dev', tasks.concat([
|
||||
'maybe_start_server',
|
||||
'watch'
|
||||
]);
|
||||
};
|
||||
]));
|
||||
};
|
||||
|
|
46
tasks/download_jruby.js
Normal file
46
tasks/download_jruby.js
Normal file
|
@ -0,0 +1,46 @@
|
|||
var zlib = require('zlib');
|
||||
var tar = require('tar');
|
||||
var request = require('request');
|
||||
var mkdirp = require('mkdirp');
|
||||
var ProgressBar = require('progress');
|
||||
var fs = require('fs');
|
||||
|
||||
module.exports = function (grunt) {
|
||||
grunt.registerTask('download_jruby', 'Downloads and installs jruby', function () {
|
||||
var done = this.async();
|
||||
var jrubyPath = grunt.config.get('jrubyPath');
|
||||
var jrubyVersion = grunt.config.get('jrubyVersion');
|
||||
var url = 'http://jruby.org.s3.amazonaws.com/downloads/' + jrubyVersion + '/jruby-bin-' + jrubyVersion + '.tar.gz';
|
||||
|
||||
fs.stat(jrubyPath, function (err, stat) {
|
||||
if (err) {
|
||||
mkdirp(jrubyPath, function (err) {
|
||||
if (err) return done(err);
|
||||
var unzip = zlib.createGunzip();
|
||||
var out = tar.Extract({ path: jrubyPath, strip: 1 });
|
||||
out.on('close', done).on('error', done);
|
||||
var req = request.get(url);
|
||||
var bar;
|
||||
if (!process.env.JENKINS_HOME) {
|
||||
req.on('response', function (resp) {
|
||||
var total = parseInt(resp.headers['content-length'], 10);
|
||||
bar = new ProgressBar('[:bar] :percent :etas', {
|
||||
complete: '=',
|
||||
incomplete: ' ',
|
||||
width: 80,
|
||||
clear: true,
|
||||
total: total
|
||||
});
|
||||
});
|
||||
req.on('data', function (buffer) {
|
||||
bar.tick(buffer.length);
|
||||
});
|
||||
}
|
||||
req.pipe(unzip).pipe(out);
|
||||
});
|
||||
} else {
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
20
tasks/install_gems.js
Normal file
20
tasks/install_gems.js
Normal file
|
@ -0,0 +1,20 @@
|
|||
var child_process = require('child_process');
|
||||
var join = require('path').join;
|
||||
module.exports = function (grunt) {
|
||||
grunt.registerTask('install_gems', 'Install Ruby Gems', function () {
|
||||
var done = this.async();
|
||||
var gemfile = join(grunt.config.get('src'), 'server', 'Gemfile');
|
||||
var jrubyPath = grunt.config.get('jrubyPath');
|
||||
var jruby = jrubyPath + '/bin/jruby -S';
|
||||
var command = jruby + ' gem install bundler && ' + jruby + ' bundle install --gemfile ' + gemfile;
|
||||
child_process.exec(command, function (err, stdout, stderr) {
|
||||
if (err) {
|
||||
grunt.log.error(stderr);
|
||||
return done(err);
|
||||
}
|
||||
grunt.log.writeln(stdout);
|
||||
return done();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue