Merge branch 'master' into feature/ingest

This commit is contained in:
Matthew Bargar 2016-03-23 09:53:34 -04:00
commit ad01828bab
123 changed files with 2117 additions and 1439 deletions

1
.gitignore vendored
View file

@ -1,4 +1,5 @@
.aws-config.json
.ackrc
.DS_Store
.node_binaries
node_modules

View file

@ -11,7 +11,7 @@
"dashboarding"
],
"private": false,
"version": "5.0.0-snapshot",
"version": "5.0.0-reskin-snapshot",
"build": {
"number": 8467,
"sha": "6cb7fec4e154faa0a4a3fee4b33dfef91b9870d9"
@ -82,10 +82,10 @@
"ansicolors": "0.3.2",
"autoprefixer": "5.1.1",
"autoprefixer-loader": "2.0.0",
"babel": "5.8.23",
"babel-core": "5.8.23",
"babel": "5.8.38",
"babel-core": "5.8.38",
"babel-loader": "5.3.2",
"babel-runtime": "5.8.20",
"babel-runtime": "5.8.38",
"bluebird": "2.9.34",
"boom": "2.8.0",
"bootstrap": "3.3.6",

View file

@ -63,6 +63,7 @@ class BaseOptimizer {
getConfig() {
let mapQ = this.sourceMaps ? '?sourceMap' : '';
let mapQPre = mapQ ? mapQ + '&' : '?';
return {
context: fromRoot('.'),
@ -101,7 +102,7 @@ class BaseOptimizer {
test: /\.less$/,
loader: ExtractTextPlugin.extract(
'style',
`css${mapQ}!autoprefixer${mapQ ? mapQ + '&' : '?'}{ "browsers": ["last 2 versions","> 5%"] }!less${mapQ}`
`css${mapQ}!autoprefixer${mapQPre}{ "browsers": ["last 2 versions","> 5%"] }!less${mapQPre}dumpLineNumbers=comments`
)
},
{ test: /\.css$/, loader: ExtractTextPlugin.extract('style', `css${mapQ}`) },

View file

@ -3,7 +3,7 @@ import search from './server/routes/api/search';
module.exports = function (kibana) {
return new kibana.Plugin({
id: 'kibana',
config: function (Joi) {
return Joi.object({
enabled: Joi.boolean().default(true),
@ -14,7 +14,9 @@ module.exports = function (kibana) {
uiExports: {
app: {
id: 'kibana',
title: 'Kibana',
listed: false,
description: 'the kibana you know and love',
//icon: 'plugins/kibana/settings/sections/about/barcode.svg',
main: 'plugins/kibana/kibana',
@ -33,9 +35,39 @@ module.exports = function (kibana) {
return {
kbnDefaultAppId: config.get('kibana.defaultAppId')
};
}
},
},
links: [
{
title: 'Discover',
order: -1003,
url: '/app/kibana#/discover',
description: 'interactively explore your data',
icon: 'plugins/kibana/assets/discover.svg',
},
{
title: 'Visualize',
order: -1002,
url: '/app/kibana#/visualize',
description: 'design data visualizations',
icon: 'plugins/kibana/assets/visualize.svg',
},
{
title: 'Dashboard',
order: -1001,
url: '/app/kibana#/dashboard',
description: 'compose visualizations for much win',
icon: 'plugins/kibana/assets/dashboard.svg',
},
{
title: 'Settings',
order: 1000,
url: '/app/kibana#/settings',
description: 'define index patterns, change config, and more',
icon: 'plugins/kibana/assets/settings.svg',
}
],
injectDefaultVars(server, options) {
return {
kbnIndex: options.index

View file

@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="8" height="8" viewBox="0 0 8 8">
<path d="M4 0c-2.2 0-4 1.8-4 4s1.8 4 4 4 4-1.8 4-4-1.8-4-4-4zm0 1c1.66 0 3 1.34 3 3s-1.34 3-3 3-3-1.34-3-3 1.34-3 3-3zm0 1c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5zm-1.66 1a.5.5 0 0 0-.19.84l.91.91c-.02.08-.06.16-.06.25 0 .55.45 1 1 1s1-.45 1-1-.45-1-1-1c-.09 0-.17.04-.25.06l-.91-.91a.5.5 0 0 0-.44-.16.5.5 0 0 0-.06 0zm3.16 0c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5z"
/>
</svg>

After

Width:  |  Height:  |  Size: 486 B

View file

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="8" height="8" viewBox="0 0 8 8">
<path d="M4 0c-2.2 0-4 1.8-4 4s1.8 4 4 4 4-1.8 4-4-1.8-4-4-4zm0 1c1.66 0 3 1.34 3 3s-1.34 3-3 3-3-1.34-3-3 1.34-3 3-3zm2 1l-3 1-1 3 3-1 1-3zm-2 1.5c.28 0 .5.22.5.5s-.22.5-.5.5-.5-.22-.5-.5.22-.5.5-.5z" />
</svg>

After

Width:  |  Height:  |  Size: 293 B

View file

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="8" height="8" viewBox="0 0 8 8">
<path d="M3 0v1h4v5h-4v1h5v-7h-5zm-1 2l-2 1.5 2 1.5v-1h4v-1h-4v-1z" />
</svg>

After

Width:  |  Height:  |  Size: 159 B

View file

@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="8" height="8" viewBox="0 0 8 8">
<path d="M3.5 0c-1.93 0-3.5 1.57-3.5 3.5s1.57 3.5 3.5 3.5c.59 0 1.17-.14 1.66-.41a1 1 0 0 0 .13.13l1 1a1.02 1.02 0 1 0 1.44-1.44l-1-1a1 1 0 0 0-.16-.13c.27-.49.44-1.06.44-1.66 0-1.93-1.57-3.5-3.5-3.5zm0 1c1.39 0 2.5 1.11 2.5 2.5 0 .66-.24 1.27-.66 1.72-.01.01-.02.02-.03.03a1 1 0 0 0-.13.13c-.44.4-1.04.63-1.69.63-1.39 0-2.5-1.11-2.5-2.5s1.11-2.5 2.5-2.5z"
/>
</svg>

After

Width:  |  Height:  |  Size: 450 B

View file

@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="8" height="8" viewBox="0 0 8 8">
<path d="M3.5 0l-.5 1.19c-.1.03-.19.08-.28.13l-1.19-.5-.72.72.5 1.19c-.05.1-.09.18-.13.28l-1.19.5v1l1.19.5c.04.1.08.18.13.28l-.5 1.19.72.72 1.19-.5c.09.04.18.09.28.13l.5 1.19h1l.5-1.19c.09-.04.19-.08.28-.13l1.19.5.72-.72-.5-1.19c.04-.09.09-.19.13-.28l1.19-.5v-1l-1.19-.5c-.03-.09-.08-.19-.13-.28l.5-1.19-.72-.72-1.19.5c-.09-.04-.19-.09-.28-.13l-.5-1.19h-1zm.5 2.5c.83 0 1.5.67 1.5 1.5s-.67 1.5-1.5 1.5-1.5-.67-1.5-1.5.67-1.5 1.5-1.5z"
/>
</svg>

After

Width:  |  Height:  |  Size: 528 B

View file

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="8" height="8" viewBox="0 0 8 8">
<path d="M0 0v7h8v-1h-7v-6h-1zm5 0v5h2v-5h-2zm-3 2v3h2v-3h-2z" />
</svg>

After

Width:  |  Height:  |  Size: 154 B

View file

@ -1,18 +1,15 @@
<div class="panel panel-default" ng-switch on="panel.type" ng-if="savedObj || error">
<div class="panel-heading">
<span class="panel-title" title="{{::savedObj.title}}">
<i
class="fa"
ng-class="savedObj.vis.type.icon"
aria-label="{{::savedObj.vis.type.title}} Icon"
title="{{::savedObj.vis.type.title}}">
</i>
{{::savedObj.title}}
</span>
<div class="btn-group">
<a aria-label="Edit" ng-show="chrome.getVisible() && editUrl" ng-href="{{::editUrl}}">
<i aria-hidden="true" class="fa fa-pencil"></i>
</a>
<a aria-label="Move" ng-show="chrome.getVisible()" class="panel-move">
<i aria-hidden="true" class="fa fa-arrows"></i>
</a>
<a aria-label="Remove" ng-show="chrome.getVisible()" ng-click="remove()">
<i aria-hidden="true" class="fa fa-times"></i>
</a>

View file

@ -27,7 +27,7 @@ app.directive('dashboardGrid', function ($compile, Notifier) {
// number of columns to render
const COLS = 12;
// number of pixed between each column/row
const SPACER = 10;
const SPACER = 0;
// pixels used by all of the spacers (gridster puts have a spacer on the ends)
const spacerSize = SPACER * COLS;
@ -46,7 +46,7 @@ app.directive('dashboardGrid', function ($compile, Notifier) {
stop: readGridsterChangeHandler
},
draggable: {
handle: '.panel-heading, .panel-title',
handle: '.panel-move, .fa-arrows',
stop: readGridsterChangeHandler
}
}).data('gridster');
@ -232,4 +232,3 @@ app.directive('dashboardGrid', function ($compile, Notifier) {
}
};
});

View file

@ -1,7 +1,62 @@
<div dashboard-app class="app-container dashboard-container">
<navbar ng-show="chrome.getVisible()" name="dashboard">
<span class="name" ng-if="dash.id" ng-bind="::dash.title" tooltip="{{::dash.title}}" tooltip-append-to-body="true"></span>
<navbar name="dashboard-options" class="kibana-nav-options">
<div class="kibana-nav-info">
<span ng-show="dash.id" class="kibana-nav-info-title">
<span ng-bind="::dash.title"></span>
</span>
</div>
<div class="button-group kibana-nav-actions" role="toolbar">
<button ng-click="newDashboard()"
aria-label="New Dashboard">
<span>New</span>
</button>
<button
aria-label="Save Dashboard"
aria-haspopup="false"
aria-expanded="{{ configTemplate.is('save') }}"
ng-class="{active: configTemplate.is('save')}"
ng-click="configTemplate.toggle('save');">
<span>Save</span>
</button>
<button
aria-label="Load Saved Dashboard"
aria-haspopup="false"
aria-expanded="{{ configTemplate.is('load') }}"
ng-class="{active: configTemplate.is('load')}"
ng-click="configTemplate.toggle('load');">
<span>Open</span>
</button>
<button
aria-label="Share Dashboard"
aria-haspopup="false"
aria-expanded="{{ configTemplate.is('share') }}"
ng-class="{active: configTemplate.is('share')}"
ng-click="configTemplate.toggle('share');">
<span>Share</span>
</button>
<button
aria-label="Add Visualization"
aria-haspopup="false"
aria-expanded="{{ configTemplate.is('pickVis') }}"
ng-class="{active: configTemplate.is('pickVis')}"
ng-click="configTemplate.toggle('pickVis');">
<span>Add visualization</span>
</button>
<button
aria-label="Options"
aria-haspopup="false"
aria-expanded="{{ configTemplate.is('options') }}"
ng-class="{active: configTemplate.is('options')}"
ng-click="configTemplate.toggle('options');">
<span>Options</span>
</button>
<div class="chrome-actions"kbn-chrome-append-nav-controls></div>
</div>
</navbar>
<config config-template="configTemplate" config-object="opts"></config>
<navbar ng-show="chrome.getVisible()" name="dashboard-search">
<form name="queryInput"
class="fill inline-form"
ng-submit="filterResults()"
@ -29,66 +84,9 @@
</div>
</form>
<div class="button-group" role="toolbar">
<kbn-tooltip text="New Dashboard" placement="bottom" append-to-body="1">
<button ng-click="newDashboard()" aria-label="New Dashboard"><i aria-hidden="true" class="fa fa-file-new-o"></i></button>
</kbn-tooltip>
<kbn-tooltip text="Save Dashboard" placement="bottom" append-to-body="1">
<button
aria-label="Save Dashboard"
aria-haspopup="true"
aria-expanded="{{ configTemplate.is('save') }}"
ng-class="{active: configTemplate.is('save')}"
ng-click="configTemplate.toggle('save');">
<i aria-hidden="true" class="fa fa-save"></i>
</button>
</kbn-tooltip>
<kbn-tooltip text="Load Saved Dashboard" placement="bottom" append-to-body="1">
<button
aria-label="Load Saved Dashboard"
aria-haspopup="true"
aria-expanded="{{ configTemplate.is('load') }}"
ng-class="{active: configTemplate.is('load')}"
ng-click="configTemplate.toggle('load');">
<i aria-hidden="true" class="fa fa-folder-open-o"></i>
</button>
</kbn-tooltip>
<kbn-tooltip text="Share" placement="bottom" append-to-body="1">
<button
aria-label="Share Dashboard"
aria-haspopup="true"
aria-expanded="{{ configTemplate.is('share') }}"
ng-class="{active: configTemplate.is('share')}"
ng-click="configTemplate.toggle('share');">
<i aria-hidden="true" class="fa fa-external-link"></i>
</button>
</kbn-tooltip>
<kbn-tooltip text="Add Visualization" placement="bottom" append-to-body="1">
<button
aria-label="Add Visualization"
aria-haspopup="true"
aria-expanded="{{ configTemplate.is('pickVis') }}"
ng-class="{active: configTemplate.is('pickVis')}"
ng-click="configTemplate.toggle('pickVis');">
<i aria-hidden="true" class="fa fa-plus-circle"></i>
</button>
</kbn-tooltip>
<kbn-tooltip text="Options" placement="bottom" append-to-body="1">
<button
aria-label="Options"
aria-haspopup="true"
aria-expanded="{{ configTemplate.is('options') }}"
ng-class="{active: configTemplate.is('options')}"
ng-click="configTemplate.toggle('options');">
<i aria-hidden="true" class="fa fa-gear"></i>
</button>
</kbn-tooltip>
</div>
<div class="button-group"></div>
</navbar>
<config config-template="configTemplate" config-object="opts"></config>
<filter-bar state="state"></filter-bar>
<div ng-show="!state.panels.length" class="text-center start-screen">

View file

@ -105,7 +105,9 @@ app.directive('dashboardApp', function (Notifier, courier, AppState, timefilter,
load: require('plugins/kibana/dashboard/partials/load_dashboard.html'),
share: require('plugins/kibana/dashboard/partials/share.html'),
pickVis: require('plugins/kibana/dashboard/partials/pick_visualization.html'),
options: require('plugins/kibana/dashboard/partials/options.html')
options: require('plugins/kibana/dashboard/partials/options.html'),
filter: require('ui/chrome/config/filter.html'),
interval: require('ui/chrome/config/interval.html')
});
$scope.refresh = _.bindKey(courier, 'fetch');
@ -239,7 +241,8 @@ app.directive('dashboardApp', function (Notifier, courier, AppState, timefilter,
ui: $state.options,
save: $scope.save,
addVis: $scope.addVis,
addSearch: $scope.addSearch
addSearch: $scope.addSearch,
timefilter: $scope.timefilter
};
init();

View file

@ -33,6 +33,24 @@ dashboard-grid {
width: 25px;
}
.gs-w {
border: 2px dashed transparent;
&:hover {
border-color: @kibanaGray4;
dashboard-panel {
.visualize-show-spy {
visibility: visible;
}
.panel .panel-heading .btn-group {
display: block !important;
}
}
}
}
i.remove {
cursor: pointer;
}
@ -55,15 +73,23 @@ dashboard-grid {
display: flex;
flex-direction: column;
justify-content: flex-start;
border: 0 solid transparent;
.panel-heading {
padding: 0px 0px 0px 5px;
flex: 0 0 auto;
white-space: nowrap;
display: flex;
border-top-right-radius: 0;
border-top-left-radius: 0;
background-color: @white;
border: none;
div.btn-group {
.btn-group {
a {
color: inherit;
}
display: none;
white-space: nowrap;
flex: 0 0 auto;
}
@ -85,6 +111,10 @@ dashboard-grid {
}
}
.panel-move:hover {
cursor: move;
}
a {
color: @dashboard-panel-heading-link-color;
border: none;
@ -96,6 +126,10 @@ dashboard-grid {
}
}
.visualize-show-spy {
visibility: hidden;
}
.load-error {
text-align: center;
font-size: 1em;
@ -119,10 +153,6 @@ dashboard-grid {
}
}
.dashboard-panel-picker > li.list-group-item {
border-top: 0px;
}
.dashboard-load {
margin: 10px;
.dashboard-panel-picker > .list-group-item {
border-top: 0;
}

View file

@ -64,7 +64,6 @@ app.directive('discoverField', function ($compile) {
$scope.toggle(field.name);
if (field.display) $scope.increaseFieldCounter(field);
// we are now displaying the field, kill its details
if (field.details) {
$scope.toggleDetails(field);
}
@ -80,10 +79,11 @@ app.directive('discoverField', function ($compile) {
detailsElem = $(detailsHtml);
$compile(detailsElem)(detailScope);
$elem.append(detailsElem);
$elem.append(detailsElem).addClass('active');
} else {
delete field.details;
detailsElem.remove();
$elem.removeClass('active');
}
};

View file

@ -1,6 +1,7 @@
<div class="sidebar-list">
<div ng-show="indexPatternList.length > 1">
<div class="index-pattern" ng-click="showIndexPatternSelection = !showIndexPatternSelection">
<div class="index-pattern" ng-click="showIndexPatternSelection = !showIndexPatternSelection"
ng-class="{active: showIndexPatternSelection === true}">
<div css-truncate title="{{indexPattern.id}}">{{indexPattern.id}}</div>
<i ng-hide="showIndexPatternSelection" class="fa fa-caret-down"></i>
<i ng-show="showIndexPatternSelection" class="fa fa-caret-up"></i>

View file

@ -96,16 +96,17 @@ app.controller('discover', function ($scope, config, courier, $route, $window, N
$scope.toggleInterval = function () {
$scope.showInterval = !$scope.showInterval;
};
// config panel templates
$scope.configTemplate = new ConfigTemplate({
load: require('plugins/kibana/discover/partials/load_search.html'),
save: require('plugins/kibana/discover/partials/save_search.html'),
share: require('plugins/kibana/discover/partials/share_search.html')
share: require('plugins/kibana/discover/partials/share_search.html'),
filter: require('ui/chrome/config/filter.html'),
interval: require('ui/chrome/config/interval.html')
});
$scope.timefilter = timefilter;
// the saved savedSearch
const savedSearch = $route.current.locals.savedSearch;
$scope.$on('$destroy', savedSearch.destroy);
@ -147,7 +148,8 @@ app.controller('discover', function ($scope, config, courier, $route, $window, N
index: $scope.indexPattern.id,
timefield: $scope.indexPattern.timeFieldName,
savedSearch: savedSearch,
indexPatternList: $route.current.locals.ip.list
indexPatternList: $route.current.locals.ip.list,
timefilter: $scope.timefilter
};
const init = _.once(function () {

View file

@ -17,7 +17,7 @@ uiModules
const init = function () {
// This elem should already have a height/width
const myChart = new vislib.Chart(elem[0], {
addLegend: false
});
$scope.$watch('data', function (data) {

View file

@ -1,5 +1,53 @@
<div ng-controller="discover" class="app-container">
<navbar name="discover">
<navbar name="discover-options" class="kibana-nav-options">
<div class="kibana-nav-info">
<span ng-show="opts.savedSearch.id" class="kibana-nav-info-title">
<span ng-bind="::opts.savedSearch.title"></span>
<i aria-label="Reload Saved Search" tooltip="Reload Saved Search" ng-click="resetQuery();" class="fa fa-undo small"></i>
</span>
<strong class="discover-info-hits">{{(hits || 0) | number:0}}</strong>
<ng-pluralize count="hits" when="{'1':'hit', 'other':'hits'}"></ng-pluralize>
</div>
<div class="kibana-nav-actions button-group" role="toolbar">
<button
ng-click="newQuery()"
aria-label="New Search">
<span>New</span>
</button>
<button
ng-click="configTemplate.toggle('save');"
ng-class="{active: configTemplate.is('save')}"
aria-haspopup="false"
aria-expanded="{{ configTemplate.is('save') }}"
aria-label="Save Search">
<span>Save</span>
</button>
<button
aria-haspopup="false"
aria-expanded="{{ configTemplate.is('load') }}"
ng-click="configTemplate.toggle('load');"
ng-class="{active: configTemplate.is('load')}"
aria-label="Load Saved Search">
<span>Open</span>
</button>
<button
aria-label="Share Search"
aria-haspopup="false"
aria-expanded="{{ configTemplate.is('share') }}"
ng-class="{active: configTemplate.is('share')}"
ng-click="configTemplate.toggle('share');">
<span>Share</span>
</button>
<div class="chrome-actions" kbn-chrome-append-nav-controls></div>
</div>
</navbar>
<config
config-template="configTemplate"
config-object="opts"
config-close="configClose"
></config>
<navbar name="discover-search">
<form role="form" class="fill inline-form" ng-submit="fetch()" name="discoverSearch">
<div class="typeahead" kbn-typeahead="discover">
<div class="input-group"
@ -21,49 +69,9 @@
<kbn-typeahead-items></kbn-typeahead-items>
</div>
</form>
<div class="button-group" role="toolbar">
<kbn-tooltip text="New Search" placement="bottom" append-to-body="1">
<button
ng-click="newQuery()"
aria-label="New Search">
<i aria-hidden="true" class="fa fa-file-new-o"></i>
</button>
</kbn-tooltip>
<kbn-tooltip text="Save Search" placement="bottom" append-to-body="1">
<button
ng-click="configTemplate.toggle('save');"
ng-class="{active: configTemplate.is('save')}"
aria-haspopup="true"
aria-expanded="{{ configTemplate.is('save') }}"
aria-label="Save Search">
<i aria-hidden="true" class="fa fa-save"></i>
</button>
</kbn-tooltip>
<kbn-tooltip text="Load Saved Search" placement="bottom" append-to-body="1">
<button
aria-haspopup="true"
aria-expanded="{{ configTemplate.is('load') }}"
ng-click="configTemplate.toggle('load');"
ng-class="{active: configTemplate.is('load')}"
aria-label="Load Saved Search">
<i aria-hidden="true" class="fa fa-folder-open-o"></i>
</button>
</kbn-tooltip>
<kbn-tooltip text="Share" placement="bottom" append-to-body="1">
<button
aria-label="Share Search"
aria-haspopup="true"
aria-expanded="{{ configTemplate.is('share') }}"
ng-class="{active: configTemplate.is('share')}"
ng-click="configTemplate.toggle('share');">
<i aria-hidden="true" class="fa fa-external-link"></i>
</button>
</kbn-tooltip>
</div>
<div class="button-group" role="toolbar"></div>
</navbar>
<config config-template="configTemplate" config-object="opts" config-close="configClose"></config>
<div class="container-fluid" role="main">
<div class="row">
@ -84,17 +92,6 @@
</div>
<div class="discover-wrapper col-md-10">
<div class="discover-info">
<span ng-show="opts.savedSearch.id" class="discover-info-title">
<span ng-bind="::opts.savedSearch.title"></span>
<i aria-label="Reload Saved Search" tooltip="Reload Saved Search" ng-click="resetQuery();" class="fa fa-undo small"></i>
</span>
<strong class="discover-info-hits">{{(hits || 0) | number:0}}</strong>
<ng-pluralize count="hits" when="{'1':'hit', 'other':'hits'}"></ng-pluralize>
</div>
<div class="discover-content">
<!-- no results -->
<div ng-show="resultState === 'none'">
@ -153,7 +150,6 @@
<div ng-show="resultState === 'loading'">
<div class="discover-overlay">
<h2>Searching</h2>
<div class="spinner large"></div>
<div ng-show="fetchStatus">{{fetchStatus.complete}}/{{fetchStatus.total}}</div>
</div>
</div>

View file

@ -29,6 +29,10 @@
height: 200px;
max-height: 600px;
.loading {
opacity: 1 !important;
}
&.only-spy {
height: auto;
@ -71,12 +75,10 @@
}
&-info {
background-color: @discover-info-bg;
float: right;
padding: 5px 10px;
line-height: 30px;
padding: 0px 10px;
border-bottom-left-radius: @border-radius-base;
text-align: right;
&-title {
font-weight: bold;
@ -245,6 +247,10 @@ disc-field-chooser {
}
}
.sidebar-item.active .sidebar-item-title {
background-color: @sidebar-active-bg;
color: @sidebar-active-color;
}
.sidebar-item-title {
position: relative;
}

View file

@ -28,36 +28,14 @@ routes
chrome
.setBrand({
'logo': 'url(' + kibanaLogoUrl + ') left no-repeat',
'smallLogo': 'url(' + kibanaLogoUrl + ') left no-repeat'
'logo': 'url(' + kibanaLogoUrl + ') 6px 10px / 140px 50px no-repeat #e8488b',
'smallLogo': 'url(' + kibanaLogoUrl + ') 6px 10px / 140px 50px no-repeat #e8488b'
})
.setNavBackground('#222222')
.setTabDefaults({
resetWhenActive: true,
lastUrlStore: window.sessionStore,
lastUrlStore: window.sessionStorage,
activeIndicatorColor: '#656a76'
})
.setTabs([
{
id: 'discover',
title: 'Discover'
},
{
id: 'visualize',
title: 'Visualize',
activeIndicatorColor: function () {
return (String(this.lastUrl).indexOf('/visualize/step/') === 0) ? 'white' : '#656a76';
}
},
{
id: 'dashboard',
title: 'Dashboard'
},
{
id: 'settings',
title: 'Settings'
}
])
.setRootController('kibana', function ($scope, $rootScope, courier, config) {
function setDefaultTimezone() {
moment.tz.setDefault(config.get('dateFormat:tz'));

View file

@ -1,6 +1,7 @@
<div class="app-container">
<nav class="navbar navbar-default navbar-static-top subnav" data-test-subj="settingsNav">
<div class="container-fluid">
<bread-crumbs></bread-crumbs>
<ul class="nav navbar-nav">
<li ng-repeat="section in sections" ng-class="section.class">
<a class="navbar-link" kbn-href="{{section.url}}" data-test-subj="{{section.name}}">{{section.display}}</a>

View file

@ -9,6 +9,7 @@ import 'ui/filters/start_from';
import 'ui/field_editor';
import 'plugins/kibana/settings/sections/indices/_indexed_fields';
import 'plugins/kibana/settings/sections/indices/_scripted_fields';
import 'ui/directives/bread_crumbs';
import registry from 'ui/registry/settings_sections';
import uiRoutes from 'ui/routes';
import uiModules from 'ui/modules';

View file

@ -11,6 +11,10 @@ kbn-settings-objects-view {
display: block;
}
nav.navbar {
height: 70px;
}
.settings-nav {
text-transform: capitalize;
}
@ -19,6 +23,15 @@ li.kbn-settings-tab:first-letter {
text-transform: capitalize;
}
kbn-settings-app {
div.app-container {
div.container-fluid {
padding-left: 0;
padding-right: 0;
}
}
}
kbn-settings-objects {
form {
margin-bottom: @line-height-computed;
@ -93,6 +106,8 @@ kbn-settings-objects-view {
}
.advanced-settings {
overflow-x: scroll;
table {
width: 100%;

View file

@ -6,7 +6,7 @@
aria-label="{{ editorOpen ? 'Close Editor' : 'Open Editor' }}"
ng-click="editorOpen = !editorOpen"
type="button"
class="btn btn-default btn-xs vis-editor-agg-header-toggle">
class="btn btn-primary btn-xs vis-editor-agg-header-toggle">
<i aria-hidden="true" ng-class="{ 'fa-caret-down': editorOpen, 'fa-caret-right': !editorOpen }" class="fa"></i>
</button>
@ -36,7 +36,7 @@
tooltip="Increase Priority"
tooltip-append-to-body="true"
type="button"
class="btn btn-xs btn-default">
class="btn btn-xs btn-primary">
<i aria-hidden="true" class="fa fa-caret-up"></i>
</button>
@ -49,7 +49,7 @@
tooltip="Decrease Priority"
tooltip-append-to-body="true"
type="button"
class="btn btn-xs btn-default">
class="btn btn-xs btn-primary">
<i aria-hidden="true" class="fa fa-caret-down"></i>
</button>

View file

@ -18,14 +18,14 @@
class="vis-editor-agg-wide-btn">
<div ng-if="!add.form">
<div class="vis-editor-agg-wide-btn-add" ng-if="groupName !== 'buckets' || !stats.count">
<i aria-hidden="true" class="fa fa-plus"></i> Add {{ groupName }}
<div class="btn btn-sm btn-primary" ng-if="groupName !== 'buckets' || !stats.count">
Add {{ groupName }}
</div>
<div class="vis-editor-agg-wide-btn-add" ng-if="groupName === 'buckets' && stats.count > 0">
<i aria-hidden="true" class="fa fa-code-fork"></i> Add sub-{{ groupName }}
<div class="btn btn-sm btn-primary" ng-if="groupName === 'buckets' && stats.count > 0">
Add sub-{{ groupName }}
</div>
</div>
<div class="vis-editor-agg-wide-btn-add" ng-if="add.form">
<div class="btn btn-sm btn-primary" ng-if="add.form">
Cancel
</div>
</div>
</div>

View file

@ -6,13 +6,6 @@
<div class="vis-editor-agg-group" ng-class="groupName">
<!-- wrapper needed for nesting-indicator -->
<div ng-repeat="agg in group" class="vis-editor-agg-wrapper">
<nesting-indicator
ng-if="groupName === 'buckets'"
item="agg"
index="$index"
list="group">
</nesting-indicator>
<!-- agg.html - controls for aggregation -->
<ng-form vis-editor-agg name="aggForm" class="vis-editor-agg"></ng-form>
</div>

View file

@ -1,5 +1,60 @@
<div ng-controller="VisEditor" class="vis-editor vis-type-{{ vis.type.name }}">
<navbar ng-if="chrome.getVisible()" name="visualize">
<div ng-controller="VisEditor" class="app-container vis-editor vis-type-{{ vis.type.name }}">
<navbar name="visualize-options" class="kibana-nav-options" ng-if="chrome.getVisible()">
<div class="vis-editor-info">
<span ng-show="savedVis.id" class="vis-editor-info-title">
<span ng-bind="::savedVis.title"></span>
</span>
</div>
<div class="button-group kibana-nav-actions">
<button ng-click="startOver()" aria-label="New Visualization">
<span>New</span>
</button>
<!-- normal save -->
<button
ng-class="{active: configTemplate.is('save')}"
ng-click="configTemplate.toggle('save')"
ng-if="!editableVis.dirty"
aria-expanded="{{ configTemplate.is('save') }}"
aria-label="Save Visualization">
<span>Save</span>
</button>
<!-- save stub with tooltip -->
<button disabled ng-if="editableVis.dirty" tooltip="Apply or Discard your changes before saving" aria-label="Apply or Discard your changes before saving">
<span>Save</span>
</button>
<button
ng-class="{active: configTemplate.is('load')}"
ng-click="configTemplate.toggle('load')"
aria-haspopup="true"
aria-expanded="{{ configTemplate.is('load') }}"
aria-label="Load Saved Visualization">
<span>Load</span>
</button>
<button
ng-class="{active: configTemplate.is('share')}"
ng-click="configTemplate.toggle('share')"
aria-haspopup="true"
aria-expanded="{{ configTemplate.is('share') }}"
aria-label="Share Visualization">
<span>Share</span>
</button>
<button
ng-click="fetch()"
aria-label="Refresh">
<span>Refresh</span>
</button>
<div class="chrome-actions"kbn-chrome-append-nav-controls></div>
</div>
</navbar>
<config
ng-if="chrome.getVisible()"
config-template="configTemplate"
config-object="opts">
</config>
<navbar ng-if="chrome.getVisible()" name="visualize-search">
<div class="fill bitty-modal-container">
<div ng-if="vis.type.requiresSearch && $state.linked && !unlinking"
ng-dblclick="unlink()"
@ -62,65 +117,9 @@
</div>
</form>
</div>
<div class="button-group">
<kbn-tooltip text="New Visualization" placement="bottom" append-to-body="1">
<button ng-click="startOver()" aria-label="New Visualization"><i aria-hidden="true" class="fa fa-file-new-o"></i></button>
</kbn-tooltip>
<kbn-tooltip text="Save Visualization" placement="bottom" append-to-body="1">
<!-- normal save -->
<button
ng-class="{active: configTemplate.is('save')}"
ng-click="configTemplate.toggle('save')"
ng-if="!editableVis.dirty"
aria-expanded="{{ configTemplate.is('save') }}"
aria-label="Save Visualization">
<i aria-hidden="true" class="fa fa-save"></i>
</button>
<!-- save stub with tooltip -->
<button disabled ng-if="editableVis.dirty" tooltip="Apply or Discard your changes before saving" aria-label="Apply or Discard your changes before saving">
<i aria-hidden="true" class="fa fa-save"></i>
</button>
</kbn-tooltip>
<kbn-tooltip text="Load Saved Visualization" placement="bottom" append-to-body="1">
<button
ng-class="{active: configTemplate.is('load')}"
ng-click="configTemplate.toggle('load')"
aria-haspopup="true"
aria-expanded="{{ configTemplate.is('load') }}"
aria-label="Load Saved Visualization">
<i aria-hidden="true" class="fa fa-folder-open-o"></i>
</button>
</kbn-tooltip>
<kbn-tooltip text="Share Visualization" placement="bottom" append-to-body="1">
<button
ng-class="{active: configTemplate.is('share')}"
ng-click="configTemplate.toggle('share')"
aria-haspopup="true"
aria-expanded="{{ configTemplate.is('share') }}"
aria-label="Share Visualization">
<i aria-hidden="true" class="fa fa-external-link"></i>
</button>
</kbn-tooltip>
<kbn-tooltip text="Refresh" placement="bottom" append-to-body="1">
<button
ng-click="fetch()"
aria-label="Refresh">
<i aria-hidden="true" class="fa fa-refresh"></i>
</button>
</kbn-tooltip>
</div>
<div class="button-group"></div>
</navbar>
<config
ng-if="chrome.getVisible()"
config-template="configTemplate"
config-object="conf">
</config>
<filter-bar state="state"></filter-bar>
<div class="vis-editor-content">
@ -130,13 +129,6 @@
</div>
<div class="vis-editor-canvas" ng-class="{ embedded: !chrome.getVisible() }">
<div class="visualize-info" ng-if="savedVis.id">
<div class="visualize-info-tab" title="{{::savedVis.vis.type.title}}">
<i class="fa" aria-label="{{::savedVis.vis.type.title}} Icon" ng-class="savedVis.vis.type.icon"></i>
<span ng-bind="::savedVis.title"></span>
</div>
</div>
<visualize
vis="vis"
ui-state="uiState"

View file

@ -85,6 +85,8 @@ uiModules
save: require('plugins/kibana/visualize/editor/panels/save.html'),
load: require('plugins/kibana/visualize/editor/panels/load.html'),
share: require('plugins/kibana/visualize/editor/panels/share.html'),
filter: require('ui/chrome/config/filter.html'),
interval: require('ui/chrome/config/interval.html')
});
if (savedVis.id) {
@ -125,8 +127,8 @@ uiModules
$scope.editableVis = editableVis;
$scope.state = $state;
$scope.uiState = $state.makeStateful('uiState');
$scope.conf = _.pick($scope, 'doSave', 'savedVis', 'shareData');
$scope.timefilter = timefilter;
$scope.opts = _.pick($scope, 'doSave', 'savedVis', 'shareData', 'timefilter');
$scope.configTemplate = configTemplate;
editableVis.listeners.click = vis.listeners.click = filterBarClickHandler($state);

View file

@ -1 +1 @@
<saved-object-finder type="visualizations"></saved-object-finder>
<saved-object-finder type="visualizations"></saved-object-finder>

View file

@ -1,7 +1,7 @@
<form role="form" ng-submit="conf.doSave()">
<form role="form" ng-submit="opts.doSave()">
<div class="form-group">
<label for="visTitle">Title</label>
<input class="form-control" input-focus="select" type="text" name="visTitle" ng-model="conf.savedVis.title" required>
<input class="form-control" input-focus="select" type="text" name="visTitle" ng-model="opts.savedVis.title" required>
</div>
<button type="submit" class="btn btn-primary">Save</button>
</form>
</form>

View file

@ -1,4 +1,4 @@
<share
object-type="visualization"
object-id="{{conf.savedVis.id}}">
object-id="{{opts.savedVis.id}}">
</share>

View file

@ -6,11 +6,40 @@
@vis-editor-nesting-width: 8px;
@vis-editor-agg-editor-spacing: 5px;
&-info {
line-height: 30px;
padding: 0px 10px;
border-bottom-left-radius: @border-radius-base;
&-title {
font-weight: bold;
margin-right: 10px;
}
}
// For the vis-editor sidebar nav
.navbar-default .navbar-nav {
&> .active > a:before {
border: 7px solid transparent;
border-bottom-color: @vis-editor-navbar-current-tab-color;
&> .active > a {
border-bottom: 2px solid @kibanaGray2;
color: @kibanaGray1;
&:before {
display: none;
}
&:hover {
background-color: transparent;
}
}
> li {
> a {
padding: 4px 0;
margin: 0 10px;
color: @kibanaGray2;
}
> a:hover {
border-bottom: 2px solid @kibanaGray2;
}
}
.danger {
@ -55,6 +84,7 @@
}
}
&-content {
.flex-parent();
z-index: 0;
@ -79,7 +109,9 @@
max-width: @vis-editor-sidebar-min-width;
}
.index-pattern,
nav {
min-height: @app-icon-height;
border-radius: 0px;
}
@ -115,19 +147,13 @@
}
.sidebar-item-title {
font-size: 20px;
font-size: 14px;
font-weight: bold;
border: inherit !important;
background-color: @vis-editor-sidebar-title-bg;
margin-bottom: @vis-editor-agg-editor-spacing;
padding: 2px 5px !important;
}
.sidebar-item-title:hover {
color: @vis-editor-sidebar-title-hover-color !important;
background-color: @vis-editor-sidebar-title-hover-bg !important;
}
.hintbox {
padding: @vis-editor-agg-editor-spacing;
margin-bottom: @vis-editor-agg-editor-spacing;
@ -178,7 +204,7 @@
&-group {
.flex-parent(0, 1, auto);
color: @vis-editor-agg-group-color;
color: @kibanaGray2;
}
&-header {
@ -275,9 +301,7 @@
}
&-wide-btn {
border-radius: 0;
border-top: 2px solid;
border-top-color: @vis-editor-agg-wide-btn-border;
text-align: center;
&-add {
width: 140px;

View file

@ -3,57 +3,99 @@
@import (reference) "~ui/styles/list-group-menu";
.vis-wizard {
h1 {
margin-top: 45px;
}
}
margin-right: 0;
margin-left: 0;
padding-left: 0;
padding-right: 0;
.wizard-vis-type {
.list-group-item();
.list-group-menu .list-group-menu-item();
@media (min-width: @screen-lg) {
.wizard {
padding: 0;
display: flex;
// overrided for tablet and desktop
@media (min-width: @screen-md-min) {
display: flex;
align-items: flex-start;
}
.wizard-column {
flex: 1;
display: flex;
flex-direction: column;
padding: 0px 2.5px;
&-heading {
flex: 0 0 200px;
display: flex;
align-items: center;
font-size: 1.2em;
.fa {
flex: 0 0 auto;
margin-right: @padding-base-horizontal;
font-size: 1.5em;
text-align: center;
}
h4 {
flex: 1 0 auto;
.wizard-row {
flex: 1;
background-color: @kibanaGray6;
}
}
}
}
&-description {
flex: 1 1 auto;
color: @wizard-vis-type-description-color;
}
}
.visualize-info {
align-self: flex-end;
&-tab {
background-color: @visualize-info-bg;
padding: 5px 10px;
margin-left: @padding-base-horizontal;
border-bottom-left-radius: @border-radius-base;
text-align: right;
font-weight: bold;
h3 {
margin-top: 0px;
margin-bottom: 8px;
padding: 0px 5px;
}
.wizard-row {
.panel {
margin-bottom: 0;
border: none;
}
.panel-default > .panel-heading {
background-color: @kibanaGray6;
}
.list-group {
margin-bottom: 0;
.list-group-item {
border-radius: 0;
border: none;
}
}
.striped {
li:nth-child(odd) {
background-color: @white;
}
li:nth-child(even) {
background-color: @kibanaGray6;
}
}
}
.wizard-type {
flex: 1;
.list-group-item();
.list-group-menu .list-group-menu-item();
border: none;
border-radius: 0;
background-color: @kibanaGray6;
&-heading {
flex: 0 0 200px;
display: flex;
align-items: center;
font-size: 1.2em;
.fa {
flex: 0 0 auto;
margin-right: @padding-base-horizontal;
font-size: 1.5em;
text-align: center;
color: @saved-object-finder-icon-color;
}
h4 {
flex: 1 0 auto;
}
}
&-description {
flex: 1 1 auto;
color: @wizard-vis-type-description-color;
}
}
}
@import "../editor/styles/_editor.less";

View file

@ -1,24 +1,23 @@
<h1>
Create a new visualization <br class="visible-xs">
<span class="pull-right label label-default hidden-xs">Step 1</span>
<span class="label label-default visible-xs">Step 1</span>
</h1>
<div class="wizard-vis-types">
<a class="wizard-vis-type"
ng-repeat="type in visTypes.inTitleOrder"
ng-href="{{ visTypeUrl(type) }}">
<div class="wizard-vis-type-heading">
<i aria-hidden="true" class="fa fa-fw" ng-class="type.icon"></i>
<h4>{{type.title}}</h4>
<bread-crumbs></bread-crumbs>
<div class="wizard">
<div class="wizard-column">
<h3>Create New Visualization</h3>
<div class="wizard-row">
<a class="wizard-type" ng-repeat="type in visTypes.inTitleOrder" ng-href="{{ visTypeUrl(type) }}">
<div class="wizard-type-heading">
<i aria-hidden="true" class="fa fa-fw" ng-class="type.icon"></i>
<h4>{{type.title}}</h4>
</div>
<p class="wizard-type-description">{{type.description}}</p>
</a>
</div>
<p class="wizard-vis-type-description">{{type.description}}</p>
</a>
</div>
<div class="wizard-column">
<h3>Or, Open a Saved Visualization</h3>
<saved-object-finder
title="Saved Visualizations"
type="visualizations"
class="wizard-row">
</saved-object-finder>
</div>
</div>
<h1>Or, open a saved visualization</h1>
<saved-object-finder
title="Saved Visualizations"
type="visualizations">
</saved-object-finder>

View file

@ -1,37 +1,27 @@
<h1>
Select a search source
<span class="pull-right label label-default hidden-xs">Step 2</span>
<span class="label label-default visible-xs">Step 2</span>
</h1>
<ul class="list-group list-group-menu" ng-switch on="stepTwoMode">
<li ng-click="stepTwoMode='new'"
ng-class="{'active': stepTwoMode=='new'}"
class="list-group-item list-group-menu-item">
From a new search
</li>
<li class="list-group-item" ng-switch-when="new">
<bread-crumbs></bread-crumbs>
<div class="wizard">
<div class="wizard-column">
<h3>From a New Search</h3>
<!-- Index patterns -->
Select an index pattern
<div class="form-group">
<select
class="form-control"
ng-model="indexPattern.selection"
ng-options="pattern as pattern for pattern in indexPattern.list | orderBy:'toString()'">
</select>
<div class="wizard-row">
<div class="panel panel-default">
<div class="panel-heading">Index Patterns</div>
</div>
<ul class="striped list-group">
<li class="list-group-item" ng-repeat="pattern in indexPattern.list | orderBy: 'toString()'">
<a class="index-link" kbn-href="{{ makeUrl(pattern) }}">{{pattern}}</a>
</li>
</ul>
</div>
</li>
<li ng-click="stepTwoMode='saved'"
ng-class="{'active': stepTwoMode=='saved'}"
class="list-group-item list-group-menu-item">
From a saved search
</li>
<li class="list-group-item" ng-switch-when="saved">
</div>
<div class="wizard-column">
<h3>Or, From a Saved Search</h3>
<!-- Saved searches -->
<saved-object-finder
title="Saved Searches"
type="searches"
make-url="step2WithSearchUrl">
title="Saved Searches"
type="searches"
class="wizard-row"
make-url="step2WithSearchUrl">
</saved-object-finder>
</li>
</ul>
</div>
</div>

View file

@ -8,7 +8,7 @@ import uiModules from 'ui/modules';
const templateStep = function (num, txt) {
return '<div ng-controller="VisualizeWizardStep' + num + '" class="container vis-wizard">' + txt + '</div>';
return '<div ng-controller="VisualizeWizardStep' + num + '" class="container-fluid vis-wizard">' + txt + '</div>';
};
const module = uiModules.get('app/visualize', ['kibana/courier']);
@ -56,16 +56,8 @@ module.controller('VisualizeWizardStep2', function ($route, $scope, $location, t
list: $route.current.locals.indexPatternIds
};
$scope.$watch('stepTwoMode', function (mode) {
if (mode === 'new') {
if ($scope.indexPattern.list && $scope.indexPattern.list.length === 1) {
$scope.indexPattern.selection = $scope.indexPattern.list[0];
}
}
});
$scope.$watch('indexPattern.selection', function (pattern) {
$scope.makeUrl = function (pattern) {
if (!pattern) return;
kbnUrl.change('/visualize/create?type={{type}}&indexPattern={{pattern}}', {type: type, pattern: pattern});
});
return `#/visualize/create?type=${type}&indexPattern=${pattern}`;
};
});

View file

@ -16,6 +16,5 @@
.metric-container {
text-align: center;
padding: 1em;
}
}

View file

@ -7,13 +7,6 @@ import 'plugins/status_page/status_page.less';
const chrome = require('ui/chrome')
.setTabs([
{
id: '',
title: 'Server Status',
activeIndicatorColor: '#EFF0F2'
}
])
.setRootTemplate(require('plugins/status_page/status_page.html'))
.setRootController('ui', function ($http, $scope) {
const ui = this;
@ -54,3 +47,8 @@ const chrome = require('ui/chrome')
ui.refresh();
});
require('ui/modules').get('kibana')
.config(function (appSwitcherEnsureNavigationProvider) {
appSwitcherEnsureNavigationProvider.forceNavigation(true);
});

View file

@ -43,13 +43,36 @@ describe('lib/config/config', function () {
describe('constructor', function () {
it('should not allow any config if the schema is not passed', function (done) {
it('should not allow any config if the schema is not passed', function () {
var config = new Config();
var run = function () {
config.set('something.enable', true);
};
expect(run).to.throwException();
done();
});
it('should allow keys in the schema', function () {
var config = new Config(schema);
var run = function () {
config.set('test.client.host', 'http://0.0.0.0');
};
expect(run).to.not.throwException();
});
it('should not allow keys not in the schema', function () {
var config = new Config(schema);
var run = function () {
config.set('paramNotDefinedInTheSchema', true);
};
expect(run).to.throwException();
});
it('should not allow child keys not in the schema', function () {
var config = new Config(schema);
var run = function () {
config.set('test.client.paramNotDefinedInTheSchema', true);
};
expect(run).to.throwException();
});
it('should set defaults', function () {
@ -198,6 +221,14 @@ describe('lib/config/config', function () {
expect(config.get('myTest.test')).to.be(true);
});
it('should allow you to extend the schema with a prefix', function () {
var newSchema = Joi.object({ test: Joi.boolean().default(true) }).default();
config.extendSchema('prefix.myTest', newSchema);
expect(config.get('prefix')).to.eql({ myTest: { test: true }});
expect(config.get('prefix.myTest')).to.eql({ test: true });
expect(config.get('prefix.myTest.test')).to.be(true);
});
it('should NOT allow you to extend the schema if somethign else is there', function () {
var newSchema = Joi.object({ test: Joi.boolean().default(true) }).default();
var run = function () {

View file

@ -0,0 +1,83 @@
import unset from '../unset';
import expect from 'expect.js';
describe('unset(obj, key)', function () {
describe('invalid input', function () {
it('should do nothing if not given an object', function () {
const obj = 'hello';
unset(obj, 'e');
expect(obj).to.equal('hello');
});
it('should do nothing if not given a key', function () {
const obj = { one: 1 };
unset(obj);
expect(obj).to.eql({ one: 1 });
});
it('should do nothing if given an empty string as a key', function () {
const obj = { one: 1 };
unset(obj, '');
expect(obj).to.eql({ one: 1 });
});
});
describe('shallow removal', function () {
let obj;
beforeEach(function () {
obj = { one: 1, two: 2, deep: { three: 3, four: 4 } };
});
it('should remove the param using a string key', function () {
unset(obj, 'two');
expect(obj).to.eql({ one: 1, deep: { three: 3, four: 4 } });
});
it('should remove the param using an array key', function () {
unset(obj, ['two']);
expect(obj).to.eql({ one: 1, deep: { three: 3, four: 4 } });
});
});
describe('deep removal', function () {
let obj;
beforeEach(function () {
obj = { one: 1, two: 2, deep: { three: 3, four: 4 } };
});
it('should remove the param using a string key', function () {
unset(obj, 'deep.three');
expect(obj).to.eql({ one: 1, two: 2, deep: { four: 4 } });
});
it('should remove the param using an array key', function () {
unset(obj, ['deep', 'three']);
expect(obj).to.eql({ one: 1, two: 2, deep: { four: 4 } });
});
});
describe('recursive removal', function () {
it('should clear object if only value is removed', function () {
const obj = { one: { two: { three: 3 } } };
unset(obj, 'one.two.three');
expect(obj).to.eql({});
});
it('should clear object if no props are left', function () {
const obj = { one: { two: { three: 3 } } };
unset(obj, 'one.two');
expect(obj).to.eql({});
});
it('should remove deep property, then clear the object', function () {
const obj = { one: { two: { three: 3, four: 4 } } };
unset(obj, 'one.two.three');
expect(obj).to.eql({ one: { two: { four: 4 } } });
unset(obj, 'one.two.four');
expect(obj).to.eql({});
});
});
});

View file

@ -2,13 +2,13 @@ import Promise from 'bluebird';
import Joi from 'joi';
import _ from 'lodash';
import override from './override';
import unset from './unset';
import createDefaultSchema from './schema';
import pkg from '../../utils/package_json';
import clone from './deep_clone_with_buffers';
import { zipObject } from 'lodash';
const schema = Symbol('Joi Schema');
const schemaKeys = Symbol('Schema Extensions');
const schemaExts = Symbol('Schema Extensions');
const vals = Symbol('config values');
const pendingSets = Symbol('Pending Settings');
@ -18,16 +18,15 @@ module.exports = class Config {
}
constructor(initialSchema, initialSettings) {
this[schemaKeys] = new Map();
this[schemaExts] = Object.create(null);
this[vals] = Object.create(null);
this[pendingSets] = new Map(_.pairs(clone(initialSettings || {})));
this[pendingSets] = _.merge(Object.create(null), initialSettings || {});
if (initialSchema) this.extendSchema(initialSchema);
}
getPendingSets() {
return this[pendingSets];
return new Map(_.pairs(this[pendingSets]));
}
extendSchema(key, extension) {
@ -41,27 +40,27 @@ module.exports = class Config {
throw new Error(`Config schema already has key: ${key}`);
}
this[schemaKeys].set(key, extension);
_.set(this[schemaExts], key, extension);
this[schema] = null;
let initialVals = this[pendingSets].get(key);
let initialVals = _.get(this[pendingSets], key);
if (initialVals) {
this.set(key, initialVals);
this[pendingSets].delete(key);
unset(this[pendingSets], key);
} else {
this._commit(this[vals]);
}
}
removeSchema(key) {
if (!this[schemaKeys].has(key)) {
if (!_.has(this[schemaExts], key)) {
throw new TypeError(`Unknown schema key: ${key}`);
}
this[schema] = null;
this[schemaKeys].delete(key);
this[pendingSets].delete(key);
delete this[vals][key];
unset(this[schemaExts], key);
unset(this[pendingSets], key);
unset(this[vals], key);
}
resetTo(obj) {
@ -138,7 +137,7 @@ module.exports = class Config {
// Catch the partial paths
if (path.join('.') === key) return true;
// Only go deep on inner objects with children
if (schema._inner.children.length) {
if (_.size(schema._inner.children)) {
for (let i = 0; i < schema._inner.children.length; i++) {
let child = schema._inner.children[i];
// If the child is an object recurse through it's children and return
@ -163,8 +162,22 @@ module.exports = class Config {
getSchema() {
if (!this[schema]) {
let objKeys = zipObject([...this[schemaKeys]]);
this[schema] = Joi.object().keys(objKeys).default();
this[schema] = (function convertToSchema(children) {
let schema = Joi.object().keys({}).default();
for (const key of Object.keys(children)) {
const child = children[key];
const childSchema = _.isPlainObject(child) ? convertToSchema(child) : child;
if (!childSchema || !childSchema.isJoi) {
throw new TypeError('Unable to convert configuration definition value to Joi schema: ' + childSchema);
}
schema = schema.keys({ [key]: childSchema });
}
return schema;
}(this[schemaExts]));
}
return this[schema];

View file

@ -0,0 +1,26 @@
import _ from 'lodash';
import toPath from 'lodash/internal/toPath';
module.exports = function unset(object, rawPath) {
if (!object) return;
const path = toPath(rawPath);
switch (path.length) {
case 0:
return;
case 1:
delete object[rawPath];
break;
default:
const leaf = path.pop();
const parentPath = path.slice();
const parent = _.get(object, parentPath);
unset(parent, leaf);
if (!_.size(parent)) {
unset(object, parentPath);
}
break;
}
};

View file

@ -1,41 +1,87 @@
import expect from 'expect.js';
import * as kbnTestServer from '../../../../test/utils/kbn_server';
import fromRoot from '../../../utils/from_root';
describe('routes', function () {
this.slow(10000);
this.timeout(60000);
describe('cookie validation', function () {
let kbnServer;
beforeEach(function () {
kbnServer = kbnTestServer.createServer();
kbnServer = kbnTestServer.createServer({
plugins: {
scanDirs: [
fromRoot('src/plugins')
]
}
});
return kbnServer.ready();
});
afterEach(function () {
return kbnServer.close();
});
it('allows non-strict cookies', function (done) {
const options = {
method: 'GET',
url: '/',
headers: {
cookie: 'test:80=value;test_80=value'
}
};
kbnTestServer.makeRequest(kbnServer, options, (res) => {
expect(res.payload).not.to.contain('Invalid cookie header');
done();
describe('cookie validation', function () {
it('allows non-strict cookies', function (done) {
const options = {
method: 'GET',
url: '/',
headers: {
cookie: 'test:80=value;test_80=value'
}
};
kbnTestServer.makeRequest(kbnServer, options, (res) => {
expect(res.payload).not.to.contain('Invalid cookie header');
done();
});
});
it('returns an error if the cookie can\'t be parsed', function (done) {
const options = {
method: 'GET',
url: '/',
headers: {
cookie: 'a'
}
};
kbnTestServer.makeRequest(kbnServer, options, (res) => {
expect(res.payload).to.contain('Invalid cookie header');
done();
});
});
});
it('returns an error if the cookie can\'t be parsed', function (done) {
const options = {
method: 'GET',
url: '/',
headers: {
cookie: 'a'
describe('url shortener', () => {
const shortenOptions = {
method: 'POST',
url: '/shorten',
payload: {
url: '/app/kibana#/visualize/create'
}
};
kbnTestServer.makeRequest(kbnServer, options, (res) => {
expect(res.payload).to.contain('Invalid cookie header');
done();
it('generates shortened urls', (done) => {
kbnTestServer.makeRequest(kbnServer, shortenOptions, (res) => {
expect(typeof res.payload).to.be('string');
expect(res.payload.length > 0).to.be(true);
done();
});
});
it('redirects shortened urls', (done) => {
kbnTestServer.makeRequest(kbnServer, shortenOptions, (res) => {
const gotoOptions = {
method: 'GET',
url: '/goto/' + res.payload
};
kbnTestServer.makeRequest(kbnServer, gotoOptions, (res) => {
expect(res.statusCode).to.be(302);
expect(res.headers.location).to.be(shortenOptions.payload.url);
done();
});
});
});
});
});

View file

@ -20,6 +20,9 @@ module.exports = class KbnServer {
require('./logging'),
require('./status'),
// writes pid file
require('./pid'),
// find plugins and set this.plugins
require('./plugins/scan'),
@ -74,9 +77,8 @@ module.exports = class KbnServer {
await this.ready();
await fromNode(cb => server.start(cb));
await require('./pid')(this, server, config);
server.log(['listening', 'info'], 'Server running at ' + server.info.uri);
server.log(['listening', 'info'], `Server running at ${server.info.uri}`);
return server;
}

View file

@ -1,4 +1,5 @@
import _ from 'lodash';
import toPath from 'lodash/internal/toPath';
import Joi from 'joi';
import Bluebird, { attempt, fromNode } from 'bluebird';
import { basename, resolve } from 'path';
@ -37,6 +38,8 @@ const defaultConfigSchema = Joi.object({
* @param {String} [opts.version=pkg.version] - the version of this plugin
* @param {Function} [opts.init] - A function that will be called to initialize
* this plugin at the appropriate time.
* @param {Function} [opts.configPrefix=this.id] - The prefix to use for configuration
* values in the main configuration service
* @param {Function} [opts.config] - A function that produces a configuration
* schema using Joi, which is passed as its
* first argument.
@ -57,6 +60,7 @@ module.exports = class Plugin {
this.requiredIds = opts.require || [];
this.version = opts.version || pkg.version;
this.externalInit = opts.init || _.noop;
this.configPrefix = opts.configPrefix || this.id;
this.getConfigSchema = opts.config || _.noop;
this.init = _.once(this.init);
this[extendInitFns] = [];
@ -86,18 +90,18 @@ module.exports = class Plugin {
async readConfig() {
let schema = await this.getConfigSchema(Joi);
let { config } = this.kbnServer;
config.extendSchema(this.id, schema || defaultConfigSchema);
config.extendSchema(this.configPrefix, schema || defaultConfigSchema);
if (config.get([this.id, 'enabled'])) {
if (config.get([...toPath(this.configPrefix), 'enabled'])) {
return true;
} else {
config.removeSchema(this.id);
config.removeSchema(this.configPrefix);
return false;
}
}
async init() {
let { id, version, kbnServer } = this;
let { id, version, kbnServer, configPrefix } = this;
let { config } = kbnServer;
// setup the hapi register function and get on with it
@ -132,7 +136,7 @@ module.exports = class Plugin {
await fromNode(cb => {
kbnServer.server.register({
register: register,
options: config.has(id) ? config.get(id) : null
options: config.has(configPrefix) ? config.get(configPrefix) : null
}, cb);
});

View file

@ -62,7 +62,7 @@ module.exports = async (kbnServer, server, config) => {
server.decorate('reply', 'renderApp', function (app) {
const payload = {
app: app,
nav: uiExports.apps,
nav: uiExports.navLinks.inOrder,
version: kbnServer.version,
buildNum: config.get('pkg.buildNum'),
buildSha: config.get('pkg.buildSha'),

View file

@ -1,8 +1,8 @@
<table>
<tbody>
<tr ng-repeat="detail in details" >
<td><b>{{detail.label}}</b></td>
<td>{{detail.value}}</td>
<td class="tooltip-label"><b>{{detail.label}}</b></td>
<td class="tooltip-value">{{detail.value}}</td>
</tr>
</tbody>
</table>

View file

@ -1,16 +1,16 @@
<table>
<thead>
<tr>
<tr class="tooltip-label">
<th>field</th>
<th>value</th>
<th>{{metricCol.label}}</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="row in rows track by $index">
<tr class="tooltip-value" ng-repeat="row in rows track by $index">
<td><span ng-bind-html="row.spacer"></span>{{row.field}}</td>
<td class="row-bucket">{{row.bucket}}</td>
<td>{{row.metric}}</td>
</tr>
</tbody>
</table>
</table>

View file

@ -1,11 +1,11 @@
<table>
<tbody>
<tr ng-repeat="detail in details" >
<td><b>{{detail.label}}</b></td>
<td>
<td class="tooltip-label"><b>{{detail.label}}</b></td>
<td class="tooltip-value">
{{detail.value}}
<span ng-if="detail.percent"> ({{detail.percent}})</span>
</td>
</tr>
</tbody>
</table>
</table>

View file

@ -4,6 +4,7 @@ export default class TabFakeStore {
constructor() { this[store] = new Map(); }
getItem(k) { return this[store].get(k); }
setItem(k, v) { return this[store].set(k, v); }
removeItem(k) { return this[store].delete(k); }
getKeys() { return [ ...this[store].keys() ]; }
getValues() { return [ ...this[store].values() ]; }
}

View file

@ -1,3 +1,4 @@
import sinon from 'auto-release-sinon';
import Tab from '../tab';
import expect from 'expect.js';
import TabFakeStore from './_tab_fake_store';
@ -89,13 +90,24 @@ describe('Chrome Tab', function () {
it('discovers the lastUrl', function () {
const lastUrlStore = new TabFakeStore();
const tab = new Tab({ id: 'foo', lastUrlStore });
expect(tab.lastUrl).to.not.equal('bar');
expect(tab.lastUrl).to.not.equal('/foo/bar');
tab.setLastUrl('bar');
expect(tab.lastUrl).to.equal('bar');
tab.setLastUrl('/foo/bar');
expect(tab.lastUrl).to.equal('/foo/bar');
const tab2 = new Tab({ id: 'foo', lastUrlStore });
expect(tab2.lastUrl).to.equal('bar');
expect(tab2.lastUrl).to.equal('/foo/bar');
});
it('logs a warning about last urls that do not match the rootUrl', function () {
const lastUrlStore = new TabFakeStore();
const tab = new Tab({ id: 'foo', baseUrl: '/bar', lastUrlStore });
tab.setLastUrl('/bar/foo/1');
const stub = sinon.stub(console, 'log');
const tab2 = new Tab({ id: 'foo', baseUrl: '/baz', lastUrlStore });
sinon.assert.calledOnce(stub);
expect(tab2.lastUrl).to.equal(null);
});
});
@ -167,14 +179,14 @@ describe('Chrome Tab', function () {
expect(tab.getLastPath()).to.equal('/index');
});
it('throws an error if the lastUrl does not extend the root url', function () {
expect(function () {
const baseUrl = 'http://local:5601/app/visualize#';
const tab = new Tab({ baseUrl });
it('logs a warning if the lastUrl does not extend the root url', function () {
const baseUrl = 'http://local:5601/app/visualize#';
const tab = new Tab({ baseUrl });
sinon.stub(console, 'log');
tab.setLastUrl('http://local:5601/');
tab.getLastPath();
}).to.throwError(/invalid.*root/);
tab.setLastUrl('http://local:5601/');
tab.getLastPath();
sinon.assert.calledOnce(console.log);// eslint-disable-line no-console
});
});

View file

@ -0,0 +1,19 @@
import expect from 'expect.js';
import kbnAngular from '../angular';
import TabFakeStore from '../../__tests__/_tab_fake_store';
import { noop } from 'lodash';
describe('Chrome API :: Angular', () => {
describe('location helper methods', () => {
it('should return the sub app based on the url', () => {
const chrome = {
getInjected: noop,
addBasePath: noop
};
kbnAngular(chrome, {});
});
it('should return breadcrumbs based on the url', () => {
});
});
});

View file

@ -86,11 +86,11 @@ describe('Chrome API :: apps', function () {
describe('#getAppUrl()', function () {
it('returns the resolved url of the current app', function () {
const chrome = {};
const app = { url: '/foo' };
const app = { navLink: { url: '/foo' } };
setup(chrome, { app });
const a = document.createElement('a');
a.setAttribute('href', app.url);
a.setAttribute('href', app.navLink.url);
expect(chrome.getAppUrl()).to.equal(a.href);
});

View file

@ -3,6 +3,9 @@ import modules from 'ui/modules';
module.exports = function (chrome, internals) {
chrome.getFirstPathSegment = _.noop;
chrome.getBreadcrumbs = _.noop;
chrome.setupAngular = function () {
var kibana = modules.get('kibana');
@ -15,12 +18,22 @@ module.exports = function (chrome, internals) {
.value('buildNum', internals.buildNum)
.value('buildSha', internals.buildSha)
.value('sessionId', Date.now())
.value('chrome', chrome)
.value('esUrl', (function () {
var a = document.createElement('a');
a.href = chrome.addBasePath('/elasticsearch');
return a.href;
}()))
.config(chrome.$setupXsrfRequestInterceptor);
.config(chrome.$setupXsrfRequestInterceptor)
.run(($location) => {
chrome.getFirstPathSegment = () => {
return $location.path().split('/')[1];
};
chrome.getBreadcrumbs = () => {
return $location.path().split('/').slice(1);
};
});
require('../directives')(chrome, internals);

View file

@ -3,8 +3,8 @@ import { resolve } from 'url';
module.exports = function (chrome, internals) {
if (internals.app) {
internals.app.url = resolve(window.location.href, internals.app.url);
if (get(internals, 'app.navLink.url')) {
internals.app.navLink.url = resolve(window.location.href, internals.app.navLink.url);
}
internals.appUrlStore = internals.appUrlStore || window.sessionStorage;
@ -35,7 +35,7 @@ module.exports = function (chrome, internals) {
};
chrome.getAppUrl = function () {
return get(internals, ['app', 'url']);
return get(internals, ['app', 'navLink', 'url']);
};
chrome.getInjected = function (name, def) {

View file

@ -1,15 +1,11 @@
import { parse, format } from 'url';
import { startsWith, isString } from 'lodash';
import { startsWith, isString, find } from 'lodash';
export default function (chrome, internals) {
chrome.getNavLinks = function () {
return internals.nav;
};
chrome.getLastSubUrlFor = function (url) {
return internals.appUrlStore.getItem(`lastSubUrl:${url}`);
};
chrome.getBasePath = function () {
return internals.basePath || '';
};
@ -34,26 +30,46 @@ export default function (chrome, internals) {
});
};
function lastSubUrlKey(link) {
return `lastSubUrl:${link.url}`;
}
function setLastUrl(link, url) {
link.lastSubUrl = url;
internals.appUrlStore.setItem(lastSubUrlKey(link), url);
}
function refreshLastUrl(link) {
link.lastSubUrl = internals.appUrlStore.getItem(lastSubUrlKey(link));
}
internals.trackPossibleSubUrl = function (url) {
for (const link of internals.nav) {
if (startsWith(url, link.url)) {
link.lastSubUrl = url;
internals.appUrlStore.setItem(`lastSubUrl:${link.url}`, url);
link.active = startsWith(url, link.url);
if (link.active) {
setLastUrl(link, url);
continue;
}
const matchingTab = find(internals.tabs, { rootUrl: link.url });
if (matchingTab) {
setLastUrl(link, matchingTab.getLastUrl());
continue;
}
refreshLastUrl(link);
}
};
internals.nav.forEach(link => {
// convert all link urls to absolute urls
var a = document.createElement('a');
a.setAttribute('href', link.url);
link.url = a.href;
link.lastSubUrl = chrome.getLastSubUrlFor(link.url);
if (link.url === chrome.getAppUrl()) {
link.active = true;
}
});
// simulate a possible change in url to initialize the
// link.active and link.lastUrl properties
internals.trackPossibleSubUrl(document.location.href);
};

View file

@ -76,12 +76,6 @@ module.exports = function (chrome, internals) {
return internals.tabs.getActive();
};
/**
* @param {any} def - the default value if there isn't any active tab
* @return {any}
*/
chrome.getActiveTabId = activeGetter('id');
/**
* @param {any} def - the default value if there isn't any active tab
* @return {any}

View file

@ -1,74 +1,86 @@
<kbn-notifications list="notifList"></kbn-notifications>
<div class="content" chrome-context >
<nav
ng-style="::{ background: chrome.getNavBackground() }"
ng-class="{ show: chrome.getVisible() }"
class="hide navbar navbar-inverse navbar-static-top">
<!-- TODO: These config dropdowns shouldn't be hard coded -->
<nav class="app-links-wrapper">
<li
ng-if="!chrome.getBrand('logo') && !chrome.getBrand('smallLogo')"
aria-label="{{ chrome.getAppTitle() }} Logo"
class="logo kibana hidden-sm"
></li>
<li
ng-if="chrome.getBrand('logo')"
ng-style="{ 'background': chrome.getBrand('logo') }"
aria-label="{{ chrome.getAppTitle() }} Logo"
class="logo hidden-sm"
></li>
<li
ng-if="chrome.getBrand('smallLogo')"
ng-style="{ 'background': chrome.getBrand('smallLogo') }"
aria-label="{{ chrome.getAppTitle() }} Logo"
class="logo-small visible-sm hidden-xs"
></li>
<!-- Mobile navbar -->
<div class="navbar-header">
<button ng-click="showCollapsed = !showCollapsed" type="button" class="navbar-toggle">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<span class="visible-xs">
<span ng-if="chrome.getBrand('title')" class="navbar-brand">{{ chrome.getBrand('title') }}</span>
<span ng-if="chrome.getActiveTabTitle()" class="navbar-brand">{{ chrome.getActiveTabTitle() }}</span>
<span ng-show="chrome.httpActive.length" class="spinner"></span>
</span>
<app-switcher>
</app-switcher>
<div class="bottom-apps hide app-links">
<div class="app-link">
<a href="http://elastic.co">
<div class="app-icon">
<i class="fa fa-gear"></i>
</div>
<div class="app-title">settings</div>
</a>
</div>
<div class="app-link">
<a href="http://elastic.co">
<div class="app-icon">
<i class="fa fa-user"></i>
</div>
<div class="app-title">Jon Doe</div>
<div class="app-title">Logout</div>
</a>
</div>
</div>
<!-- /Mobile navbar -->
<!-- Full navbar -->
<div collapse="!showCollapsed" class="navbar-collapse" kbn-chrome-append-nav-controls>
<ul class="nav navbar-nav" role="navigation">
<li
ng-if="chrome.getBrand('logo')"
ng-style="{ 'background': chrome.getBrand('logo') }"
aria-label="{{ chrome.getAppTitle() }} Logo"
class="logo hidden-sm"
></li>
<li
ng-if="chrome.getBrand('smallLogo')"
ng-style="{ 'background': chrome.getBrand('smallLogo') }"
aria-label="{{ chrome.getAppTitle() }} Logo"
class="logo-small visible-sm hidden-xs"
></li>
<li ng-if="chrome.getBrand('title')" class="navbar-brand">{{ chrome.getBrand('title') }}</li>
<li ng-repeat="tab in chrome.getTabs()" ng-class="{ active: tab.active }">
<a ng-href="{{ tab.href() }}" ng-style="{ 'border-bottom-color': tab.activeIndicatorColor }">
{{ tab.title }}
</a>
</li>
<li class="to-body" ng-class="{ active: appSwitcherTemplate.is('switcher') }" ng-if="chrome.getShowAppsLink()">
<a ng-click="appSwitcherTemplate.toggle('switcher')">
<i class="fa fa-th" alt="Show app switcher"></i>
</a>
</li>
</ul>
</div>
<!-- /Full navbar -->
</nav>
<!-- TODO: These config dropdowns shouldn't be hard coded -->
<config
config-template="appSwitcherTemplate"
config-object="chrome"
config-close="appSwitcherTemplate.close">
</config>
<div class="app-wrapper">
<div class="app-wrapper-panel">
<kbn-notifications list="notifList"></kbn-notifications>
<nav
ng-style="::{ background: chrome.getNavBackground() }"
ng-class="{ show: chrome.getTabs().length > 0 }"
class="hide navbar navbar-inverse navbar-static-top">
<config
ng-show="timefilter.enabled"
config-template="pickerTemplate"
config-object="timefilter"
config-close="pickerTemplate.close">
</config>
<!-- Mobile navbar -->
<div class="navbar-header">
<button ng-click="showCollapsed = !showCollapsed" type="button" class="navbar-toggle">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<span class="visible-xs">
<span ng-if="chrome.getBrand('title')" class="navbar-brand">{{ chrome.getBrand('title') }}</span>
<span ng-if="chrome.getActiveTabTitle()" class="navbar-brand">{{ chrome.getActiveTabTitle() }}</span>
</span>
</div>
<!-- /Mobile navbar -->
<div class="application" ng-class="'tab-' + chrome.getActiveTabId('-none-') + ' ' + chrome.getApplicationClasses()" ng-view></div>
<!-- Full navbar -->
<div collapse="!showCollapsed" class="navbar-collapse" kbn-chrome-append-nav-controls>
<div ng-if="chrome.getBrand('title')" class="navbar-brand">{{ chrome.getBrand('title') }}</div>
<ul class="nav navbar-nav" role="navigation">
<li ng-repeat="tab in chrome.getTabs()" ng-class="{ active: tab.active }">
<a ng-href="{{ tab.href() }}" ng-style="{ 'border-bottom-color': tab.activeIndicatorColor }">
{{ tab.title }}
</a>
</li>
</ul>
</div>
<!-- /Full navbar -->
</nav>
<div class="application" ng-class="'tab-' + chrome.getFirstPathSegment() + ' ' + chrome.getApplicationClasses()" ng-view></div>
</div>
</div>
</div>

View file

@ -9,6 +9,7 @@ import $ from 'jquery';
import 'ui/timefilter';
import 'ui/private';
import 'ui/promises';
import 'ui/directives/kbn_src';
var chrome = {};
var internals = _.defaults(

View file

@ -1,7 +1,7 @@
<kbn-timepicker
from="timefilter.time.from"
to="timefilter.time.to"
mode="timefilter.time.mode"
from="opts.timefilter.time.from"
to="opts.timefilter.time.to"
mode="opts.timefilter.time.mode"
active-tab="'filter'"
interval="timefilter.refreshInterval">
interval="opts.timefilter.refreshInterval">
</kbn-timepicker>

View file

@ -1,7 +1,7 @@
<kbn-timepicker
from="timefilter.time.from"
to="timefilter.time.to"
mode="timefilter.time.mode"
from="opts.timefilter.time.from"
to="opts.timefilter.time.to"
mode="opts.timefilter.time.mode"
active-tab="'interval'"
interval="timefilter.refreshInterval">
interval="opts.timefilter.refreshInterval">
</kbn-timepicker>

View file

@ -21,11 +21,6 @@ uiModules
// chrome is responsible for timepicker ui and state transfer...
$scope.timefilter = timefilter;
$scope.pickerTemplate = new ConfigTemplate({
filter: require('ui/chrome/config/filter.html'),
interval: require('ui/chrome/config/interval.html')
});
$scope.toggleRefresh = function () {
timefilter.refreshInterval.pause = !timefilter.refreshInterval.pause;
};

View file

@ -1,5 +1 @@
<ul class="nav navbar-nav navbar-right navbar-timepicker" >
<li ng-show="chrome.httpActive.length" class="navbar-text hidden-xs">
<div class="spinner"></div>
</li>
</ul>
<div class="spinner" ng-show="chrome.httpActive.length"></div>

View file

@ -1,7 +1,7 @@
<div class="app-links">
<div
class="app-link"
ng-repeat="link in switcher.getNavLinks() | orderBy:'title'"
ng-repeat="link in switcher.getNavLinks()"
ng-class="{ active: link.active }">
<a
@ -9,8 +9,8 @@
ng-href="{{ link.active ? link.url : (link.lastSubUrl || link.url) }}"
data-test-subj="appLink">
<div ng-if="link.icon" ng-style="{ 'background-image': 'url(../' + link.icon + ')' }" class="app-icon"></div>
<div ng-if="!link.icon" class="app-icon app-icon-missing">{{ link.title[0] }}</div>
<div ng-if="link.icon" class="app-icon"><img kbn-src="{{'/' + link.icon}}"></div>
<div ng-if="!link.icon" class="app-icon-missing">{{ link.title[0] }}</div>
<div class="app-title">{{ link.title }}</div>
</a>

View file

@ -1,4 +1,3 @@
import DomLocationProvider from 'ui/dom_location';
import { parse } from 'url';
import { bindKey } from 'lodash';
@ -8,14 +7,45 @@ import appSwitcherTemplate from './app_switcher.html';
uiModules
.get('kibana')
.provider('appSwitcherEnsureNavigation', function () {
let forceNavigation = false;
this.forceNavigation = function (val) {
forceNavigation = !!val;
};
this.$get = ['Private', function (Private) {
const domLocation = Private(DomLocationProvider);
return function (event) {
if (!forceNavigation || event.isDefaultPrevented() || event.altKey || event.metaKey || event.ctrlKey) {
return;
}
const toParsed = parse(event.delegateTarget.href);
const fromParsed = parse(domLocation.href);
const sameProto = toParsed.protocol === fromParsed.protocol;
const sameHost = toParsed.host === fromParsed.host;
const samePath = toParsed.path === fromParsed.path;
if (sameProto && sameHost && samePath) {
toParsed.hash && domLocation.reload();
// event.preventDefault() keeps the browser from seeing the new url as an update
// and even setting window.location does not mimic that behavior, so instead
// we use stopPropagation() to prevent angular from seeing the click and
// starting a digest cycle/attempting to handle it in the router.
event.stopPropagation();
}
};
}];
})
.directive('appSwitcher', function () {
return {
restrict: 'E',
template: appSwitcherTemplate,
controllerAs: 'switcher',
controller: function ($scope, Private) {
var domLocation = Private(DomLocationProvider);
controller($scope, appSwitcherEnsureNavigation) {
// since we render this in an isolate scope we can't "require: ^chrome", but
// rather than remove all helpfull checks we can just check here.
if (!$scope.chrome || !$scope.chrome.getNavLinks) {
@ -26,28 +56,7 @@ uiModules
// links don't cause full-navigation events in certain scenarios
// so we force them when needed
this.ensureNavigation = function (event, app) {
if (event.isDefaultPrevented() || event.altKey || event.metaKey || event.ctrlKey) {
return;
}
var toParsed = parse(event.delegateTarget.href);
var fromParsed = parse(domLocation.href);
var sameProto = toParsed.protocol === fromParsed.protocol;
var sameHost = toParsed.host === fromParsed.host;
var samePath = toParsed.path === fromParsed.path;
if (sameProto && sameHost && samePath) {
toParsed.hash && domLocation.reload();
// event.preventDefault() keeps the browser from seeing the new url as an update
// and even setting window.location does not mimic that behavior, so instead
// we use stopPropagation() to prevent angular from seeing the click and
// starting a digest cycle/attempting to handle it in the router.
event.stopPropagation();
}
};
this.ensureNavigation = appSwitcherEnsureNavigation;
}
};
});

View file

@ -1,55 +1,155 @@
@import (reference) "~ui/styles/mixins";
@import (reference) "~ui/styles/variables";
@app-icon-size: 48px;
@app-icon-padding: 10px;
body { overflow-x: hidden; }
.app-links-wrapper {
width: @as-open-width;
position: fixed;
left: 0;
top: 0;
bottom: 0;
z-index: 0;
background-color: @app-links-wrapper-background;
overflow: hidden;
transition: width @transition-time;
transition-delay: @transition-delay;
&:hover {
.app-title {
display: inline-block;
}
+ .app-wrapper {
transform: translateX(@as-open-width - @as-closed-width);
}
}
.logo {
height: 70px;
width: @as-open-width;
list-style-type: none;
&.kibana {
background-image: url("~ui/images/kibana.svg");
background-position: 6px 10px;
background-size: 140px 50px;
background-repeat: no-repeat;
background-color: #e8488b;
}
}
.bottom-apps {
position: absolute;
bottom: 0;
}
}
.app-wrapper {
.real-flex-parent();
position: absolute;
transition: transform @transition-time;
transition-delay: @transition-delay;
left: @as-closed-width;
top: 0;
right: 0;
bottom: 0;
z-index: 1;
margin: 0 auto;
background-color: #fff;
&-panel {
.flex-parent(@shrink: 0);
box-shadow: -4px 0px 3px rgba(0,0,0,0.2);
}
.navbar-right {
margin-right: 0;
}
}
.app-links {
text-align: justify;
.app-link {
display: inline-block;
vertical-align: top;
text-align: left;
width: @app-icon-size + (@app-icon-padding * 2);
margin: 0px 10px;
padding: @app-icon-padding;
border-radius: @border-radius-base;
width: @as-open-width;
height: @app-icon-height;
line-height: @app-line-height;
> a {
display: block;
height: 100%;
color: @white;
}
&:hover {
background-color: @app-links-active-background;
}
.app-icon {
display: block;
height: @app-icon-size;
width: @app-icon-size;
background-position: center;
background-size: contain;
border-radius: @border-radius-base;
background-color: @gray-light;
width: 100%;
float: left;
filter: invert(100%);
font-weight: bold;
text-align: center;
font-size: 1.7em;
display: inline-block;
height: @app-icon-height;
width: @as-closed-width;
&-missing {
text-align: center;
font-size: 2.7em;
font-weight: bold;
font-family: @font-family-sans-serif;
color: #fff;
> img {
height: 18px;
margin-top: 8px;
}
}
.app-title {
color: @text-color;
font-size: 0.9em;
width: 100%;
.app-icon-missing {
float: left;
text-align: center;
margin-top: 3px;
font-size: 1.7em;
display: inline-block;
height: @app-icon-height;
line-height: @app-icon-height;
width: @as-closed-width;
background-position: center;
background-size: contain;
background-repeat: no-repeat;
}
&:hover .app-title {
text-decoration: underline;
.app-title {
width: calc(@as-open-width - @as-closed-width);
display: inline-block;
float: right;
font-size: 0.9em;
text-align: left;
padding-left: 3px;
line-height: @app-icon-height;
}
&.active {
background: @gray-lighter;
.app-title {
text-decoration: underline;
background-color: @app-links-active-background;
> a {
color: #333;
text-decoration: none;
}
img {
filter: invert(100%);
}
}
&:nth-child(1) {
background-color: @firstLinkColor;
&:hover {
background-color: lighten(@firstLinkColor, 5%);
}
}
&:nth-child(2) {
background-color: @secondLinkColor;
&:hover {
background-color: lighten(@secondLinkColor, 5%);
}
}
&:nth-child(3) {
background-color: @thirdLinkColor;
&:hover {
background-color: lighten(@thirdLinkColor, 5%);
}
}

View file

@ -35,8 +35,8 @@ export default function (chrome, internals) {
const onRouteChange = function () {
let { href } = window.location;
let persist = chrome.getVisible();
internals.trackPossibleSubUrl(href);
internals.tabs.consumeRouteUpdate(href, persist);
internals.trackPossibleSubUrl(href);
};
$rootScope.$on('$routeChangeSuccess', onRouteChange);

View file

@ -1,3 +1,4 @@
import notify from 'ui/notify';
import _ from 'lodash';
import { escapeRegExp as reEsc } from 'lodash';
import { parse, format } from 'url';
@ -28,7 +29,16 @@ export default class Tab {
this.lastUrlStoreKey = `lastUrl:${this.id}`;
this.lastUrlStore = spec.lastUrlStore;
this.lastUrl = this.lastUrlStore ? this.lastUrlStore.getItem(this.lastUrlStoreKey) : null;
this.lastUrl = null;
if (this.lastUrlStore) {
this.lastUrl = this.lastUrlStore.getItem(this.lastUrlStoreKey);
if (this.lastUrl && !this.lastUrl.startsWith(this.rootUrl)) {
notify.log(`Found invalid lastUrl for tab with root url ${this.rootUrl}: "${this.lastUrl}"`);
this.lastUrl = null;
this.lastUrlStore.removeItem(this.lastUrlStoreKey);
}
}
}
href() {
@ -54,7 +64,8 @@ export default class Tab {
let lastUrl = this.getLastUrl();
if (!lastUrl.startsWith(rootUrl)) {
throw new Error(`Tab "${id}" has invalid root "${rootUrl}" for last url "${lastUrl}"`);
notify.log(`Tab "${id}" has invalid root "${rootUrl}" for last url "${lastUrl}"`);
lastUrl = rootUrl;
}
return lastUrl.slice(rootUrl.length);

View file

@ -5,8 +5,6 @@
z-index: 1;
.sidebar-collapser {
background-color: @collapser-bg;
height: 100%;
position: absolute;
top: 0;
right: -(@collapser-width);
@ -14,21 +12,15 @@
cursor: pointer;
z-index: -1;
&:hover {
background-color: @collapser-hover-bg;
border-color: @collapser-hover-bg;
color: @collapser-hover-color;
}
.chevron-cont{
position: absolute;
left: 2px;
top: 8px;
font-size: 10px;
left: 5px;
top: 5px;
color: @kibanaGray4;
&:before {
font-family: FontAwesome;
content: "\F053";
content: "\F137";
}
}
}
@ -45,7 +37,7 @@
.sidebar-collapser {
visibility: visible;
.chevron-cont:before {
content: "\F054";
content: "\F138";
}
}
}

View file

@ -2,7 +2,7 @@ import moment from 'moment-timezone';
import _ from 'lodash';
export default function configDefaultsProvider() {
// wraped in provider so that a new instance is given to each app/test
// wrapped in provider so that a new instance is given to each app/test
return {
'buildNum': {
@ -112,7 +112,7 @@ export default function configDefaultsProvider() {
'visualization:colorMapping': {
type: 'json',
value: JSON.stringify({
'Count': '#57c17b'
'Count': '#6eadc1'
}),
description: 'Maps values to specified colors within visualizations'
},

View file

@ -1,52 +0,0 @@
import angular from 'angular';
import expect from 'expect.js';
import ngMock from 'ng_mock';
import $ from 'jquery';
import 'plugins/kibana/discover/index';
var $parentScope;
var $scope;
var $elem;
var init = function () {
// Load the application
ngMock.module('kibana');
// Create the scope
ngMock.inject(function ($rootScope, $compile) {
// Give us a scope
$parentScope = $rootScope;
// Create the element
$elem = angular.element(
'<span class="spinner"></span>'
);
// And compile it
$compile($elem)($parentScope);
// Fire a digest cycle
$elem.scope().$digest();
// Grab the isolate scope so we can test it
$scope = $elem.isolateScope();
});
};
describe('spinner directive', function () {
beforeEach(function () {
init();
});
it('should contain 3 divs', function (done) {
expect($elem.children('div').length).to.be(3);
done();
});
});

View file

@ -0,0 +1,21 @@
import _ from 'lodash';
import chrome from 'ui/chrome/chrome';
import breadCrumbsTemplate from 'ui/partials/bread_crumbs.html';
import uiModules from 'ui/modules';
var module = uiModules.get('kibana');
module.directive('breadCrumbs', function () {
return {
restrict: 'E',
scope: true,
template: breadCrumbsTemplate,
controller: function ($scope) {
$scope.crumbs = chrome.getBreadcrumbs();
if (_.last($scope.crumbs) === '') {
// Remove the empty string from the end of the array
$scope.crumbs.pop();
}
}
};
});

View file

@ -57,8 +57,8 @@ module.directive('config', function ($compile) {
html = $compile('' +
'<div class="config" ng-show="configTemplate">' +
wrapTmpl(tmpl) +
' <div class="config-close remove" ng-click="close()">' +
' <i class="fa fa-chevron-up"></i>' +
' <div class="config-close remove">' +
' <i class="fa fa-chevron-circle-up" ng-click="close()"></i>' +
' </div>' +
'</div>' +
''

View file

@ -1,5 +1,4 @@
import UiModules from 'ui/modules';
import chrome from 'ui/chrome';
import { words, camelCase, kebabCase } from 'lodash';
export function kbnUrlDirective(name) {
@ -8,7 +7,7 @@ export function kbnUrlDirective(name) {
UiModules
.get('kibana')
.directive(name, function (Private) {
.directive(name, function (Private, chrome) {
return {
restrict: 'A',
link: function ($scope, $el, $attr) {

View file

@ -48,6 +48,23 @@ module.directive('savedObjectFinder', function ($location, $injector, kbnUrl, Pr
filterResults();
/**
* Boolean that keeps track of whether hits are sorted ascending (true)
* or descending (false) by title
* @type {Boolean}
*/
self.isAscending = true;
/**
* Sorts saved object finder hits either ascending or descending
* @param {Array} hits Array of saved finder object hits
* @return {Array} Array sorted either ascending or descending
*/
self.sortHits = function (hits) {
self.isAscending = !self.isAscending;
self.hits = self.isAscending ? _.sortBy(hits, 'title') : _.sortBy(hits, 'title').reverse();
};
/**
* Passed the hit objects and will determine if the
* hit should have a url in the UI, returns it if so

View file

@ -1,11 +0,0 @@
import uiModules from 'ui/modules';
var html = '<div class="bounce1"></div> <div class="bounce2"></div> <div class="bounce3"></div>';
var module = uiModules.get('kibana');
module.directive('spinner', function () {
return {
restrict: 'C',
template: html
};
});

View file

@ -47,13 +47,12 @@ filter-bar .confirm {
}
filter-bar .bar {
padding: 6px 6px 4px 6px;
padding: 5px 6px 3px 6px;
background: @filter-bar-bar-bg;
border-bottom: 1px solid;
border-bottom-color: @filter-bar-bar-border;
border-bottom: 0;
&-condensed {
padding: 2px 6px 0px 6px !important;
padding: 6px 6px 2px 6px !important;
font-size: 0.9em;
background: @filter-bar-bar-condensed-bg;
}

View file

@ -1,6 +1,4 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 16.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
@ -9,132 +7,104 @@
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
id="Layer_1"
x="0px"
y="0px"
width="252px"
height="45px"
viewBox="0 0 252 45"
enable-background="new 0 0 252 45"
xml:space="preserve"
data-name="Layer 1"
viewBox="0 0 141.37 51"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="kibana.svg"><metadata
id="metadata4270"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
id="defs4268" /><sodipodi:namedview
pagecolor="#ffffff"
sodipodi:docname="kibana.svg">
<metadata
id="metadata31">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title>Kibana-Full-Logo</dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<sodipodi:namedview
pagecolor="#515151"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageopacity="1"
inkscape:pageshadow="2"
inkscape:window-width="2046"
inkscape:window-height="1132"
id="namedview4266"
inkscape:window-width="1796"
inkscape:window-height="1079"
id="namedview29"
showgrid="false"
inkscape:zoom="1.0416667"
inkscape:cx="126"
inkscape:cy="22.5"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:zoom="2.0725756"
inkscape:cx="70.684998"
inkscape:cy="25.5"
inkscape:window-x="349"
inkscape:window-y="210"
inkscape:window-maximized="0"
inkscape:current-layer="Layer_1" /><font
horiz-adv-x="1000"
id="font4232"><!-- &quot;Helvetica Neue&quot; is a trademark of Heidelberger Druckmaschinen AG, which may be registered in certain jurisdictions, exclusivly licensed through Linotype Library GmbH, a wholly owned subsidiary of Heidelberger Druckmaschinen AG. --><font-face
font-family="HelveticaNeue-Bold"
units-per-em="1000"
underline-position="-100"
underline-thickness="50"
id="font-face4234" /><missing-glyph
horiz-adv-x="500"
d="M391,607l-280,0l0,-512l280,0M482,698l0,-695l-465,0l0,695z"
id="missing-glyph4236" /><glyph
unicode="a"
horiz-adv-x="574"
d="M48,358C50,391 58,419 73,441C88,463 106,481 129,494C152,507 177,517 206,523C234,528 262,531 291,531C317,531 343,529 370,526C397,522 421,515 443,504C465,493 483,479 497,460C511,441 518,415 518,384l0,-269C518,92 519,69 522,48C525,27 529,11 536,0l-144,0C389,8 387,16 386,25C384,33 383,41 382,50C359,27 333,10 302,1C271,-8 240,-13 208,-13C183,-13 160,-10 139,-4C118,2 99,11 83,24C67,37 55,53 46,72C37,91 32,114 32,141C32,170 37,195 48,214C58,233 71,248 88,259C104,270 123,279 144,285C165,290 186,295 207,298C228,301 249,304 270,306C291,308 309,311 325,315C341,319 354,325 363,333C372,340 377,351 376,366C376,381 374,394 369,403C364,412 357,419 349,424C340,429 331,432 320,434C309,435 297,436 284,436C256,436 234,430 218,418C202,406 193,386 190,358M376,253C370,248 363,244 354,241C345,238 335,235 325,233C314,231 303,229 292,228C281,227 269,225 258,223C247,221 237,218 227,215C216,212 207,207 200,202C192,196 186,189 181,180C176,171 174,160 174,147C174,134 176,124 181,115C186,106 192,100 200,95C208,90 217,86 228,84C239,82 250,81 261,81C289,81 311,86 326,95C341,104 353,116 360,129C367,142 372,155 374,168C375,181 376,192 376,200z"
id="glyph4238" /><glyph
unicode="b"
horiz-adv-x="611"
d="M433,258C433,237 431,216 426,196C421,176 414,158 404,143C394,128 382,116 367,107C352,98 333,93 312,93C291,93 273,98 258,107C243,116 230,128 220,143C210,158 203,176 198,196C193,216 191,237 191,258C191,280 193,301 198,321C203,341 210,359 220,374C230,389 243,402 258,411C273,420 291,424 312,424C333,424 352,420 367,411C382,402 394,389 404,374C414,359 421,341 426,321C431,301 433,280 433,258M54,714l0,-714l135,0l0,66l2,0C206,37 229,17 259,5C289,-7 323,-13 361,-13C387,-13 413,-8 438,3C463,14 486,30 507,52C527,74 543,102 556,137C569,171 575,212 575,259C575,306 569,347 556,382C543,416 527,444 507,466C486,488 463,504 438,515C413,526 387,531 361,531C329,531 298,525 269,513C239,500 215,481 198,454l-2,0l0,260z"
id="glyph4240" /><glyph
unicode="i"
horiz-adv-x="258"
d="M200,597l0,117l-142,0l0,-117M58,517l0,-517l142,0l0,517z"
id="glyph4242" /><glyph
unicode="k"
horiz-adv-x="574"
d="M67,714l0,-714l142,0l0,178l55,53l142,-231l172,0l-217,327l195,190l-168,0l-179,-186l0,383z"
id="glyph4244" /><glyph
unicode="n"
horiz-adv-x="593"
d="M54,517l0,-517l142,0l0,271C196,324 205,362 222,385C239,408 267,419 306,419C340,419 364,409 377,388C390,367 397,335 397,292l0,-292l142,0l0,318C539,350 536,379 531,406C525,432 515,454 501,473C487,491 468,505 444,516C419,526 388,531 350,531C320,531 291,524 262,511C233,497 210,475 192,445l-3,0l0,72z"
id="glyph4246" /></font><rect
fill="#3C3C3C"
width="252"
height="45"
id="rect4248"
style="opacity:1" /><rect
fill="#85C441"
width="6.094"
height="45"
id="rect4250"
style="opacity:1" /><rect
x="5.958"
fill="#2C448E"
width="6.094"
height="45"
id="rect4252"
style="opacity:1" /><rect
x="12.052"
fill="#F2BB1A"
width="9.847"
height="45"
id="rect4254"
style="opacity:1" /><rect
x="21.899"
fill="#3BBEB1"
width="6.068"
height="45"
id="rect4256"
style="opacity:1" /><rect
x="28.029"
fill="#006656"
width="2.84"
height="45"
id="rect4258"
style="opacity:1" /><rect
x="30.869"
fill="#EA458B"
width="15.006"
height="45"
id="rect4260"
style="opacity:1" /><rect
x="41.5"
fill="none"
width="207.5"
height="74"
id="rect4262" /><g
style="font-size:64.99947357px;font-family:HelveticaNeue-Bold;letter-spacing:-2.99203849px;opacity:1;fill:#ffffff"
id="text4264"><path
d="m 45.721,-0.49421886 0,46.40962386 9.229925,0 0,-11.569906 3.574971,-3.444972 9.229926,15.014878 11.179909,0 -14.104886,-21.254828 12.674898,-12.3499 -10.919912,0 -11.634906,12.089903 0,-24.89479886 -9.229925,0 z"
style="letter-spacing:-1.33094156px"
id="path4912" /><path
d="m 90.358907,7.1107196 0,-7.60493846 -9.229925,0 0,7.60493846 9.229925,0 z m -9.229925,5.1999574 0,33.604728 9.229925,0 0,-33.604728 -9.229925,0 z"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-family:'Helvetica Neue';-inkscape-font-specification:'Helvetica Neue Bold';letter-spacing:-2.87854815px"
id="path4914" /><path
d="m 119.38291,29.145541 q 0,2.079983 -0.45499,4.029967 -0.455,1.949985 -1.42999,3.444973 -0.97499,1.494987 -2.46998,2.40498 -1.42999,0.844993 -3.50997,0.844993 -2.01499,0 -3.50998,-0.844993 -1.49498,-0.909993 -2.46998,-2.40498 -0.97499,-1.494988 -1.42998,-3.444973 -0.455,-1.949984 -0.455,-4.029967 0,-2.144983 0.455,-4.094967 0.45499,-1.949984 1.42998,-3.444972 0.975,-1.494988 2.46998,-2.339981 1.49499,-0.909993 3.50998,-0.909993 2.07998,0 3.50997,0.909993 1.49499,0.844993 2.46998,2.339981 0.97499,1.494988 1.42999,3.444972 0.45499,1.949984 0.45499,4.094967 z m -24.634798,-29.63975986 0,46.40962386 8.774928,0 0,-4.289965 0.13,0 q 1.49499,2.794977 4.41996,3.964968 2.92498,1.16999 6.62995,1.16999 2.53498,0 5.00496,-1.039991 2.46998,-1.039992 4.41996,-3.184974 2.01499,-2.144983 3.24998,-5.459956 1.23499,-3.379973 1.23499,-7.994935 0,-4.614963 -1.23499,-7.929936 -1.23499,-3.379973 -3.24998,-5.524955 -1.94998,-2.144983 -4.41996,-3.184975 -2.46998,-1.039991 -5.00496,-1.039991 -3.11997,0 -6.04495,1.23499 -2.85998,1.16999 -4.54996,3.769969 l -0.13,0 0,-16.89986286 -9.229928,0 z"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-family:'Helvetica Neue';-inkscape-font-specification:'Helvetica Neue Bold';letter-spacing:-2.87854815px"
id="path4916" /><path
d="m 131.21557,22.645594 q 0.195,-3.249974 1.62499,-5.394957 1.42999,-2.144982 3.63997,-3.444972 2.20998,-1.299989 4.93996,-1.819985 2.79498,-0.584995 5.58995,-0.584995 2.53498,0 5.13496,0.389997 2.59998,0.324997 4.74496,1.364989 2.14499,1.039991 3.50997,2.924976 1.36499,1.819985 1.36499,4.87496 l 0,17.484859 q 0,2.274981 0.26,4.354965 0.26,2.079983 0.90999,3.119974 l -9.35992,0 q -0.26,-0.779993 -0.455,-1.559987 -0.13,-0.844993 -0.19499,-1.689986 -2.20999,2.274981 -5.19996,3.184974 -2.98998,0.909992 -6.10995,0.909992 -2.40498,0 -4.48497,-0.584995 -2.07998,-0.584995 -3.63997,-1.819985 -1.55998,-1.23499 -2.46998,-3.119975 -0.84499,-1.884985 -0.84499,-4.484964 0,-2.859976 0.97499,-4.679962 1.03999,-1.884984 2.59998,-2.989975 1.62499,-1.104991 3.63997,-1.624987 2.07998,-0.584995 4.15997,-0.909993 2.07998,-0.324997 4.09496,-0.519996 2.01499,-0.194998 3.57498,-0.584995 1.55998,-0.389997 2.46998,-1.104991 0.90999,-0.779994 0.84499,-2.209982 0,-1.494988 -0.52,-2.339981 -0.45499,-0.909993 -1.29999,-1.364989 -0.77999,-0.519996 -1.88498,-0.649995 -1.03999,-0.194998 -2.27498,-0.194998 -2.72998,0 -4.28997,1.16999 -1.55999,1.169991 -1.81998,3.899969 l -9.22993,0 z m 21.31983,6.824944 q -0.585,0.519996 -1.49499,0.844994 -0.84499,0.259998 -1.88498,0.454996 -0.975,0.194998 -2.07999,0.324997 -1.10499,0.129999 -2.20998,0.324998 -1.03999,0.194998 -2.07998,0.519995 -0.97499,0.324998 -1.75499,0.909993 -0.71499,0.519996 -1.16999,1.364989 -0.455,0.844993 -0.455,2.144983 0,1.23499 0.455,2.079983 0.455,0.844993 1.23499,1.364989 0.77999,0.454996 1.81999,0.649994 1.03999,0.194999 2.14498,0.194999 2.72998,0 4.22496,-0.909993 1.49499,-0.909992 2.20999,-2.144982 0.71499,-1.29999 0.84499,-2.599979 0.195,-1.29999 0.195,-2.079983 l 0,-3.444973 z"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-family:'Helvetica Neue';-inkscape-font-specification:'Helvetica Neue Bold';letter-spacing:-2.87854815px"
id="path4918" /><path
d="m 165.92399,12.310677 0,33.604728 9.22992,0 0,-17.614857 q 0,-5.134958 1.68999,-7.344941 1.68999,-2.274981 5.45996,-2.274981 3.31497,0 4.61496,2.079983 1.29999,2.014984 1.29999,6.17495 l 0,18.979846 9.22992,0 0,-20.669832 q 0,-3.119975 -0.58499,-5.654955 -0.52,-2.599978 -1.88499,-4.354964 -1.36499,-1.819986 -3.76997,-2.794978 -2.33998,-1.039991 -6.04495,-1.039991 -2.92497,0 -5.71995,1.364989 -2.79498,1.299989 -4.54996,4.224966 l -0.195,0 0,-4.679963 -8.77493,0 z"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-family:'Helvetica Neue';-inkscape-font-specification:'Helvetica Neue Bold';letter-spacing:-2.87854815px"
id="path4920" /><path
d="m 201.24887,22.645594 q 0.195,-3.249974 1.62499,-5.394957 1.42999,-2.144982 3.63997,-3.444972 2.20998,-1.299989 4.93996,-1.819985 2.79498,-0.584995 5.58996,-0.584995 2.53497,0 5.13495,0.389997 2.59998,0.324997 4.74497,1.364989 2.14498,1.039991 3.50997,2.924976 1.36499,1.819985 1.36499,4.87496 l 0,17.484859 q 0,2.274981 0.25999,4.354965 0.26,2.079983 0.91,3.119974 l -9.35993,0 q -0.26,-0.779993 -0.45499,-1.559987 -0.13,-0.844993 -0.195,-1.689986 -2.20998,2.274981 -5.19996,3.184974 -2.98998,0.909992 -6.10995,0.909992 -2.40498,0 -4.48496,-0.584995 -2.07999,-0.584995 -3.63998,-1.819985 -1.55998,-1.23499 -2.46998,-3.119975 -0.84499,-1.884985 -0.84499,-4.484964 0,-2.859976 0.97499,-4.679962 1.04,-1.884984 2.59998,-2.989975 1.62499,-1.104991 3.63997,-1.624987 2.07999,-0.584995 4.15997,-0.909993 2.07998,-0.324997 4.09497,-0.519996 2.01498,-0.194998 3.57497,-0.584995 1.55998,-0.389997 2.46998,-1.104991 0.90999,-0.779994 0.84499,-2.209982 0,-1.494988 -0.52,-2.339981 -0.45499,-0.909993 -1.29998,-1.364989 -0.78,-0.519996 -1.88499,-0.649995 -1.03999,-0.194998 -2.27498,-0.194998 -2.72998,0 -4.28997,1.16999 -1.55998,1.169991 -1.81998,3.899969 l -9.22993,0 z m 21.31983,6.824944 q -0.58499,0.519996 -1.49499,0.844994 -0.84499,0.259998 -1.88498,0.454996 -0.97499,0.194998 -2.07999,0.324997 -1.10499,0.129999 -2.20998,0.324998 -1.03999,0.194998 -2.07998,0.519995 -0.97499,0.324998 -1.75499,0.909993 -0.71499,0.519996 -1.16999,1.364989 -0.45499,0.844993 -0.45499,2.144983 0,1.23499 0.45499,2.079983 0.455,0.844993 1.23499,1.364989 0.78,0.454996 1.81999,0.649994 1.03999,0.194999 2.14498,0.194999 2.72998,0 4.22497,-0.909993 1.49498,-0.909992 2.20998,-2.144982 0.71499,-1.29999 0.84499,-2.599979 0.195,-1.29999 0.195,-2.079983 l 0,-3.444973 z"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-family:'Helvetica Neue';-inkscape-font-specification:'Helvetica Neue Bold';letter-spacing:-2.87854815px"
id="path4922" /></g></svg>
inkscape:current-layer="Layer_1" />
<defs
id="defs3">
<style
id="style5">.cls-1,.cls-2,.cls-3,.cls-4{fill:#fff;}.cls-1{opacity:0.6;}.cls-2{opacity:0.4;}.cls-3{opacity:0.9;}</style>
</defs>
<title
id="title7">Kibana-Full-Logo</title>
<path
class="cls-1"
d="M409.68,273.59a38.94,38.94,0,0,1,18.71,4.76L448.2,254.5H408.94v19.11Z"
transform="translate(-408.94 -254.5)"
id="path9" />
<path
class="cls-1"
d="M428.39,278.35l-19.45,23.42v3.73h39.2A39.2,39.2,0,0,0,428.39,278.35Z"
transform="translate(-408.94 -254.5)"
id="path11" />
<path
class="cls-2"
d="M428.39,278.35l-19.45,23.42v3.73h7l18.87-22.77s-1.25-1.06-3-2.3C430.41,279.46,428.39,278.35,428.39,278.35Z"
transform="translate(-408.94 -254.5)"
id="path13" />
<path
class="cls-3"
d="M409.68,273.59l-0.74,0v28.17l19.45-23.42A38.94,38.94,0,0,0,409.68,273.59Z"
transform="translate(-408.94 -254.5)"
id="path15" />
<g
style="font-style:normal;font-weight:normal;font-size:33.04906845px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:0.92528737;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="text4159"
transform="translate(0,-13.509761)">
<path
d="m 53.346838,50.53812 0,-6.74201 2.14819,-0.231343 4.031986,6.973353 4.065036,0 -5.023459,-8.791052 4.759066,-7.733482 -4.031986,0 -3.89979,6.444568 -2.049043,0.198295 0,-13.517069 -3.602348,0 0,23.39874 3.602348,0 z"
style="font-style:normal;font-variant:normal;font-weight:600;font-stretch:normal;font-family:Titillium;-inkscape-font-specification:'Titillium Semi-Bold';fill:#ffffff;fill-opacity:0.92528737"
id="path4891"
inkscape:connector-curvature="0" />
<path
d="m 66.269024,50.53812 3.602349,0 0,-16.524534 -3.602349,0 0,16.524534 z m 0,-19.333705 3.602349,0 0,-3.800643 -3.602349,0 0,3.800643 z"
style="font-style:normal;font-variant:normal;font-weight:600;font-stretch:normal;font-family:Titillium;-inkscape-font-specification:'Titillium Semi-Bold';fill:#ffffff;fill-opacity:0.92528737"
id="path4893"
inkscape:connector-curvature="0" />
<path
d="m 81.973529,33.650046 c -1.850747,0 -4.131133,0.991472 -4.131133,0.991472 l 0,-7.502138 -3.5693,0 0,23.365691 c 0,0 4.296379,0.396589 6.04798,0.396589 5.915783,0 8.063973,-2.015993 8.063973,-8.85715 0,-6.180176 -1.916846,-8.394464 -6.41152,-8.394464 z M 80.321076,47.6959 c -0.660981,0 -2.47868,-0.132196 -2.47868,-0.132196 l 0,-10.046917 c 0,0 1.949895,-0.660981 3.701495,-0.660981 2.214288,0 3.172711,1.454159 3.172711,5.188704 0,4.031986 -0.727079,5.65139 -4.395526,5.65139 z"
style="font-style:normal;font-variant:normal;font-weight:600;font-stretch:normal;font-family:Titillium;-inkscape-font-specification:'Titillium Semi-Bold';fill:#ffffff;fill-opacity:0.92528737"
id="path4895"
inkscape:connector-curvature="0" />
<path
d="m 104.18766,39.103143 c 0,-3.767594 -1.65245,-5.453097 -5.684435,-5.453097 -3.040515,0 -6.642863,0.859276 -6.642863,0.859276 l 0.132196,2.544778 c 0,0 3.998938,-0.33049 6.213225,-0.33049 1.619405,0 2.412587,0.561834 2.412587,2.379533 l 0,1.189766 -4.263335,0.36354 c -3.53625,0.297441 -5.453096,1.487208 -5.453096,4.990409 0,3.437103 1.652453,5.254802 4.924311,5.254802 2.676975,0 5.3209,-1.222816 5.3209,-1.222816 1.22282,0.958423 2.37953,1.222816 4.39553,1.222816 l 0.0991,-2.743073 c -0.95842,-0.132196 -1.38806,-0.528785 -1.45416,-1.520257 l 0,-7.535187 z m -3.56929,3.734544 0,4.395526 c 0,0 -2.214292,0.72708 -4.098089,0.72708 -1.388061,0 -2.015993,-0.925374 -2.015993,-2.412582 0,-1.487208 0.760128,-2.214288 2.280385,-2.346484 l 3.833697,-0.36354 z"
style="font-style:normal;font-variant:normal;font-weight:600;font-stretch:normal;font-family:Titillium;-inkscape-font-specification:'Titillium Semi-Bold';fill:#ffffff;fill-opacity:0.92528737"
id="path4897"
inkscape:connector-curvature="0" />
<path
d="m 112.28004,50.53812 0,-12.823038 c 0,0 1.8177,-0.859276 3.73454,-0.859276 2.51173,0 2.94137,1.619404 2.94137,4.924311 l 0,8.758003 3.5693,0 0,-8.85715 c 0,-5.420047 -1.12367,-8.030924 -5.71749,-8.030924 -2.14819,0 -4.56077,1.388061 -4.56077,1.388061 l 0,-1.024521 -3.5693,0 0,16.524534 3.60235,0 z"
style="font-style:normal;font-variant:normal;font-weight:600;font-stretch:normal;font-family:Titillium;-inkscape-font-specification:'Titillium Semi-Bold';fill:#ffffff;fill-opacity:0.92528737"
id="path4899"
inkscape:connector-curvature="0" />
<path
d="m 138.91501,39.103143 c 0,-3.767594 -1.65246,-5.453097 -5.68444,-5.453097 -3.04052,0 -6.64287,0.859276 -6.64287,0.859276 l 0.1322,2.544778 c 0,0 3.99894,-0.33049 6.21322,-0.33049 1.61941,0 2.41259,0.561834 2.41259,2.379533 l 0,1.189766 -4.26333,0.36354 c -3.53625,0.297441 -5.4531,1.487208 -5.4531,4.990409 0,3.437103 1.65245,5.254802 4.92431,5.254802 2.67698,0 5.3209,-1.222816 5.3209,-1.222816 1.22282,0.958423 2.37953,1.222816 4.39553,1.222816 l 0.0991,-2.743073 c -0.95842,-0.132196 -1.38806,-0.528785 -1.45415,-1.520257 l 0,-7.535187 z m -3.5693,3.734544 0,4.395526 c 0,0 -2.21429,0.72708 -4.09809,0.72708 -1.38806,0 -2.01599,-0.925374 -2.01599,-2.412582 0,-1.487208 0.76013,-2.214288 2.28038,-2.346484 l 3.8337,-0.36354 z"
style="font-style:normal;font-variant:normal;font-weight:600;font-stretch:normal;font-family:Titillium;-inkscape-font-specification:'Titillium Semi-Bold';fill:#ffffff;fill-opacity:0.92528737"
id="path4901"
inkscape:connector-curvature="0" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

Before After
Before After

View file

@ -33,5 +33,4 @@
<!-- auto-inserted by the paginate directive... -->
<!-- <paginate-controls></paginate-controls> -->
<div class="pagination-container" ng-transclude></div>
</paginate>

View file

@ -0,0 +1,6 @@
<ul>
<li ng-repeat="crumb in crumbs">
<span>{{crumb}}</span>
<span ng-hide="$last"> / </span>
</li>
</ul>

View file

@ -2,7 +2,7 @@
<form role="form" class="container-fluid" ng-submit="configSubmit()">
<div ng-bind-template="{{configTemplate}}" />
</form>
<div class="config-close remove" ng-click="close()">
<i class="fa fa-chevron-up" />
<div class="config-close remove">
<i class="fa fa-chevron-circle-up" ng-click="close()"/>
</div>
</div>

View file

@ -1,26 +1,42 @@
<form role="form">
<div class="form-group finder-form">
<div class="finder-form-options">
<a class="small" ng-click="finder.manageObjects(finder.properties.name)">manage {{finder.properties.nouns}}</a>
<form role="form" class="form-inline">
<div class="container-fluid">
<div class="row">
<div class="input-group form-group finder-form col-md-7">
<span class="input-group-addon">
<i class="fa fa-search"></i>
</span>
<input
input-focus
ng-model="filter"
ng-attr-placeholder="{{finder.properties.nouns}} Filter..."
ng-keydown="finder.filterKeyDown($event)"
class="form-control"
name="filter"
type="text"
autocomplete="off" />
</div>
<div class="finder-hit-count col-md-2">
<span>{{finder.hitCount}} of {{finder.hitCount}}</span>
</div>
<div class="finder-manage-object col-md-2">
<a class="small" ng-click="finder.manageObjects(finder.properties.name)">Manage {{finder.properties.nouns}}</a>
</div>
</div>
<div class="clearfix visible-xs-block"></div>
<input
input-focus
ng-model="filter"
ng-attr-placeholder="{{finder.properties.noun}} Filter"
ng-keydown="finder.filterKeyDown($event)"
class="form-control"
name="filter"
type="text"
autocomplete="off" />
<span class="finder-hit-count"><strong>{{finder.hitCount}}</strong> {{finder.hitCountNoun()}}</span>
</div>
</form>
<paginate list="finder.hits" per-page="{{perPage}}">
<ul
class="list-group list-group-menu"
ng-class="{'select-mode': finder.selector.enabled}">
<paginate list="finder.hits" per-page="20">
<ul class="li-striped list-group list-group-menu" ng-class="{'select-mode': finder.selector.enabled}">
<li class="list-group-item list-group-menu-item">
<span class="paginate-heading">
Name
<i
class="fa"
ng-click="finder.sortHits(finder.hits)"
ng-class="finder.isAscending ? 'fa-caret-up' : 'fa-caret-down'">
</i>
</span>
</li>
<li
class="list-group-item list-group-menu-item"
@ -29,19 +45,20 @@
ng-keydown="finder.hitKeyDown($event, page, paginate)"
ng-click="finder.onChoose(hit, $event)">
<a
ng-href="{{finder.makeUrl(hit)}}"
ng-blur="finder.hitBlur($event)"
ng-click="finder.preventClick($event)">
<i aria-hidden="true" class="fa" ng-if="hit.icon" ng-class="hit.icon"></i> {{hit.title}}
<p ng-if="hit.description" ng-bind="hit.description"></p>
<a ng-href="{{finder.makeUrl(hit)}}"
ng-blur="finder.hitBlur($event)"
ng-click="finder.preventClick($event)">
<i aria-hidden="true" class="finder-type fa" ng-if="hit.icon" ng-class="hit.icon"></i>
<span>{{hit.title}}</span>
<p ng-if="hit.description" ng-bind="hit.description"></p>
</a>
</li>
<li
class="list-group-item list-group-no-results"
ng-if="finder.hits.length === 0">
<p ng-bind="'No matching ' + finder.properties.nouns + ' found.'"></p>
</li>
</ul>
</paginate>

View file

@ -0,0 +1,124 @@
import _ from 'lodash';
import sinon from 'sinon';
import expect from 'expect.js';
import ngMock from 'ng_mock';
import chrome from 'ui/chrome';
import LibUrlShortenerProvider from 'ui/share/lib/url_shortener';
describe('Url shortener', () => {
let $rootScope;
let $location;
let $http;
let urlShortener;
let $httpBackend;
const shareId = 'id123';
beforeEach(ngMock.module('kibana'));
beforeEach(ngMock.inject(function (_$rootScope_, _$location_, _$httpBackend_, Private) {
$location = _$location_;
$rootScope = _$rootScope_;
$httpBackend = _$httpBackend_;
urlShortener = Private(LibUrlShortenerProvider);
}));
describe('Shorten without base path', () => {
it('should shorten urls with a port', function (done) {
$httpBackend.when('POST', '/shorten').respond(function (type, route, data) {
expect(JSON.parse(data).url).to.be('/app/kibana#123');
return [200, shareId];
});
urlShortener.shortenUrl('http://localhost:5601/app/kibana#123').then(function (url) {
expect(url).to.be(`http://localhost:5601/goto/${shareId}`);
done();
});
$httpBackend.flush();
});
it('should shorten urls without a port', function (done) {
$httpBackend.when('POST', '/shorten').respond(function (type, route, data) {
expect(JSON.parse(data).url).to.be('/app/kibana#123');
return [200, shareId];
});
urlShortener.shortenUrl('http://localhost/app/kibana#123').then(function (url) {
expect(url).to.be(`http://localhost/goto/${shareId}`);
done();
});
$httpBackend.flush();
});
});
describe('Shorten with base path', () => {
const basePath = '/foo';
let getBasePath;
beforeEach(ngMock.inject((Private) => {
getBasePath = sinon.stub(chrome, 'getBasePath', () => basePath);
urlShortener = Private(LibUrlShortenerProvider);
}));
it('should shorten urls with a port', (done) => {
$httpBackend.when('POST', `${basePath}/shorten`).respond((type, route, data) => {
expect(JSON.parse(data).url).to.be('/app/kibana#123');
return [200, shareId];
});
urlShortener.shortenUrl(`http://localhost:5601${basePath}/app/kibana#123`).then((url) => {
expect(url).to.be(`http://localhost:5601${basePath}/goto/${shareId}`);
done();
});
$httpBackend.flush();
});
it('should shorten urls without a port', (done) => {
$httpBackend.when('POST', `${basePath}/shorten`).respond((type, route, data) => {
expect(JSON.parse(data).url).to.be('/app/kibana#123');
return [200, shareId];
});
urlShortener.shortenUrl(`http://localhost${basePath}/app/kibana#123`).then((url) => {
expect(url).to.be(`http://localhost${basePath}/goto/${shareId}`);
done();
});
$httpBackend.flush();
});
it('should shorten urls with a query string', (done) => {
$httpBackend.when('POST', `${basePath}/shorten`).respond((type, route, data) => {
expect(JSON.parse(data).url).to.be('/app/kibana?foo#123');
return [200, shareId];
});
urlShortener.shortenUrl(`http://localhost${basePath}/app/kibana?foo#123`).then((url) => {
expect(url).to.be(`http://localhost${basePath}/goto/${shareId}`);
done();
});
$httpBackend.flush();
});
it('should shorten urls without a hash', (done) => {
$httpBackend.when('POST', `${basePath}/shorten`).respond((type, route, data) => {
expect(JSON.parse(data).url).to.be('/app/kibana');
return [200, shareId];
});
urlShortener.shortenUrl(`http://localhost${basePath}/app/kibana`).then((url) => {
expect(url).to.be(`http://localhost${basePath}/goto/${shareId}`);
done();
});
$httpBackend.flush();
});
it('should shorten urls with a query string in the hash', (done) => {
const relativeUrl = "/app/kibana#/discover?_g=(refreshInterval:(display:Off,pause:!f,value:0),time:(from:now-15m,mode:quick,to:now))&_a=(columns:!(_source),index:%27logstash-*%27,interval:auto,query:(query_string:(analyze_wildcard:!t,query:%27*%27)),sort:!(%27@timestamp%27,desc))"; //eslint-disable-line max-len, quotes
$httpBackend.when('POST', `${basePath}/shorten`).respond((type, route, data) => {
expect(JSON.parse(data).url).to.be(relativeUrl);
return [200, shareId];
});
urlShortener.shortenUrl(`http://localhost${basePath}${relativeUrl}`).then((url) => {
expect(url).to.be(`http://localhost${basePath}/goto/${shareId}`);
done();
});
$httpBackend.flush();
});
afterEach(() => {
getBasePath.restore();
});
});
});

View file

@ -1,24 +1,30 @@
import chrome from 'ui/chrome';
import url from 'url';
export default function createUrlShortener(Notifier, $http, $location) {
const notify = new Notifier({
location: 'Url Shortener'
});
const basePath = chrome.getBasePath();
const baseUrl = `${$location.protocol()}://${$location.host()}:${$location.port()}${basePath}`;
async function shortenUrl(url) {
const relativeUrl = url.replace(baseUrl, '');
function shortenUrl(absoluteUrl) {
const basePath = chrome.getBasePath();
const parsedUrl = url.parse(absoluteUrl);
const path = parsedUrl.path.replace(basePath, '');
const hash = parsedUrl.hash ? parsedUrl.hash : '';
const relativeUrl = path + hash;
const formData = { url: relativeUrl };
try {
const result = await $http.post(`${basePath}/shorten`, formData);
return `${baseUrl}/goto/${result.data}`;
} catch (err) {
notify.error(err);
throw err;
}
return $http.post(`${basePath}/shorten`, formData).then((result) => {
return url.format({
protocol: parsedUrl.protocol,
host: parsedUrl.host,
pathname: `${basePath}/goto/${result.data}`
});
}).catch((response) => {
notify.error(response);
});
}
return {

View file

@ -7,7 +7,7 @@ html,
body {
.flex-parent();
height: 100%;
margin: 0px;
margin: 0;
}
label > small {
@ -76,16 +76,16 @@ ul.navbar-inline li {
}
.content {
.flex-parent();
position: relative;
z-index: 0;
.real-flex-parent(@flow: row nowrap);
width: 100%;
height: 100%;
overflow: hidden;
> nav {
position: relative;
z-index: 1;
.navbar-right {
margin-right: 0;
margin-right: 0 !important;
}
}
}
@ -94,6 +94,7 @@ ul.navbar-inline li {
.flex-parent(@shrink: 0);
position: relative;
z-index: 0;
background-color: @white;
}
.top-fixed {
@ -139,9 +140,31 @@ a {
z-index: 0;
}
.kibana-nav-options {
padding-bottom: 0;
padding-right: 0px;
.kibana-nav-actions {
margin-left: auto;
.button-group > :last-child {
border-radius: 0;
}
}
.kibana-nav-info {
line-height: 30px;
padding: 0 10px;
&-title {
font-weight: bold;
margin-right: 10px;
}
}
}
> config {
z-index: 1;
}
> navbar { padding-bottom: 4px; }
> nav,
> navbar {
@ -161,6 +184,7 @@ a {
&:not(.pull-right):first-child {
// This is how .navbar-brand accomplishes it's solid placement
margin-left: -15px;
margin-top: 4px;
}
}
}
@ -197,6 +221,11 @@ a {
font-size: 16px;
vertical-align: middle;
}
button {
background-color: transparent;
border-radius: 0;
}
}
kbn-info i {
@ -264,30 +293,136 @@ table {
}
}
//== breadCrumbsTemplate
bread-crumbs {
ul {
margin: 0;
padding: 8px 10px;
list-style-type: none;
text-transform: capitalize;
>li {
display: inline;
>span {
color: @kibanaGray2;
}
>span:hover {
color: @kibanaGray1;
}
}
>li:last-child {
>span {
color: @kibanaGray1;
}
}
}
}
//== SavedObjectFinder
saved-object-finder {
.row {
background-color: @kibanaGray6;
padding: 10px;
display: flex;
flex-direction: row;
}
.form-group {
margin-bottom: 0;
float: left;
flex: 8;
input {
.border-bottom-radius(0);
border: none;
padding: 5px 0px;
border-radius: @border-radius-base;
text-transform: capitalize;
}
span {
padding: 5px 0px;
background-color: @white;
border: none;
i {
color: @kibanaGray3;
width: 15px;
}
}
.finder-hit-count, .finder-manage-object {
min-width: 80px;
padding: 5px;
}
.finder-hit-count {
flex: 1;
text-align: center;
span {
color: @kibanaGray3;
}
}
.finder-manage-object {
flex: 3;
text-align: left;
text-transform: capitalize;
}
}
.list-group-item {
a {
i {
color: @saved-object-finder-icon-color !important;
.list-group-item-menu:hover {
background-color: transparent;
}
ul.li-striped {
li {
border: none;
}
li:nth-child(odd) {
background-color: @kibanaGray6;
}
li:nth-child(even) {
background-color: @white;
}
.paginate-heading {
font-weight: normal;
color: @kibanaGray1;
}
.list-group-item {
padding: 8px 15px;
ul {
padding: 0;
display: flex;
flex-direction: row;
.finder-type {
margin-right: 10px;
}
}
color: @saved-object-finder-link-color !important;
}
a {
i {
color: @saved-object-finder-icon-color !important;
margin-right: 10px;
}
&:first-child {
.border-top-radius(0) !important;
}
color: @saved-object-finder-link-color !important;
}
&.list-group-no-results p {
margin-bottom: 0 !important;
&:first-child {
.border-top-radius(0) !important;
}
&.list-group-no-results p {
margin-bottom: 0 !important;
}
}
}
@ -301,13 +436,6 @@ saved-object-finder {
}
}
span.finder-hit-count {
position: absolute;
right: 10px;
top: 25px;
font-size: 0.85em;
}
.finder-options {
max-height: 300px;
overflow: auto;

View file

@ -11,13 +11,13 @@
width: 100%;
background-color: @config-close-bg;
border-radius: 0;
@shadow: inset 0 1px 0 rgba(255,255,255,.15), 0 1px 5px rgba(0,0,0,.075);
.box-shadow(@shadow);
text-align: center;
text-align: right;
padding: 10px;
line-height: 1em;
}
.container-fluid {
padding: 10px 10px;
background-color: @config-bg;
padding: 10px;
}
}

View file

@ -23,37 +23,6 @@
}
}
button {
padding: @padding-base-vertical @padding-base-horizontal;
font-size: @font-size-base;
color: @control-group-link-color;
background-color: @control-group-bg;
&:hover {
color: @control-group-link-hover-color;
background-color: @control-group-link-hover-bg;
}
.active, &:active, &:focus {
color: @control-group-link-active-color;
background-color: @control-group-link-active-bg;
}
&[disabled] {
color: @control-group-link-disabled-color;
background-color: @control-group-link-disabled-bg;
}
&:focus {
// because some buttons are all side-to-side, the browser's outline
// is cut off by the next button. This causes the outline to be
// drawn inside the button. Appplied to all buttons so that it's
// uniform within this widget
outline-offset: -4px;
}
}
// horizontal group of buttons/form elements
.button-group,
.inline-form .input-group {

View file

@ -581,4 +581,3 @@
}
}
}

View file

@ -1,7 +1,7 @@
@import (reference) "~ui/styles/theme";
.list-group-menu {
&.select-mode a{
&.select-mode a {
outline: none;
color: @list-group-menu-item-select-color;
}

View file

@ -1,15 +1,20 @@
@import "~ui/styles/variables";
@import (reference) "~bootstrap/less/mixins";
.flex-parent(@grow: 1, @shrink: 1, @basis: auto) {
.flex-parent(@grow: 1, @shrink: 1, @basis: auto, @direction: column) {
flex: @grow @shrink @basis;
display: flex;
flex-direction: column;
flex-direction: @direction;
> * {
flex-shrink: 0;
}
}
.real-flex-parent(@flow: column nowrap) {
display: flex;
flex-flow: @flow;
}
.ellipsis() {
white-space: nowrap;

View file

@ -1,17 +1,17 @@
@import "./control_group";
@navbar-margin: @padding-small-vertical;
navbar {
.control-group();
max-height: @navbar-collapse-max-height;
margin-bottom: @navbar-margin-bottom;
// top and bottom padding are normally calcuated and stored in @navbar-padding-vertical, but flexbox
padding: @padding-base-vertical @navbar-padding-horizontal;
padding: 0 @navbar-padding-horizontal 6px @navbar-padding-horizontal;
color: @navbar-default-color;
background-color: @navbar-default-bg;
border-style: solid;
border-color: @navbar-default-border;
border-width: 0 0 1px;
border: none;
z-index: @zindex-navbar;
/***
@ -22,6 +22,11 @@ navbar {
padding-right: @navbar-padding-horizontal;
}
.navbar-text {
margin-top: @navbar-margin;
margin-bottom: @navbar-margin;
}
// the "brand" that is displayed, usually on the left of the navbar
> .name {
align-self: center;
@ -47,6 +52,10 @@ navbar {
&:active, &.active {
color: @navbar-default-link-active-color;
background-color: @navbar-default-link-active-bg;
box-shadow: none;
&:focus {
outline: none;
}
}
&[disabled] {
@ -59,9 +68,7 @@ navbar {
button {
color: @navbar-button-color;
background-color: @navbar-button-bg;
border: 2px solid;
border-left: 0;
border-color: @navbar-button-border;
border: none;
}
}

View file

@ -29,6 +29,9 @@ paginate {
a {
text-decoration: none;
background-color: @white;
margin-left: 2px;
padding: 8px 11px;
}
a:hover {

View file

@ -11,8 +11,26 @@
.sidebar-well {
background-color: @sidebar-well-bg;
}
.vis-editor-agg-header-controls {
.btn-primary {
&.disabled,
&[disabled] {
background-color: @kibanaGray4;
}
}
}
.sidebar-list {
.navbar-nav.navbar-right {
border-radius: 4px;
overflow:hidden;
margin-right: -13px;
margin-top: 2px;
.navbar-btn-link {
padding-left: 10px;
padding-right: 10px;
}
}
ul {
list-style: none;
margin-bottom: 0px;
@ -58,12 +76,6 @@
&.full-title {
white-space: normal;
}
&:hover,
&:hover .text-muted {
color: @sidebar-hover-color;
background-color: @sidebar-hover-bg;
}
}
&-text {
@ -81,7 +93,7 @@
&.primary {
background-color: @btn-primary-bg;
color: @btn-primary-color
color: @btn-primary-color;
}
&.info {
@ -123,19 +135,27 @@
}
.index-pattern {
background-color: @sidebar-active-bg;
font-weight: bold;
padding: 5px 10px;
color: @sidebar-active-color;
display: flex;
justify-content: space-between;
background-color: @kibanaGray4;
line-height: 28px;
> * {
flex: 0 1 auto;
}
&.active {
background-color: @sidebar-active-bg;
color: @sidebar-active-color;
}
}
.index-pattern-selection .sidebar-item-title {
background-color: @sidebar-index-pattern-selection-bg;
&:hover {
background-color: @sidebar-index-pattern-selection-bg-hover;
}
}
}

View file

@ -1,55 +1,40 @@
@import "~ui/styles/variables";
@size: 400px;
@color1: @kibanaPink1;
@color2: @kibanaPink2;
.spinner.ng-hide {
visibility: hidden;
display: block !important;
opacity: 0;
transition-delay: 0.25s;
}
.spinner {
margin: 0px auto 0;
white-space: nowrap;
text-align: center;
display: inline;
top: 0;
left: 0;
right: 0;
height: 2px;
position: absolute;
visibility: visible;
display: block;
animation: move 2s linear infinite;
background-color: @kibanaPink2;
background-image: linear-gradient(to right,
@color1 0%,
@color1 50%,
@color2 50%,
@color2 100%
);
background-size: @size @size;
}
.spinner > div {
width: 10px;
height: 10px;
background-color: @spinner-bg;
border-radius: 100%;
display: inline-block;
-webkit-animation: bouncedelay 1s infinite ease-in-out;
animation: bouncedelay 1s infinite ease-in-out;
/* Prevent first frame from flickering when animation starts */
-webkit-animation-fill-mode: both;
animation-fill-mode: both;
}
.navbar .spinner > div {
background-color: @navbar-default-link-color;
}
.spinner.large > div {
width: 24px;
height: 24px;
}
.spinner .bounce1 {
-webkit-animation-delay: -0.32s;
animation-delay: -0.32s;
}
.spinner .bounce2 {
-webkit-animation-delay: -0.16s;
animation-delay: -0.16s;
}
@-webkit-keyframes bouncedelay {
0%, 80%, 100% { -webkit-transform: scale(0.0) }
40% { -webkit-transform: scale(1.0) }
}
@keyframes bouncedelay {
0%, 80%, 100% {
transform: scale(0.0);
-webkit-transform: scale(0.0);
} 40% {
transform: scale(1.0);
-webkit-transform: scale(1.0);
@keyframes move {
0% {
background-position: 0 0;
}
100% {
background-position: @size @size;
}
}

View file

@ -16,12 +16,39 @@
.navbar {
border-width: 0;
box-shadow: inset 0 -10px 10px -12px #333;
-moz-box-shadow: inset 0 -10px 10px -12px #333;
-webkit-box-shadow: inset 0 -10px 10px -12px #333;
&-static-top {
background-color: @kibanaGray5;
color: @kibanaGray2;
.navbar-brand:hover,
.navbar-brand {
float: none;
color: @kibanaGray2;
font-size: 1em;
background-color: transparent;
padding-top: 8px;
height: 35px;
}
.navbar-nav {
> li > a {
color: @kibanaGray2;
font-size: 1.5em;
padding: 5px 0 6px 0;
margin: 0 10px;
}
// Active, hover state for the getTabs
> .active > a,
> .active > a:hover,
> .active > a:active,
> li > a:active,
> li > a:hover {
color: @kibanaGray1;
background-color: transparent;
border-bottom: 2px solid @kibanaGray1;
}
}
}
&-btn-link {
height: @navbar-height;
margin: 0;
border-radius: 0;
@ -58,34 +85,13 @@
}
.navbar-nav > .active > a {
border-bottom-color: @navbar-nav-active-link-border;
// navbar active arrow base styles
&:before {
content: "";
display: inline-block;
position: absolute;
border: @navbar-arrow-size solid transparent;
border-bottom-color: inherit;
top: (@navbar-height - @navbar-arrow-size*2);
left: 0;
right: 0;
margin: 0 auto;
width: 0;
height: 0;
}
@media (max-width: @screen-sm-min) {
&:before {
display: none !important;
}
}
border-bottom-color: @kibanaGray2;
background-color: transparent;
}
&-brand {
cursor: default;
font-size: 1.8em;
background-color: @navbar-inverse-brand-bg;
user-select: none;
}

View file

@ -6,12 +6,12 @@
//** Background color for `<body>`.
@body-bg: @white;
//** Global text color on `<body>`.
@text-color: @brand-primary;
@text-color: @kibanaGray1;
//** Global textual link color.
@link-color: @brand-info;
@link-hover-color: lighten(@link-color, 10%);
@link-color: @kibanaBlue2;
@link-hover-color: @kibanaBlue1;
@link-hover-decoration: none;
//** Disabled cursor for form controls and buttons.
@ -26,7 +26,7 @@
@font-family-monospace: Menlo, Monaco, Consolas, "Courier New", monospace;
@font-family-base: @font-family-sans-serif;
@font-size-base: 13px;
@font-size-base: 14px;
@font-size-large: ceil((@font-size-base * 1.25)); // ~18px
@font-size-small: ceil((@font-size-base * 0.85)); // ~12px
@ -120,13 +120,13 @@
@btn-default-bg: @gray;
@btn-default-border: @btn-default-bg;
@btn-primary-color: @btn-default-color;
@btn-primary-bg: @brand-primary;
@btn-primary-border: @btn-primary-bg;
@btn-primary-color: @kibanaGray6;
@btn-primary-bg: @kibanaBlue3;
@btn-primary-border: transparent;
@btn-success-color: @btn-default-color;
@btn-success-bg: @brand-success;
@btn-success-border: @btn-success-bg;
@btn-success-bg: @kibanaBlue3;
@btn-success-border: @kibanaBlue3;
@btn-info-color: @btn-default-color;
@btn-info-bg: @brand-info;
@ -332,22 +332,22 @@
@navbar-height: 45px;
@navbar-margin-bottom: 0px;
@navbar-border-radius: @border-radius-base;
@navbar-padding-horizontal: floor((@grid-gutter-width / 2));
@navbar-padding-horizontal: 10px;
@navbar-padding-vertical: ((@navbar-height - @line-height-computed) / 2);
@navbar-collapse-max-height: 340px;
@navbar-arrow-size: 7px;
// Default theme
@navbar-default-bg: #656A76;
@navbar-default-color: @white;
@navbar-default-border: darken(@navbar-default-bg, 10%);
@navbar-default-link-color: @gray-lighter;
@navbar-default-link-hover-color: @white;
@navbar-default-bg: @kibanaGray5;
@navbar-default-color: @kibanaGray2;
@navbar-default-border: transparent;
@navbar-default-link-color: @kibanaGray2;
@navbar-default-link-hover-color: @kibanaGray2;
@navbar-default-link-hover-bg: transparent;
@navbar-default-link-active-color: @white;
@navbar-default-link-active-bg: darken(@navbar-default-bg, 10%);
@navbar-default-link-disabled-color: @gray12;
@navbar-default-link-active-color: @kibanaGray1;
@navbar-default-link-active-bg: @kibanaGray6;
@navbar-default-link-disabled-color: @kibanaGray3;
@navbar-default-link-disabled-bg: transparent;
// Navbar brand label
@ -411,8 +411,8 @@
//== Pills
@nav-pills-active-link-hover-bg: @component-active-bg;
@nav-pills-active-link-hover-color: @component-active-color;
@nav-pills-active-link-hover-bg: @kibanaYellow1;
@nav-pills-active-link-hover-color: @kibanaGray1;
@nav-pills-border-radius: @border-radius-base;
//== Pagination
@ -486,8 +486,8 @@
//** Tooltip text color
@tooltip-color: @white;
//** Tooltip background color
@tooltip-bg: rgba(0,0,0,.9);
@tooltip-opacity: .9;
@tooltip-bg: rgba(0,0,0,.8);
@tooltip-opacity: .8;
//** Tooltip arrow color
@tooltip-arrow-color: @tooltip-bg;
@ -607,11 +607,11 @@
@progress-border-radius: @border-radius-base;
//** Background color of the whole progress component
@progress-bg: @gray-lighter;
@progress-bg: @kibanaGray4;
//** Progress bar text color
@progress-bar-color: @white;
//** Default progress bar color
@progress-bar-bg: @brand-primary;
@progress-bar-bg: @kibanaPink1;
//** Success progress bar color
@progress-bar-success-bg: @brand-success;
//** Warning progress bar color
@ -708,7 +708,7 @@
//
//##
@well-bg: @gray-lighter;
@well-bg: @kibanaGray6;
@well-border: transparent;
@ -791,7 +791,7 @@
//** Abbreviations and acronyms border color
@abbr-border-color: @gray-light;
//** Headings small color
@headings-small-color: @gray-light;
@headings-small-color: @kibanaGray1;
//** Blockquote small color
@blockquote-small-color: @gray-light;
//** Blockquote border color

View file

@ -34,3 +34,31 @@
@brand-info: #1F6B7A;
@brand-warning: #F39C12;
@brand-danger: #E74C3C;
@kibanaGray1: #2D2D2D;
@kibanaGray2: #5A5A5A;
@kibanaGray3: #9C9C9C;
@kibanaGray4: #BEBEBE;
@kibanaGray5: #E4E4E4;
@kibanaGray6: #F6F6F6;
@kibanaGray7: #FFFFFF;
@kibanaBlue1: #105A73;
@kibanaBlue2: #328CAA;
@kibanaBlue3: #6EADC1;
@kibanaBlue4: #9DD0E0;
@kibanaGreen1: #80C383;
@kibanaGreen2: #AEE8B0;
@kibanaYellow1: #FBCE47;
@kibanaYellow2: #F5DD94;
@kibanaRed1: #D76051;
@kibanaRed2: #FF9183;
@kibanaPurple1: #9980B2;
@kibanaPurple2: #CDB5E4;
@kibanaPink1: #E8488B;
@kibanaPink2: #FF95C1;

View file

@ -26,17 +26,16 @@
@sidebar-hover-color: @sidebar-color;
@sidebar-hover-bg: darken(@sidebar-bg, 10%);
@sidebar-active-color: @component-active-color;
@sidebar-active-bg: @component-active-bg;
@sidebar-active-hover-bg: @component-active-bg;
@sidebar-active-hover-color: @component-active-color;
@sidebar-active-color: @kibanaGray1;
@sidebar-active-bg: @kibanaGray4;
@sidebar-active-hover-bg: @kibanaGray4;
@sidebar-active-hover-color: @kibanaGray1;
@sidebar-index-pattern-selection-bg: @white;
@sidebar-index-pattern-selection-bg: @kibanaGray5;
@sidebar-index-pattern-selection-bg-hover: @kibanaGray4;
// Discover ====================================================================
@discover-info-bg: @well-bg;
@discover-link-color: @link-color;
@discover-link-btn-color: @text-color;
@ -48,9 +47,9 @@
@discover-shard-failures-border: @brand-danger;
@discover-field-toggle-color: @btn-success-color;
@discover-field-details-border: @well-border;
@discover-field-details-color: @text-color;
@discover-field-details-bg: @body-bg;
@discover-field-details-border: @kibanaGray4;
@discover-field-details-color: @kibanaGray2;
@discover-field-details-bg: @kibanaGray5;
@discover-field-details-close-border: @well-border;
@discover-field-details-close-bg: @well-bg;
@discover-field-details-close-hover-bg: @sidebar-hover-bg;
@ -59,25 +58,25 @@
// Field name ==================================================================
@field-name-color: @text-muted;
@field-name-no-results-color: @text-muted;
@field-name-color: @kibanaGray3;
@field-name-no-results-color: @kibanaGray3;
// Collapser ===================================================================
@collapser-bg: @gray-lighter;
@collapser-border: @collapser-bg;
@collapser-color: @gray3;
@collapser-bg: @kibanaGray7;
@collapser-border: @kibanaGray7;
@collapser-color: @kibanaGray2;
@collapser-hover-bg: @gray-light;
@collapser-hover-bg: @kibanaGray5;
@collapser-hover-border: @collapser-hover-bg;
@collapser-hover-color: @white;
@collapser-hover-color: @kibanaGray7;
@collapser-width: 12px;
// Dashboard ===================================================================
@dashboard-bg: @gray-lighter;
@dashboard-panel-bg: @panel-bg;
@dashboard-bg: #fff;
@dashboard-panel-bg: #fff;
@dashboard-panel-color: @text-color;
@dashboard-panel-heading-link-color: @text-color;
@dashboard-panel-heading-link-hover-color: @link-hover-color;
@ -101,8 +100,8 @@
// NavBar ======================================================================
@navbar-button-border: @input-border;
@navbar-button-color: @brand-primary;
@navbar-button-bg: @input-group-addon-bg;
@navbar-button-color: @kibanaGray7;
@navbar-button-bg: @kibanaGray3;
// AppSwitcher =================================================================
@ -149,8 +148,8 @@
@vis-editor-navbar-error-state-color: @btn-danger-color;
@vis-editor-navbar-modal-bg: @navbar-default-bg;
@vis-editor-navbar-modal-color: @white;
@vis-editor-navbar-modal-link-color: @white;
@vis-editor-navbar-modal-color: @kibanaGray1;
@vis-editor-navbar-modal-link-color: @kibanaGray1;
@vis-editor-sidebar-bg: @body-bg;
@vis-editor-sidebar-border: @sidebar-bg;
@ -208,7 +207,7 @@
// Config ======================================================================
@config-border: darken(@body-bg, 10%);
@config-bg: @body-bg;
@config-bg: @kibanaGray6;
@config-close-bg: @well-bg;
@ -226,7 +225,7 @@
// Paginate ====================================================================
@paginate-page-link-active-color: @text-color;
@paginate-page-link-active-color: @kibanaGray2;
// Spinner =====================================================================
@ -311,6 +310,8 @@
@tooltip-color: @gray-lighter;
@tooltip-table-border: @gray;
@tooltip-bold: 600;
// Svg =========================================================================
@ -326,3 +327,17 @@
// Saved Object Finder =========================================================
@saved-object-finder-link-color: @link-color;
@saved-object-finder-icon-color: darken(@saved-object-finder-link-color, 10%);
// App-switcher ================================================================
@as-open-width: 160px;
@as-closed-width: 53px;
@app-icon-height: 38px;
@app-line-height: 24px;
@transition-time: .35s;
@transition-delay: .25s;
@app-links-wrapper-background: #3caed2;
@app-links-active-background: lighten(@app-links-wrapper-background, 7.5%);
@firstLinkColor: #E4BB51;
@secondLinkColor: #8AC336;
@thirdLinkColor: #59C6C5;

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