Merge branch 'master' into new-sidemenu

This commit is contained in:
Torkel Ödegaard
2017-06-14 10:00:43 -04:00
43 changed files with 1334 additions and 262 deletions
+1 -1
View File
@@ -298,7 +298,7 @@
# Use space to separate multiple modes, e.g. "console file"
;mode = console file
# Either "trace", "debug", "info", "warn", "error", "critical", default is "info"
# Either "debug", "info", "warn", "error", "critical", default is "info"
;level = info
# optional settings to set different levels for specific loggers. Ex filters = sqlstore:debug
-1
View File
@@ -4,7 +4,6 @@ graphite:
- "8080:80"
- "2003:2003"
volumes:
- /var/docker/gfdev/graphite:/opt/graphite/storage/whisper
- /etc/localtime:/etc/localtime:ro
- /etc/timezone:/etc/timezone:ro
+18 -1
View File
@@ -18,11 +18,28 @@ make docs-build
**Build the Documentation**:
Now that the docker image has been prepared we can build the
grafana docs and start a docs server. Switch your working directory back to the directory this file
grafana docs and start a docs server.
If you have not cloned the Grafana repository already then:
```
cd ..
git clone https://github.com/grafana/grafana
```
Switch your working directory to the directory this file
(README.md) is in.
```
cd grafana/docs
```
An AWS config file is required to build the docs Docker image and to publish the site to AWS. If you are building locally only and do not have any AWS credentials for docs.grafana.org then create an empty file named `awsconfig` in the current directory.
```
touch awsconfig
```
Then run (possibly with ``sudo``):
```
+9
View File
@@ -52,6 +52,15 @@ parent = "http_api"
"expires": 3600
}
JSON Body schema:
- **dashboard** – Required. The complete dashboard model.
- **name** – Optional. snapshot name
- **expires** - Optional. When the snapshot should expire in seconds. 3600 is 1 hour, 86400 is 1 day. Default is never to expire.
- **external** - Optional. Save the snapshot on an external server rather than locally. Default is `false`.
- **key** - Optional. Define the unique key. Required if **external** is `true`.
- **deleteKey** - Optional. Unique key used to delete the snapshot. It is different from the **key** so that only the creator can delete the snapshot. Required if **external** is `true`.
**Example Response**:
HTTP/1.1 200
@@ -0,0 +1,58 @@
///<reference path="../../headers/common.d.ts" />
import coreModule from 'app/core/core_module';
const template = `
<div class="collapse-box">
<div class="collapse-box__header">
<a class="collapse-box__header-title pointer" ng-click="ctrl.toggle()">
<span class="fa fa-fw fa-caret-right" ng-hide="ctrl.isOpen"></span>
<span class="fa fa-fw fa-caret-down" ng-hide="!ctrl.isOpen"></span>
{{ctrl.title}}
</a>
<div class="collapse-box__header-actions" ng-transclude="actions" ng-if="ctrl.isOpen"></div>
</div>
<div class="collapse-box__body" ng-transclude="body" ng-if="ctrl.isOpen">
</div>
</div>
`;
export class CollapseBoxCtrl {
isOpen: boolean;
stateChanged: () => void;
/** @ngInject **/
constructor(private $timeout) {
this.isOpen = false;
}
toggle() {
this.isOpen = !this.isOpen;
this.$timeout(() => {
this.stateChanged();
});
}
}
export function collapseBox() {
return {
restrict: 'E',
template: template,
controller: CollapseBoxCtrl,
bindToController: true,
controllerAs: 'ctrl',
scope: {
"title": "@",
"isOpen": "=?",
"stateChanged": "&"
},
transclude: {
'actions': '?collapseBoxActions',
'body': 'collapseBoxBody',
},
link: function(scope, elem, attrs) {
}
};
}
coreModule.directive('collapseBox', collapseBox);
@@ -0,0 +1,113 @@
// Based on work https://github.com/mohsen1/json-formatter-js
// Licence MIT, Copyright (c) 2015 Mohsen Azimi
/*
* Escapes `"` charachters from string
*/
function escapeString(str: string): string {
return str.replace('"', '\"');
}
/*
* Determines if a value is an object
*/
export function isObject(value: any): boolean {
var type = typeof value;
return !!value && (type === 'object');
}
/*
* Gets constructor name of an object.
* From http://stackoverflow.com/a/332429
*
*/
export function getObjectName(object: Object): string {
if (object === undefined) {
return '';
}
if (object === null) {
return 'Object';
}
if (typeof object === 'object' && !object.constructor) {
return 'Object';
}
const funcNameRegex = /function ([^(]*)/;
const results = (funcNameRegex).exec((object).constructor.toString());
if (results && results.length > 1) {
return results[1];
} else {
return '';
}
}
/*
* Gets type of an object. Returns "null" for null objects
*/
export function getType(object: Object): string {
if (object === null) { return 'null'; }
return typeof object;
}
/*
* Generates inline preview for a JavaScript object based on a value
*/
export function getValuePreview (object: Object, value: string): string {
var type = getType(object);
if (type === 'null' || type === 'undefined') { return type; }
if (type === 'string') {
value = '"' + escapeString(value) + '"';
}
if (type === 'function'){
// Remove content of the function
return object.toString()
.replace(/[\r\n]/g, '')
.replace(/\{.*\}/, '') + '{…}';
}
return value;
}
/*
* Generates inline preview for a JavaScript object
*/
export function getPreview(object: string): string {
let value = '';
if (isObject(object)) {
value = getObjectName(object);
if (Array.isArray(object)) {
value += '[' + object.length + ']';
}
} else {
value = getValuePreview(object, object);
}
return value;
}
/*
* Generates a prefixed CSS class name
*/
export function cssClass(className: string): string {
return `json-formatter-${className}`;
}
/*
* Creates a new DOM element wiht given type and class
* TODO: move me to helpers
*/
export function createElement(type: string, className?: string, content?: Element|string): Element {
const el = document.createElement(type);
if (className) {
el.classList.add(cssClass(className));
}
if (content !== undefined) {
if (content instanceof Node) {
el.appendChild(content);
} else {
el.appendChild(document.createTextNode(String(content)));
}
}
return el;
}
@@ -0,0 +1,431 @@
// Based on work https://github.com/mohsen1/json-formatter-js
// Licence MIT, Copyright (c) 2015 Mohsen Azimi
import {
isObject,
getObjectName,
getType,
getValuePreview,
getPreview,
cssClass,
createElement
} from './helpers';
import _ from 'lodash';
const DATE_STRING_REGEX = /(^\d{1,4}[\.|\\/|-]\d{1,2}[\.|\\/|-]\d{1,4})(\s*(?:0?[1-9]:[0-5]|1(?=[012])\d:[0-5])\d\s*[ap]m)?$/;
const PARTIAL_DATE_REGEX = /\d{2}:\d{2}:\d{2} GMT-\d{4}/;
const JSON_DATE_REGEX = /\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z/;
// When toggleing, don't animated removal or addition of more than a few items
const MAX_ANIMATED_TOGGLE_ITEMS = 10;
const requestAnimationFrame = window.requestAnimationFrame || function(cb: ()=>void) { cb(); return 0; };
export interface JsonExplorerConfig {
animateOpen?: boolean;
animateClose?: boolean;
theme?: string;
}
const _defaultConfig: JsonExplorerConfig = {
animateOpen: true,
animateClose: true,
theme: null
};
/**
* @class JsonExplorer
*
* JsonExplorer allows you to render JSON objects in HTML with a
* **collapsible** navigation.
*/
export class JsonExplorer {
// Hold the open state after the toggler is used
private _isOpen: boolean = null;
// A reference to the element that we render to
private element: Element;
private skipChildren = false;
/**
* @param {object} json The JSON object you want to render. It has to be an
* object or array. Do NOT pass raw JSON string.
*
* @param {number} [open=1] his number indicates up to how many levels the
* rendered tree should expand. Set it to `0` to make the whole tree collapsed
* or set it to `Infinity` to expand the tree deeply
*
* @param {object} [config=defaultConfig] -
* defaultConfig = {
* hoverPreviewEnabled: false,
* hoverPreviewArrayCount: 100,
* hoverPreviewFieldCount: 5
* }
*
* Available configurations:
* #####Hover Preview
* * `hoverPreviewEnabled`: enable preview on hover
* * `hoverPreviewArrayCount`: number of array items to show in preview Any
* array larger than this number will be shown as `Array[XXX]` where `XXX`
* is length of the array.
* * `hoverPreviewFieldCount`: number of object properties to show for object
* preview. Any object with more properties that thin number will be
* truncated.
*
* @param {string} [key=undefined] The key that this object in it's parent
* context
*/
constructor(public json: any, private open = 1, private config: JsonExplorerConfig = _defaultConfig, private key?: string) {
}
/*
* is formatter open?
*/
private get isOpen(): boolean {
if (this._isOpen !== null) {
return this._isOpen;
} else {
return this.open > 0;
}
}
/*
* set open state (from toggler)
*/
private set isOpen(value: boolean) {
this._isOpen = value;
}
/*
* is this a date string?
*/
private get isDate(): boolean {
return (this.type === 'string') &&
(DATE_STRING_REGEX.test(this.json) ||
JSON_DATE_REGEX.test(this.json) ||
PARTIAL_DATE_REGEX.test(this.json));
}
/*
* is this a URL string?
*/
private get isUrl(): boolean {
return this.type === 'string' && (this.json.indexOf('http') === 0);
}
/*
* is this an array?
*/
private get isArray(): boolean {
return Array.isArray(this.json);
}
/*
* is this an object?
* Note: In this context arrays are object as well
*/
private get isObject(): boolean {
return isObject(this.json);
}
/*
* is this an empty object with no properties?
*/
private get isEmptyObject(): boolean {
return !this.keys.length && !this.isArray;
}
/*
* is this an empty object or array?
*/
private get isEmpty(): boolean {
return this.isEmptyObject || (this.keys && !this.keys.length && this.isArray);
}
/*
* did we recieve a key argument?
* This means that the formatter was called as a sub formatter of a parent formatter
*/
private get hasKey(): boolean {
return typeof this.key !== 'undefined';
}
/*
* if this is an object, get constructor function name
*/
private get constructorName(): string {
return getObjectName(this.json);
}
/*
* get type of this value
* Possible values: all JavaScript primitive types plus "array" and "null"
*/
private get type(): string {
return getType(this.json);
}
/*
* get object keys
* If there is an empty key we pad it wit quotes to make it visible
*/
private get keys(): string[] {
if (this.isObject) {
return Object.keys(this.json).map((key)=> key ? key : '""');
} else {
return [];
}
}
/**
* Toggles `isOpen` state
*
*/
toggleOpen() {
this.isOpen = !this.isOpen;
if (this.element) {
if (this.isOpen) {
this.appendChildren(this.config.animateOpen);
} else{
this.removeChildren(this.config.animateClose);
}
this.element.classList.toggle(cssClass('open'));
}
}
/**
* Open all children up to a certain depth.
* Allows actions such as expand all/collapse all
*
*/
openAtDepth(depth = 1) {
if (depth < 0) {
return;
}
this.open = depth;
this.isOpen = (depth !== 0);
if (this.element) {
this.removeChildren(false);
if (depth === 0) {
this.element.classList.remove(cssClass('open'));
} else {
this.appendChildren(this.config.animateOpen);
this.element.classList.add(cssClass('open'));
}
}
}
isNumberArray() {
return (this.json.length > 0 && this.json.length < 4) &&
(_.isNumber(this.json[0]) || _.isNumber(this.json[1]));
}
renderArray() {
const arrayWrapperSpan = createElement('span');
arrayWrapperSpan.appendChild(createElement('span', 'bracket', '['));
// some pretty handling of number arrays
if (this.isNumberArray()) {
this.json.forEach((val, index) => {
if (index > 0) {
arrayWrapperSpan.appendChild(createElement('span', 'array-comma', ','));
}
arrayWrapperSpan.appendChild(createElement('span', 'number', val));
});
this.skipChildren = true;
} else {
arrayWrapperSpan.appendChild(createElement('span', 'number', (this.json.length)));
}
arrayWrapperSpan.appendChild(createElement('span', 'bracket', ']'));
return arrayWrapperSpan;
}
/**
* Renders an HTML element and installs event listeners
*
* @returns {HTMLDivElement}
*/
render(skipRoot = false): HTMLDivElement {
// construct the root element and assign it to this.element
this.element = createElement('div', 'row');
// construct the toggler link
const togglerLink = createElement('a', 'toggler-link');
const togglerIcon = createElement('span', 'toggler');
// if this is an object we need a wrapper span (toggler)
if (this.isObject) {
togglerLink.appendChild(togglerIcon);
}
// if this is child of a parent formatter we need to append the key
if (this.hasKey) {
togglerLink.appendChild(createElement('span', 'key', `${this.key}:`));
}
// Value for objects and arrays
if (this.isObject) {
// construct the value holder element
const value = createElement('span', 'value');
// we need a wrapper span for objects
const objectWrapperSpan = createElement('span');
// get constructor name and append it to wrapper span
var constructorName = createElement('span', 'constructor-name', this.constructorName);
objectWrapperSpan.appendChild(constructorName);
// if it's an array append the array specific elements like brackets and length
if (this.isArray) {
const arrayWrapperSpan = this.renderArray();
objectWrapperSpan.appendChild(arrayWrapperSpan);
}
// append object wrapper span to toggler link
value.appendChild(objectWrapperSpan);
togglerLink.appendChild(value);
// Primitive values
} else {
// make a value holder element
const value = this.isUrl ? createElement('a') : createElement('span');
// add type and other type related CSS classes
value.classList.add(cssClass(this.type));
if (this.isDate) {
value.classList.add(cssClass('date'));
}
if (this.isUrl) {
value.classList.add(cssClass('url'));
value.setAttribute('href', this.json);
}
// Append value content to value element
const valuePreview = getValuePreview(this.json, this.json);
value.appendChild(document.createTextNode(valuePreview));
// append the value element to toggler link
togglerLink.appendChild(value);
}
// construct a children element
const children = createElement('div', 'children');
// set CSS classes for children
if (this.isObject) {
children.classList.add(cssClass('object'));
}
if (this.isArray) {
children.classList.add(cssClass('array'));
}
if (this.isEmpty) {
children.classList.add(cssClass('empty'));
}
// set CSS classes for root element
if (this.config && this.config.theme) {
this.element.classList.add(cssClass(this.config.theme));
}
if (this.isOpen) {
this.element.classList.add(cssClass('open'));
}
// append toggler and children elements to root element
if (!skipRoot) {
this.element.appendChild(togglerLink);
}
if (!this.skipChildren) {
this.element.appendChild(children);
} else {
// remove togglerIcon
togglerLink.removeChild(togglerIcon);
}
// if formatter is set to be open call appendChildren
if (this.isObject && this.isOpen) {
this.appendChildren();
}
// add event listener for toggling
if (this.isObject) {
togglerLink.addEventListener('click', this.toggleOpen.bind(this));
}
return this.element as HTMLDivElement;
}
/**
* Appends all the children to children element
* Animated option is used when user triggers this via a click
*/
appendChildren(animated = false) {
const children = this.element.querySelector(`div.${cssClass('children')}`);
if (!children || this.isEmpty) { return; }
if (animated) {
let index = 0;
const addAChild = ()=> {
const key = this.keys[index];
const formatter = new JsonExplorer(this.json[key], this.open - 1, this.config, key);
children.appendChild(formatter.render());
index += 1;
if (index < this.keys.length) {
if (index > MAX_ANIMATED_TOGGLE_ITEMS) {
addAChild();
} else {
requestAnimationFrame(addAChild);
}
}
};
requestAnimationFrame(addAChild);
} else {
this.keys.forEach(key => {
const formatter = new JsonExplorer(this.json[key], this.open - 1, this.config, key);
children.appendChild(formatter.render());
});
}
}
/**
* Removes all the children from children element
* Animated option is used when user triggers this via a click
*/
removeChildren(animated = false) {
const childrenElement = this.element.querySelector(`div.${cssClass('children')}`) as HTMLDivElement;
if (animated) {
let childrenRemoved = 0;
const removeAChild = ()=> {
if (childrenElement && childrenElement.children.length) {
childrenElement.removeChild(childrenElement.children[0]);
childrenRemoved += 1;
if (childrenRemoved > MAX_ANIMATED_TOGGLE_ITEMS) {
removeAChild();
} else {
requestAnimationFrame(removeAChild);
}
}
};
requestAnimationFrame(removeAChild);
} else {
if (childrenElement) {
childrenElement.innerHTML = '';
}
}
}
}
+4
View File
@@ -45,6 +45,8 @@ import {assignModelProperties} from './utils/model_utils';
import {contextSrv} from './services/context_srv';
import {KeybindingSrv} from './services/keybindingSrv';
import {helpModal} from './components/help/help';
import {collapseBox} from './components/collapse_box';
import {JsonExplorer} from './components/json_explorer/json_explorer';
import {NavModelSrv, NavModel} from './nav_model_srv';
@@ -70,6 +72,8 @@ export {
contextSrv,
KeybindingSrv,
helpModal,
collapseBox,
JsonExplorer,
NavModelSrv,
NavModel,
};
+30 -6
View File
@@ -1,9 +1,10 @@
define([
'angular',
'require',
'../core_module',
'app/core/utils/kbn',
],
function (angular, coreModule, kbn) {
function (angular, require, coreModule, kbn) {
'use strict';
coreModule.default.directive('tip', function($compile) {
@@ -18,6 +19,29 @@ function (angular, coreModule, kbn) {
};
});
coreModule.default.directive('clipboardButton', function() {
return {
scope: {
getText: '&clipboardButton'
},
link: function(scope, elem) {
require(['vendor/clipboard/dist/clipboard'], function(Clipboard) {
scope.clipboard = new Clipboard(elem[0], {
text: function() {
return scope.getText();
}
});
});
scope.$on('$destroy', function() {
if (scope.clipboard) {
scope.clipboard.destroy();
}
});
}
};
});
coreModule.default.directive('compile', function($compile) {
return {
restrict: 'A',
@@ -77,10 +101,10 @@ function (angular, coreModule, kbn) {
text + tip + '</label>';
var template =
'<input class="cr1" id="' + scope.$id + model + '" type="checkbox" ' +
' ng-model="' + model + '"' + ngchange +
' ng-checked="' + model + '"></input>' +
' <label for="' + scope.$id + model + '" class="cr1"></label>';
'<input class="cr1" id="' + scope.$id + model + '" type="checkbox" ' +
' ng-model="' + model + '"' + ngchange +
' ng-checked="' + model + '"></input>' +
' <label for="' + scope.$id + model + '" class="cr1"></label>';
template = template + label;
elem.addClass('gf-form-checkbox');
@@ -105,7 +129,7 @@ function (angular, coreModule, kbn) {
var li = '<li' + (item.submenu && item.submenu.length ? ' class="dropdown-submenu"' : '') + '>' +
'<a tabindex="-1" ng-href="' + (item.href || '') + '"' + (item.click ? ' ng-click="' + item.click + '"' : '') +
(item.target ? ' target="' + item.target + '"' : '') + (item.method ? ' data-method="' + item.method + '"' : '') +
'>' + (item.text || '') + '</a>';
'>' + (item.text || '') + '</a>';
if (item.submenu && item.submenu.length) {
li += buildTemplate(item.submenu).join('\n');
@@ -109,7 +109,7 @@ function pluginDirectiveLoader($compile, datasourceSrv, $rootScope, $q, $http, $
baseUrl: ds.meta.baseUrl,
name: 'query-ctrl-' + ds.meta.id,
bindings: {target: "=", panelCtrl: "=", datasource: "="},
attrs: {"target": "target", "panel-ctrl": "ctrl", datasource: "datasource"},
attrs: {"target": "target", "panel-ctrl": "ctrl.panelCtrl", datasource: "datasource"},
Component: dsModule.QueryCtrl
};
});
@@ -127,7 +127,7 @@ function pluginDirectiveLoader($compile, datasourceSrv, $rootScope, $q, $http, $
baseUrl: ds.meta.baseUrl,
name: 'query-options-ctrl-' + ds.meta.id,
bindings: {panelCtrl: "="},
attrs: {"panel-ctrl": "ctrl"},
attrs: {"panel-ctrl": "ctrl.panelCtrl"},
Component: dsModule.QueryOptionsCtrl
};
});
@@ -181,7 +181,7 @@ function pluginDirectiveLoader($compile, datasourceSrv, $rootScope, $q, $http, $
return System.import(appModel.module).then(function(appModule) {
return {
baseUrl: appModel.baseUrl,
name: 'app-page-' + appModel.appId + '-' + scope.ctrl.page.slug,
name: 'app-page-' + appModel.id + '-' + scope.ctrl.page.slug,
bindings: {appModel: "="},
attrs: {"app-model": "ctrl.appModel"},
Component: appModule[scope.ctrl.page.component],
+8 -2
View File
@@ -4,6 +4,7 @@ import angular from 'angular';
import _ from 'lodash';
import config from 'app/core/config';
import coreModule from 'app/core/core_module';
import appEvents from 'app/core/app_events';
export class BackendSrv {
inFlightRequests = {};
@@ -150,7 +151,10 @@ export class BackendSrv {
}
}
return this.$http(options).catch(err => {
return this.$http(options).then(response => {
appEvents.emit('ds-request-response', response);
return response;
}).catch(err => {
if (err.status === this.HTTP_REQUEST_CANCELLED) {
throw {err, cancelled: true};
}
@@ -166,7 +170,7 @@ export class BackendSrv {
});
}
//populate error obj on Internal Error
// populate error obj on Internal Error
if (_.isString(err.data) && err.status === 500) {
err.data = {
error: err.statusText,
@@ -179,7 +183,9 @@ export class BackendSrv {
err.data.message = err.data.error;
}
appEvents.emit('ds-request-error', err);
throw err;
}).finally(() => {
// clean up
if (options.requestId) {
@@ -103,7 +103,7 @@ export class DashboardExporter {
templateizeDatasourceUsage(variable);
variable.options = [];
variable.current = {};
variable.refresh = 1;
variable.refresh = variable.refresh > 0 ? variable.refresh : 1;
}
}
@@ -27,6 +27,7 @@ export class HistoryListCtrl {
/** @ngInject */
constructor(private $scope,
private $route,
private $rootScope,
private $location,
private $window,
@@ -179,6 +180,7 @@ export class HistoryListCtrl {
this.loading = true;
return this.historySrv.restoreDashboard(this.dashboard, version).then(response => {
this.$location.path('dashboard/db/' + response.slug);
this.$route.reload();
this.$rootScope.appEvent('alert-success', ['Dashboard restored', 'Restored from version ' + version]);
}).catch(() => {
this.mode = 'list';
@@ -86,7 +86,7 @@
<input type="text" data-share-panel-url class="gf-form-input" ng-model="shareUrl"></input>
</div>
<div class="gf-form">
<button class="btn btn-inverse" data-clipboard-text="{{shareUrl}}" clipboard-button><i class="fa fa-clipboard"></i> Copy</button>
<button class="btn btn-inverse" clipboard-button="getShareUrl()"><i class="fa fa-clipboard"></i> Copy</button>
</div>
</div>
</div>
@@ -143,7 +143,7 @@
{{snapshotUrl}}
</a>
<br>
<button class="btn btn-inverse" data-clipboard-text="{{snapshotUrl}}" clipboard-button><i class="fa fa-clipboard"></i> Copy Link</button>
<button class="btn btn-inverse" clipboard-button="getSnapshotUrl()"><i class="fa fa-clipboard"></i> Copy Link</button>
</div>
</div>
</div>
@@ -2,10 +2,9 @@ define(['angular',
'lodash',
'jquery',
'moment',
'require',
'app/core/config',
],
function (angular, _, $, moment, require, config) {
function (angular, _, $, moment, config) {
'use strict';
var module = angular.module('grafana.controllers');
@@ -89,20 +88,10 @@ function (angular, _, $, moment, require, config) {
$scope.imageUrl += '&tz=UTC' + encodeURIComponent(moment().format("Z"));
};
});
module.directive('clipboardButton',function() {
return function(scope, elem) {
require(['vendor/clipboard/dist/clipboard'], function(Clipboard) {
scope.clipboard = new Clipboard(elem[0]);
});
scope.$on('$destroy', function() {
if (scope.clipboard) {
scope.clipboard.destroy();
}
});
$scope.getShareUrl = function() {
return $scope.shareUrl;
};
});
});
@@ -96,6 +96,10 @@ function (angular, _) {
});
};
$scope.getSnapshotUrl = function() {
return $scope.snapshotUrl;
};
$scope.scrubDashboard = function(dash) {
// change title
dash.title = $scope.snapshot.name;
+1 -1
View File
@@ -5,5 +5,5 @@ define([
'./query_ctrl',
'./panel_editor_tab',
'./query_editor_row',
'./metrics_ds_selector',
'./query_troubleshooter',
], function () {});
@@ -1,113 +0,0 @@
///<reference path="../../headers/common.d.ts" />
import angular from 'angular';
import _ from 'lodash';
var module = angular.module('grafana.directives');
var template = `
<div class="gf-form-group">
<div class="gf-form-inline">
<div class="gf-form">
<label class="gf-form-label">
<i class="icon-gf icon-gf-datasources"></i>
</label>
<label class="gf-form-label">
Data Source
</label>
<metric-segment segment="ctrl.dsSegment"
get-options="ctrl.getOptions(true)"
on-change="ctrl.datasourceChanged()"></metric-segment>
</div>
<div class="gf-form gf-form--offset-1">
<button class="btn btn-secondary gf-form-btn" ng-click="ctrl.addDataQuery()" ng-hide="ctrl.current.meta.mixed">
<i class="fa fa-plus"></i>&nbsp;
Add query
</button>
<div class="dropdown" ng-if="ctrl.current.meta.mixed">
<metric-segment segment="ctrl.mixedDsSegment"
get-options="ctrl.getOptions(false)"
on-change="ctrl.mixedDatasourceChanged()"></metric-segment>
</div>
</div>
</div>
</div>
`;
export class MetricsDsSelectorCtrl {
dsSegment: any;
mixedDsSegment: any;
dsName: string;
panelCtrl: any;
datasources: any[];
current: any;
/** @ngInject */
constructor(private uiSegmentSrv, datasourceSrv) {
this.datasources = datasourceSrv.getMetricSources();
var dsValue = this.panelCtrl.panel.datasource || null;
for (let ds of this.datasources) {
if (ds.value === dsValue) {
this.current = ds;
}
}
if (!this.current) {
this.current = {name: dsValue + ' not found', value: null};
}
this.dsSegment = uiSegmentSrv.newSegment({value: this.current.name, selectMode: true});
this.mixedDsSegment = uiSegmentSrv.newSegment({value: 'Add query', selectMode: true});
}
getOptions(includeBuiltin) {
return Promise.resolve(this.datasources.filter(value => {
return includeBuiltin || !value.meta.builtIn;
}).map(value => {
return this.uiSegmentSrv.newSegment(value.name);
}));
}
datasourceChanged() {
var ds = _.find(this.datasources, {name: this.dsSegment.value});
if (ds) {
this.current = ds;
this.panelCtrl.setDatasource(ds);
}
}
mixedDatasourceChanged() {
var target: any = {isNew: true};
var ds = _.find(this.datasources, {name: this.mixedDsSegment.value});
if (ds) {
target.datasource = ds.name;
this.panelCtrl.panel.targets.push(target);
this.mixedDsSegment.value = '';
}
}
addDataQuery() {
var target: any = {isNew: true};
this.panelCtrl.panel.targets.push(target);
}
}
module.directive('metricsDsSelector', function() {
return {
restrict: 'E',
template: template,
controller: MetricsDsSelectorCtrl,
bindToController: true,
controllerAs: 'ctrl',
transclude: true,
scope: {
panelCtrl: "="
}
};
});
@@ -1,5 +1,6 @@
///<reference path="../../headers/common.d.ts" />
import angular from 'angular';
import config from 'app/core/config';
import $ from 'jquery';
import _ from 'lodash';
@@ -10,6 +11,7 @@ import * as rangeUtil from 'app/core/utils/rangeutil';
import * as dateMath from 'app/core/utils/datemath';
import {Subject} from 'vendor/npm/rxjs/Subject';
import {metricsTabDirective} from './metrics_tab';
class MetricsPanelCtrl extends PanelCtrl {
scope: any;
@@ -32,6 +34,7 @@ class MetricsPanelCtrl extends PanelCtrl {
dataStream: any;
dataSubscription: any;
dataList: any;
nextRefId: string;
constructor($scope, $injector) {
super($scope, $injector);
@@ -61,7 +64,7 @@ class MetricsPanelCtrl extends PanelCtrl {
}
private onInitMetricsPanelEditMode() {
this.addEditorTab('Metrics', 'public/app/partials/metrics.html');
this.addEditorTab('Metrics', metricsTabDirective);
this.addEditorTab('Time range', 'public/app/features/panel/partials/panelTime.html');
}
@@ -256,7 +259,7 @@ class MetricsPanelCtrl extends PanelCtrl {
result = {data: []};
}
return this.events.emit('data-received', result.data);
this.events.emit('data-received', result.data);
}
handleDataStream(stream) {
@@ -306,6 +309,25 @@ class MetricsPanelCtrl extends PanelCtrl {
this.datasource = null;
this.refresh();
}
addQuery(target) {
target.refId = this.dashboard.getNextQueryLetter(this.panel);
this.panel.targets.push(target);
this.nextRefId = this.dashboard.getNextQueryLetter(this.panel);
}
removeQuery(target) {
var index = _.indexOf(this.panel.targets, target);
this.panel.targets.splice(index, 1);
this.nextRefId = this.dashboard.getNextQueryLetter(this.panel);
this.refresh();
}
moveQuery(target, direction) {
var index = _.indexOf(this.panel.targets, target);
_.move(this.panel.targets, index, index + direction);
}
}
export {MetricsPanelCtrl};
+94
View File
@@ -0,0 +1,94 @@
///<reference path="../../headers/common.d.ts" />
import _ from 'lodash';
//import {coreModule} from 'app/core/core';
import {DashboardModel} from '../dashboard/model';
export class MetricsTabCtrl {
dsSegment: any;
mixedDsSegment: any;
dsName: string;
panel: any;
panelCtrl: any;
datasources: any[];
current: any;
nextRefId: string;
dashboard: DashboardModel;
/** @ngInject */
constructor($scope, private uiSegmentSrv, datasourceSrv) {
this.panelCtrl = $scope.ctrl;
$scope.ctrl = this;
this.panel = this.panelCtrl.panel;
this.dashboard = this.panelCtrl.dashboard;
this.datasources = datasourceSrv.getMetricSources();
var dsValue = this.panelCtrl.panel.datasource || null;
for (let ds of this.datasources) {
if (ds.value === dsValue) {
this.current = ds;
}
}
if (!this.current) {
this.current = {name: dsValue + ' not found', value: null};
}
this.dsSegment = uiSegmentSrv.newSegment({value: this.current.name, selectMode: true});
this.mixedDsSegment = uiSegmentSrv.newSegment({value: 'Add Query', selectMode: true, fake: true});
// update next ref id
this.panelCtrl.nextRefId = this.dashboard.getNextQueryLetter(this.panel);
}
getOptions(includeBuiltin) {
return Promise.resolve(this.datasources.filter(value => {
return includeBuiltin || !value.meta.builtIn;
}).map(value => {
return this.uiSegmentSrv.newSegment(value.name);
}));
}
datasourceChanged() {
var ds = _.find(this.datasources, {name: this.dsSegment.value});
if (ds) {
this.current = ds;
this.panelCtrl.setDatasource(ds);
}
}
mixedDatasourceChanged() {
var target: any = {isNew: true};
var ds = _.find(this.datasources, {name: this.mixedDsSegment.value});
if (ds) {
target.datasource = ds.name;
this.panelCtrl.addQuery(target);
}
// metric segments are really bad, requires hacks to update
const segment = this.uiSegmentSrv.newSegment({value: 'Add Query', selectMode: true, fake: true});
this.mixedDsSegment.value = segment.value;
this.mixedDsSegment.html = segment.html;
this.mixedDsSegment.text = segment.text;
}
addQuery() {
this.panelCtrl.addQuery({isNew: true});
}
}
/** @ngInject **/
export function metricsTabDirective() {
'use strict';
return {
restrict: 'E',
scope: true,
templateUrl: 'public/app/features/panel/partials/metrics_tab.html',
controller: MetricsTabCtrl,
};
}
//coreModule.directive('metricsTab', metricsTabDirective);
+1 -1
View File
@@ -75,7 +75,7 @@ export class PanelCtrl {
}
refresh() {
this.events.emit('refresh', null);
this.events.emit('refresh', null);
}
publishAppEvent(evtName, evt) {
@@ -16,10 +16,15 @@ function panelEditorTab(dynamicDirectiveSrv) {
directive: scope => {
var pluginId = scope.ctrl.pluginId;
var tabIndex = scope.index;
// create a wrapper for directiveFn
// required for metrics tab directive
// that is the same for many panels but
// given different names in this function
var fn = () => scope.editorTab.directiveFn();
return Promise.resolve({
name: `panel-editor-tab-${pluginId}${tabIndex}`,
fn: scope.editorTab.directiveFn,
fn: fn,
});
}
});
@@ -0,0 +1,45 @@
<div class="query-editor-rows gf-form-group">
<div ng-repeat="target in ctrl.panel.targets" ng-class="{'gf-form-disabled': target.hide}">
<rebuild-on-change property="ctrl.panel.datasource || target.datasource" show-null="true">
<plugin-component type="query-ctrl">
</plugin-component>
</rebuild-on-change>
</div>
<div class="gf-form-query">
<div class="gf-form gf-form-query-letter-cell">
<label class="gf-form-label">
<span class="gf-form-query-letter-cell-carret">
<i class="fa fa-caret-down"></i>
</span>
<span class="gf-form-query-letter-cell-letter">{{ctrl.panelCtrl.nextRefId}}</span>
</label>
<button class="btn btn-secondary gf-form-btn" ng-click="ctrl.addQuery()" ng-hide="ctrl.current.meta.mixed">
Add Query
</button>
<div class="dropdown" ng-if="ctrl.current.meta.mixed">
<metric-segment segment="ctrl.mixedDsSegment" get-options="ctrl.getOptions(false)" on-change="ctrl.mixedDatasourceChanged()"></metric-segment>
</div>
</div>
</div>
</div>
<!-- <query&#45;troubleshooter panel&#45;ctrl="ctrl.panelCtrl"></query&#45;troubleshooter> -->
<div class="gf-form-group">
<div class="gf-form-inline">
<div class="gf-form">
<label class="gf-form-label">
Panel Data Source
</label>
<metric-segment segment="ctrl.dsSegment" get-options="ctrl.getOptions(true)" on-change="ctrl.datasourceChanged()"></metric-segment>
</div>
</div>
</div>
<rebuild-on-change property="ctrl.panel.datasource" show-null="true">
<plugin-component type="query-options-ctrl">
</plugin-component>
</rebuild-on-change>
</div>
+4 -17
View File
@@ -21,7 +21,7 @@ export class QueryRowCtrl {
this.panel = this.panelCtrl.panel;
if (!this.target.refId) {
this.target.refId = this.getNextQueryLetter();
this.target.refId = this.panelCtrl.dashboard.getNextQueryLetter(this.panel);
}
this.toggleCollapse(true);
@@ -40,16 +40,6 @@ export class QueryRowCtrl {
this.panelCtrl.refresh();
}
getNextQueryLetter() {
var letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
return _.find(letters, refId => {
return _.every(this.panel.targets, function(other) {
return other.refId !== refId;
});
});
}
toggleCollapse(init) {
if (!this.canCollapse) {
return;
@@ -87,19 +77,16 @@ export class QueryRowCtrl {
delete this.panelCtrl.__collapsedQueryCache[this.target.refId];
}
this.panel.targets = _.without(this.panel.targets, this.target);
this.panelCtrl.refresh();
this.panelCtrl.removeQuery(this.target);
}
duplicateQuery() {
var clone = angular.copy(this.target);
clone.refId = this.getNextQueryLetter();
this.panel.targets.push(clone);
this.panelCtrl.addQuery(clone);
}
moveQuery(direction) {
var index = _.indexOf(this.panel.targets, this.target);
_.move(this.panel.targets, index, index + direction);
this.panelCtrl.moveQuery(this.target, direction);
}
}
@@ -0,0 +1,154 @@
///<reference path="../../headers/common.d.ts" />
import _ from 'lodash';
import appEvents from 'app/core/app_events';
import {coreModule, JsonExplorer} from 'app/core/core';
const template = `
<collapse-box title="Query Troubleshooter" is-open="ctrl.isOpen" state-changed="ctrl.stateChanged()"
ng-class="{'collapse-box--error': ctrl.hasError}">
<collapse-box-actions>
<a class="pointer" ng-click="ctrl.toggleExpand()" ng-hide="ctrl.allNodesExpanded">
<i class="fa fa-plus-square-o"></i> Expand All
</a>
<a class="pointer" ng-click="ctrl.toggleExpand()" ng-show="ctrl.allNodesExpanded">
<i class="fa fa-minus-square-o"></i> Collapse All
</a>
<a class="pointer" clipboard-button="ctrl.getClipboardText()"><i class="fa fa-clipboard"></i> Copy to Clipboard</a>
</collapse-box-actions>
<collapse-box-body>
<i class="fa fa-spinner fa-spin" ng-show="ctrl.isLoading"></i>
<div class="query-troubleshooter-json"></div>
</collapse-box-body>
</collapse-box>
`;
export class QueryTroubleshooterCtrl {
isOpen: any;
isLoading: boolean;
showResponse: boolean;
panelCtrl: any;
renderJsonExplorer: (data) => void;
onRequestErrorEventListener: any;
onRequestResponseEventListener: any;
hasError: boolean;
allNodesExpanded: boolean;
jsonExplorer: JsonExplorer;
/** @ngInject **/
constructor($scope, private $timeout) {
this.onRequestErrorEventListener = this.onRequestError.bind(this);
this.onRequestResponseEventListener = this.onRequestResponse.bind(this);
appEvents.on('ds-request-response', this.onRequestResponseEventListener);
appEvents.on('ds-request-error', this.onRequestErrorEventListener);
$scope.$on('$destroy', this.removeEventsListeners.bind(this));
}
removeEventsListeners() {
appEvents.off('ds-request-response', this.onRequestResponseEventListener);
appEvents.off('ds-request-error', this.onRequestErrorEventListener);
}
onRequestError(err) {
this.isOpen = true;
this.hasError = true;
this.onRequestResponse(err);
}
stateChanged() {
if (this.isOpen) {
this.panelCtrl.refresh();
this.isLoading = true;
}
}
getClipboardText() {
if (this.jsonExplorer) {
return JSON.stringify(this.jsonExplorer.json, null, 2);
}
}
onRequestResponse(data) {
// ignore if closed
if (!this.isOpen) {
return;
}
this.isLoading = false;
data = _.cloneDeep(data);
if (data.headers) {
delete data.headers;
}
if (data.config) {
data.request = data.config;
delete data.config;
delete data.request.transformRequest;
delete data.request.transformResponse;
delete data.request.paramSerializer;
delete data.request.jsonpCallbackParam;
delete data.request.headers;
delete data.request.requestId;
delete data.request.inspect;
delete data.request.retry;
delete data.request.timeout;
}
if (data.data) {
data.response = data.data;
if (data.status === 200) {
// if we are in error state, assume we automatically opened
// and auto close it again
if (this.hasError) {
this.hasError = false;
this.isOpen = false;
}
}
delete data.data;
delete data.status;
delete data.statusText;
delete data.$$config;
}
this.$timeout(_.partial(this.renderJsonExplorer, data));
}
toggleExpand(depth) {
if (this.jsonExplorer) {
this.allNodesExpanded = !this.allNodesExpanded;
this.jsonExplorer.openAtDepth(this.allNodesExpanded ? 20 : 1);
}
}
}
export function queryTroubleshooter() {
return {
restrict: 'E',
template: template,
controller: QueryTroubleshooterCtrl,
bindToController: true,
controllerAs: 'ctrl',
scope: {
panelCtrl: "="
},
link: function(scope, elem, attrs, ctrl) {
ctrl.renderJsonExplorer = function(data) {
var jsonElem = elem.find('.query-troubleshooter-json');
ctrl.jsonExplorer = new JsonExplorer(data, 3, {
animateOpen: true,
});
const html = ctrl.jsonExplorer.render(true);
jsonElem.html(html);
};
}
};
}
coreModule.directive('queryTroubleshooter', queryTroubleshooter);
-1
View File
@@ -72,4 +72,3 @@ declare module 'd3' {
var d3: any;
export default d3;
}
+1 -1
View File
@@ -60,7 +60,7 @@
GitHub
</a>
<a class="btn btn-large btn-grafana-com" href="login/grafana_com" target="_self" ng-if="oauth.grafana_com">
<img src="public/img/grafana_icon.svg"></img>
<img src="public/img/grafana_com_auth_icon.svg"></img>
<span>Grafana.com</span>
</a>
<a class="btn btn-large btn-generic-oauth" href="login/generic_oauth" target="_self" ng-if="oauth.generic_oauth">
-20
View File
@@ -1,20 +0,0 @@
<div class="query-editor-rows gf-form-group">
<div ng-repeat="target in ctrl.panel.targets" ng-class="{'gf-form-disabled': target.hide}">
<rebuild-on-change property="ctrl.panel.datasource || target.datasource" show-null="true">
<plugin-component type="query-ctrl">
</plugin-component>
</rebuild-on-change>
</div>
</div>
<metrics-ds-selector panel-ctrl="ctrl"></metrics-ds-selector>
<div class="gf-form-group">
<rebuild-on-change property="ctrl.panel.datasource" show-null="true">
<plugin-component type="query-options-ctrl">
</plugin-component>
</rebuild-on-change>
</div>
<div class="clearfix"></div>
@@ -1,4 +1,4 @@
<section class="grafana-metric-options gf-form-group">
<section class="gf-form-group">
<div class="gf-form-inline">
<div class="gf-form max-width-15">
<span class="gf-form-label width-8">
@@ -210,10 +210,12 @@ export default class InfluxDatasource {
var currentUrl = self.urls.shift();
self.urls.push(currentUrl);
var params: any = {
u: self.username,
p: self.password,
};
var params: any = {};
if (self.username) {
params.username = self.username;
params.password = self.password;
}
if (self.database) {
params.db = self.database;
+1 -1
View File
@@ -32,7 +32,7 @@ System.config({
"jquery.flot.fillbelow": "vendor/flot/jquery.flot.fillbelow",
"jquery.flot.gauge": "vendor/flot/jquery.flot.gauge",
"d3": "vendor/d3/d3.js",
"jquery.flot.dashes": "vendor/flot/jquery.flot.dashes"
"jquery.flot.dashes": "vendor/flot/jquery.flot.dashes",
},
packages: {
+57
View File
@@ -0,0 +1,57 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 20.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="351px" height="365px" viewBox="0 0 351 365" style="enable-background:new 0 0 351 365;" xml:space="preserve">
<style type="text/css">
.st0{fill:url(#SVGID_1_);}
</style>
<g id="Layer_1_1_">
</g>
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="175.5" y1="445.4948" x2="175.5" y2="114.0346">
<stop offset="0" style="stop-color:#FFF100"/>
<stop offset="1" style="stop-color:#F05A28"/>
</linearGradient>
<path class="st0" d="M342,161.2c-0.6-6.1-1.6-13.1-3.6-20.9c-2-7.7-5-16.2-9.4-25c-4.4-8.8-10.1-17.9-17.5-26.8
c-2.9-3.5-6.1-6.9-9.5-10.2c5.1-20.3-6.2-37.9-6.2-37.9c-19.5-1.2-31.9,6.1-36.5,9.4c-0.8-0.3-1.5-0.7-2.3-1
c-3.3-1.3-6.7-2.6-10.3-3.7c-3.5-1.1-7.1-2.1-10.8-3c-3.7-0.9-7.4-1.6-11.2-2.2c-0.7-0.1-1.3-0.2-2-0.3
c-8.5-27.2-32.9-38.6-32.9-38.6c-27.3,17.3-32.4,41.5-32.4,41.5s-0.1,0.5-0.3,1.4c-1.5,0.4-3,0.9-4.5,1.3c-2.1,0.6-4.2,1.4-6.2,2.2
c-2.1,0.8-4.1,1.6-6.2,2.5c-4.1,1.8-8.2,3.8-12.2,6c-3.9,2.2-7.7,4.6-11.4,7.1c-0.5-0.2-1-0.4-1-0.4c-37.8-14.4-71.3,2.9-71.3,2.9
c-3.1,40.2,15.1,65.5,18.7,70.1c-0.9,2.5-1.7,5-2.5,7.5c-2.8,9.1-4.9,18.4-6.2,28.1c-0.2,1.4-0.4,2.8-0.5,4.2
C18.8,192.7,8.5,228,8.5,228c29.1,33.5,63.1,35.6,63.1,35.6c0,0,0.1-0.1,0.1-0.1c4.3,7.7,9.3,15,14.9,21.9c2.4,2.9,4.8,5.6,7.4,8.3
c-10.6,30.4,1.5,55.6,1.5,55.6c32.4,1.2,53.7-14.2,58.2-17.7c3.2,1.1,6.5,2.1,9.8,2.9c10,2.6,20.2,4.1,30.4,4.5
c2.5,0.1,5.1,0.2,7.6,0.1l1.2,0l0.8,0l1.6,0l1.6-0.1l0,0.1c15.3,21.8,42.1,24.9,42.1,24.9c19.1-20.1,20.2-40.1,20.2-44.4l0,0
c0,0,0-0.1,0-0.3c0-0.4,0-0.6,0-0.6l0,0c0-0.3,0-0.6,0-0.9c4-2.8,7.8-5.8,11.4-9.1c7.6-6.9,14.3-14.8,19.9-23.3
c0.5-0.8,1-1.6,1.5-2.4c21.6,1.2,36.9-13.4,36.9-13.4c-3.6-22.5-16.4-33.5-19.1-35.6l0,0c0,0-0.1-0.1-0.3-0.2
c-0.2-0.1-0.2-0.2-0.2-0.2c0,0,0,0,0,0c-0.1-0.1-0.3-0.2-0.5-0.3c0.1-1.4,0.2-2.7,0.3-4.1c0.2-2.4,0.2-4.9,0.2-7.3l0-1.8l0-0.9
l0-0.5c0-0.6,0-0.4,0-0.6l-0.1-1.5l-0.1-2c0-0.7-0.1-1.3-0.2-1.9c-0.1-0.6-0.1-1.3-0.2-1.9l-0.2-1.9l-0.3-1.9
c-0.4-2.5-0.8-4.9-1.4-7.4c-2.3-9.7-6.1-18.9-11-27.2c-5-8.3-11.2-15.6-18.3-21.8c-7-6.2-14.9-11.2-23.1-14.9
c-8.3-3.7-16.9-6.1-25.5-7.2c-4.3-0.6-8.6-0.8-12.9-0.7l-1.6,0l-0.4,0c-0.1,0-0.6,0-0.5,0l-0.7,0l-1.6,0.1c-0.6,0-1.2,0.1-1.7,0.1
c-2.2,0.2-4.4,0.5-6.5,0.9c-8.6,1.6-16.7,4.7-23.8,9c-7.1,4.3-13.3,9.6-18.3,15.6c-5,6-8.9,12.7-11.6,19.6c-2.7,6.9-4.2,14.1-4.6,21
c-0.1,1.7-0.1,3.5-0.1,5.2c0,0.4,0,0.9,0,1.3l0.1,1.4c0.1,0.8,0.1,1.7,0.2,2.5c0.3,3.5,1,6.9,1.9,10.1c1.9,6.5,4.9,12.4,8.6,17.4
c3.7,5,8.2,9.1,12.9,12.4c4.7,3.2,9.8,5.5,14.8,7c5,1.5,10,2.1,14.7,2.1c0.6,0,1.2,0,1.7,0c0.3,0,0.6,0,0.9,0c0.3,0,0.6,0,0.9-0.1
c0.5,0,1-0.1,1.5-0.1c0.1,0,0.3,0,0.4-0.1l0.5-0.1c0.3,0,0.6-0.1,0.9-0.1c0.6-0.1,1.1-0.2,1.7-0.3c0.6-0.1,1.1-0.2,1.6-0.4
c1.1-0.2,2.1-0.6,3.1-0.9c2-0.7,4-1.5,5.7-2.4c1.8-0.9,3.4-2,5-3c0.4-0.3,0.9-0.6,1.3-1c1.6-1.3,1.9-3.7,0.6-5.3
c-1.1-1.4-3.1-1.8-4.7-0.9c-0.4,0.2-0.8,0.4-1.2,0.6c-1.4,0.7-2.8,1.3-4.3,1.8c-1.5,0.5-3.1,0.9-4.7,1.2c-0.8,0.1-1.6,0.2-2.5,0.3
c-0.4,0-0.8,0.1-1.3,0.1c-0.4,0-0.9,0-1.2,0c-0.4,0-0.8,0-1.2,0c-0.5,0-1,0-1.5-0.1c0,0-0.3,0-0.1,0l-0.2,0l-0.3,0
c-0.2,0-0.5,0-0.7-0.1c-0.5-0.1-0.9-0.1-1.4-0.2c-3.7-0.5-7.4-1.6-10.9-3.2c-3.6-1.6-7-3.8-10.1-6.6c-3.1-2.8-5.8-6.1-7.9-9.9
c-2.1-3.8-3.6-8-4.3-12.4c-0.3-2.2-0.5-4.5-0.4-6.7c0-0.6,0.1-1.2,0.1-1.8c0,0.2,0-0.1,0-0.1l0-0.2l0-0.5c0-0.3,0.1-0.6,0.1-0.9
c0.1-1.2,0.3-2.4,0.5-3.6c1.7-9.6,6.5-19,13.9-26.1c1.9-1.8,3.9-3.4,6-4.9c2.1-1.5,4.4-2.8,6.8-3.9c2.4-1.1,4.8-2,7.4-2.7
c2.5-0.7,5.1-1.1,7.8-1.4c1.3-0.1,2.6-0.2,4-0.2c0.4,0,0.6,0,0.9,0l1.1,0l0.7,0c0.3,0,0,0,0.1,0l0.3,0l1.1,0.1
c2.9,0.2,5.7,0.6,8.5,1.3c5.6,1.2,11.1,3.3,16.2,6.1c10.2,5.7,18.9,14.5,24.2,25.1c2.7,5.3,4.6,11,5.5,16.9c0.2,1.5,0.4,3,0.5,4.5
l0.1,1.1l0.1,1.1c0,0.4,0,0.8,0,1.1c0,0.4,0,0.8,0,1.1l0,1l0,1.1c0,0.7-0.1,1.9-0.1,2.6c-0.1,1.6-0.3,3.3-0.5,4.9
c-0.2,1.6-0.5,3.2-0.8,4.8c-0.3,1.6-0.7,3.2-1.1,4.7c-0.8,3.1-1.8,6.2-3,9.3c-2.4,6-5.6,11.8-9.4,17.1
c-7.7,10.6-18.2,19.2-30.2,24.7c-6,2.7-12.3,4.7-18.8,5.7c-3.2,0.6-6.5,0.9-9.8,1l-0.6,0l-0.5,0l-1.1,0l-1.6,0l-0.8,0
c0.4,0-0.1,0-0.1,0l-0.3,0c-1.8,0-3.5-0.1-5.3-0.3c-7-0.5-13.9-1.8-20.7-3.7c-6.7-1.9-13.2-4.6-19.4-7.8
c-12.3-6.6-23.4-15.6-32-26.5c-4.3-5.4-8.1-11.3-11.2-17.4c-3.1-6.1-5.6-12.6-7.4-19.1c-1.8-6.6-2.9-13.3-3.4-20.1l-0.1-1.3l0-0.3
l0-0.3l0-0.6l0-1.1l0-0.3l0-0.4l0-0.8l0-1.6l0-0.3c0,0,0,0.1,0-0.1l0-0.6c0-0.8,0-1.7,0-2.5c0.1-3.3,0.4-6.8,0.8-10.2
c0.4-3.4,1-6.9,1.7-10.3c0.7-3.4,1.5-6.8,2.5-10.2c1.9-6.7,4.3-13.2,7.1-19.3c5.7-12.2,13.1-23.1,22-31.8c2.2-2.2,4.5-4.2,6.9-6.2
c2.4-1.9,4.9-3.7,7.5-5.4c2.5-1.7,5.2-3.2,7.9-4.6c1.3-0.7,2.7-1.4,4.1-2c0.7-0.3,1.4-0.6,2.1-0.9c0.7-0.3,1.4-0.6,2.1-0.9
c2.8-1.2,5.7-2.2,8.7-3.1c0.7-0.2,1.5-0.4,2.2-0.7c0.7-0.2,1.5-0.4,2.2-0.6c1.5-0.4,3-0.8,4.5-1.1c0.7-0.2,1.5-0.3,2.3-0.5
c0.8-0.2,1.5-0.3,2.3-0.5c0.8-0.1,1.5-0.3,2.3-0.4l1.1-0.2l1.2-0.2c0.8-0.1,1.5-0.2,2.3-0.3c0.9-0.1,1.7-0.2,2.6-0.3
c0.7-0.1,1.9-0.2,2.6-0.3c0.5-0.1,1.1-0.1,1.6-0.2l1.1-0.1l0.5-0.1l0.6,0c0.9-0.1,1.7-0.1,2.6-0.2l1.3-0.1c0,0,0.5,0,0.1,0l0.3,0
l0.6,0c0.7,0,1.5-0.1,2.2-0.1c2.9-0.1,5.9-0.1,8.8,0c5.8,0.2,11.5,0.9,17,1.9c11.1,2.1,21.5,5.6,31,10.3
c9.5,4.6,17.9,10.3,25.3,16.5c0.5,0.4,0.9,0.8,1.4,1.2c0.4,0.4,0.9,0.8,1.3,1.2c0.9,0.8,1.7,1.6,2.6,2.4c0.9,0.8,1.7,1.6,2.5,2.4
c0.8,0.8,1.6,1.6,2.4,2.5c3.1,3.3,6,6.6,8.6,10c5.2,6.7,9.4,13.5,12.7,19.9c0.2,0.4,0.4,0.8,0.6,1.2c0.2,0.4,0.4,0.8,0.6,1.2
c0.4,0.8,0.8,1.6,1.1,2.4c0.4,0.8,0.7,1.5,1.1,2.3c0.3,0.8,0.7,1.5,1,2.3c1.2,3,2.4,5.9,3.3,8.6c1.5,4.4,2.6,8.3,3.5,11.7
c0.3,1.4,1.6,2.3,3,2.1c1.5-0.1,2.6-1.3,2.6-2.8C342.6,170.4,342.5,166.1,342,161.2z"/>
</svg>

After

Width:  |  Height:  |  Size: 5.6 KiB

+57 -58
View File
@@ -1,58 +1,57 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="351px"
height="365px" viewBox="0 0 351 365" style="enable-background:new 0 0 351 365;" xml:space="preserve">
<style type="text/css">
.st0{fill:url(#SVGID_1_);}
</style>
<g id="Layer_1">
</g>
<g id="Layer_2">
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="170.963" y1="439.9414" x2="170.963" y2="106.0154">
<stop offset="0" style="stop-color:#FFF200"/>
<stop offset="1" style="stop-color:#F15A29"/>
</linearGradient>
<path class="st0" d="M228.8,239.8c-1.1-1.4-3.1-1.8-4.7-0.9c-0.4,0.2-0.8,0.4-1.2,0.6c-1.4,0.7-2.8,1.3-4.3,1.8
c-1.5,0.5-3.1,0.9-4.7,1.2c-0.8,0.1-1.6,0.2-2.5,0.3c-0.4,0-0.8,0.1-1.3,0.1c-0.4,0-0.9,0-1.2,0c-0.4,0-0.8,0-1.2,0
c-0.5,0-1,0-1.5-0.1c0,0-0.3,0-0.1,0l-0.2,0l-0.3,0c-0.2,0-0.5,0-0.7-0.1c-0.5-0.1-0.9-0.1-1.4-0.2c-3.7-0.5-7.4-1.6-10.9-3.2
c-3.6-1.6-7-3.8-10.1-6.6c-3.1-2.8-5.8-6.1-7.9-9.9c-2.1-3.8-3.6-8-4.3-12.4c-0.3-2.2-0.5-4.5-0.4-6.7c0-0.6,0.1-1.2,0.1-1.8
c0,0.2,0-0.1,0-0.1l0-0.2l0-0.5c0-0.3,0.1-0.6,0.1-0.9c0.1-1.2,0.3-2.4,0.5-3.6c1.7-9.6,6.5-19,13.9-26.1c1.9-1.8,3.9-3.4,6-4.9
c2.1-1.5,4.4-2.8,6.8-3.9c2.4-1.1,4.8-2,7.4-2.7c2.5-0.7,5.1-1.1,7.8-1.4c1.3-0.1,2.6-0.2,4-0.2c0.4,0,0.6,0,0.9,0l1.1,0l0.7,0
c0.3,0,0,0,0.1,0l0.3,0l1.1,0.1c2.9,0.2,5.7,0.6,8.5,1.3c5.6,1.2,11.1,3.3,16.2,6.1c10.2,5.7,18.9,14.5,24.2,25.1
c2.7,5.3,4.6,11,5.5,16.9c0.2,1.5,0.4,3,0.5,4.5l0.1,1.1l0.1,1.1c0,0.4,0,0.8,0,1.1c0,0.4,0,0.8,0,1.1l0,1l0,1.1
c0,0.7-0.1,1.9-0.1,2.6c-0.1,1.6-0.3,3.3-0.5,4.9c-0.2,1.6-0.5,3.2-0.8,4.8c-0.3,1.6-0.7,3.2-1.1,4.7c-0.8,3.1-1.8,6.2-3,9.3
c-2.4,6-5.6,11.8-9.4,17.1c-7.7,10.6-18.2,19.2-30.1,24.7c-6,2.7-12.3,4.7-18.8,5.7c-3.2,0.6-6.5,0.9-9.8,1l-0.6,0l-0.5,0l-1.1,0
l-1.6,0l-0.8,0c0.4,0-0.1,0-0.1,0l-0.3,0c-1.8,0-3.5-0.1-5.3-0.3c-7-0.5-13.9-1.8-20.7-3.7c-6.7-1.9-13.2-4.6-19.4-7.8
c-12.3-6.6-23.3-15.6-32-26.5c-4.3-5.4-8.1-11.3-11.2-17.4c-3.1-6.1-5.6-12.6-7.4-19.1c-1.8-6.6-2.9-13.3-3.4-20.1l-0.1-1.3l0-0.3
l0-0.3l0-0.6l0-1.1l0-0.3l0-0.4l0-0.8l0-1.6l0-0.3c0,0,0,0.1,0-0.1l0-0.6c0-0.8,0-1.7,0-2.5c0.1-3.3,0.4-6.8,0.8-10.2
c0.4-3.4,1-6.9,1.7-10.3c0.7-3.4,1.5-6.8,2.5-10.2c1.9-6.7,4.3-13.2,7.1-19.3c5.7-12.2,13.1-23.1,22-31.8c2.2-2.2,4.5-4.2,6.9-6.2
c2.4-1.9,4.9-3.7,7.5-5.4c2.5-1.7,5.2-3.2,7.9-4.6c1.3-0.7,2.7-1.4,4.1-2c0.7-0.3,1.4-0.6,2.1-0.9c0.7-0.3,1.4-0.6,2.1-0.9
c2.8-1.2,5.7-2.2,8.7-3.1c0.7-0.2,1.5-0.4,2.2-0.7c0.7-0.2,1.5-0.4,2.2-0.6c1.5-0.4,3-0.8,4.5-1.1c0.7-0.2,1.5-0.3,2.3-0.5
c0.8-0.2,1.5-0.3,2.3-0.5c0.8-0.1,1.5-0.3,2.3-0.4l1.1-0.2l1.1-0.2c0.8-0.1,1.5-0.2,2.3-0.3c0.9-0.1,1.7-0.2,2.6-0.3
c0.7-0.1,1.9-0.2,2.6-0.3c0.5-0.1,1.1-0.1,1.6-0.2l1.1-0.1l0.5-0.1l0.6,0c0.9-0.1,1.7-0.1,2.6-0.2l1.3-0.1c0,0,0.5,0,0.1,0l0.3,0
l0.6,0c0.7,0,1.5-0.1,2.2-0.1c2.9-0.1,5.9-0.1,8.8,0c5.8,0.2,11.5,0.9,17,1.9c11.1,2.1,21.5,5.6,30.9,10.3
c9.5,4.6,17.9,10.3,25.3,16.5c0.5,0.4,0.9,0.8,1.4,1.2c0.4,0.4,0.9,0.8,1.3,1.2c0.9,0.8,1.7,1.6,2.6,2.4c0.9,0.8,1.7,1.6,2.5,2.4
c0.8,0.8,1.6,1.6,2.4,2.5c3.1,3.3,6,6.6,8.6,10c5.2,6.7,9.4,13.5,12.7,19.9c0.2,0.4,0.4,0.8,0.6,1.2c0.2,0.4,0.4,0.8,0.6,1.2
c0.4,0.8,0.8,1.6,1.1,2.3c0.4,0.8,0.7,1.5,1.1,2.3c0.3,0.8,0.7,1.5,1,2.3c1.2,3,2.4,5.8,3.3,8.6c1.5,4.4,2.6,8.3,3.5,11.7
c0.3,1.4,1.6,2.3,3,2.1l0,0c1.5-0.1,2.6-1.3,2.6-2.8c0.1-3.7,0-8-0.4-12.9c-0.6-6.1-1.6-13.1-3.6-20.9c-2-7.7-5-16.2-9.4-25
c-4.4-8.8-10.1-17.9-17.5-26.8c-2.9-3.5-6.1-6.9-9.5-10.2c5.1-20.3-6.2-37.9-6.2-37.9c-19.5-1.2-31.9,6.1-36.5,9.4
c-0.8-0.3-1.5-0.7-2.3-1c-3.3-1.3-6.7-2.6-10.2-3.7c-3.5-1.1-7.1-2.1-10.8-3c-3.7-0.9-7.4-1.6-11.2-2.2c-0.7-0.1-1.3-0.2-2-0.3
C209.6,12.4,185.2,1,185.2,1c-27.3,17.3-32.4,41.5-32.4,41.5c0.2,0.4,0.5,0.8,0.7,1.1c-1.8,0.5-3.7,1.1-5.5,1.6
c-2.1,0.6-4.2,1.4-6.2,2.2c-2.1,0.8-4.1,1.6-6.2,2.5c-4.1,1.8-8.2,3.8-12.2,6c-4,2.2-7.9,4.7-11.7,7.4c-0.1,0.1-0.2,0.2-0.4,0.3
c-0.2-0.6-0.3-0.9-0.3-0.9c-37.7-14.4-71.3,2.9-71.3,2.9c-3.1,40.2,15.1,65.4,18.7,70c-0.9,2.5-1.7,5-2.5,7.5
c-2.8,9.1-4.9,18.4-6.2,28.1c-0.2,1.4-0.4,2.8-0.5,4.2C14.3,192.6,4,227.9,4,227.9c29.1,33.5,63,35.5,63,35.5c0,0,0.1-0.1,0.1-0.1
c4.3,7.7,9.3,15,14.9,21.9c2.4,2.9,4.8,5.6,7.4,8.3c-10.6,30.3,1.5,55.6,1.5,55.6c32.4,1.2,53.7-14.2,58.2-17.7
c3.2,1.1,6.5,2.1,9.8,2.9c10,2.6,20.2,4.1,30.3,4.5c2.5,0.1,5.1,0.2,7.6,0.1l1.2,0l0.8,0l1.6,0l1.6-0.1c0,0,0,0.1,0,0.1
c15.3,21.8,42.1,24.9,42.1,24.9c21.6-22.7,20.2-45.3,20.2-45.3c-0.2-0.2-0.4-0.3-0.6-0.5c4.2-2.9,8.2-6.1,12-9.5
c7.6-6.9,14.3-14.8,19.9-23.3c0.5-0.8,1-1.6,1.5-2.4c21.6,1.2,36.9-13.4,36.9-13.4c-4-25.2-19.6-36-19.6-36c-0.2,0-0.3,0.1-0.5,0.1
c0.2-1.5,0.3-3,0.4-4.5c0.2-2.4,0.2-4.9,0.2-7.3l0-1.8l0-0.9l0-0.5c0-0.6,0-0.4,0-0.6l-0.1-1.5l-0.1-2c0-0.7-0.1-1.3-0.2-1.9
c-0.1-0.6-0.1-1.3-0.2-1.9l-0.2-1.9l-0.3-1.9c-0.4-2.5-0.8-4.9-1.4-7.4c-2.3-9.7-6.1-18.9-11-27.2c-5-8.3-11.2-15.6-18.2-21.8
c-7-6.2-14.9-11.2-23.1-14.9c-8.2-3.7-16.9-6.1-25.5-7.2c-4.3-0.6-8.6-0.8-12.9-0.7l-1.6,0l-0.4,0c-0.1,0-0.6,0-0.5,0l-0.7,0
l-1.6,0.1c-0.6,0-1.2,0.1-1.7,0.1c-2.2,0.2-4.4,0.5-6.5,0.9c-8.6,1.6-16.6,4.7-23.8,9c-7.1,4.3-13.3,9.6-18.3,15.6
c-5,6-8.9,12.7-11.6,19.6c-2.7,6.9-4.2,14.1-4.6,21c-0.1,1.7-0.1,3.5-0.1,5.2c0,0.4,0,0.9,0,1.3l0.1,1.4c0.1,0.8,0.1,1.7,0.2,2.5
c0.3,3.5,1,6.9,1.9,10.1c1.9,6.5,4.9,12.4,8.6,17.4c3.7,5,8.2,9.1,12.9,12.4c4.7,3.2,9.8,5.5,14.8,7c5,1.5,10,2.1,14.7,2.1
c0.6,0,1.2,0,1.7,0c0.3,0,0.6,0,0.9,0c0.3,0,0.6,0,0.9-0.1c0.5,0,1-0.1,1.5-0.1c0.1,0,0.3,0,0.4-0.1l0.5-0.1c0.3,0,0.6-0.1,0.9-0.1
c0.6-0.1,1.1-0.2,1.7-0.3c0.6-0.1,1.1-0.2,1.6-0.4c1.1-0.2,2.1-0.6,3.1-0.9c2-0.7,3.9-1.5,5.7-2.4c1.8-0.9,3.4-2,5-3
c0.4-0.3,0.9-0.6,1.3-1C229.9,243.8,230.1,241.4,228.8,239.8L228.8,239.8z"/>
</g>
</svg>
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 20.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="351px" height="365px" viewBox="0 0 351 365" style="enable-background:new 0 0 351 365;" xml:space="preserve">
<style type="text/css">
.st0{fill:url(#SVGID_1_);}
</style>
<g id="Layer_1_1_">
</g>
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="175.5" y1="445.4948" x2="175.5" y2="114.0346">
<stop offset="0" style="stop-color:#FFF100"/>
<stop offset="1" style="stop-color:#F05A28"/>
</linearGradient>
<path class="st0" d="M342,161.2c-0.6-6.1-1.6-13.1-3.6-20.9c-2-7.7-5-16.2-9.4-25c-4.4-8.8-10.1-17.9-17.5-26.8
c-2.9-3.5-6.1-6.9-9.5-10.2c5.1-20.3-6.2-37.9-6.2-37.9c-19.5-1.2-31.9,6.1-36.5,9.4c-0.8-0.3-1.5-0.7-2.3-1
c-3.3-1.3-6.7-2.6-10.3-3.7c-3.5-1.1-7.1-2.1-10.8-3c-3.7-0.9-7.4-1.6-11.2-2.2c-0.7-0.1-1.3-0.2-2-0.3
c-8.5-27.2-32.9-38.6-32.9-38.6c-27.3,17.3-32.4,41.5-32.4,41.5s-0.1,0.5-0.3,1.4c-1.5,0.4-3,0.9-4.5,1.3c-2.1,0.6-4.2,1.4-6.2,2.2
c-2.1,0.8-4.1,1.6-6.2,2.5c-4.1,1.8-8.2,3.8-12.2,6c-3.9,2.2-7.7,4.6-11.4,7.1c-0.5-0.2-1-0.4-1-0.4c-37.8-14.4-71.3,2.9-71.3,2.9
c-3.1,40.2,15.1,65.5,18.7,70.1c-0.9,2.5-1.7,5-2.5,7.5c-2.8,9.1-4.9,18.4-6.2,28.1c-0.2,1.4-0.4,2.8-0.5,4.2
C18.8,192.7,8.5,228,8.5,228c29.1,33.5,63.1,35.6,63.1,35.6c0,0,0.1-0.1,0.1-0.1c4.3,7.7,9.3,15,14.9,21.9c2.4,2.9,4.8,5.6,7.4,8.3
c-10.6,30.4,1.5,55.6,1.5,55.6c32.4,1.2,53.7-14.2,58.2-17.7c3.2,1.1,6.5,2.1,9.8,2.9c10,2.6,20.2,4.1,30.4,4.5
c2.5,0.1,5.1,0.2,7.6,0.1l1.2,0l0.8,0l1.6,0l1.6-0.1l0,0.1c15.3,21.8,42.1,24.9,42.1,24.9c19.1-20.1,20.2-40.1,20.2-44.4l0,0
c0,0,0-0.1,0-0.3c0-0.4,0-0.6,0-0.6l0,0c0-0.3,0-0.6,0-0.9c4-2.8,7.8-5.8,11.4-9.1c7.6-6.9,14.3-14.8,19.9-23.3
c0.5-0.8,1-1.6,1.5-2.4c21.6,1.2,36.9-13.4,36.9-13.4c-3.6-22.5-16.4-33.5-19.1-35.6l0,0c0,0-0.1-0.1-0.3-0.2
c-0.2-0.1-0.2-0.2-0.2-0.2c0,0,0,0,0,0c-0.1-0.1-0.3-0.2-0.5-0.3c0.1-1.4,0.2-2.7,0.3-4.1c0.2-2.4,0.2-4.9,0.2-7.3l0-1.8l0-0.9
l0-0.5c0-0.6,0-0.4,0-0.6l-0.1-1.5l-0.1-2c0-0.7-0.1-1.3-0.2-1.9c-0.1-0.6-0.1-1.3-0.2-1.9l-0.2-1.9l-0.3-1.9
c-0.4-2.5-0.8-4.9-1.4-7.4c-2.3-9.7-6.1-18.9-11-27.2c-5-8.3-11.2-15.6-18.3-21.8c-7-6.2-14.9-11.2-23.1-14.9
c-8.3-3.7-16.9-6.1-25.5-7.2c-4.3-0.6-8.6-0.8-12.9-0.7l-1.6,0l-0.4,0c-0.1,0-0.6,0-0.5,0l-0.7,0l-1.6,0.1c-0.6,0-1.2,0.1-1.7,0.1
c-2.2,0.2-4.4,0.5-6.5,0.9c-8.6,1.6-16.7,4.7-23.8,9c-7.1,4.3-13.3,9.6-18.3,15.6c-5,6-8.9,12.7-11.6,19.6c-2.7,6.9-4.2,14.1-4.6,21
c-0.1,1.7-0.1,3.5-0.1,5.2c0,0.4,0,0.9,0,1.3l0.1,1.4c0.1,0.8,0.1,1.7,0.2,2.5c0.3,3.5,1,6.9,1.9,10.1c1.9,6.5,4.9,12.4,8.6,17.4
c3.7,5,8.2,9.1,12.9,12.4c4.7,3.2,9.8,5.5,14.8,7c5,1.5,10,2.1,14.7,2.1c0.6,0,1.2,0,1.7,0c0.3,0,0.6,0,0.9,0c0.3,0,0.6,0,0.9-0.1
c0.5,0,1-0.1,1.5-0.1c0.1,0,0.3,0,0.4-0.1l0.5-0.1c0.3,0,0.6-0.1,0.9-0.1c0.6-0.1,1.1-0.2,1.7-0.3c0.6-0.1,1.1-0.2,1.6-0.4
c1.1-0.2,2.1-0.6,3.1-0.9c2-0.7,4-1.5,5.7-2.4c1.8-0.9,3.4-2,5-3c0.4-0.3,0.9-0.6,1.3-1c1.6-1.3,1.9-3.7,0.6-5.3
c-1.1-1.4-3.1-1.8-4.7-0.9c-0.4,0.2-0.8,0.4-1.2,0.6c-1.4,0.7-2.8,1.3-4.3,1.8c-1.5,0.5-3.1,0.9-4.7,1.2c-0.8,0.1-1.6,0.2-2.5,0.3
c-0.4,0-0.8,0.1-1.3,0.1c-0.4,0-0.9,0-1.2,0c-0.4,0-0.8,0-1.2,0c-0.5,0-1,0-1.5-0.1c0,0-0.3,0-0.1,0l-0.2,0l-0.3,0
c-0.2,0-0.5,0-0.7-0.1c-0.5-0.1-0.9-0.1-1.4-0.2c-3.7-0.5-7.4-1.6-10.9-3.2c-3.6-1.6-7-3.8-10.1-6.6c-3.1-2.8-5.8-6.1-7.9-9.9
c-2.1-3.8-3.6-8-4.3-12.4c-0.3-2.2-0.5-4.5-0.4-6.7c0-0.6,0.1-1.2,0.1-1.8c0,0.2,0-0.1,0-0.1l0-0.2l0-0.5c0-0.3,0.1-0.6,0.1-0.9
c0.1-1.2,0.3-2.4,0.5-3.6c1.7-9.6,6.5-19,13.9-26.1c1.9-1.8,3.9-3.4,6-4.9c2.1-1.5,4.4-2.8,6.8-3.9c2.4-1.1,4.8-2,7.4-2.7
c2.5-0.7,5.1-1.1,7.8-1.4c1.3-0.1,2.6-0.2,4-0.2c0.4,0,0.6,0,0.9,0l1.1,0l0.7,0c0.3,0,0,0,0.1,0l0.3,0l1.1,0.1
c2.9,0.2,5.7,0.6,8.5,1.3c5.6,1.2,11.1,3.3,16.2,6.1c10.2,5.7,18.9,14.5,24.2,25.1c2.7,5.3,4.6,11,5.5,16.9c0.2,1.5,0.4,3,0.5,4.5
l0.1,1.1l0.1,1.1c0,0.4,0,0.8,0,1.1c0,0.4,0,0.8,0,1.1l0,1l0,1.1c0,0.7-0.1,1.9-0.1,2.6c-0.1,1.6-0.3,3.3-0.5,4.9
c-0.2,1.6-0.5,3.2-0.8,4.8c-0.3,1.6-0.7,3.2-1.1,4.7c-0.8,3.1-1.8,6.2-3,9.3c-2.4,6-5.6,11.8-9.4,17.1
c-7.7,10.6-18.2,19.2-30.2,24.7c-6,2.7-12.3,4.7-18.8,5.7c-3.2,0.6-6.5,0.9-9.8,1l-0.6,0l-0.5,0l-1.1,0l-1.6,0l-0.8,0
c0.4,0-0.1,0-0.1,0l-0.3,0c-1.8,0-3.5-0.1-5.3-0.3c-7-0.5-13.9-1.8-20.7-3.7c-6.7-1.9-13.2-4.6-19.4-7.8
c-12.3-6.6-23.4-15.6-32-26.5c-4.3-5.4-8.1-11.3-11.2-17.4c-3.1-6.1-5.6-12.6-7.4-19.1c-1.8-6.6-2.9-13.3-3.4-20.1l-0.1-1.3l0-0.3
l0-0.3l0-0.6l0-1.1l0-0.3l0-0.4l0-0.8l0-1.6l0-0.3c0,0,0,0.1,0-0.1l0-0.6c0-0.8,0-1.7,0-2.5c0.1-3.3,0.4-6.8,0.8-10.2
c0.4-3.4,1-6.9,1.7-10.3c0.7-3.4,1.5-6.8,2.5-10.2c1.9-6.7,4.3-13.2,7.1-19.3c5.7-12.2,13.1-23.1,22-31.8c2.2-2.2,4.5-4.2,6.9-6.2
c2.4-1.9,4.9-3.7,7.5-5.4c2.5-1.7,5.2-3.2,7.9-4.6c1.3-0.7,2.7-1.4,4.1-2c0.7-0.3,1.4-0.6,2.1-0.9c0.7-0.3,1.4-0.6,2.1-0.9
c2.8-1.2,5.7-2.2,8.7-3.1c0.7-0.2,1.5-0.4,2.2-0.7c0.7-0.2,1.5-0.4,2.2-0.6c1.5-0.4,3-0.8,4.5-1.1c0.7-0.2,1.5-0.3,2.3-0.5
c0.8-0.2,1.5-0.3,2.3-0.5c0.8-0.1,1.5-0.3,2.3-0.4l1.1-0.2l1.2-0.2c0.8-0.1,1.5-0.2,2.3-0.3c0.9-0.1,1.7-0.2,2.6-0.3
c0.7-0.1,1.9-0.2,2.6-0.3c0.5-0.1,1.1-0.1,1.6-0.2l1.1-0.1l0.5-0.1l0.6,0c0.9-0.1,1.7-0.1,2.6-0.2l1.3-0.1c0,0,0.5,0,0.1,0l0.3,0
l0.6,0c0.7,0,1.5-0.1,2.2-0.1c2.9-0.1,5.9-0.1,8.8,0c5.8,0.2,11.5,0.9,17,1.9c11.1,2.1,21.5,5.6,31,10.3
c9.5,4.6,17.9,10.3,25.3,16.5c0.5,0.4,0.9,0.8,1.4,1.2c0.4,0.4,0.9,0.8,1.3,1.2c0.9,0.8,1.7,1.6,2.6,2.4c0.9,0.8,1.7,1.6,2.5,2.4
c0.8,0.8,1.6,1.6,2.4,2.5c3.1,3.3,6,6.6,8.6,10c5.2,6.7,9.4,13.5,12.7,19.9c0.2,0.4,0.4,0.8,0.6,1.2c0.2,0.4,0.4,0.8,0.6,1.2
c0.4,0.8,0.8,1.6,1.1,2.4c0.4,0.8,0.7,1.5,1.1,2.3c0.3,0.8,0.7,1.5,1,2.3c1.2,3,2.4,5.9,3.3,8.6c1.5,4.4,2.6,8.3,3.5,11.7
c0.3,1.4,1.6,2.3,3,2.1c1.5-0.1,2.6-1.3,2.6-2.8C342.6,170.4,342.5,166.1,342,161.2z"/>
</svg>

Before

Width:  |  Height:  |  Size: 5.6 KiB

After

Width:  |  Height:  |  Size: 5.6 KiB

+2
View File
@@ -75,6 +75,8 @@
@import "components/jsontree";
@import "components/edit_sidemenu.scss";
@import "components/row.scss";
@import "components/json_explorer.scss";
@import "components/collapse_box.scss";
// PAGES
@import "pages/login";
+18
View File
@@ -291,6 +291,24 @@ $card-shadow: -1px -1px 0 0 hsla(0, 0%, 100%, .1), 1px 1px 0 0 rgba(0, 0, 0, .3)
$footer-link-color: $gray-1;
$footer-link-hover: $gray-4;
// collapse box
$collapse-box-body-border: $dark-5;
$collapse-box-body-error-border: $red;
// json-explorer
$json-explorer-default-color: $text-color;
$json-explorer-string-color: #23d662;
$json-explorer-number-color: $variable;
$json-explorer-boolean-color: $variable;
$json-explorer-null-color: #EEC97D;
$json-explorer-undefined-color: rgb(239, 143, 190);
$json-explorer-function-color: #FD48CB;
$json-explorer-rotate-time: 100ms;
$json-explorer-toggler-opacity: 0.6;
$json-explorer-toggler-color: #45376F;
$json-explorer-bracket-color: #9494FF;
$json-explorer-key-color: #23A0DB;
$json-explorer-url-color: #027BFF;
// Changelog and diff
// -------------------------
+18
View File
@@ -313,6 +313,24 @@ $card-shadow: -1px -1px 0 0 hsla(0, 0%, 100%, .1), 1px 1px 0 0 rgba(0, 0, 0, .1)
$footer-link-color: $gray-3;
$footer-link-hover: $dark-5;
// collapse box
$collapse-box-body-border: $gray-4;
$collapse-box-body-error-border: $red;
// json explorer
$json-explorer-default-color: black;
$json-explorer-string-color: green;
$json-explorer-number-color: blue;
$json-explorer-boolean-color: red;
$json-explorer-null-color: #855A00;
$json-explorer-undefined-color: rgb(202, 11, 105);
$json-explorer-function-color: #FF20ED;
$json-explorer-rotate-time: 100ms;
$json-explorer-toggler-opacity: 0.6;
$json-explorer-toggler-color: #45376F;
$json-explorer-bracket-color: blue;
$json-explorer-key-color: #00008B;
$json-explorer-url-color: blue;
// Changelog and diff
// -------------------------
+46
View File
@@ -0,0 +1,46 @@
.collapse-box {
margin-bottom: $spacer;
&--error {
.collapse-box__header {
border-color: $collapse-box-body-error-border;
}
.collapse-box__body {
border-color: $collapse-box-body-error-border;
}
}
}
.collapse-box__header {
display: flex;
flex-direction: row;
padding: $input-padding-y $input-padding-x;
margin-right: $gf-form-margin;
background-color: $input-label-bg;
font-size: $font-size-sm;
margin-right: $gf-form-margin;
border: $input-btn-border-width solid $collapse-box-body-border;
@include border-radius($label-border-radius-sm);
}
.collapse-box__header-title {
flex-grow: 1;
}
.collapse-box__body {
padding: $input-padding-y*2 $input-padding-x;
display: block;
margin-right: $gf-form-margin;
border: $input-btn-border-width solid $collapse-box-body-border;
border-top: none;
@include border-radius($label-border-radius-sm);
}
.collapse-box__header-actions {
display: flex;
flex-direction: row;
a {
margin-left: $spacer;
}
}
+1
View File
@@ -103,6 +103,7 @@ $gf-form-margin: 0.25rem;
padding: $input-padding-y $input-padding-x;
margin-right: $gf-form-margin;
font-size: $font-size-base;
margin-right: $gf-form-margin;
line-height: $input-line-height;
color: $input-color;
background-color: $input-bg;
@@ -0,0 +1,98 @@
.json-formatter-row {
font-family: monospace;
&, a, a:hover {
color: $json-explorer-default-color;
text-decoration: none;
}
.json-formatter-row {
margin-left: 1rem;
}
.json-formatter-children {
&.json-formatter-empty {
opacity: 0.5;
margin-left: 1rem;
&::after { display: none; }
&.json-formatter-object::after { content: "No properties"; }
&.json-formatter-array::after { content: "[]"; }
}
}
.json-formatter-string {
color: $json-explorer-string-color;
white-space: normal;
word-wrap: break-word;
}
.json-formatter-number { color: $json-explorer-number-color; }
.json-formatter-boolean { color: $json-explorer-boolean-color; }
.json-formatter-null { color: $json-explorer-null-color; }
.json-formatter-undefined { color: $json-explorer-undefined-color; }
.json-formatter-function { color: $json-explorer-function-color; }
.json-formatter-date { background-color: fade($json-explorer-default-color, 5%); }
.json-formatter-url {
text-decoration: underline;
color: $json-explorer-url-color;
cursor: pointer;
}
.json-formatter-bracket { color: $json-explorer-bracket-color; }
.json-formatter-key {
color: $json-explorer-key-color;
cursor: pointer;
padding-right: 0.2rem;
margin-right: 4px;
}
.json-formatter-constructor-name {
cursor: pointer;
}
.json-formatter-array-comma { margin-right: 4px; }
.json-formatter-toggler {
line-height: 1.2rem;
font-size: 0.7rem;
vertical-align: middle;
opacity: $json-explorer-toggler-opacity;
cursor: pointer;
padding-right: 0.2rem;
&::after {
display: inline-block;
transition: transform $json-explorer-rotate-time ease-in;
content: "►";
}
}
// Inline preview on hover (optional)
> a > .json-formatter-preview-text {
opacity: 0;
transition: opacity .15s ease-in;
font-style: italic;
}
&:hover > a > .json-formatter-preview-text {
opacity: 0.6;
}
// Open state
&.json-formatter-open {
> .json-formatter-toggler-link .json-formatter-toggler::after{
transform: rotate(90deg);
}
> .json-formatter-children::after {
display: inline-block;
}
> a > .json-formatter-preview-text {
display: none;
}
&.json-formatter-empty::after {
display: block;
}
}
}
+1
View File
@@ -1,3 +1,4 @@
@import "variables";
@import "variables.dark";
@import "grafana";
+1
View File
@@ -119,6 +119,7 @@
img {
width: 19px;
vertical-align: sub;
}
}
}
+1 -1
View File
@@ -40,7 +40,7 @@
"jquery.flot.fillbelow": "vendor/flot/jquery.flot.fillbelow",
"jquery.flot.gauge": "vendor/flot/jquery.flot.gauge",
"d3": "vendor/d3/d3.js",
"jquery.flot.dashes": "vendor/flot/jquery.flot.dashes"
"jquery.flot.dashes": "vendor/flot/jquery.flot.dashes",
},
packages: {
+8 -8
View File
@@ -1663,9 +1663,9 @@ glob@7.0.5:
once "^1.3.0"
path-is-absolute "^1.0.0"
glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@~7.0.0:
version "7.0.6"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.0.6.tgz#211bafaf49e525b8cd93260d14ab136152b3f57a"
glob@^7.0.0, glob@^7.1.1, glob@~7.1.1:
version "7.1.1"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.1.tgz#805211df04faaf1c63a3600306cdf5ade50b2ec8"
dependencies:
fs.realpath "^1.0.0"
inflight "^1.0.4"
@@ -1674,9 +1674,9 @@ glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@~7.0.0:
once "^1.3.0"
path-is-absolute "^1.0.0"
glob@^7.1.1, glob@~7.1.1:
version "7.1.1"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.1.tgz#805211df04faaf1c63a3600306cdf5ade50b2ec8"
glob@^7.0.3, glob@^7.0.5, glob@~7.0.0:
version "7.0.6"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.0.6.tgz#211bafaf49e525b8cd93260d14ab136152b3f57a"
dependencies:
fs.realpath "^1.0.0"
inflight "^1.0.4"
@@ -3807,11 +3807,11 @@ resolve-pkg@^0.1.0:
dependencies:
resolve-from "^2.0.0"
resolve@1.1.x, resolve@^1.1.6, resolve@~1.1.0:
resolve@1.1.x, resolve@~1.1.0:
version "1.1.7"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b"
resolve@^1.3.2:
resolve@^1.1.6, resolve@^1.3.2:
version "1.3.3"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.3.3.tgz#655907c3469a8680dc2de3a275a8fdd69691f0e5"
dependencies: