Merge remote-tracking branch 'upstream/master' into zerofillDefault

This commit is contained in:
Rashid Khan
2013-10-23 09:36:05 -07:00
50 changed files with 642 additions and 369 deletions
+2 -1
View File
@@ -28,6 +28,7 @@
"globals": {
"define": true,
"require": true
"require": true,
"Chromath": false
}
}
+61
View File
@@ -189,6 +189,42 @@ module.exports = function (grunt) {
dest: '<%= pkg.name %>-latest'
}
]
},
zip_release: {
options: {
archive: '<%= tempDir %>/<%= pkg.name %>-<%= pkg.version %>.zip'
},
files : [
{
expand: true,
cwd: '<%= destDir %>',
src: ['**/*'],
dest: '<%= pkg.name %>-<%= pkg.version %>'
},
{
expand: true,
src: ['LICENSE.md', 'README.md'],
dest: '<%= pkg.name %>-<%= pkg.version %>'
}
]
},
tgz_release: {
options: {
archive: '<%= tempDir %>/<%= pkg.name %>-<%= pkg.version %>.tar.gz'
},
files : [
{
expand: true,
cwd: '<%= destDir %>',
src: ['**/*'],
dest: '<%= pkg.name %>-<%= pkg.version %>'
},
{
expand: true,
src: ['LICENSE.md', 'README.md'],
dest: '<%= pkg.name %>-<%= pkg.version %>'
}
]
}
},
s3: {
@@ -206,6 +242,21 @@ module.exports = function (grunt) {
dest: 'kibana/kibana/<%= pkg.name %>-latest.tar.gz',
}
]
},
release: {
bucket: 'download.elasticsearch.org',
access: 'private',
// debug: true, // uncommment to prevent actual upload
upload: [
{
src: '<%= tempDir %>/<%= pkg.name %>-<%= pkg.version %>.zip',
dest: 'kibana/kibana/<%= pkg.name %>-<%= pkg.version %>.zip',
},
{
src: '<%= tempDir %>/<%= pkg.name %>-<%= pkg.version %>.tar.gz',
dest: 'kibana/kibana/<%= pkg.name %>-<%= pkg.version %>.tar.gz',
}
]
}
}
};
@@ -306,6 +357,16 @@ module.exports = function (grunt) {
'clean:temp'
]);
// build, then zip and upload to s3
grunt.registerTask('release', [
'distribute:load_s3_config',
'build',
'compress:zip_release',
'compress:tgz_release',
's3:release',
'clean:temp'
]);
// collect the key and secret from the .aws-config.json file, finish configuring the s3 task
grunt.registerTask('distribute:load_s3_config', function () {
var config = grunt.file.readJSON('.aws-config.json');
+1 -1
View File
@@ -4,7 +4,7 @@
"company": "Elasticsearch BV"
},
"name": "kibana",
"version": "3.0.0m4pre",
"version": "3.0.0milestone4",
"devDependencies": {
"rjs-build-analysis": "0.0.3",
"grunt": "~0.4.0",
+7 -7
View File
@@ -31,15 +31,15 @@
</Proxy>
# Proxy for _aliases and .*/_search
<LocationMatch "^(/_aliases|.*/_search|.*/_mapping)$">
ProxyPassMatch http://127.0.0.1:9200
ProxyPassReverse http://127.0.0.1:9200
<LocationMatch "^/(_nodes|_aliases|_search|.*/_search|_mapping|.*/_mapping)$">
ProxyPassMatch http://127.0.0.1:9200/$1
ProxyPassReverse http://127.0.0.1:9200/$1
</LocationMatch>
# Proxy for kibana-int/{dashboard,temp} stuff (if you don't want auth on /, then you will want these to be protected)
<LocationMatch "^(/kibana-int/dashboard/|/kibana-int/temp).*$">
ProxyPassMatch http://127.0.0.1:9200
ProxyPassReverse http://127.0.0.1:9200
<LocationMatch "^/(kibana-int/dashboard/|kibana-int/temp)(.*)$">
ProxyPassMatch http://127.0.0.1:9200/$1$2
ProxyPassReverse http://127.0.0.1:9200/$1$2
</LocationMatch>
# Optional disable auth for a src IP (eg: your monitoring host or subnet)
+5 -1
View File
@@ -25,6 +25,10 @@ server {
proxy_pass http://127.0.0.1:9200;
proxy_read_timeout 90;
}
location ~ ^/_nodes$ {
proxy_pass http://127.0.0.1:9200;
proxy_read_timeout 90;
}
location ~ ^/.*/_search$ {
proxy_pass http://127.0.0.1:9200;
proxy_read_timeout 90;
@@ -53,4 +57,4 @@ server {
auth_basic_user_file /etc/nginx/conf.d/kibana.myhost.org.htpasswd;
}
}
}
}
-1
View File
@@ -6,7 +6,6 @@ define([
'jquery',
'underscore',
'require',
'elasticjs',
'bootstrap',
'angular-sanitize',
+29 -3
View File
@@ -1,4 +1,4 @@
define(['jquery', 'underscore','moment'],
define(['jquery','underscore','moment','chromath'],
function($, _, moment) {
'use strict';
@@ -14,9 +14,10 @@ function($, _, moment) {
};
kbn.get_all_fields = function(data) {
var _d = data;
var fields = [];
_.each(data,function(hit) {
fields = _.uniq(fields.concat(_.keys(hit)));
_.each(_d,function(hit) {
fields = _.uniq(fields.concat(_.keys(kbn.flatten_json(hit._source))));
});
// Remove stupid angular key
fields = _.without(fields,'$$hashKey');
@@ -315,6 +316,15 @@ function($, _, moment) {
}
unit = mathString.charAt(i++);
switch (unit) {
case 'y':
if (type === 0) {
roundUp ? dateTime.endOf('year') : dateTime.startOf('year');
} else if (type === 1) {
dateTime.add('years',num);
} else if (type === 2) {
dateTime.subtract('years',num);
}
break;
case 'M':
if (type === 0) {
roundUp ? dateTime.endOf('month') : dateTime.startOf('month');
@@ -458,5 +468,21 @@ function($, _, moment) {
].join(';') + '"></div>';
};
kbn.colorSteps = function(col,steps) {
var _d = steps > 5 ? 1.6/steps : 0.3, // distance between steps
_p = []; // adjustment percentage
// Create a range of numbers between -0.8 and 0.8
for(var i = 1; i<steps+1; i+=1) {
_p.push(i%2 ? ((i-1)*_d*-1)/2 : i*_d/2);
}
// Create the color range
return _.map(_p.sort(function(a,b){return a-b;}),function(v) {
return v<0 ? Chromath.darken(col,v*-1).toString() : Chromath.lighten(col,v).toString();
});
};
return kbn;
});
+1 -1
View File
@@ -13,7 +13,7 @@ require.config({
text: '../vendor/require/text',
moment: '../vendor/moment',
filesaver: '../vendor/filesaver',
chromath: '../vendor/chromath',
angular: '../vendor/angular/angular',
'angular-dragdrop': '../vendor/angular/angular-dragdrop',
'angular-strap': '../vendor/angular/angular-strap',
-1
View File
@@ -16,7 +16,6 @@ function (angular) {
restrict: 'A',
link: function(scope, elem, attr) {
if(!esVersion.is(attr.esVersion)) {
console.log('hiding');
elem.hide();
}
}
+9 -8
View File
@@ -13,8 +13,15 @@ function (angular) {
'<div class="row-fluid panel-extra"><div class="panel-extra-container">' +
'<span class="extra row-button" ng-show="panel.editable != false">' +
'<span confirm-click="row.panels = _.without(row.panels,panel)" '+
'confirmation="Are you sure you want to remove this {{panel.type}} panel?" class="pointer">'+
'<i class="icon-remove pointer" bs-tooltip="\'Remove\'"></i></span>'+
'</span>' +
'<span class="extra row-button" ng-hide="panel.draggable == false">' +
'<span class="row-text pointer" bs-tooltip="\'Drag here to move\'"' +
'<span class="pointer" bs-tooltip="\'Drag here to move\'"' +
'data-drag=true data-jqyoui-options="{revert: \'invalid\',helper:\'clone\'}"'+
' jqyoui-draggable="'+
'{'+
@@ -23,18 +30,12 @@ function (angular) {
'index:{{$index}},'+
'onStart:\'panelMoveStart\','+
'onStop:\'panelMoveStop\''+
'}" ng-model="row.panels">{{panel.type}}</span>'+
'}" ng-model="row.panels"><i class="icon-move"></i></span>'+
'</span>' +
'<span class="extra row-button" ng-show="panel.draggable == false">' +
'<span class="row-text">{{panel.type}}</span>'+
'</span>' +
'<span class="extra row-button" ng-show="panel.editable != false">' +
'<span confirm-click="row.panels = _.without(row.panels,panel)" '+
'confirmation="Are you sure you want to remove this {{panel.type}} panel?" class="pointer">'+
'<i class="icon-remove pointer" bs-tooltip="\'Remove\'"></i></span>'+
'</span>' +
'<span class="row-button extra" ng-show="panel.editable != false">' +
'<span bs-modal="\'app/partials/paneleditor.html\'" class="pointer">'+
'<i class="icon-cog pointer" bs-tooltip="\'Configure\'"></i></span>'+
+1 -1
View File
@@ -49,7 +49,7 @@ define(['angular', 'jquery', 'underscore', 'moment'], function (angular, $, _, m
if(_.isObject(arr) && !_.isArray(arr)) {
return angular.toJson(arr);
} else {
return arr.toString();
return _.isNull(arr) ? null : arr.toString();
}
};
});
+4 -3
View File
@@ -101,10 +101,11 @@ function (angular, app, _, L, localRequire) {
var _segment = _.isUndefined(segment) ? 0 : segment;
$scope.panel.queries.ids = querySrv.idsByMode($scope.panel.queries);
// This could probably be changed to a BoolFilter
var queries = querySrv.getQueryObjs($scope.panel.queries.ids);
var boolQuery = $scope.ejs.BoolQuery();
_.each($scope.panel.queries.ids,function(id) {
boolQuery = boolQuery.should(querySrv.getEjsObj(id));
_.each(queries,function(q) {
boolQuery = boolQuery.should(querySrv.toEjsObj(q));
});
var request = $scope.ejs.Request().indices(dashboard.indices[_segment])
+1 -23
View File
@@ -1,23 +1 @@
<div>
<div class="row-fluid">
<div class="span1">
<label class="small">Length</label>
<input type="number" style="width:80%" ng-model="panel.size" ng-change="set_refresh(true)">
</div>
<div class="span3">
<label class="small">Field</label>
<input type="text" bs-typeahead="fields.list" style="width:80%" ng-change="set_refresh(true)" ng-model='panel.field'></select>
</div>
<div class="span3">
<label class="small">Query Mode</label>
<select style="width:80%" ng-change="set_refresh(true)" ng-model='panel.mode' ng-options="f for f in ['terms only','AND', 'OR']"></select>
</div>
<div class="span4">
<label class="small">Exclude Terms(s) (comma seperated)</label>
<input array-join type="text" style="width:90%" ng-change="set_refresh(true)" ng-model='panel.exclude'></input>
</div>
<div class="span1">
<label class="small"> Rest </label><input type="checkbox" ng-model="panel.rest" ng-checked="panel.rest" ng-change="set_refresh(true)">
</div>
</div>
</div>
<div></div>
+1 -31
View File
@@ -1,33 +1,3 @@
<div ng-controller='derivequeries' ng-init="init()">
<style>
.end-derive {
position:absolute;
right:15px;
top:5px;
}
.panel-derive-field {
text-decoration: underline;
cursor: pointer;
}
.panel-derive {
padding-right: 35px !important;
height: 31px !important;
-webkit-box-sizing: border-box; /* Safari/Chrome, other WebKit */
-moz-box-sizing: border-box; /* Firefox, other Gecko */
box-sizing: border-box; /* Opera/IE 8+ */
}
</style>
<label class="small">Create new queries from
<span class="panel-derive-field" ng-show="!editing" ng-click="editing=true">{{panel.field}}</span>
<select ng-show="editing && fields.list.length>1" class="input-medium" ng-model="panel.field" ng-options="f for f in fields.list" ng-change='editing=false' ng-blur="editing=false"></select>
<input ng-show="editing && fields.list.length<2" type="text" ng-model="panel.field" ng-blur="editing=false"/>
({{panel.mode}} mode)</label>
<div>
<form class="form-search" style="position:relative" ng-submit="get_data()">
<input class="search-query panel-derive input-block-level" bs-typeahead="panel.history" data-min-length=0 data-items=100 type="text" ng-model="panel.query"/>
<span class="end-derive">
<i class="icon-search pointer" ng-click="get_data()"></i>
</span
</form>
</div>
<h4>This panel has been removed and replaced with the new topN query type. Click the colored dot associated with a query to configure the, much improved, equivilent of a derived query.</h4>
</div>
+3 -110
View File
@@ -22,21 +22,10 @@ function (angular, app, _) {
var module = angular.module('kibana.panels.derivequeries', []);
app.useModule(module);
module.controller('derivequeries', function($scope, $rootScope, querySrv, fields, dashboard, filterSrv) {
module.controller('derivequeries', function($scope) {
$scope.panelMeta = {
modals : [
{
description: "Inspect",
icon: "icon-info-sign",
partial: "app/partials/inspector.html",
show: $scope.panel.spyable
}
],
status : "Experimental",
description : "Creates a new set of queries using the Elasticsearch terms facet. For example,"+
" you might want to create 5 queries showing the most frequent HTTP response codes. Be "+
"careful not to select a high cardinality field, as Elasticsearch must load all unique values"+
" into memory."
status : "Deprecated",
description : "This panel has been replaced with the 'topN' mode in the query pull down."
};
// Set and populate defaults
@@ -59,102 +48,6 @@ function (angular, app, _) {
$scope.init = function() {
$scope.editing = false;
$scope.panel.fields = fields.list;
};
$scope.get_data = function() {
update_history($scope.panel.query);
// Make sure we have everything for the request to complete
if(dashboard.indices.length === 0) {
return;
}
$scope.panelMeta.loading = true;
var request = $scope.ejs.Request().indices(dashboard.indices);
// Terms mode
request = request
.facet($scope.ejs.TermsFacet('query')
.field($scope.panel.field)
.size($scope.panel.size)
.exclude($scope.panel.exclude)
.facetFilter($scope.ejs.QueryFilter(
$scope.ejs.FilteredQuery(
$scope.ejs.QueryStringQuery($scope.panel.query || '*'),
filterSrv.getBoolFilter(filterSrv.ids)
)))).size(0);
$scope.populate_modal(request);
var results = request.doSearch();
// Populate scope when we have results
results.then(function(results) {
$scope.panelMeta.loading = false;
var suffix;
if ($scope.panel.query === '' || $scope.panel.mode === 'terms only') {
suffix = '';
} else if ($scope.panel.mode === 'AND') {
suffix = ' AND (' + $scope.panel.query + ')';
} else if ($scope.panel.mode === 'OR') {
suffix = ' OR (' + $scope.panel.query + ')';
}
var ids = [];
var terms = results.facets.query.terms;
var others = [];
_.each(terms, function(v) {
var _q = $scope.panel.field+':"'+v.term+'"'+suffix;
// if it isn't in the list, remove it
var _iq = querySrv.findQuery(_q);
if(!_iq) {
ids.push(querySrv.set({alias: v.term, query:_q}));
} else {
ids.push(_iq.id);
}
others.push("NOT (" + _q + ")");
});
if ($scope.panel.rest) {
var _other_q = others.join(' AND ');
var _iq = querySrv.findQuery(_other_q);
if (!_iq) {
ids.push(querySrv.set({alias: 'other', query: _other_q}));
} else {
ids.push(_iq.id);
}
}
_.each(_.difference($scope.panel.ids,ids),function(id){
querySrv.remove(id);
});
$scope.panel.ids = ids;
dashboard.refresh();
});
};
$scope.set_refresh = function (state) {
$scope.refresh = state;
};
$scope.close_edit = function() {
if($scope.refresh) {
$scope.get_data();
}
$scope.refresh = false;
};
$scope.populate_modal = function(request) {
$scope.inspector = angular.toJson(JSON.parse(request.toString()),true);
};
var update_history = function(query) {
query = _.isArray(query) ? query : [query];
if($scope.panel.remember > 0) {
$scope.panel.history = _.union(query.reverse(),$scope.panel.history);
var _length = $scope.panel.history.length;
if(_length > $scope.panel.remember) {
$scope.panel.history = $scope.panel.history.slice(0,$scope.panel.remember);
}
}
};
});
});
+1 -1
View File
@@ -1,7 +1,7 @@
<div class="row-fluid">
<div class="span2">
<label class="small">Mode</label>
<select ng-change="set_refresh(true)" class="input-small" ng-model="panel.mode" ng-options="f for f in ['count','min','mean','max','total','change']"></select>
<select ng-change="set_refresh(true)" class="input-small" ng-model="panel.mode" ng-options="f for f in ['count','min','mean','max','total']"></select>
</div>
<div class="span2">
<label class="small">Time Field</label>
+8 -6
View File
@@ -63,12 +63,6 @@
Lines
</label>
</div>
<div class="checkbox">
<label class="small">
<input type="checkbox" ng-model="panel.points" ng-checked="panel.points" ng-change="render()">
Points
</label>
</div>
<div class="checkbox">
<label class="small">
<input type="checkbox" ng-model="panel.stack" ng-checked="panel.stack" ng-change="render()">
@@ -84,6 +78,14 @@
</label>
</div>
</span>
<span>
<div class="checkbox">
<label class="small">
<input type="checkbox" ng-model="panel.legend" ng-checked="panel.legend" ng-change="render()">
Legend
</label>
</div>
</span>
<span>
<label class="small">Interval</label> <select ng-change="set_interval(panel.interval);get_data();" class="input-small" ng-model="panel.interval" ng-options="interval_label(time) for time in _.union([panel.interval],panel.intervals)"></select>
</span>
+11 -10
View File
@@ -190,14 +190,17 @@ function (angular, app, $, _, kbn, moment, timeSeries) {
var request = $scope.ejs.Request().indices(dashboard.indices[segment]);
$scope.panel.queries.ids = querySrv.idsByMode($scope.panel.queries);
var queries = querySrv.getQueryObjs($scope.panel.queries.ids);
// Build the query
_.each($scope.panel.queries.ids, function(id) {
_.each(queries, function(q) {
var query = $scope.ejs.FilteredQuery(
querySrv.getEjsObj(id),
querySrv.toEjsObj(q),
filterSrv.getBoolFilter(filterSrv.ids)
);
var facet = $scope.ejs.DateHistogramFacet(id);
var facet = $scope.ejs.DateHistogramFacet(q.id);
if($scope.panel.mode === 'count') {
facet = facet.field($scope.panel.time_field);
@@ -220,6 +223,7 @@ function (angular, app, $, _, kbn, moment, timeSeries) {
// Populate scope when we have results
results.then(function(results) {
$scope.panelMeta.loading = false;
if(segment === 0) {
$scope.hits = 0;
@@ -233,18 +237,15 @@ function (angular, app, $, _, kbn, moment, timeSeries) {
return;
}
// Convert facet ids to numbers
var facetIds = _.map(_.keys(results.facets),function(k){return parseInt(k, 10);});
// Make sure we're still on the same query/queries
if($scope.query_id === query_id && _.difference(facetIds, $scope.panel.queries.ids).length === 0) {
if($scope.query_id === query_id) {
var i = 0,
time_series,
hits;
_.each($scope.panel.queries.ids, function(id) {
var query_results = results.facets[id];
_.each(queries, function(q) {
var query_results = results.facets[q.id];
// we need to initialize the data variable on the first run,
// and when we are working on the first segment of the data.
if(_.isUndefined($scope.data[i]) || segment === 0) {
@@ -267,7 +268,7 @@ function (angular, app, $, _, kbn, moment, timeSeries) {
$scope.hits += entry.count; // Entire dataset level hits counter
});
$scope.data[i] = {
info: querySrv.list[id],
info: q,
time_series: time_series,
hits: hits
};
+10 -13
View File
@@ -84,14 +84,16 @@ define([
var request = $scope.ejs.Request().indices(dashboard.indices[_segment]);
$scope.panel.queries.ids = querySrv.idsByMode($scope.panel.queries);
var queries = querySrv.getQueryObjs($scope.panel.queries.ids);
// Build the question part of the query
_.each($scope.panel.queries.ids, function(id) {
_.each(queries, function(q) {
var _q = $scope.ejs.FilteredQuery(
querySrv.getEjsObj(id),
querySrv.toEjsObj(q),
filterSrv.getBoolFilter(filterSrv.ids));
request = request
.facet($scope.ejs.QueryFacet(id)
.facet($scope.ejs.QueryFacet(q.id)
.query(_q)
).size(0);
});
@@ -117,24 +119,19 @@ define([
return;
}
// Convert facet ids to numbers
var facetIds = _.map(_.keys(results.facets),function(k){return parseInt(k, 10);});
// Make sure we're still on the same query/queries
if($scope.query_id === query_id &&
_.intersection(facetIds,$scope.panel.queries.ids).length === $scope.panel.queries.ids.length
) {
if($scope.query_id === query_id) {
var i = 0;
_.each($scope.panel.queries.ids, function(id) {
var v = results.facets[id];
_.each(queries, function(q) {
var v = results.facets[q.id];
var hits = _.isUndefined($scope.data[i]) || _segment === 0 ?
v.count : $scope.data[i].hits+v.count;
$scope.hits += v.count;
// Create series
$scope.data[i] = {
info: querySrv.list[id],
id: id,
info: q,
id: q.id,
hits: hits,
data: [[i,hits]]
};
+9 -6
View File
@@ -76,14 +76,17 @@ function (angular, app, _, $) {
$scope.panelMeta.loading = true;
var request;
request = $scope.ejs.Request().indices(dashboard.indices);
var request,
boolQuery,
queries;
$scope.panel.queries.ids = querySrv.idsByMode($scope.panel.queries);
// This could probably be changed to a BoolFilter
var boolQuery = $scope.ejs.BoolQuery();
_.each($scope.panel.queries.ids,function(id) {
boolQuery = boolQuery.should(querySrv.getEjsObj(id));
request = $scope.ejs.Request().indices(dashboard.indices);
queries = querySrv.getQueryObjs($scope.panel.queries.ids);
boolQuery = $scope.ejs.BoolQuery();
_.each(queries,function(q) {
boolQuery = boolQuery.should(querySrv.toEjsObj(q));
});
// Then the insert into facet and make the request
+4 -3
View File
@@ -109,13 +109,14 @@ define([
var request = $scope.ejs.Request().indices(dashboard.indices);
$scope.panel.queries.ids = querySrv.idsByMode($scope.panel.queries);
var queries = querySrv.getQueryObjs($scope.panel.queries.ids);
// This could probably be changed to a BoolFilter
var boolQuery = $scope.ejs.BoolQuery();
_.each($scope.panel.queries.ids,function(id) {
boolQuery = boolQuery.should(querySrv.getEjsObj(id));
_.each(queries,function(q) {
boolQuery = boolQuery.should(querySrv.toEjsObj(q));
});
var results;
// Terms mode
+12
View File
@@ -0,0 +1,12 @@
<fieldset>
<label class="small">Field</label><br>
<input ng-model="querySrv.list[id].field" type="text" bs-typeahead="fields.list" placeholder="Field">
<p>
<label class="small">Count</label><br>
<input ng-model="querySrv.list[id].size" type="number">
<p>
<label class="small">Union</label><br>
<select class="input-small" ng-model="querySrv.list[id].union">
<option ng-repeat="mode in ['none','AND','OR']">{{mode}}</option>
</select>
</fieldset>
+30
View File
@@ -0,0 +1,30 @@
The lucene query type uses <a target="_blank" href='http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/query-dsl-query-string-query.html#query-string-syntax'>LUCENE query string syntax</a> to find matching documents or events within Elasticsearch.
<h4>Examples</h4>
<ul class="unstyled" type="disc">
<li class="listitem"><p class="simpara">
<code class="literal">status</code> field contains <code class="literal">active</code>
</p><pre class="literallayout">status:active</pre></li>
<li class="listitem"><p class="simpara">
<code class="literal">title</code> field contains <code class="literal">quick</code> or <code class="literal">brown</code>
</p><pre class="literallayout">title:(quick brown)</pre></li>
<li class="listitem"><p class="simpara">
<code class="literal">author</code> field contains the exact phrase <code class="literal">"john smith"</code>
</p><pre class="literallayout">author:"John Smith"</pre></li>
</ul>
<p>Wildcard searches can be run on individual terms, using <code class="literal">?</code> to replace
a single character, and <code class="literal">*</code> to replace zero or more characters:</p>
<pre class="literallayout">qu?ck bro*</pre>
<ul class="unstyled" type="disc">
<li class="listitem"><p class="simpara">
Numbers 1..5
</p><pre class="literallayout">count:[1 TO 5]</pre></li>
<li class="listitem"><p class="simpara">
Tags between <code class="literal">alpha</code> and <code class="literal">omega</code>, excluding <code class="literal">alpha</code> and <code class="literal">omega</code>:
</p><pre class="literallayout">tag:{alpha TO omega}</pre></li>
<li class="listitem"><p class="simpara">
Numbers from 10 upwards
</p><pre class="literallayout">count:[10 TO *]</pre></li>
</ul>
+10
View File
@@ -0,0 +1,10 @@
The regex query allows you to use regular expressions to match terms in the <i>_all</i> field.
A detailed overview of lucene's regex engine is available here: <a target="_blank" href="http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/query-dsl-regexp-query.html#regexp-syntax">Regular expressions in Elasticsearch</a>
<h5>A note on anchoring</h5>
Lucenes patterns are always anchored. The pattern provided must match the entire string. For string "abcde":
<p>
<code>ab.*</code> will match<br>
<code>abcd</code> will not match</br>
+14
View File
@@ -0,0 +1,14 @@
The topN query uses an <a target="_blank" href="http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-facets-terms-facet.html">Elasticsearch terms facet</a> to find the most common terms in a field and build queries from the result. The topN query uses <a target="_blank" href='http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/query-dsl-query-string-query.html#query-string-syntax'>LUCENE query string syntax</a>
<h4>Parameters</h4>
<ul>
<li>
<strong>Field</strong> / The field to facet on. Fields with a large number of unique terms will <a target="_blank" href="http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-facets-terms-facet.html#_memory_considerations_2">use more memory</a> to calculate.
</li>
<li>
<strong>Count</strong> / How many queries to generate. The resulting queries will use brightness variations on the original query's color for their own.
</li>
<li>
<strong>Union</strong> / The relation the generated queries have to the original. For example, if your field was set to 'extension', your original query was "user:B.Awesome" and your union was AND. Kibana might generate the following example query: <code>extension:"html" AND (user:B.Awesome)</code>
</li>
</ul>
+12
View File
@@ -0,0 +1,12 @@
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h3>About the {{help.type}} query</h3>
</div>
<div class="modal-body">
<div ng-include="queryHelpPath(help.type)"></div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-danger" ng-click="dismiss()">Close</button>
</div>
+27 -21
View File
@@ -1,28 +1,34 @@
<div class="panel-query-meta row-fluid" style="width:220px">
<div class="panel-query-meta row-fluid" style="width:260px">
<style>
.input-query-alias {
margin-bottom: 5px !important;
.panel-query-meta fieldset label {
margin-top: 3px;
}
.panel-query-meta .pin {
text-decoration: underline;
}
</style>
<a class="close" ng-click="render();dismiss();" href="">×</a>
<label class="strong small ">Query Alias <button class="btn btn-mini" ng-class="{active:querySrv.list[id].pin}" ng-click="toggle_pin(id);dismiss();" class="pointer"><i class="icon-pushpin"></i></button></label>
<form>
<input class="input-large input-query-alias" type="text" ng-model="querySrv.list[id].alias" placeholder='Alias...' />
<div>
<i ng-repeat="color in querySrv.colors" class="pointer" ng-class="{'icon-circle-blank':querySrv.list[id].color == color,'icon-circle':querySrv.list[id].color != color}" ng-style="{color:color}" ng-click="querySrv.list[id].color = color;render();"> </i>
</div>
</form>
<fieldset>
<select class="input-small" ng-model="querySrv.list[id].type" ng-change="typeChange(querySrv.list[id])">
<option ng-repeat="type in queryTypes|esVersion:'require'">{{type.name}}</option>
</select> &nbsp<a href="" class="small" ng-click="queryHelp(querySrv.list[id].type)"> About the {{querySrv.list[id].type}} query</a>
<span>
<label class="small">Query type</label>
<select ng-change="dismiss();" class="input-small" ng-model="querySrv.list[id].type">
<option ng-repeat="type in querySrv.queryTypes|esVersion:'require'">{{type.name}}</option>
</select>
</span>
<hr class="small">
<label class="small">Legend value</label>
<input type="text" ng-model="querySrv.list[id].alias" placeholder="Alias...">
</fieldset>
<div ng-include src="queryConfig(querySrv.list[id].type)"></div>
<hr class="small">
<div>
<i ng-repeat="color in querySrv.colors" class="pointer" ng-class="{'icon-circle-blank':querySrv.list[id].color == color,'icon-circle':querySrv.list[id].color != color}" ng-style="{color:color}" ng-click="querySrv.list[id].color = color;render();"> </i>
</div>
<div class="pull-right">
<a class="btn btn-mini" ng-class="{active:querySrv.list[id].pin}" ng-click="toggle_pin(id);dismiss();" class="pointer">Pin <i class="icon-pushpin"></i></a>
<input class="btn btn-mini" ng-click="dashboard.refresh();dismiss();" type="submit"/ value="Close">
</div>
</div>
+1 -1
View File
@@ -2,7 +2,7 @@
<div ng-repeat="id in (unPinnedQueries = (querySrv.ids|pinnedQuery:false))" ng-class="{'short-query': unPinnedQueries.length>1}">
<form class="form-search" style="position:relative;margin-bottom:5px;" ng-submit="refresh()">
<span class="begin-query">
<i class="icon-circle pointer" data-unique="1" bs-popover="'app/panels/query/meta.html'" data-placement="bottomLeft" ng-style="{color: querySrv.list[id].color}"></i>
<i class="pointer" ng-class="queryIcon(querySrv.list[id].type)" data-unique="1" bs-popover="'app/panels/query/meta.html'" data-placement="bottomLeft" ng-style="{color: querySrv.list[id].color}"></i>
<i class="icon-remove-sign pointer remove-query" ng-show="querySrv.ids.length > 1" ng-click="querySrv.remove(id);refresh()"></i>
</span>
<span>
+60 -2
View File
@@ -19,7 +19,7 @@ define([
var module = angular.module('kibana.panels.query', []);
app.useModule(module);
module.controller('query', function($scope, querySrv, $rootScope) {
module.controller('query', function($scope, querySrv, $rootScope, dashboard, $q, $modal) {
$scope.panelMeta = {
status : "Stable",
description : "Manage all of the queries on the dashboard. You almost certainly need one of "+
@@ -37,12 +37,27 @@ define([
$scope.querySrv = querySrv;
// A list of query types for the query config popover
$scope.queryTypes = _.map(querySrv.queryTypes, function(v,k) {
return {
name:k,
require:v.require
};
});
var queryHelpModal = $modal({
template: './app/panels/query/helpModal.html',
persist: true,
show: false,
scope: $scope,
});
$scope.init = function() {
};
$scope.refresh = function() {
update_history(_.pluck($scope.querySrv.list,'query'));
$rootScope.$broadcast('refresh');
dashboard.refresh();
};
$scope.render = function() {
@@ -53,6 +68,38 @@ define([
querySrv.list[id].pin = querySrv.list[id].pin ? false : true;
};
$scope.queryIcon = function(type) {
return querySrv.queryTypes[type].icon;
};
$scope.queryConfig = function(type) {
return "./app/panels/query/editors/"+(type||'lucene')+".html";
};
$scope.queryHelpPath = function(type) {
return "./app/panels/query/help/"+(type||'lucene')+".html";
};
$scope.queryHelp = function(type) {
$scope.help = {
type: type
};
$q.when(queryHelpModal).then(function(modalEl) {
modalEl.modal('show');
});
};
$scope.typeChange = function(q) {
var _nq = {
id : q.id,
type : q.type,
query: q.query,
alias: q.alias,
color: q.color
};
querySrv.list[_nq.id] = querySrv.defaults(_nq);
};
var update_history = function(query) {
if($scope.panel.remember > 0) {
$scope.panel.history = _.union(query.reverse(),$scope.panel.history);
@@ -66,4 +113,15 @@ define([
$scope.init();
});
module.directive('queryConfig', function() {
return {
restrict: 'A',
template: '<div></div>',
link: function(scope, elem) {
console.log(elem);
}
};
});
});
+3
View File
@@ -2,6 +2,9 @@
display:inline-block;
margin-right: 10px;
}
.short-query input.search-query {
width: 280px;
}
.begin-query {
position:absolute;
left:13px;
+17 -19
View File
@@ -1,23 +1,22 @@
<a class="close" ng-click="dismiss()" href="">×</a>
<h4>
Micro Analysis of {{micropanel.field}}
<i class="pointer icon-search" ng-click="fieldExists(micropanel.field,'must');dismiss();"></i>
<i class="pointer icon-ban-circle" ng-click="fieldExists(micropanel.field,'mustNot');dismiss();"></i>
<br>
<small>
{{micropanel.count}} events in the table set
<span ng-show="micropanel.hasArrays">
as
<a class="link" ng-class="{'strong':micropanel.grouped}" ng-click="toggle_micropanel(micropanel.field,true)">Groups</a> /
<a class="link" ng-class="{'strong':!micropanel.grouped}" ng-click="toggle_micropanel(micropanel.field,false)">Singles</a>
</span>
</small>
</h4>
<table style="width:100%;table-layout:fixed" class='table table-striped table-condensed'>
<style>
</style>
<span>
<i class="pointer icon-search" ng-click="fieldExists(micropanel.field,'must');dismiss();" bs-tooltip="'Find events with this field'"></i>
<i class="pointer icon-ban-circle" ng-click="fieldExists(micropanel.field,'mustNot');dismiss();" bs-tooltip="'Find events without this field'"></i>
<strong>Micro Analysis of {{micropanel.field}}</strong>
<span ng-show="micropanel.hasArrays">
as
<a class="link" ng-class="{'strong':micropanel.grouped}" ng-click="toggle_micropanel(micropanel.field,true)">Groups</a> /
<a class="link" ng-class="{'strong':!micropanel.grouped}" ng-click="toggle_micropanel(micropanel.field,false)">Singles</a>
</span>
</span>
<table style="width:100%;table-layout:fixed" class='table table-striped table-unpadded'>
<thead>
<th style="width:260px">{{micropanel.field}}</th>
<th style="width:40px">Action</th>
<th style="width:100px;text-align:right">Count</th>
<th style="width:100px;text-align:right">Count / {{micropanel.count}} events</th>
</thead>
<tbody>
<tr ng-repeat='field in micropanel.values'>
@@ -38,16 +37,15 @@
</tr>
</tbody>
</table>
<div class="progress" ng-show="micropanel.grouped">
<div class="progress nomargin" ng-show="micropanel.grouped">
<div ng-repeat='field in micropanel.values' bs-tooltip="field[0]+' ('+percent(field[1],data.length)+')'" class="bar {{micropanelColor($index)}}" ng-style="{width: percent(field[1],data.length)};"></div>
</div>
<div>
<span ng-repeat="field in micropanel.related|orderBy:'count':true|limitTo:micropanel.limit track by $index"><a ng-click="toggle_field(field)">{{field.name}}</a> ({{Math.round((field.count / micropanel.count) * 100)}}%), </span>
<span ng-repeat="field in micropanel.related|orderBy:'count':true|limitTo:micropanel.limit track by $index"><a ng-click="toggle_field(field.name)" bs-tooltip="'Toggle {{field.name}} column'">{{field.name}}</a> ({{Math.round((field.count / micropanel.count) * 100)}}%), </span>
<a class="link" ng-show="micropanel.related.length > micropanel.limit" ng-click="micropanel.limit = micropanel.limit + 10">More <i class="icon-caret-right"></i></a>
</div>
<div class="row-fluid">
<div class="span12">
<h5>Facets</h5>
<div class="btn-group">
<a class="btn dropdown-toggle pointer" data-toggle="dropdown">
<i class="icon-list-ol"></i> Terms
+20 -3
View File
@@ -6,12 +6,29 @@
overflow-x: scroll;
}
</style>
<div class="row-fluid">
<div ng-class="{'span3':panel.field_list}" ng-show="panel.field_list">
<div class="sidebar-nav">
<h5>Fields <i class=" icon-chevron-sign-left pointer " ng-click="panel.field_list = !panel.field_list" bs-tooltip="'Hide field list'" ng-show="panel.field_list"></i></h5>
<strong>Fields <i class=" icon-chevron-sign-left pointer " ng-click="panel.field_list = !panel.field_list" bs-tooltip="'Hide field list'" ng-show="panel.field_list"></i></strong><p>
<div class="small">
<span class="link" ng-click="panel.all_fields = true;" ng-class="{strong:panel.all_fields}">All</span> /
<span class="link" ng-click="panel.all_fields = false;" ng-class="{strong:!panel.all_fields}">Current</span>
</div>
<div><input type="text" class="input-medium" placeholder="Type to filter..." ng-model="fieldFilter"></div>
<ul class="unstyled" style="{{panel.overflow}}:{{panel.height || row.height}};overflow-y:auto;overflow-x:hidden;">
<li ng-style="panel.style" ng-repeat="field in fields.list" >
<li ng-style="panel.style" ng-repeat="field in metaFields|filter:fieldFilter|orderBy:identity">
<i class="pointer" ng-class="{'icon-check': _.contains(panel.fields,field),'icon-check-empty': !_.contains(panel.fields,field)}" ng-click="toggle_field(field)"></i>
<a class="pointer" data-unique="1" bs-popover="'app/panels/table/micropanel.html'" data-placement="rightTop" ng-click="toggle_micropanel(field,true)" ng-class="{label: _.contains(panel.fields,field)}">{{field}}</a>
</li>
<li ng-style="panel.style" ng-repeat="field in fields.list|filter:fieldFilter|orderBy:identity" ng-show="panel.all_fields">
<i class="pointer" ng-class="{'icon-check': _.contains(panel.fields,field),'icon-check-empty': !_.contains(panel.fields,field)}" ng-click="toggle_field(field)"></i>
<a class="pointer" data-unique="1" bs-popover="'app/panels/table/micropanel.html'" data-placement="rightTop" ng-click="toggle_micropanel(field,true)" ng-class="{label: _.contains(panel.fields,field)}">{{field}}</a>
</li>
<li ng-style="panel.style" ng-repeat="field in current_fields|filter:fieldFilter|orderBy:identity" ng-hide="panel.all_fields">
<i class="pointer" ng-class="{'icon-check': _.contains(panel.fields,field),'icon-check-empty': !_.contains(panel.fields,field)}" ng-click="toggle_field(field)"></i>
<a class="pointer" data-unique="1" bs-popover="'app/panels/table/micropanel.html'" data-placement="rightTop" ng-click="toggle_micropanel(field,true)" ng-class="{label: _.contains(panel.fields,field)}">{{field}}</a>
</li>
@@ -51,7 +68,7 @@
</thead>
<tbody ng-repeat="event in data| slice:panel.offset:panel.offset+panel.size" ng-class-odd="'odd'">
<tr ng-click="toggle_details(event)" class="pointer">
<td ng-show="panel.fields.length<1">{{event._source|stringify|tableTruncate:panel.trimFactor:1}}</td>
<td ng-show="panel.fields.length<1">{{event.kibana._source|stringify|tableTruncate:panel.trimFactor:1}}</td>
<td ng-show="panel.fields.length>0" ng-repeat="field in panel.fields" ng-bind-html-unsafe="(event.kibana.highlight[field]||event.kibana._source[field]) |tableHighlight | tableTruncate:panel.trimFactor:panel.fields.length"></td>
</tr>
<tr ng-show="event.kibana.details">
+29 -14
View File
@@ -78,6 +78,7 @@ function (angular, app, _, kbn, moment) {
header : true,
paging : true,
field_list: true,
all_fields: false,
trimFactor: 300,
normTimes : true,
spyable : true
@@ -86,7 +87,8 @@ function (angular, app, _, kbn, moment) {
$scope.init = function () {
$scope.Math = Math;
$scope.metaFields = [];
$scope.identity = angular.identity;
$scope.$on('refresh',function(){$scope.get_data();});
$scope.fields = fields;
@@ -109,7 +111,6 @@ function (angular, app, _, kbn, moment) {
};
var showModal = function(panel,type) {
$scope.facetPanel = panel;
$scope.facetType = type;
@@ -204,6 +205,12 @@ function (angular, app, _, kbn, moment) {
};
$scope.get_data = function(segment,query_id) {
var
_segment,
request,
boolQuery,
results;
$scope.panel.error = false;
// Make sure we have everything for the request to complete
@@ -213,16 +220,17 @@ function (angular, app, _, kbn, moment) {
$scope.panelMeta.loading = true;
$scope.panel.queries.ids = querySrv.idsByMode($scope.panel.queries);
var _segment = _.isUndefined(segment) ? 0 : segment;
_segment = _.isUndefined(segment) ? 0 : segment;
$scope.segment = _segment;
var request = $scope.ejs.Request().indices(dashboard.indices[_segment]);
request = $scope.ejs.Request().indices(dashboard.indices[_segment]);
var boolQuery = $scope.ejs.BoolQuery();
_.each($scope.panel.queries.ids,function(id) {
boolQuery = boolQuery.should(querySrv.getEjsObj(id));
$scope.panel.queries.ids = querySrv.idsByMode($scope.panel.queries);
var queries = querySrv.getQueryObjs($scope.panel.queries.ids);
boolQuery = $scope.ejs.BoolQuery();
_.each(queries,function(q) {
boolQuery = boolQuery.should(querySrv.toEjsObj(q));
});
request = request.query(
@@ -241,7 +249,7 @@ function (angular, app, _, kbn, moment) {
$scope.populate_modal(request);
var results = request.doSearch();
results = request.doSearch();
// Populate scope when we have results
results.then(function(results) {
@@ -262,11 +270,15 @@ function (angular, app, _, kbn, moment) {
// Check that we're still on the same query, if not stop
if($scope.query_id === query_id) {
$scope.data= $scope.data.concat(_.map(results.hits.hits, function(hit) {
var _h = _.clone(hit);
//_h._source = kbn.flatten_json(hit._source);
//_h.highlight = kbn.flatten_json(hit.highlight||{});
var
_h = _.clone(hit),
_p = _.omit(hit,'_source','sort','_score');
$scope.metaFields = _.union(_.keys(_p),$scope.metaFields);
// _source is kind of a lie here, never display it, only select values from it
_h.kibana = {
_source : kbn.flatten_json(hit._source),
_source : _.extend(kbn.flatten_json(hit._source),_p),
highlight : kbn.flatten_json(hit.highlight||{})
};
return _h;
@@ -291,6 +303,9 @@ function (angular, app, _, kbn, moment) {
// Keep only what we need for the set
$scope.data = $scope.data.slice(0,$scope.panel.size * $scope.panel.pages);
// Populate current_fields list
$scope.current_fields = kbn.get_all_fields($scope.data);
} else {
return;
}
+7 -3
View File
@@ -83,17 +83,21 @@ function (angular, app, _, $, kbn) {
$scope.panelMeta.loading = true;
var request,
results,
boolQuery;
boolQuery,
queries;
request = $scope.ejs.Request().indices(dashboard.indices);
$scope.panel.queries.ids = querySrv.idsByMode($scope.panel.queries);
queries = querySrv.getQueryObjs($scope.panel.queries.ids);
// This could probably be changed to a BoolFilter
boolQuery = $scope.ejs.BoolQuery();
_.each($scope.panel.queries.ids,function(id) {
boolQuery = boolQuery.should(querySrv.getEjsObj(id));
_.each(queries,function(q) {
boolQuery = boolQuery.should(querySrv.toEjsObj(q));
});
// Terms mode
request = request
.facet($scope.ejs.TermsFacet('terms')
+3
View File
@@ -47,6 +47,9 @@
</ul>
</li>
<li ng-show="!dashboard.current.refresh">
<a class="icon-refresh" ng-click="dashboard.refresh()"></a>
</li>
</ul>
</form>
+8 -2
View File
@@ -1,11 +1,17 @@
<div ng-controller='trends' ng-init="init()">
<div ng-style="panel.style" style="line-height:{{panel.style['font-size']}};display:inline-block;padding-right: 5px;" ng-repeat="query in trends">
<style>
div.trends-horizontal {
display:inline-block;
padding-right: 5px;
}
</style>
<div ng-class="{'trends-horizontal':panel.arrangement == 'horizontal'}" ng-style="panel.style" style="line-height:{{panel.style['font-size']}};" ng-repeat="query in trends">
<i class="icon-circle" style="color:{{query.info.color}}"></i>
<span bs-tooltip="'Then: '+query.hits.old+', Now: '+query.hits.new" ng-class="{'text-success': query.hits.new >= query.hits.old, 'text-error': query.hits.old > query.hits.new}" class='pointer strong'>
<i class='large' ng-class="{'icon-caret-up': query.hits.new >= query.hits.old, 'icon-caret-down': query.hits.old > query.hits.new}"></i> {{query.percent}}%
</span>
<span class="tiny light" ng-show="query.info.alias != ''">({{query.info.alias}})</span>
<br ng-show="panel.arrangement == 'vertical'">
</div>
</div>
+15 -18
View File
@@ -73,8 +73,6 @@ function (angular, app, _, kbn) {
$scope.index = segment > 0 ? $scope.index : dashboard.indices;
}
$scope.panel.queries.ids = querySrv.idsByMode($scope.panel.queries);
// Determine a time field
var timeField = _.uniq(_.pluck(filterSrv.getByType('time'),'field'));
if(timeField.length > 1) {
@@ -98,11 +96,13 @@ function (angular, app, _, kbn) {
var request = $scope.ejs.Request();
var _ids_without_time = _.difference(filterSrv.ids,filterSrv.idsByType('time'));
$scope.panel.queries.ids = querySrv.idsByMode($scope.panel.queries);
var queries = querySrv.getQueryObjs($scope.panel.queries.ids);
// Build the question part of the query
_.each($scope.panel.queries.ids, function(id) {
_.each(queries, function(query) {
var q = $scope.ejs.FilteredQuery(
querySrv.getEjsObj(id),
querySrv.toEjsObj(query),
filterSrv.getBoolFilter(_ids_without_time).must(
$scope.ejs.RangeFilter(timeField)
.from($scope.time.from)
@@ -110,23 +110,23 @@ function (angular, app, _, kbn) {
));
request = request
.facet($scope.ejs.QueryFacet(id)
.facet($scope.ejs.QueryFacet(query.id)
.query(q)
).size(0);
});
// And again for the old time period
_.each($scope.panel.queries.ids, function(id) {
_.each(queries, function(query) {
var q = $scope.ejs.FilteredQuery(
querySrv.getEjsObj(id),
querySrv.toEjsObj(query),
filterSrv.getBoolFilter(_ids_without_time).must(
$scope.ejs.RangeFilter(timeField)
.from($scope.old_time.from)
.to($scope.old_time.to)
));
request = request
.facet($scope.ejs.QueryFacet("old_"+id)
.facet($scope.ejs.QueryFacet("old_"+query.id)
.query(q)
).size(0);
});
@@ -169,17 +169,14 @@ function (angular, app, _, kbn) {
return;
}
// Convert facet ids to numbers
var facetIds = _.map(_.keys(results.facets),function(k){if(!isNaN(k)){return parseInt(k, 10);}});
// Make sure we're still on the same query/queries
if($scope.query_id === query_id &&
_.intersection(facetIds,$scope.panel.queries.ids).length === $scope.panel.queries.ids.length
) {
if($scope.query_id === query_id) {
var i = 0;
_.each($scope.panel.queries.ids, function(id) {
var n = results.facets[id].count;
var o = results.facets['old_'+id].count;
var queries = querySrv.getQueryObjs($scope.panel.queries.ids);
_.each(queries, function(query) {
var n = results.facets[query.id].count;
var o = results.facets['old_'+query.id].count;
var hits = {
new : _.isUndefined($scope.data[i]) || _segment === 0 ? n : $scope.data[i].hits.new+n,
@@ -193,7 +190,7 @@ function (angular, app, _, kbn) {
'?' : Math.round(percentage(hits.old,hits.new)*100)/100;
// Create series
$scope.data[i] = {
info: querySrv.list[id],
info: query,
hits: {
new : hits.new,
old : hits.old
+1 -1
View File
@@ -54,7 +54,7 @@
<div ng-repeat="(name, panel) in row.panels|filter:isPanel" ng-hide="panel.span == 0 || panel.hide" class="span{{panel.span}} panel nospace" style="min-height:{{row.height}}; position:relative" data-drop="true" ng-model="row.panels" data-jqyoui-options jqyoui-droppable="{index:$index,mutate:false,onDrop:'panelMoveDrop',onOver:'panelMoveOver(true)',onOut:'panelMoveOut'}">
<!-- Error Panel -->
<div class="row-fluid">
<div class="span12 alert alert-error panel-error" ng-hide="!panel.error">
<div class="span12 alert-error panel-error" ng-hide="!panel.error">
<a class="close" ng-click="panel.error=false">&times;</a>
<i class="icon-exclamation-sign"></i> <strong>Oops!</strong> {{panel.error}}
</div>
+4 -3
View File
@@ -143,12 +143,13 @@ function (angular, $, kbn, _, config, moment, Modernizr) {
return false;
}
}
$rootScope.$broadcast('refresh');
// Don't resolve queries until indices are updated
querySrv.resolve().then(function(){$rootScope.$broadcast('refresh');});
});
} else {
if(self.current.failover) {
self.indices = [self.current.index.default];
$rootScope.$broadcast('refresh');
querySrv.resolve().then(function(){$rootScope.$broadcast('refresh');});
} else {
alertSrv.set("No time filter",
'Timestamped indices are configured without a failover. Waiting for time filter.',
@@ -157,7 +158,7 @@ function (angular, $, kbn, _, config, moment, Modernizr) {
}
} else {
self.indices = [self.current.index.default];
$rootScope.$broadcast('refresh');
querySrv.resolve().then(function(){$rootScope.$broadcast('refresh');});
}
};
+2 -2
View File
@@ -19,7 +19,7 @@ function (angular, _, config) {
if(!_.isUndefined(n) && n.length) {
// Only get the mapping for indices we don't know it for
var indices = _.difference(n,_.keys(self.mapping));
// Only get the mapping if there are indices
// Only get the mapping if there are new indices
if(indices.length > 0) {
self.map(indices).then(function(result) {
self.mapping = _.extend(self.mapping,result);
@@ -36,7 +36,7 @@ function (angular, _, config) {
var fields = [];
_.each(m, function(types) {
_.each(types, function(v) {
fields = _.union(fields,_.keys(v));
fields = _.without(_.union(fields,_.keys(v)),'_all','_source');
});
});
return fields;
+144 -38
View File
@@ -1,14 +1,16 @@
define([
'angular',
'underscore',
'config'
'config',
'kbn'
],
function (angular, _, config) {
function (angular, _, config, kbn) {
'use strict';
var module = angular.module('kibana.services');
module.service('querySrv', function(dashboard, ejsResource) {
module.service('querySrv', function(dashboard, ejsResource, filterSrv, $q) {
// Create an object to hold our service state on the dashboard
dashboard.current.services.query = dashboard.current.services.query || {};
_.defaults(dashboard.current.services.query,{
@@ -17,18 +19,6 @@ function (angular, _, config) {
ids : [],
});
// Defaults for query objects
var _query = {
query: '*',
alias: '',
pin: false,
type: 'lucene'
};
// For convenience
var ejs = ejsResource(config.elasticsearch);
var _q = dashboard.current.services.query;
this.colors = [
"#7EB26D","#EAB839","#6ED0E0","#EF843C","#E24D42","#1F78C1","#BA43A9","#705DA0", //1
"#508642","#CCA300","#447EBC","#C15C17","#890F02","#0A437C","#6D1F62","#584477", //2
@@ -39,25 +29,112 @@ function (angular, _, config) {
"#E0F9D7","#FCEACA","#CFFAFF","#F9E2D2","#FCE2DE","#BADFF4","#F9D9F9","#DEDAF7" //7
];
// Define the query types and the version of elasticsearch they were first available in
this.queryTypes = [
{name:'lucene',require:">=0.17.0"},
{name:'regex',require:">=0.90.3"}
];
// For convenience
var ejs = ejsResource(config.elasticsearch);
var _q = dashboard.current.services.query;
// Holds all actual queries, including all resolved abstract queries
var resolvedQueries = [];
// Defaults for generic query object
var _query = {
alias: '',
pin: false,
type: 'lucene'
};
// Defaults for specific query types
var _dTypes = {
"lucene": {
query: "*"
},
"regex": {
query: ".*"
},
"topN": {
query: "*",
field: "_type",
size: 5,
union: 'AND'
}
};
// query type meta data that is not stored on the dashboard object
this.queryTypes = {
lucene: {
require:">=0.17.0",
icon: "icon-circle",
resolve: function(query) {
// Simply returns itself
var p = $q.defer();
p.resolve(_.extend(query,{parent:query.id}));
return p.promise;
}
},
regex: {
require:">=0.90.3",
icon: "icon-circle",
resolve: function(query) {
// Simply returns itself
var p = $q.defer();
p.resolve(_.extend(query,{parent:query.id}));
return p.promise;
}
},
topN : {
require:">=0.90.3",
icon: "icon-cog",
resolve: function(q) {
var suffix = '';
if (q.union === 'AND') {
suffix = ' AND (' + (q.query||'*') + ')';
} else if (q.union === 'OR') {
suffix = ' OR (' + (q.query||'*') + ')';
}
var request = ejs.Request().indices(dashboard.indices);
// Terms mode
request = request
.facet(ejs.TermsFacet('query')
.field(q.field)
.size(q.size)
.facetFilter(ejs.QueryFilter(
ejs.FilteredQuery(
ejs.QueryStringQuery(q.query || '*'),
filterSrv.getBoolFilter(filterSrv.ids)
)))).size(0);
var results = request.doSearch();
return results.then(function(data) {
var _colors = kbn.colorSteps(q.color,data.facets.query.terms.length);
var i = -1;
return _.map(data.facets.query.terms,function(t) {
++i;
return self.defaults({
query : q.field+':"'+t.term+'"'+suffix,
alias : t.term + (q.alias ? " ("+q.alias+")" : ""),
type : 'lucene',
color : _colors[i],
parent : q.id
});
});
});
}
}
};
// Save a reference to this
var self = this;
this.init = function() {
_q = dashboard.current.services.query;
self.list = dashboard.current.services.query.list;
self.ids = dashboard.current.services.query.ids;
// Check each query object, populate its defaults
_.each(self.list,function(query,id) {
_.defaults(query,_query);
query.color = query.color || colorAt(id);
_.each(self.list,function(query) {
query = self.defaults(query);
});
if (self.ids.length === 0) {
@@ -75,17 +152,23 @@ function (angular, _, config) {
return false;
}
} else {
var _id = query.id || nextId();
query.id = _id;
query.color = query.color || colorAt(_id);
_.defaults(query,_query);
self.list[_id] = query;
self.ids.push(_id);
return _id;
// Query must have an id and color already
query.id = _.isUndefined(query.id) ? nextId() : query.id;
query.color = query.color || colorAt(query.id);
// Then it can get defaults
query = self.defaults(query);
self.list[query.id] = query;
self.ids.push(query.id);
return query.id;
}
};
this.defaults = function(query) {
_.defaults(query,_query);
_.defaults(query,_dTypes[query.type]);
return query;
};
this.remove = function(id) {
if(!_.isUndefined(self.list[id])) {
delete self.list[id];
@@ -101,10 +184,8 @@ function (angular, _, config) {
}
};
this.getEjsObj = function(id) {
return self.toEjsObj(self.list[id]);
};
// In the case of a compound query, such as a derived query, we'd need to
// return an array of elasticJS objects. Not sure if that is appropriate?
this.toEjsObj = function (q) {
switch(q.type)
{
@@ -113,14 +194,22 @@ function (angular, _, config) {
case 'regex':
return ejs.RegexpQuery('_all',q.query);
default:
return _.isUndefined(q.query) ? false : ejs.QueryStringQuery(q.query || '*');
return false;
}
};
this.findQuery = function(queryString) {
return _.findWhere(self.list,{query:queryString});
//
this.getQueryObjs = function(ids) {
if(_.isUndefined(ids)) {
return resolvedQueries;
} else {
return _.flatten(_.map(ids,function(id) {
return _.where(resolvedQueries,{parent:id});
}));
}
};
// BROKEN
this.idsByMode = function(config) {
switch(config.mode)
{
@@ -137,6 +226,23 @@ function (angular, _, config) {
}
};
// This populates the internal query list and returns a promise containing it
this.resolve = function() {
// Find ids of all abstract queries
// Get a list of resolvable ids, constrast with total list to get abstract ones
return $q.all(_.map(self.ids,function(q) {
return self.queryTypes[self.list[q].type].resolve(_.clone(self.list[q])).then(function(data){
return data;
});
})).then(function(data) {
resolvedQueries = _.flatten(data);
_.each(resolvedQueries,function(q,i) {
q.id = i;
});
return resolvedQueries;
});
};
var nextId = function() {
if(_q.idQueue.length > 0) {
return _q.idQueue.shift();
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 480 B

+1 -1
View File
@@ -34,7 +34,7 @@
<div class="navbar navbar-static-top">
<div class="navbar-inner">
<div class="container-fluid">
<span class="brand">{{dashboard.current.title}}</span>
<span class="brand"><img src="img/small.png" bs-tooltip="'Kibana 3 milestone 4'" data-placement="bottom"> {{dashboard.current.title}}</span>
<ul class="nav pull-right" ng-controller='dashLoader' ng-init="init()" ng-include="'app/partials/dashLoader.html'">
</ul>
+2 -2
View File
@@ -2085,7 +2085,7 @@
}
, keydown: function (e) {
this.suppressKeyPressRepeat = ~$.inArray(e.keyCode, [40,38,9,13,27])
this.suppressKeyPressRepeat = ~$.inArray(e.keyCode, [40,38,9,13,27,57])
this.move(e)
}
@@ -2115,7 +2115,7 @@
break
default:
this.lookup()
this.lookup();
}
if(e.keyCode === 13 && typeof this.$menu.find('.active').attr('data-value') === 'undefined') {
+16 -6
View File
@@ -16,6 +16,10 @@ div.fake-input {
.border-radius(@inputBorderRadius @inputBorderRadius @inputBorderRadius @inputBorderRadius);
}
hr.small {
margin: 5px 0px;
}
form input.ng-invalid {
color: @errorText;
}
@@ -37,6 +41,13 @@ form input.ng-invalid {
border: 1px solid @tableBorder;
}
.table-unpadded {
th,
td {
padding: 0px 2px;
}
}
.spy {
position:absolute;
right:0px;
@@ -121,12 +132,6 @@ form input.ng-invalid {
border-top: 0px;
}
.panel-error {
opacity: 0.9;
position:absolute;
z-index: 1000;
}
.panel-loading {
position:absolute;
top: 0px;
@@ -264,6 +269,11 @@ div.flot-text {
padding-left:20px;
}
.panel-error {
color: @textColor;
padding: 3px 10px 0px 10px;
}
/* ===================================================
* popover-extra-placements.css v0.1
* http://twitter.github.com/bootstrap-popover-extra-placements
+34
View File
@@ -0,0 +1,34 @@
(function(){function a(d){var c,b,e,f;if("[object String]"===Object.prototype.toString.call(d)||"[object Number]"===Object.prototype.toString.call(d))c=a.parse(d);else{if(m(d))throw Error("Unsure how to parse array `"+d+"`, please pass an object or CSS style or try Chromath.rgb, Chromath.hsl, or Chromath.hsv");d instanceof a?c=k({},d):n(d)&&(c=k({},d))}if(c)isFinite(c.a)||(c.a=1);else throw Error("Could not parse `"+d+"`");if("r"in c)f=[c.r,c.g,c.b],b=a.rgb2hsl(f),e=a.rgb2hsv(f);else if("h"in c)if("l"in
c)b=[c.h,c.s,c.l],f=a.hsl2rgb(b),e=a.rgb2hsv(f);else if("v"in c||"b"in c)"b"in c&&(c.v=c.b),e=[c.h,c.s,c.v],f=a.hsv2rgb(e),b=a.rgb2hsl(f);k(this,{r:f[0],g:f[1],b:f[2],h:b[0],sl:b[1],l:b[2],sv:e[1],v:e[2],a:c.a});this.h=(this.h%360+360)%360;1<this.sl&&(this.sl/=100);1<this.sv&&(this.sv/=100);1<this.l&&(this.l/=100);this.r=h(parseInt(this.r,10),0,255);this.g=h(parseInt(this.g,10),0,255);this.b=h(parseInt(this.b,10),0,255);this.a=h(this.a,0,1);return this}function q(d,a,b,e){var f=d;m(f)&&(d=f[0],a=
f[1],b=f[2],e=f[3]);n(f)&&(d=f.r,a=f.g,b=f.b,e=f.a);return[d,a,b,e]}function p(d,a,b,e){var f=d;m(f)&&(d=f[0],a=f[1],b=f[2],e=f[3]);n(f)&&(d=f.h,a=f.s,b=f.l||f.v,e=f.a);return[d,a,b,e]}function r(a,c,b){isFinite(c)||(b=q(a,c,b),a=b[0],c=b[1],b=b[2]);1<a&&(a/=255);1<c&&(c/=255);1<b&&(b/=255);return[a,c,b]}function s(a,c,b){isFinite(c)||(b=p(a,c,b),a=b[0],c=b[1],b=b[2]);1<c&&(c/=100);1<b&&(b/=100);return[(a%360+360)%360,c,b]}function k(){for(var a=arguments[0],c=1,b,e;b=arguments[c++];)for(e in b)a[e]=
b[e];return a}function m(a){return"[object Array]"===Object.prototype.toString.call(a)}function n(a){return"[object Object]"===Object.prototype.toString.call(a)}function h(a,c,b){return a>b?b:a<c?c:a}function l(a,c,b){return a+(c-a)*b}a.rgb=function(d,c,b,e){e=q(d,c,b,e);d=e[0];c=e[1];b=e[2];e=e[3];return new a({r:d,g:c,b:b,a:e})};a.rgba=a.rgb;a.hsl=function(d,c,b,e){e=p(d,c,b,e);d=e[0];c=e[1];b=e[2];e=e[3];return new a({h:d,s:c,l:b,a:e})};a.hsla=a.hsl;a.hsv=function(d,c,b,e){e=p(d,c,b,e);d=e[0];
c=e[1];b=e[2];e=e[3];return new a({h:d,s:c,v:b,a:e})};a.hsva=a.hsv;a.hsb=a.hsv;a.hsba=a.hsva;a.toInteger=function(d){d=(new a(d)).toRGBObject();return d.b|d.g<<8|d.r<<16};a.toName=function(d){var d=+new a(d),c;for(c in a.colors)if(+a[c]==d)return c};a.rgb2hex=function(d,c,b){d=a.toInteger({r:d,g:c,b:b}).toString(16).toUpperCase();c=6;b=0;d=d.toString();c||(c=2);for(b||(b="0");d.length<c;)d=b+d;return"#"+d};a.rgb2hsl=function(a,c,b){var b=r(a,c,b),a=b[0],c=b[1],b=b[2],e=Math.max(a,c,b),f=Math.min(a,
c,b),g=e-f,f=0.5*(e+f),h=0===g?0:g/(1-Math.abs(2*f-1)),i;0===g?i=0:e===a?i=(c-b)/g%6:e===c?i=(b-a)/g+2:e===b&&(i=(a-c)/g+4);return[60*i,parseFloat(h),parseFloat(f)]};a.rgb2hsv=function(a,c,b){var b=r(a,c,b),a=b[0],c=b[1],b=b[2],e=Math.max(a,c,b),f=Math.min(a,c,b),f=e-f,g;0===f?g=0:e===a?g=(c-b)/f%6:e===c?g=(b-a)/f+2:e===b&&(g=(a-c)/f+4);return[60*g,parseFloat(0===f?0:f/e),parseFloat(e)]};a.rgb2hsb=a.rgb2hsv;a.hsl2rgb=function(a,c,b){var b=s(a,c,b),a=b[0],c=b[1],b=b[2],c=(1-Math.abs(2*b-1))*c,a=a/
60,e=c*(1-Math.abs(a%2-1));switch(Math.floor(a)){case 0:a=[c,e,0];break;case 1:a=[e,c,0];break;case 2:a=[0,c,e];break;case 3:a=[0,e,c];break;case 4:a=[e,0,c];break;case 5:a=[c,0,e];break;default:a=[0,0,0]}b-=c/2;return[255*(a[0]+b),255*(a[1]+b),255*(a[2]+b)]};a.hsv2rgb=function(a,c,b){var b=s(a,c,b),a=b[0],c=b[1],b=b[2],c=b*c,e=a/60,f=c*(1-Math.abs(e%2-1)),g;void 0==a?g=[0,0,0]:0<=e&&1>e?g=[c,f,0]:1<=e&&2>e?g=[f,c,0]:2<=e&&3>e?g=[0,c,f]:3<=e&&4>e?g=[0,f,c]:4<=e&&5>e?g=[f,0,c]:5<=e&&6>e&&(g=[c,0,f]);
a=b-c;return[255*(g[0]+a),255*(g[1]+a),255*(g[2]+a)]};a.hsb2rgb=a.hsv2rgb;a.complement=function(d){d=(new a(d)).toHSLObject();d.h=(d.h+180)%360;return new a(d)};a.triad=function(d){d=new a(d);return[d,new a({r:d.b,g:d.r,b:d.g}),new a({r:d.g,g:d.b,b:d.r})]};a.tetrad=function(d){d=new a(d);return[d,new a({r:d.b,g:d.r,b:d.b}),new a({r:d.b,g:d.g,b:d.r}),new a({r:d.r,g:d.b,b:d.r})]};a.analogous=function(d,c,b){isFinite(c)||(c=8);isFinite(b)||(b=30);var e=new a(d),d=e.toHSVObject(),b=360/b,e=[e];for(d.h=
(d.h-(b*c>>1)+720)%360;--c;)d.h+=b,d.h%=360,e.push(new a(d));return e};a.monochromatic=function(d,c){c||(c=5);for(var b=(new a(d)).toHSVObject(),e=1/c,f=[],g=0;g++<c;)b.v=g*e,f.push(new a(b));return f};a.splitcomplement=function(d){var c=new a(d),d=c.toHSVObject(),c=[c];d.h*=0.2;d.h%=360;c.push(new a(d));d.h*=0.4;d.h%=360;c.push(new a(d));return c};a.tint=function(d,c){return a.towards(d,"#FFFFFF",c)};a.lighten=a.tint;a.shade=function(d,c){return a.towards(d,"#000000",c)};a.darken=a.shade;a.desaturate=
function(d,c){var b=new a(d);switch(c){case 1:b=0.35+13*(b.r+b.g+b.b)/60;break;case 2:b=(13*(b.r+b.g+b.b)+5355)/60;break;default:b=0.3*b.r+0.59*b.g+0.11*b.b}b=h(b,0,255);return new a({r:b,g:b,b:b})};a.greyscale=a.desaturate;a.websafe=function(d){d=new a(d);d.r=51*Math.round(d.r/51);d.g=51*Math.round(d.g/51);d.b=51*Math.round(d.b/51);return new a(d)};a.additive=function(){for(var d=arguments.length-2,c=-1,b,e;c++<d;){b=b||new a(arguments[c]);e=new a(arguments[c+1]);if(255<(b.r+=e.r))b.r=255;if(255<
(b.g+=e.g))b.g=255;if(255<(b.b+=e.b))b.b=255;b=new a(b)}return b};a.subtractive=function(){for(var d=arguments.length-2,c=-1,b,e;c++<d;){b=b||new a(arguments[c]);e=new a(arguments[c+1]);if(0>(b.r+=e.r-255))b.r=0;if(0>(b.g+=e.g-255))b.g=0;if(0>(b.b+=e.b-255))b.b=0;b=new a(b)}return b};a.multiply=function(){for(var d=arguments.length-2,c=-1,b,e;c++<d;)b=b||new a(arguments[c]),e=new a(arguments[c+1]),b.r=b.r/255*e.r|0,b.g=b.g/255*e.g|0,b.b=b.b/255*e.b|0,b=new a(b);return b};a.average=function(){for(var d=
arguments.length-2,c=-1,b,e;c++<d;)b=b||new a(arguments[c]),e=new a(arguments[c+1]),b.r=b.r+e.r>>1,b.g=b.g+e.g>>1,b.b=b.b+e.b>>1,b=new a(b);return b};a.overlay=function(d,c,b){d=new a(d);c=new a(c);1<b&&(b/=100);b=h(b-1+c.a,0,1);return new a({r:l(d.r,c.r,b),g:l(d.g,c.g,b),b:l(d.b,c.b,b)})};a.towards=function(d,c,b,e){if(!c)return d;if(!isFinite(b))throw Error("TypeError: `by`("+b+") should be between 0 and 1");d instanceof a||(d=new a(d));c instanceof a||(c=new a(c||"#FFFFFF"));e||(e=l);b=parseFloat(b);
return new a({r:e(d.r,c.r,b),g:e(d.g,c.g,b),b:e(d.b,c.b,b),a:e(d.a,c.a,b)})};a.gradient=function(d,c,b,e){var f=[],g;b||(b=20);g=b-1;if(isFinite(e))return a.towards(d,c,e/g);for(e=-1;++e<b;)f.push(a.towards(d,c,e/g));return f};a.parse=function(d){var c=a.parsers,b,e,f,g,h;b=0;for(e=c.length;b<e;b++)if(f=c[b],(g=f.regex.exec(d))&&g.length&&(h=f.process.apply(this,g)),h)return h};a.parsers=[{example:["red","burlywood"],regex:/^[a-z]+$/i,process:function(d){if(a.colors[d])return a.colors[d]}},{example:[3554431,
16809984],regex:/^\d+$/,process:function(a){return{r:a>>16&255,g:a>>8&255,b:a&255}}},{example:["#fb0","f0f"],regex:/^#?([\dA-F]{1})([\dA-F]{1})([\dA-F]{1})$/i,process:function(a,c,b,e){return{r:parseInt(c+c,16),g:parseInt(b+b,16),b:parseInt(e+e,16)}}},{example:["#00ff00","336699"],regex:/^#?([\dA-F]{2})([\dA-F]{2})([\dA-F]{2})$/i,process:function(a,c,b,e){return{r:parseInt(c,16),g:parseInt(b,16),b:parseInt(e,16)}}},{example:["rgb(123, 234, 45)","rgb(25, 50%, 100%)","rgba(12%, 34, 56%, 0.78)"],regex:/^rgba*\((\d{1,3}\%*),\s*(\d{1,3}\%*),\s*(\d{1,3}\%*)(?:,\s*([0-9.]+))?\)/,
process:function(a,c,b,e,f){c=c&&"%"==c.slice(-1)?Math.round(2.55*c.slice(0,-1)):1*c;b=b&&"%"==b.slice(-1)?Math.round(2.55*b.slice(0,-1)):1*b;e=e&&"%"==e.slice(-1)?Math.round(2.55*e.slice(0,-1)):1*e;f=f&&"%"==f.slice(-1)?Math.round(100*f.slice(0,-1)):1*f;return{r:h(c,0,255),g:h(b,0,255),b:h(e,0,255),a:h(f,0,1)||void 0}}},{example:["hsl(123, 34%, 45%)","hsla(25, 50%, 100%, 0.75)","hsv(12, 34%, 56%)"],regex:/^hs([bvl])a*\((\d{1,3}\%*),\s*(\d{1,3}\%*),\s*(\d{1,3}\%*)(?:,\s*([0-9.]+))?\)/,process:function(a,
c,b,e,f){b*=1;e=e.slice(0,-1)/100;f=f.slice(0,-1)/100;a={h:h(b,0,360),s:h(e,0,1),a:h(f,0,1)};a[c]=h(f,0,1);return a}}];a.colors={aqua:new a({r:0,g:255,b:255}),black:new a({r:0,g:0,b:0}),blue:new a({r:0,g:0,b:255}),fuchsia:new a({r:255,g:0,b:255}),gray:new a({r:128,g:128,b:128}),green:new a({r:0,g:128,b:0}),lime:new a({r:0,g:255,b:0}),maroon:new a({r:128,g:0,b:0}),navy:new a({r:0,g:0,b:128}),olive:new a({r:128,g:128,b:0}),purple:new a({r:128,g:0,b:128}),red:new a({r:255,g:0,b:0}),silver:new a({r:192,
g:192,b:192}),teal:new a({r:0,g:128,b:128}),white:new a({r:255,g:255,b:255}),yellow:new a({r:255,g:255,b:0}),aliceblue:new a({r:240,g:248,b:255}),antiquewhite:new a({r:250,g:235,b:215}),aquamarine:new a({r:127,g:255,b:212}),azure:new a({r:240,g:255,b:255}),beige:new a({r:245,g:245,b:220}),bisque:new a({r:255,g:228,b:196}),blanchedalmond:new a({r:255,g:235,b:205}),blueviolet:new a({r:138,g:43,b:226}),brown:new a({r:165,g:42,b:42}),burlywood:new a({r:222,g:184,b:135}),cadetblue:new a({r:95,g:158,b:160}),
chartreuse:new a({r:127,g:255,b:0}),chocolate:new a({r:210,g:105,b:30}),coral:new a({r:255,g:127,b:80}),cornflowerblue:new a({r:100,g:149,b:237}),cornsilk:new a({r:255,g:248,b:220}),crimson:new a({r:220,g:20,b:60}),cyan:new a({r:0,g:255,b:255}),darkblue:new a({r:0,g:0,b:139}),darkcyan:new a({r:0,g:139,b:139}),darkgoldenrod:new a({r:184,g:134,b:11}),darkgray:new a({r:169,g:169,b:169}),darkgreen:new a({r:0,g:100,b:0}),darkgrey:new a({r:169,g:169,b:169}),darkkhaki:new a({r:189,g:183,b:107}),darkmagenta:new a({r:139,
g:0,b:139}),darkolivegreen:new a({r:85,g:107,b:47}),darkorange:new a({r:255,g:140,b:0}),darkorchid:new a({r:153,g:50,b:204}),darkred:new a({r:139,g:0,b:0}),darksalmon:new a({r:233,g:150,b:122}),darkseagreen:new a({r:143,g:188,b:143}),darkslateblue:new a({r:72,g:61,b:139}),darkslategray:new a({r:47,g:79,b:79}),darkslategrey:new a({r:47,g:79,b:79}),darkturquoise:new a({r:0,g:206,b:209}),darkviolet:new a({r:148,g:0,b:211}),deeppink:new a({r:255,g:20,b:147}),deepskyblue:new a({r:0,g:191,b:255}),dimgray:new a({r:105,
g:105,b:105}),dimgrey:new a({r:105,g:105,b:105}),dodgerblue:new a({r:30,g:144,b:255}),firebrick:new a({r:178,g:34,b:34}),floralwhite:new a({r:255,g:250,b:240}),forestgreen:new a({r:34,g:139,b:34}),gainsboro:new a({r:220,g:220,b:220}),ghostwhite:new a({r:248,g:248,b:255}),gold:new a({r:255,g:215,b:0}),goldenrod:new a({r:218,g:165,b:32}),greenyellow:new a({r:173,g:255,b:47}),grey:new a({r:128,g:128,b:128}),honeydew:new a({r:240,g:255,b:240}),hotpink:new a({r:255,g:105,b:180}),indianred:new a({r:205,
g:92,b:92}),indigo:new a({r:75,g:0,b:130}),ivory:new a({r:255,g:255,b:240}),khaki:new a({r:240,g:230,b:140}),lavender:new a({r:230,g:230,b:250}),lavenderblush:new a({r:255,g:240,b:245}),lawngreen:new a({r:124,g:252,b:0}),lemonchiffon:new a({r:255,g:250,b:205}),lightblue:new a({r:173,g:216,b:230}),lightcoral:new a({r:240,g:128,b:128}),lightcyan:new a({r:224,g:255,b:255}),lightgoldenrodyellow:new a({r:250,g:250,b:210}),lightgray:new a({r:211,g:211,b:211}),lightgreen:new a({r:144,g:238,b:144}),lightgrey:new a({r:211,
g:211,b:211}),lightpink:new a({r:255,g:182,b:193}),lightsalmon:new a({r:255,g:160,b:122}),lightseagreen:new a({r:32,g:178,b:170}),lightskyblue:new a({r:135,g:206,b:250}),lightslategray:new a({r:119,g:136,b:153}),lightslategrey:new a({r:119,g:136,b:153}),lightsteelblue:new a({r:176,g:196,b:222}),lightyellow:new a({r:255,g:255,b:224}),limegreen:new a({r:50,g:205,b:50}),linen:new a({r:250,g:240,b:230}),magenta:new a({r:255,g:0,b:255}),mediumaquamarine:new a({r:102,g:205,b:170}),mediumblue:new a({r:0,
g:0,b:205}),mediumorchid:new a({r:186,g:85,b:211}),mediumpurple:new a({r:147,g:112,b:219}),mediumseagreen:new a({r:60,g:179,b:113}),mediumslateblue:new a({r:123,g:104,b:238}),mediumspringgreen:new a({r:0,g:250,b:154}),mediumturquoise:new a({r:72,g:209,b:204}),mediumvioletred:new a({r:199,g:21,b:133}),midnightblue:new a({r:25,g:25,b:112}),mintcream:new a({r:245,g:255,b:250}),mistyrose:new a({r:255,g:228,b:225}),moccasin:new a({r:255,g:228,b:181}),navajowhite:new a({r:255,g:222,b:173}),oldlace:new a({r:253,
g:245,b:230}),olivedrab:new a({r:107,g:142,b:35}),orange:new a({r:255,g:165,b:0}),orangered:new a({r:255,g:69,b:0}),orchid:new a({r:218,g:112,b:214}),palegoldenrod:new a({r:238,g:232,b:170}),palegreen:new a({r:152,g:251,b:152}),paleturquoise:new a({r:175,g:238,b:238}),palevioletred:new a({r:219,g:112,b:147}),papayawhip:new a({r:255,g:239,b:213}),peachpuff:new a({r:255,g:218,b:185}),peru:new a({r:205,g:133,b:63}),pink:new a({r:255,g:192,b:203}),plum:new a({r:221,g:160,b:221}),powderblue:new a({r:176,
g:224,b:230}),rosybrown:new a({r:188,g:143,b:143}),royalblue:new a({r:65,g:105,b:225}),saddlebrown:new a({r:139,g:69,b:19}),salmon:new a({r:250,g:128,b:114}),sandybrown:new a({r:244,g:164,b:96}),seagreen:new a({r:46,g:139,b:87}),seashell:new a({r:255,g:245,b:238}),sienna:new a({r:160,g:82,b:45}),skyblue:new a({r:135,g:206,b:235}),slateblue:new a({r:106,g:90,b:205}),slategray:new a({r:112,g:128,b:144}),slategrey:new a({r:112,g:128,b:144}),snow:new a({r:255,g:250,b:250}),springgreen:new a({r:0,g:255,
b:127}),steelblue:new a({r:70,g:130,b:180}),tan:new a({r:210,g:180,b:140}),thistle:new a({r:216,g:191,b:216}),tomato:new a({r:255,g:99,b:71}),turquoise:new a({r:64,g:224,b:208}),violet:new a({r:238,g:130,b:238}),wheat:new a({r:245,g:222,b:179}),whitesmoke:new a({r:245,g:245,b:245}),yellowgreen:new a({r:154,g:205,b:50})};a.prototype={toName:function(){return a.toName(this)},toString:function(){return this.toHexString()},valueOf:function(){return a.toInteger(this)},rgb:function(){return this.toRGBArray()},
toRGBArray:function(){return this.toRGBAArray().slice(0,3)},toRGBObject:function(){var a=this.toRGBArray();return{r:a[0],g:a[1],b:a[2]}},toRGBString:function(){return"rgb("+this.toRGBArray().join(",")+")"},rgba:function(){return this.toRGBAArray()},toRGBAArray:function(){return[Math.round(this.r),Math.round(this.g),Math.round(this.b),parseFloat(this.a)]},toRGBAObject:function(){var a=this.toRGBAArray();return{r:a[0],g:a[1],b:a[2],a:a[3]}},toRGBAString:function(){return"rgba("+this.toRGBAArray().join(",")+
")"},hex:function(){return this.toHexArray()},toHexArray:function(){return this.toHexString().slice(1).match(/([\dA-F]{2})/ig)},toHexObject:function(){var a=this.toHexArray();return{r:a[0],g:a[1],b:a[2]}},toHexString:function(){return a.rgb2hex(this.r,this.g,this.b)},hsl:function(){return this.toHSLArray()},toHSLArray:function(){return this.toHSLAArray().slice(0,3)},toHSLObject:function(){var a=this.toHSLArray();return{h:a[0],s:a[1],l:a[2]}},toHSLString:function(){return"hsl("+this.toHSLArray().join(",")+
")"},hsla:function(){return this.toHSLAArray()},toHSLAArray:function(){return[Math.round(this.h),parseFloat(this.sl),parseFloat(this.l),parseFloat(this.a)]},toHSLAObject:function(){var a=this.toHSLArray();return{h:a[0],s:a[1],l:a[2],a:a[3]}},toHSLAString:function(){return"hsla("+this.toHSLAArray().join(",")+")"},hsv:function(){return this.toHSVArray()},toHSVArray:function(){return[Math.round(this.h),parseFloat(this.sv),parseFloat(this.v)]},toHSVObject:function(){return{h:Math.round(this.h),s:parseFloat(this.sv),
v:parseFloat(this.v)}},toHSVString:function(){var a=this.toHSVArray();return"hsv("+[a[0],100*a[1]+"%",100*a[2]+"%"]+")"},hsva:function(){return this.toHSVAArray()},toHSVAArray:function(){return[Math.round(this.h),parseFloat(this.sv),parseFloat(this.v),parseFloat(this.a)]},toHSVAObject:function(){var a=this.toHSVAArray();return{h:a[0],s:a[1],l:a[2],a:a[3]}},toHSVAString:function(){var a=this.toHSVAArray();return"hsva("+[a[0],100*a[1]+"%",100*a[2]+"%",a[3]]+")"},hsb:function(){return this.hsv()},toHSBArray:function(){return this.toHSVArray()},
toHSBObject:function(){return this.toHSVObject()},toHSBString:function(){return this.toHSVString()},hsba:function(){return this.hsva()},toHSBAArray:function(){return this.toHSVAArray()},toHSBAObject:function(){return this.toHSVAObject()},toHSBAString:function(){return this.toHSVAString()},complement:function(){return a.complement(this)},triad:function(){return a.triad(this)},tetrad:function(){return a.tetrad(this)},analogous:function(d,c){return a.analogous(this,d,c)},monochromatic:function(d){return a.monochromatic(this,
d)},splitcomplement:function(){return a.splitcomplement(this)},tint:function(d){return a.tint(this,d)},lighten:function(a){return this.tint(a)},shade:function(d){return a.shade(this,d)},darken:function(a){return this.shade(a)},desaturate:function(d){return a.desaturate(this,d)},greyscale:function(a){return this.desaturate(a)},websafe:function(){return a.websafe(this)},additive:function(){var d=Array.prototype.slice.call(arguments);return a.additive.apply(a,[this].concat(d))},subtractive:function(){var d=
Array.prototype.slice.call(arguments);return a.subtractive.apply(a,[this].concat(d))},multiply:function(){var d=Array.prototype.slice.call(arguments);return a.multiply.apply(a,[this].concat(d))},average:function(){var d=Array.prototype.slice.call(arguments);return a.average.apply(a,[this].concat(d))},overlay:function(d,c){return a.overlay(this,d,c)},clone:function(){return new a(this)},towards:function(d,c){return a.towards(this,d,c)},gradient:function(d,c,b){return a.gradient(this,d,c,b)}};k(a,a.colors);
var j="Chromath",i=a,t,u;"string"!==typeof j&&(i=j,j=null);t=function(){return this}.call(null);i=i||{};"undefined"!==typeof module&&("undefined"!==typeof exports&&module.exports)&&(u=!0,module.exports=exports=i);"undefined"!==typeof window&&!u&&(j&&i)&&(t[j]=i)})();