mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Merge branch 'master' into query-editor-style
This commit is contained in:
commit
1a433de0b4
14
CHANGELOG.md
14
CHANGELOG.md
@ -1,3 +1,17 @@
|
|||||||
|
# 3.0.0-beta6 (unreleased)
|
||||||
|
|
||||||
|
### Enhancements
|
||||||
|
* **Singlestat**: Support for gauges in singlestat panel. closes [#3688](https://github.com/grafana/grafana/pull/3688)
|
||||||
|
|
||||||
|
### Bug fixes
|
||||||
|
* **InfluxDB 0.12**: Fixed issue templating and `show tag values` query only returning tags for first measurement, fixes [#4726](https://github.com/grafana/grafana/issues/4726)
|
||||||
|
* **Templating**: Fixed issue with regex formating when matching multiple values, fixes [#4755](https://github.com/grafana/grafana/issues/4755)
|
||||||
|
* **Templating**: Fixed issue with custom all value and escaping, fixes [#4736](https://github.com/grafana/grafana/issues/4736)
|
||||||
|
* **Dashlist**: Fixed issue dashboard list panel and caching tags, fixes [#4768](https://github.com/grafana/grafana/issues/4768)
|
||||||
|
* **Graph**: Fixed issue with unneeded scrollbar in legend for Firefox, fixes [#4760](https://github.com/grafana/grafana/issues/4760)
|
||||||
|
* **Table panel**: Fixed issue table panel formating string array properties, fixes [#4791](https://github.com/grafana/grafana/issues/4791)
|
||||||
|
* **grafana-cli**: Improve error message when failing to install plugins due to corrupt response, fixes [#4651](https://github.com/grafana/grafana/issues/4651)
|
||||||
|
|
||||||
# 3.0.0-beta5 (2016-04-15)
|
# 3.0.0-beta5 (2016-04-15)
|
||||||
|
|
||||||
### Bug fixes
|
### Bug fixes
|
||||||
|
@ -191,7 +191,7 @@ Will return the home dashboard.
|
|||||||
|
|
||||||
`GET /api/dashboards/tags`
|
`GET /api/dashboards/tags`
|
||||||
|
|
||||||
Get all tabs of dashboards
|
Get all tags of dashboards
|
||||||
|
|
||||||
**Example Request**:
|
**Example Request**:
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ Grafana already have a strong community of contributors and plugin developers.
|
|||||||
By making it easier to develop and install plugins we hope that the community
|
By making it easier to develop and install plugins we hope that the community
|
||||||
can grow even stronger and develop new plugins that we would never think about.
|
can grow even stronger and develop new plugins that we would never think about.
|
||||||
|
|
||||||
You can discover available plugins on [Grafana.net](http://grafana.net)
|
You can discover available plugins on [Grafana.net](https://grafana.net)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -54,7 +54,7 @@
|
|||||||
"phantomjs-prebuilt": "^2.1.3",
|
"phantomjs-prebuilt": "^2.1.3",
|
||||||
"reflect-metadata": "0.1.2",
|
"reflect-metadata": "0.1.2",
|
||||||
"rxjs": "5.0.0-beta.4",
|
"rxjs": "5.0.0-beta.4",
|
||||||
"sass-lint": "^1.5.0",
|
"sass-lint": "^1.6.0",
|
||||||
"systemjs": "0.19.24"
|
"systemjs": "0.19.24"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
@ -56,7 +56,7 @@ func init() {
|
|||||||
"HbaseBackupFailed", "MostRecentBackupDuration", "TimeSinceLastSuccessfulBackup"},
|
"HbaseBackupFailed", "MostRecentBackupDuration", "TimeSinceLastSuccessfulBackup"},
|
||||||
"AWS/ES": {"ClusterStatus.green", "ClusterStatus.yellow", "ClusterStatus.red", "Nodes", "SearchableDocuments", "DeletedDocuments", "CPUUtilization", "FreeStorageSpace", "JVMMemoryPressure", "AutomatedSnapshotFailure", "MasterCPUUtilization", "MasterFreeStorageSpace", "MasterJVMMemoryPressure", "ReadLatency", "WriteLatency", "ReadThroughput", "WriteThroughput", "DiskQueueLength", "ReadIOPS", "WriteIOPS"},
|
"AWS/ES": {"ClusterStatus.green", "ClusterStatus.yellow", "ClusterStatus.red", "Nodes", "SearchableDocuments", "DeletedDocuments", "CPUUtilization", "FreeStorageSpace", "JVMMemoryPressure", "AutomatedSnapshotFailure", "MasterCPUUtilization", "MasterFreeStorageSpace", "MasterJVMMemoryPressure", "ReadLatency", "WriteLatency", "ReadThroughput", "WriteThroughput", "DiskQueueLength", "ReadIOPS", "WriteIOPS"},
|
||||||
"AWS/Events": {"Invocations", "FailedInvocations", "TriggeredRules", "MatchedEvents", "ThrottledRules"},
|
"AWS/Events": {"Invocations", "FailedInvocations", "TriggeredRules", "MatchedEvents", "ThrottledRules"},
|
||||||
"AWS/Kinesis": {"PutRecord.Bytes", "PutRecord.Latency", "PutRecord.Success", "PutRecords.Bytes", "PutRecords.Latency", "PutRecords.Records", "PutRecords.Success", "IncomingBytes", "IncomingRecords", "GetRecords.Bytes", "GetRecords.IteratorAgeMilliseconds", "GetRecords.Latency", "GetRecords.Success"},
|
"AWS/Kinesis": {"GetRecords.Bytes", "GetRecords.IteratorAge", "GetRecords.IteratorAgeMilliseconds", "GetRecords.Latency", "GetRecords.Records", "GetRecords.Success", "IncomingBytes", "IncomingRecords", "PutRecord.Bytes", "PutRecord.Latency", "PutRecord.Success", "PutRecords.Bytes", "PutRecords.Latency", "PutRecords.Records", "PutRecords.Success", "ReadProvisionedThroughputExceeded", "WriteProvisionedThroughputExceeded", "IteratorAgeMilliseconds", "OutgoingBytes", "OutgoingRecords"},
|
||||||
"AWS/Lambda": {"Invocations", "Errors", "Duration", "Throttles"},
|
"AWS/Lambda": {"Invocations", "Errors", "Duration", "Throttles"},
|
||||||
"AWS/Logs": {"IncomingBytes", "IncomingLogEvents", "ForwardedBytes", "ForwardedLogEvents", "DeliveryErrors", "DeliveryThrottling"},
|
"AWS/Logs": {"IncomingBytes", "IncomingLogEvents", "ForwardedBytes", "ForwardedLogEvents", "DeliveryErrors", "DeliveryThrottling"},
|
||||||
"AWS/ML": {"PredictCount", "PredictFailureCount"},
|
"AWS/ML": {"PredictCount", "PredictFailureCount"},
|
||||||
@ -88,7 +88,7 @@ func init() {
|
|||||||
"AWS/ElasticMapReduce": {"ClusterId", "JobFlowId", "JobId"},
|
"AWS/ElasticMapReduce": {"ClusterId", "JobFlowId", "JobId"},
|
||||||
"AWS/ES": {},
|
"AWS/ES": {},
|
||||||
"AWS/Events": {"RuleName"},
|
"AWS/Events": {"RuleName"},
|
||||||
"AWS/Kinesis": {"StreamName"},
|
"AWS/Kinesis": {"StreamName", "ShardID"},
|
||||||
"AWS/Lambda": {"FunctionName"},
|
"AWS/Lambda": {"FunctionName"},
|
||||||
"AWS/Logs": {"LogGroupName", "DestinationType", "FilterName"},
|
"AWS/Logs": {"LogGroupName", "DestinationType", "FilterName"},
|
||||||
"AWS/ML": {"MLModelId", "RequestMode"},
|
"AWS/ML": {"MLModelId", "RequestMode"},
|
||||||
|
@ -21,6 +21,10 @@ func GetSharingOptions(c *middleware.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func CreateDashboardSnapshot(c *middleware.Context, cmd m.CreateDashboardSnapshotCommand) {
|
func CreateDashboardSnapshot(c *middleware.Context, cmd m.CreateDashboardSnapshotCommand) {
|
||||||
|
if cmd.Name == "" {
|
||||||
|
cmd.Name = "Unnamed snapshot"
|
||||||
|
}
|
||||||
|
|
||||||
if cmd.External {
|
if cmd.External {
|
||||||
// external snapshot ref requires key and delete key
|
// external snapshot ref requires key and delete key
|
||||||
if cmd.Key == "" || cmd.DeleteKey == "" {
|
if cmd.Key == "" || cmd.DeleteKey == "" {
|
||||||
|
@ -41,7 +41,6 @@ func NewReverseProxy(ds *m.DataSource, proxyPath string, targetUrl *url.URL) *ht
|
|||||||
req.URL.RawQuery = reqQueryVals.Encode()
|
req.URL.RawQuery = reqQueryVals.Encode()
|
||||||
} else if ds.Type == m.DS_INFLUXDB {
|
} else if ds.Type == m.DS_INFLUXDB {
|
||||||
req.URL.Path = util.JoinUrlFragments(targetUrl.Path, proxyPath)
|
req.URL.Path = util.JoinUrlFragments(targetUrl.Path, proxyPath)
|
||||||
reqQueryVals.Add("db", ds.Database)
|
|
||||||
req.URL.RawQuery = reqQueryVals.Encode()
|
req.URL.RawQuery = reqQueryVals.Encode()
|
||||||
if !ds.BasicAuth {
|
if !ds.BasicAuth {
|
||||||
req.Header.Del("Authorization")
|
req.Header.Del("Authorization")
|
||||||
|
@ -103,6 +103,10 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, include := range plugin.Includes {
|
for _, include := range plugin.Includes {
|
||||||
|
if !c.HasUserRole(include.Role) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
if include.Type == "page" && include.AddToNav {
|
if include.Type == "page" && include.AddToNav {
|
||||||
link := &dtos.NavLink{
|
link := &dtos.NavLink{
|
||||||
Url: setting.AppSubUrl + "/plugins/" + plugin.Id + "/page/" + include.Slug,
|
Url: setting.AppSubUrl + "/plugins/" + plugin.Id + "/page/" + include.Slug,
|
||||||
@ -110,6 +114,7 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) {
|
|||||||
}
|
}
|
||||||
appLink.Children = append(appLink.Children, link)
|
appLink.Children = append(appLink.Children, link)
|
||||||
}
|
}
|
||||||
|
|
||||||
if include.Type == "dashboard" && include.AddToNav {
|
if include.Type == "dashboard" && include.AddToNav {
|
||||||
link := &dtos.NavLink{
|
link := &dtos.NavLink{
|
||||||
Url: setting.AppSubUrl + "/dashboard/db/" + include.Slug,
|
Url: setting.AppSubUrl + "/dashboard/db/" + include.Slug,
|
||||||
@ -124,7 +129,9 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) {
|
|||||||
appLink.Children = append(appLink.Children, &dtos.NavLink{Text: "Plugin Config", Icon: "fa fa-cog", Url: setting.AppSubUrl + "/plugins/" + plugin.Id + "/edit"})
|
appLink.Children = append(appLink.Children, &dtos.NavLink{Text: "Plugin Config", Icon: "fa fa-cog", Url: setting.AppSubUrl + "/plugins/" + plugin.Id + "/edit"})
|
||||||
}
|
}
|
||||||
|
|
||||||
data.MainNavLinks = append(data.MainNavLinks, appLink)
|
if len(appLink.Children) > 0 {
|
||||||
|
data.MainNavLinks = append(data.MainNavLinks, appLink)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/codegangsta/cli"
|
"github.com/codegangsta/cli"
|
||||||
|
"github.com/fatih/color"
|
||||||
"github.com/grafana/grafana/pkg/cmd/grafana-cli/log"
|
"github.com/grafana/grafana/pkg/cmd/grafana-cli/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -12,7 +13,7 @@ func runCommand(command func(commandLine CommandLine) error) func(context *cli.C
|
|||||||
|
|
||||||
cmd := &contextCommandLine{context}
|
cmd := &contextCommandLine{context}
|
||||||
if err := command(cmd); err != nil {
|
if err := command(cmd); err != nil {
|
||||||
log.Error("\nError: ")
|
log.Errorf("\n%s: ", color.RedString("Error"))
|
||||||
log.Errorf("%s\n\n", err)
|
log.Errorf("%s\n\n", err)
|
||||||
|
|
||||||
cmd.ShowHelp()
|
cmd.ShowHelp()
|
||||||
|
@ -126,11 +126,16 @@ func downloadFile(pluginName, filePath, url string) (err error) {
|
|||||||
defer func() {
|
defer func() {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
retryCount++
|
retryCount++
|
||||||
if retryCount == 1 {
|
if retryCount < 3 {
|
||||||
log.Debug("\nFailed downloading. Will retry once.\n")
|
fmt.Println("Failed downloading. Will retry once.")
|
||||||
downloadFile(pluginName, filePath, url)
|
err = downloadFile(pluginName, filePath, url)
|
||||||
} else {
|
} else {
|
||||||
panic(r)
|
failure := fmt.Sprintf("%v", r)
|
||||||
|
if failure == "runtime error: makeslice: len out of range" {
|
||||||
|
err = fmt.Errorf("Corrupt http response from source. Please try again.\n")
|
||||||
|
} else {
|
||||||
|
panic(r)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
@ -164,14 +169,14 @@ func downloadFile(pluginName, filePath, url string) (err error) {
|
|||||||
return fmt.Errorf(permissionsDeniedMessage, newFile)
|
return fmt.Errorf(permissionsDeniedMessage, newFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
defer dst.Close()
|
|
||||||
src, err := zf.Open()
|
src, err := zf.Open()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("%v", err)
|
log.Errorf("Failed to extract file: %v", err)
|
||||||
}
|
}
|
||||||
defer src.Close()
|
|
||||||
|
|
||||||
io.Copy(dst, src)
|
io.Copy(dst, src)
|
||||||
|
dst.Close()
|
||||||
|
src.Close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@ package commands
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/cmd/grafana-cli/log"
|
"fmt"
|
||||||
m "github.com/grafana/grafana/pkg/cmd/grafana-cli/models"
|
m "github.com/grafana/grafana/pkg/cmd/grafana-cli/models"
|
||||||
services "github.com/grafana/grafana/pkg/cmd/grafana-cli/services"
|
services "github.com/grafana/grafana/pkg/cmd/grafana-cli/services"
|
||||||
)
|
)
|
||||||
@ -15,22 +15,17 @@ func removeCommand(c CommandLine) error {
|
|||||||
pluginPath := c.GlobalString("pluginsDir")
|
pluginPath := c.GlobalString("pluginsDir")
|
||||||
localPlugins := getPluginss(pluginPath)
|
localPlugins := getPluginss(pluginPath)
|
||||||
|
|
||||||
log.Info("remove!\n")
|
|
||||||
|
|
||||||
plugin := c.Args().First()
|
plugin := c.Args().First()
|
||||||
log.Info("plugin: " + plugin + "\n")
|
|
||||||
if plugin == "" {
|
if plugin == "" {
|
||||||
return errors.New("Missing plugin parameter")
|
return errors.New("Missing plugin parameter")
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Infof("plugins : \n%v\n", localPlugins)
|
|
||||||
|
|
||||||
for _, p := range localPlugins {
|
for _, p := range localPlugins {
|
||||||
if p.Id == c.Args().First() {
|
if p.Id == c.Args().First() {
|
||||||
log.Infof("removing plugin %s", p.Id)
|
|
||||||
removePlugin(pluginPath, p.Id)
|
removePlugin(pluginPath, p.Id)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return fmt.Errorf("Could not find plugin named %s", c.Args().First())
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,6 @@ import (
|
|||||||
"github.com/codegangsta/cli"
|
"github.com/codegangsta/cli"
|
||||||
"github.com/grafana/grafana/pkg/cmd/grafana-cli/commands"
|
"github.com/grafana/grafana/pkg/cmd/grafana-cli/commands"
|
||||||
"github.com/grafana/grafana/pkg/cmd/grafana-cli/log"
|
"github.com/grafana/grafana/pkg/cmd/grafana-cli/log"
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var version = "master"
|
var version = "master"
|
||||||
@ -18,7 +17,7 @@ func getGrafanaPluginDir() string {
|
|||||||
defaultNix := "/var/lib/grafana/plugins"
|
defaultNix := "/var/lib/grafana/plugins"
|
||||||
|
|
||||||
if currentOS == "windows" {
|
if currentOS == "windows" {
|
||||||
return "C:\\opt\\grafana\\plugins"
|
return "../data/plugins"
|
||||||
}
|
}
|
||||||
|
|
||||||
pwd, err := os.Getwd()
|
pwd, err := os.Getwd()
|
||||||
@ -29,16 +28,17 @@ func getGrafanaPluginDir() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if isDevenvironment(pwd) {
|
if isDevenvironment(pwd) {
|
||||||
return "../../../data/plugins"
|
return "../data/plugins"
|
||||||
}
|
}
|
||||||
|
|
||||||
return defaultNix
|
return defaultNix
|
||||||
}
|
}
|
||||||
|
|
||||||
func isDevenvironment(pwd string) bool {
|
func isDevenvironment(pwd string) bool {
|
||||||
// if grafana-cli is executed from the cmd folder we can assume
|
// if ../conf/defaults.ini exists, grafana is not installed as package
|
||||||
// that its in development environment.
|
// that its in development environment.
|
||||||
return strings.HasSuffix(pwd, "/pkg/cmd/grafana-cli")
|
_, err := os.Stat("../conf/defaults.ini")
|
||||||
|
return err == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
@ -45,7 +45,7 @@ type DashboardSnapshotDTO struct {
|
|||||||
|
|
||||||
type CreateDashboardSnapshotCommand struct {
|
type CreateDashboardSnapshotCommand struct {
|
||||||
Dashboard *simplejson.Json `json:"dashboard" binding:"Required"`
|
Dashboard *simplejson.Json `json:"dashboard" binding:"Required"`
|
||||||
Name string `json:"name" binding:"Required"`
|
Name string `json:"name"`
|
||||||
Expires int64 `json:"expires"`
|
Expires int64 `json:"expires"`
|
||||||
|
|
||||||
// these are passed when storing an external snapshot ref
|
// these are passed when storing an external snapshot ref
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -37,6 +39,26 @@ func (r RoleType) Includes(other RoleType) bool {
|
|||||||
return r == other
|
return r == other
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *RoleType) UnmarshalJSON(data []byte) error {
|
||||||
|
var str string
|
||||||
|
err := json.Unmarshal(data, &str)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
*r = RoleType(str)
|
||||||
|
|
||||||
|
if (*r).IsValid() == false {
|
||||||
|
if (*r) != "" {
|
||||||
|
return errors.New(fmt.Sprintf("JSON validation error: invalid role value: %s", *r))
|
||||||
|
}
|
||||||
|
|
||||||
|
*r = ROLE_VIEWER
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
type OrgUser struct {
|
type OrgUser struct {
|
||||||
Id int64
|
Id int64
|
||||||
OrgId int64
|
OrgId int64
|
||||||
|
@ -7,7 +7,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/log"
|
"github.com/grafana/grafana/pkg/log"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
m "github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -69,6 +69,12 @@ func (pb *PluginBase) registerPlugin(pluginDir string) error {
|
|||||||
pb.Dependencies.GrafanaVersion = "*"
|
pb.Dependencies.GrafanaVersion = "*"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, include := range pb.Includes {
|
||||||
|
if include.Role == "" {
|
||||||
|
include.Role = m.RoleType(m.ROLE_VIEWER)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pb.PluginDir = pluginDir
|
pb.PluginDir = pluginDir
|
||||||
Plugins[pb.Id] = pb
|
Plugins[pb.Id] = pb
|
||||||
return nil
|
return nil
|
||||||
@ -80,14 +86,14 @@ type PluginDependencies struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type PluginInclude struct {
|
type PluginInclude struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Path string `json:"path"`
|
Path string `json:"path"`
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
Component string `json:"component"`
|
Component string `json:"component"`
|
||||||
Role models.RoleType `json:"role"`
|
Role m.RoleType `json:"role"`
|
||||||
AddToNav bool `json:"addToNav"`
|
AddToNav bool `json:"addToNav"`
|
||||||
DefaultNav bool `json:"defaultNav"`
|
DefaultNav bool `json:"defaultNav"`
|
||||||
Slug string `json:"slug"`
|
Slug string `json:"slug"`
|
||||||
|
|
||||||
Id string `json:"-"`
|
Id string `json:"-"`
|
||||||
}
|
}
|
||||||
|
@ -216,7 +216,7 @@ func GetDashboardTags(query *m.GetDashboardTagsQuery) error {
|
|||||||
func DeleteDashboard(cmd *m.DeleteDashboardCommand) error {
|
func DeleteDashboard(cmd *m.DeleteDashboardCommand) error {
|
||||||
return inTransaction2(func(sess *session) error {
|
return inTransaction2(func(sess *session) error {
|
||||||
dashboard := m.Dashboard{Slug: cmd.Slug, OrgId: cmd.OrgId}
|
dashboard := m.Dashboard{Slug: cmd.Slug, OrgId: cmd.OrgId}
|
||||||
has, err := x.Get(&dashboard)
|
has, err := sess.Get(&dashboard)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
} else if has == false {
|
} else if has == false {
|
||||||
|
@ -206,9 +206,12 @@ function pluginDirectiveLoader($compile, datasourceSrv, $rootScope, $q, $http, $
|
|||||||
});
|
});
|
||||||
|
|
||||||
$compile(child)(scope);
|
$compile(child)(scope);
|
||||||
|
|
||||||
elem.empty();
|
elem.empty();
|
||||||
elem.append(child);
|
|
||||||
|
// let a binding digest cycle complete before adding to dom
|
||||||
|
setTimeout(function() {
|
||||||
|
elem.append(child);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function registerPluginComponent(scope, elem, attrs, componentInfo) {
|
function registerPluginComponent(scope, elem, attrs, componentInfo) {
|
||||||
|
@ -36,7 +36,7 @@ function (angular, _, $) {
|
|||||||
self.update(payload);
|
self.update(payload);
|
||||||
});
|
});
|
||||||
|
|
||||||
$scope.onAppEvent('panel-instantiated', function(evt, payload) {
|
$scope.onAppEvent('panel-initialized', function(evt, payload) {
|
||||||
self.registerPanel(payload.scope);
|
self.registerPanel(payload.scope);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -50,8 +50,11 @@ export class PanelCtrl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
this.publishAppEvent('panel-instantiated', {scope: this.$scope});
|
|
||||||
this.calculatePanelHeight();
|
this.calculatePanelHeight();
|
||||||
|
|
||||||
|
this.publishAppEvent('panel-initialized', {scope: this.$scope});
|
||||||
|
this.events.emit('panel-initialized');
|
||||||
|
|
||||||
this.refresh();
|
this.refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
<div class="gf-form-group">
|
<div class="gf-form-group">
|
||||||
<p>Type the following on the command line to update {{plugin.name}}.</p>
|
<p>Type the following on the command line to update {{plugin.name}}.</p>
|
||||||
<pre><code>grafana-cli plugins update {{plugin.id}}</code></pre>
|
<pre><code>grafana-cli plugins update {{plugin.id}}</code></pre>
|
||||||
<span class="small">Check out {{plugin.name}} on <a href="http://grafana/net/plugins/{{plugin.id}}">Grafana.net</a> for README and changelog. If you do not have access to the command line, ask your Grafana administator.</span>
|
<span class="small">Check out {{plugin.name}} on <a href="https://grafana.net/plugins/{{plugin.id}}">Grafana.net</a> for README and changelog. If you do not have access to the command line, ask your Grafana administator.</span>
|
||||||
</div>
|
</div>
|
||||||
<p class="pluginlist-none-installed"><img class="pluginlist-inline-logo" src="public/img/grafana_icon.svg"><strong>Pro tip</strong>: To update all plugins at once, type <code class="code--small">grafana-cli plugins update-all</code> on the command line.</div>
|
<p class="pluginlist-none-installed"><img class="pluginlist-inline-logo" src="public/img/grafana_icon.svg"><strong>Pro tip</strong>: To update all plugins at once, type <code class="code--small">grafana-cli plugins update-all</code> on the command line.</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -57,7 +57,7 @@ function (angular, _) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var escapedValues = _.map(value, regexEscape);
|
var escapedValues = _.map(value, regexEscape);
|
||||||
return escapedValues.join('|');
|
return '(' + escapedValues.join('|') + ')';
|
||||||
}
|
}
|
||||||
case "lucene": {
|
case "lucene": {
|
||||||
if (typeof value === 'string') {
|
if (typeof value === 'string') {
|
||||||
@ -152,6 +152,10 @@ function (angular, _) {
|
|||||||
value = variable.current.value;
|
value = variable.current.value;
|
||||||
if (self.isAllValue(value)) {
|
if (self.isAllValue(value)) {
|
||||||
value = self.getAllValue(variable);
|
value = self.getAllValue(variable);
|
||||||
|
// skip formating of custom all values
|
||||||
|
if (variable.allValue) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var res = self.formatValue(value, format, variable);
|
var res = self.formatValue(value, format, variable);
|
||||||
|
@ -294,11 +294,6 @@ function (angular, _, kbn) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
this.addAllOption = function(variable) {
|
this.addAllOption = function(variable) {
|
||||||
if (variable.allValue) {
|
|
||||||
variable.options.unshift({text: 'All', value: variable.allValue});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
variable.options.unshift({text: 'All', value: "$__all"});
|
variable.options.unshift({text: 'All', value: "$__all"});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 38 KiB |
@ -80,6 +80,13 @@ function (_, $) {
|
|||||||
category: categories.Calculate,
|
category: categories.Calculate,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
addFuncDef({
|
||||||
|
name: 'stddevSeries',
|
||||||
|
params: optionalSeriesRefArgs,
|
||||||
|
defaultParams: [''],
|
||||||
|
category: categories.Calculate,
|
||||||
|
});
|
||||||
|
|
||||||
addFuncDef({
|
addFuncDef({
|
||||||
name: 'divideSeries',
|
name: 'divideSeries',
|
||||||
params: optionalSeriesRefArgs,
|
params: optionalSeriesRefArgs,
|
||||||
|
@ -6,8 +6,8 @@ There are currently two separate datasources for InfluxDB in Grafana: InfluxDB 0
|
|||||||
|
|
||||||
This is the plugin for InfluxDB 0.9. It is rapidly evolving and we continue to track its API.
|
This is the plugin for InfluxDB 0.9. It is rapidly evolving and we continue to track its API.
|
||||||
|
|
||||||
InfluxDB 0.8 is no longer maintained by InfluxDB Inc, but we provide support as a convenience to existing users. You can find it [here](https://www.grafana.net/plugins/grafana-influxdb-08-datasource).
|
InfluxDB 0.8 is no longer maintained by InfluxDB Inc, but we provide support as a convenience to existing users. You can find it [here](https://grafana.net/plugins/grafana-influxdb-08-datasource).
|
||||||
|
|
||||||
Read more about InfluxDB here:
|
Read more about InfluxDB here:
|
||||||
|
|
||||||
[http://docs.grafana.org/datasources/influxdb/](http://docs.grafana.org/datasources/influxdb/)
|
[http://docs.grafana.org/datasources/influxdb/](http://docs.grafana.org/datasources/influxdb/)
|
||||||
|
@ -152,7 +152,9 @@ export default class InfluxQuery {
|
|||||||
if (interpolate) {
|
if (interpolate) {
|
||||||
value = this.templateSrv.replace(value, this.scopedVars);
|
value = this.templateSrv.replace(value, this.scopedVars);
|
||||||
}
|
}
|
||||||
value = "'" + value.replace('\\', '\\\\') + "'";
|
if (isNaN(+value)) {
|
||||||
|
value = "'" + value.replace('\\', '\\\\') + "'";
|
||||||
|
}
|
||||||
} else if (interpolate){
|
} else if (interpolate){
|
||||||
value = this.templateSrv.replace(value, this.scopedVars, 'regex');
|
value = this.templateSrv.replace(value, this.scopedVars, 'regex');
|
||||||
}
|
}
|
||||||
@ -166,6 +168,8 @@ export default class InfluxQuery {
|
|||||||
|
|
||||||
if (!measurement.match('^/.*/')) {
|
if (!measurement.match('^/.*/')) {
|
||||||
measurement = '"' + measurement+ '"';
|
measurement = '"' + measurement+ '"';
|
||||||
|
} else {
|
||||||
|
measurement = this.templateSrv.replace(measurement, this.scopedVars, 'regex');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (policy !== 'default') {
|
if (policy !== 'default') {
|
||||||
|
@ -25,8 +25,8 @@ function (_) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// quote value unless regex
|
// quote value unless regex or number
|
||||||
if (operator !== '=~' && operator !== '!~') {
|
if (operator !== '=~' && operator !== '!~' && isNaN(+value)) {
|
||||||
value = "'" + value + "'";
|
value = "'" + value + "'";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,17 +12,29 @@ export default class ResponseParser {
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
var series = influxResults.series[0];
|
var influxdb11format = query.toLowerCase().indexOf('show tag values') >= 0;
|
||||||
return _.map(series.values, (value) => {
|
|
||||||
if (_.isArray(value)) {
|
var res = {};
|
||||||
if (query.toLowerCase().indexOf('show tag values') >= 0) {
|
_.each(influxResults.series, serie => {
|
||||||
return { text: (value[1] || value[0]) };
|
_.each(serie.values, value => {
|
||||||
|
if (_.isArray(value)) {
|
||||||
|
if (influxdb11format) {
|
||||||
|
addUnique(res, value[1] || value[0]);
|
||||||
|
} else {
|
||||||
|
addUnique(res, value[0]);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return { text: value[0] };
|
addUnique(res, value);
|
||||||
}
|
}
|
||||||
} else {
|
});
|
||||||
return { text: value };
|
});
|
||||||
}
|
|
||||||
|
return _.map(res, value => {
|
||||||
|
return { text: value};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function addUnique(arr, value) {
|
||||||
|
arr[value] = value;
|
||||||
|
}
|
||||||
|
@ -38,7 +38,7 @@ describe("influxdb response parser", () => {
|
|||||||
{
|
{
|
||||||
"name": "hostnameTagValues",
|
"name": "hostnameTagValues",
|
||||||
"columns": ["hostname"],
|
"columns": ["hostname"],
|
||||||
"values": [ ["server1"], ["server2"] ]
|
"values": [ ["server1"], ["server2"], ["server2"] ]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -54,7 +54,7 @@ describe("influxdb response parser", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("response from 0.11.0", () => {
|
describe("response from 0.12.0", () => {
|
||||||
var response = {
|
var response = {
|
||||||
"results": [
|
"results": [
|
||||||
{
|
{
|
||||||
@ -62,8 +62,19 @@ describe("influxdb response parser", () => {
|
|||||||
{
|
{
|
||||||
"name": "cpu",
|
"name": "cpu",
|
||||||
"columns": [ "key", "value"],
|
"columns": [ "key", "value"],
|
||||||
"values": [ [ "source", "site" ], [ "source", "api" ] ]
|
"values": [
|
||||||
}
|
[ "source", "site" ],
|
||||||
|
[ "source", "api" ]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "logins",
|
||||||
|
"columns": [ "key", "value"],
|
||||||
|
"values": [
|
||||||
|
[ "source", "site" ],
|
||||||
|
[ "source", "webapi"]
|
||||||
|
]
|
||||||
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@ -72,15 +83,12 @@ describe("influxdb response parser", () => {
|
|||||||
var result = this.parser.parse(query, response);
|
var result = this.parser.parse(query, response);
|
||||||
|
|
||||||
it("should get two responses", () => {
|
it("should get two responses", () => {
|
||||||
expect(_.size(result)).to.be(2);
|
expect(_.size(result)).to.be(3);
|
||||||
expect(result[0].text).to.be('site');
|
expect(result[0].text).to.be('site');
|
||||||
expect(result[1].text).to.be('api');
|
expect(result[1].text).to.be('api');
|
||||||
|
expect(result[2].text).to.be('webapi');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("SHOW FIELD response", () => {
|
describe("SHOW FIELD response", () => {
|
||||||
|
@ -5,27 +5,26 @@ import config from 'app/core/config';
|
|||||||
import {PanelCtrl} from 'app/plugins/sdk';
|
import {PanelCtrl} from 'app/plugins/sdk';
|
||||||
import {impressions} from 'app/features/dashboard/impression_store';
|
import {impressions} from 'app/features/dashboard/impression_store';
|
||||||
|
|
||||||
// Set and populate defaults
|
|
||||||
var panelDefaults = {
|
|
||||||
query: '',
|
|
||||||
limit: 10,
|
|
||||||
tags: [],
|
|
||||||
recent: false,
|
|
||||||
search: false,
|
|
||||||
starred: true,
|
|
||||||
headings: true,
|
|
||||||
};
|
|
||||||
|
|
||||||
class DashListCtrl extends PanelCtrl {
|
class DashListCtrl extends PanelCtrl {
|
||||||
static templateUrl = 'module.html';
|
static templateUrl = 'module.html';
|
||||||
|
|
||||||
groups: any[];
|
groups: any[];
|
||||||
modes: any[];
|
modes: any[];
|
||||||
|
|
||||||
|
panelDefaults = {
|
||||||
|
query: '',
|
||||||
|
limit: 10,
|
||||||
|
tags: [],
|
||||||
|
recent: false,
|
||||||
|
search: false,
|
||||||
|
starred: true,
|
||||||
|
headings: true,
|
||||||
|
};
|
||||||
|
|
||||||
/** @ngInject */
|
/** @ngInject */
|
||||||
constructor($scope, $injector, private backendSrv) {
|
constructor($scope, $injector, private backendSrv) {
|
||||||
super($scope, $injector);
|
super($scope, $injector);
|
||||||
_.defaults(this.panel, panelDefaults);
|
_.defaults(this.panel, this.panelDefaults);
|
||||||
|
|
||||||
if (this.panel.tag) {
|
if (this.panel.tag) {
|
||||||
this.panel.tags = [this.panel.tag];
|
this.panel.tags = [this.panel.tag];
|
||||||
|
@ -73,7 +73,7 @@ function (angular, $, moment, _, kbn, GraphTooltip) {
|
|||||||
var legendSeries = _.filter(data, function(series) {
|
var legendSeries = _.filter(data, function(series) {
|
||||||
return series.hideFromLegend(panel.legend) === false;
|
return series.hideFromLegend(panel.legend) === false;
|
||||||
});
|
});
|
||||||
var total = 23 + (22 * legendSeries.length);
|
var total = 23 + (21 * legendSeries.length);
|
||||||
return Math.min(total, Math.floor(panelHeight/2));
|
return Math.min(total, Math.floor(panelHeight/2));
|
||||||
} else {
|
} else {
|
||||||
return 26;
|
return 26;
|
||||||
|
@ -13,85 +13,6 @@ import TimeSeries from 'app/core/time_series2';
|
|||||||
import * as fileExport from 'app/core/utils/file_export';
|
import * as fileExport from 'app/core/utils/file_export';
|
||||||
import {MetricsPanelCtrl} from 'app/plugins/sdk';
|
import {MetricsPanelCtrl} from 'app/plugins/sdk';
|
||||||
|
|
||||||
var panelDefaults = {
|
|
||||||
// datasource name, null = default datasource
|
|
||||||
datasource: null,
|
|
||||||
// sets client side (flot) or native graphite png renderer (png)
|
|
||||||
renderer: 'flot',
|
|
||||||
yaxes: [
|
|
||||||
{
|
|
||||||
label: null,
|
|
||||||
show: true,
|
|
||||||
logBase: 1,
|
|
||||||
min: null,
|
|
||||||
max: null,
|
|
||||||
format: 'short'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: null,
|
|
||||||
show: true,
|
|
||||||
logBase: 1,
|
|
||||||
min: null,
|
|
||||||
max: null,
|
|
||||||
format: 'short'
|
|
||||||
}
|
|
||||||
],
|
|
||||||
xaxis: {
|
|
||||||
show: true
|
|
||||||
},
|
|
||||||
grid : {
|
|
||||||
threshold1: null,
|
|
||||||
threshold2: null,
|
|
||||||
threshold1Color: 'rgba(216, 200, 27, 0.27)',
|
|
||||||
threshold2Color: 'rgba(234, 112, 112, 0.22)'
|
|
||||||
},
|
|
||||||
// show/hide lines
|
|
||||||
lines : true,
|
|
||||||
// fill factor
|
|
||||||
fill : 1,
|
|
||||||
// line width in pixels
|
|
||||||
linewidth : 2,
|
|
||||||
// show hide points
|
|
||||||
points : false,
|
|
||||||
// point radius in pixels
|
|
||||||
pointradius : 5,
|
|
||||||
// show hide bars
|
|
||||||
bars : false,
|
|
||||||
// enable/disable stacking
|
|
||||||
stack : false,
|
|
||||||
// stack percentage mode
|
|
||||||
percentage : false,
|
|
||||||
// legend options
|
|
||||||
legend: {
|
|
||||||
show: true, // disable/enable legend
|
|
||||||
values: false, // disable/enable legend values
|
|
||||||
min: false,
|
|
||||||
max: false,
|
|
||||||
current: false,
|
|
||||||
total: false,
|
|
||||||
avg: false
|
|
||||||
},
|
|
||||||
// how null points should be handled
|
|
||||||
nullPointMode : 'connected',
|
|
||||||
// staircase line mode
|
|
||||||
steppedLine: false,
|
|
||||||
// tooltip options
|
|
||||||
tooltip : {
|
|
||||||
value_type: 'cumulative',
|
|
||||||
shared: true,
|
|
||||||
msResolution: false,
|
|
||||||
},
|
|
||||||
// time overrides
|
|
||||||
timeFrom: null,
|
|
||||||
timeShift: null,
|
|
||||||
// metric queries
|
|
||||||
targets: [{}],
|
|
||||||
// series color overrides
|
|
||||||
aliasColors: {},
|
|
||||||
// other style overrides
|
|
||||||
seriesOverrides: [],
|
|
||||||
};
|
|
||||||
|
|
||||||
class GraphCtrl extends MetricsPanelCtrl {
|
class GraphCtrl extends MetricsPanelCtrl {
|
||||||
static template = template;
|
static template = template;
|
||||||
|
|
||||||
@ -105,14 +26,93 @@ class GraphCtrl extends MetricsPanelCtrl {
|
|||||||
datapointsWarning: boolean;
|
datapointsWarning: boolean;
|
||||||
colors: any = [];
|
colors: any = [];
|
||||||
|
|
||||||
|
panelDefaults = {
|
||||||
|
// datasource name, null = default datasource
|
||||||
|
datasource: null,
|
||||||
|
// sets client side (flot) or native graphite png renderer (png)
|
||||||
|
renderer: 'flot',
|
||||||
|
yaxes: [
|
||||||
|
{
|
||||||
|
label: null,
|
||||||
|
show: true,
|
||||||
|
logBase: 1,
|
||||||
|
min: null,
|
||||||
|
max: null,
|
||||||
|
format: 'short'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: null,
|
||||||
|
show: true,
|
||||||
|
logBase: 1,
|
||||||
|
min: null,
|
||||||
|
max: null,
|
||||||
|
format: 'short'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
xaxis: {
|
||||||
|
show: true
|
||||||
|
},
|
||||||
|
grid : {
|
||||||
|
threshold1: null,
|
||||||
|
threshold2: null,
|
||||||
|
threshold1Color: 'rgba(216, 200, 27, 0.27)',
|
||||||
|
threshold2Color: 'rgba(234, 112, 112, 0.22)'
|
||||||
|
},
|
||||||
|
// show/hide lines
|
||||||
|
lines : true,
|
||||||
|
// fill factor
|
||||||
|
fill : 1,
|
||||||
|
// line width in pixels
|
||||||
|
linewidth : 2,
|
||||||
|
// show hide points
|
||||||
|
points : false,
|
||||||
|
// point radius in pixels
|
||||||
|
pointradius : 5,
|
||||||
|
// show hide bars
|
||||||
|
bars : false,
|
||||||
|
// enable/disable stacking
|
||||||
|
stack : false,
|
||||||
|
// stack percentage mode
|
||||||
|
percentage : false,
|
||||||
|
// legend options
|
||||||
|
legend: {
|
||||||
|
show: true, // disable/enable legend
|
||||||
|
values: false, // disable/enable legend values
|
||||||
|
min: false,
|
||||||
|
max: false,
|
||||||
|
current: false,
|
||||||
|
total: false,
|
||||||
|
avg: false
|
||||||
|
},
|
||||||
|
// how null points should be handled
|
||||||
|
nullPointMode : 'connected',
|
||||||
|
// staircase line mode
|
||||||
|
steppedLine: false,
|
||||||
|
// tooltip options
|
||||||
|
tooltip : {
|
||||||
|
value_type: 'cumulative',
|
||||||
|
shared: true,
|
||||||
|
msResolution: false,
|
||||||
|
},
|
||||||
|
// time overrides
|
||||||
|
timeFrom: null,
|
||||||
|
timeShift: null,
|
||||||
|
// metric queries
|
||||||
|
targets: [{}],
|
||||||
|
// series color overrides
|
||||||
|
aliasColors: {},
|
||||||
|
// other style overrides
|
||||||
|
seriesOverrides: [],
|
||||||
|
};
|
||||||
|
|
||||||
/** @ngInject */
|
/** @ngInject */
|
||||||
constructor($scope, $injector, private annotationsSrv) {
|
constructor($scope, $injector, private annotationsSrv) {
|
||||||
super($scope, $injector);
|
super($scope, $injector);
|
||||||
|
|
||||||
_.defaults(this.panel, angular.copy(panelDefaults));
|
_.defaults(this.panel, this.panelDefaults);
|
||||||
_.defaults(this.panel.tooltip, panelDefaults.tooltip);
|
_.defaults(this.panel.tooltip, this.panelDefaults.tooltip);
|
||||||
_.defaults(this.panel.grid, panelDefaults.grid);
|
_.defaults(this.panel.grid, this.panelDefaults.grid);
|
||||||
_.defaults(this.panel.legend, panelDefaults.legend);
|
_.defaults(this.panel.legend, this.panelDefaults.legend);
|
||||||
|
|
||||||
this.colors = $scope.$root.colors;
|
this.colors = $scope.$root.colors;
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="pluginlist-item" ng-show="category.list.length === 0">
|
<div class="pluginlist-item" ng-show="category.list.length === 0">
|
||||||
<a class="pluginlist-link pluginlist-link-{{plugin.state}}" href="http://grafana.net/plugins/">
|
<a class="pluginlist-link pluginlist-link-{{plugin.state}}" href="https://grafana.net/plugins">
|
||||||
<span class="pluginlist-none-installed">No additional panels installed. <span class="pluginlist-emphasis">Browse Grafana.net</span></span>
|
<span class="pluginlist-none-installed">No additional panels installed. <span class="pluginlist-emphasis">Browse Grafana.net</span></span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
@ -4,20 +4,20 @@ import _ from 'lodash';
|
|||||||
import config from 'app/core/config';
|
import config from 'app/core/config';
|
||||||
import {PanelCtrl} from '../../../features/panel/panel_ctrl';
|
import {PanelCtrl} from '../../../features/panel/panel_ctrl';
|
||||||
|
|
||||||
// Set and populate defaults
|
|
||||||
var panelDefaults = {
|
|
||||||
};
|
|
||||||
|
|
||||||
class PluginListCtrl extends PanelCtrl {
|
class PluginListCtrl extends PanelCtrl {
|
||||||
static templateUrl = 'module.html';
|
static templateUrl = 'module.html';
|
||||||
|
|
||||||
pluginList: any[];
|
pluginList: any[];
|
||||||
viewModel: any;
|
viewModel: any;
|
||||||
|
|
||||||
|
// Set and populate defaults
|
||||||
|
panelDefaults = {
|
||||||
|
};
|
||||||
|
|
||||||
/** @ngInject */
|
/** @ngInject */
|
||||||
constructor($scope, $injector, private backendSrv, private $location) {
|
constructor($scope, $injector, private backendSrv, private $location) {
|
||||||
super($scope, $injector);
|
super($scope, $injector);
|
||||||
_.defaults(this.panel, panelDefaults);
|
_.defaults(this.panel, this.panelDefaults);
|
||||||
|
|
||||||
this.events.on('init-edit-mode', this.onInitEditMode.bind(this));
|
this.events.on('init-edit-mode', this.onInitEditMode.bind(this));
|
||||||
this.pluginList = [];
|
this.pluginList = [];
|
||||||
|
@ -156,6 +156,43 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="editor-row">
|
||||||
|
<div class="section" style="margin-bottom: 20px">
|
||||||
|
<div class="tight-form last">
|
||||||
|
<ul class="tight-form-list">
|
||||||
|
<li class="tight-form-item" style="width: 80px">
|
||||||
|
<strong>Gauge</strong>
|
||||||
|
</li>
|
||||||
|
<li class="tight-form-item">
|
||||||
|
Show
|
||||||
|
<input class="cr1" id="panel.gauge.show" type="checkbox"
|
||||||
|
ng-model="ctrl.panel.gauge.show" ng-checked="ctrl.panel.gauge.show" ng-change="ctrl.render()">
|
||||||
|
<label for="panel.gauge.show" class="cr1"></label>
|
||||||
|
</li>
|
||||||
|
<li class="tight-form-item">
|
||||||
|
Threshold labels
|
||||||
|
<input class="cr1" id="panel.gauge.thresholdLabels" type="checkbox"
|
||||||
|
ng-model="ctrl.panel.gauge.thresholdLabels" ng-checked="ctrl.panel.gauge.thresholdLabels" ng-change="ctrl.render()">
|
||||||
|
<label for="panel.gauge.thresholdLabels" class="cr1"></label>
|
||||||
|
</li>
|
||||||
|
<li class="tight-form-item">
|
||||||
|
Min
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<input type="text" class="input-small tight-form-input" ng-model="ctrl.panel.gauge.minValue" ng-blur="ctrl.render()" placeholder="0"></input>
|
||||||
|
</li>
|
||||||
|
<li class="tight-form-item last">
|
||||||
|
Max
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<input type="text" class="input-small tight-form-input last" ng-model="ctrl.panel.gauge.maxValue" ng-blur="ctrl.render()" placeholder="100"></input>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<div class="clearfix"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="editor-row">
|
<div class="editor-row">
|
||||||
<div class="section" style="margin-bottom: 20px">
|
<div class="section" style="margin-bottom: 20px">
|
||||||
<div class="tight-form last">
|
<div class="tight-form last">
|
||||||
|
@ -4,43 +4,13 @@ import angular from 'angular';
|
|||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import $ from 'jquery';
|
import $ from 'jquery';
|
||||||
import 'jquery.flot';
|
import 'jquery.flot';
|
||||||
|
import 'jquery.flot.gauge';
|
||||||
|
|
||||||
import kbn from 'app/core/utils/kbn';
|
import kbn from 'app/core/utils/kbn';
|
||||||
|
import config from 'app/core/config';
|
||||||
import TimeSeries from 'app/core/time_series2';
|
import TimeSeries from 'app/core/time_series2';
|
||||||
import {MetricsPanelCtrl} from 'app/plugins/sdk';
|
import {MetricsPanelCtrl} from 'app/plugins/sdk';
|
||||||
|
|
||||||
// Set and populate defaults
|
|
||||||
var panelDefaults = {
|
|
||||||
links: [],
|
|
||||||
datasource: null,
|
|
||||||
maxDataPoints: 100,
|
|
||||||
interval: null,
|
|
||||||
targets: [{}],
|
|
||||||
cacheTimeout: null,
|
|
||||||
format: 'none',
|
|
||||||
prefix: '',
|
|
||||||
postfix: '',
|
|
||||||
nullText: null,
|
|
||||||
valueMaps: [
|
|
||||||
{ value: 'null', op: '=', text: 'N/A' }
|
|
||||||
],
|
|
||||||
nullPointMode: 'connected',
|
|
||||||
valueName: 'avg',
|
|
||||||
prefixFontSize: '50%',
|
|
||||||
valueFontSize: '80%',
|
|
||||||
postfixFontSize: '50%',
|
|
||||||
thresholds: '',
|
|
||||||
colorBackground: false,
|
|
||||||
colorValue: false,
|
|
||||||
colors: ["rgba(245, 54, 54, 0.9)", "rgba(237, 129, 40, 0.89)", "rgba(50, 172, 45, 0.97)"],
|
|
||||||
sparkline: {
|
|
||||||
show: false,
|
|
||||||
full: false,
|
|
||||||
lineColor: 'rgb(31, 120, 193)',
|
|
||||||
fillColor: 'rgba(31, 118, 189, 0.18)',
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class SingleStatCtrl extends MetricsPanelCtrl {
|
class SingleStatCtrl extends MetricsPanelCtrl {
|
||||||
static templateUrl = 'module.html';
|
static templateUrl = 'module.html';
|
||||||
|
|
||||||
@ -49,10 +19,48 @@ class SingleStatCtrl extends MetricsPanelCtrl {
|
|||||||
fontSizes: any[];
|
fontSizes: any[];
|
||||||
unitFormats: any[];
|
unitFormats: any[];
|
||||||
|
|
||||||
|
// Set and populate defaults
|
||||||
|
panelDefaults = {
|
||||||
|
links: [],
|
||||||
|
datasource: null,
|
||||||
|
maxDataPoints: 100,
|
||||||
|
interval: null,
|
||||||
|
targets: [{}],
|
||||||
|
cacheTimeout: null,
|
||||||
|
format: 'none',
|
||||||
|
prefix: '',
|
||||||
|
postfix: '',
|
||||||
|
nullText: null,
|
||||||
|
valueMaps: [
|
||||||
|
{ value: 'null', op: '=', text: 'N/A' }
|
||||||
|
],
|
||||||
|
nullPointMode: 'connected',
|
||||||
|
valueName: 'avg',
|
||||||
|
prefixFontSize: '50%',
|
||||||
|
valueFontSize: '80%',
|
||||||
|
postfixFontSize: '50%',
|
||||||
|
thresholds: '',
|
||||||
|
colorBackground: false,
|
||||||
|
colorValue: false,
|
||||||
|
colors: ["rgba(245, 54, 54, 0.9)", "rgba(237, 129, 40, 0.89)", "rgba(50, 172, 45, 0.97)"],
|
||||||
|
sparkline: {
|
||||||
|
show: false,
|
||||||
|
full: false,
|
||||||
|
lineColor: 'rgb(31, 120, 193)',
|
||||||
|
fillColor: 'rgba(31, 118, 189, 0.18)',
|
||||||
|
},
|
||||||
|
gauge: {
|
||||||
|
show: false,
|
||||||
|
minValue: 0,
|
||||||
|
maxValue: 100,
|
||||||
|
thresholdLabels: true
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/** @ngInject */
|
/** @ngInject */
|
||||||
constructor($scope, $injector, private $location, private linkSrv) {
|
constructor($scope, $injector, private $location, private linkSrv) {
|
||||||
super($scope, $injector);
|
super($scope, $injector);
|
||||||
_.defaults(this.panel, panelDefaults);
|
_.defaults(this.panel, this.panelDefaults);
|
||||||
|
|
||||||
this.events.on('data-received', this.onDataReceived.bind(this));
|
this.events.on('data-received', this.onDataReceived.bind(this));
|
||||||
this.events.on('data-error', this.onDataError.bind(this));
|
this.events.on('data-error', this.onDataError.bind(this));
|
||||||
@ -270,6 +278,86 @@ class SingleStatCtrl extends MetricsPanelCtrl {
|
|||||||
return body;
|
return body;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function addGauge() {
|
||||||
|
var plotCanvas = $('<div></div>');
|
||||||
|
var plotCss = {
|
||||||
|
top: '10px',
|
||||||
|
margin: 'auto',
|
||||||
|
position: 'relative',
|
||||||
|
height: (elem.height() * 0.9) + 'px',
|
||||||
|
width: elem.width() + 'px'
|
||||||
|
};
|
||||||
|
|
||||||
|
plotCanvas.css(plotCss);
|
||||||
|
|
||||||
|
var thresholds = [];
|
||||||
|
for (var i = 0; i < data.thresholds.length; i++) {
|
||||||
|
thresholds.push({
|
||||||
|
value: data.thresholds[i],
|
||||||
|
color: data.colorMap[i]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
thresholds.push({
|
||||||
|
value: panel.gauge.maxValue,
|
||||||
|
color: data.colorMap[data.colorMap.length - 1]
|
||||||
|
});
|
||||||
|
|
||||||
|
var bgColor = config.bootData.user.lightTheme
|
||||||
|
? 'rgb(230,230,230)'
|
||||||
|
: 'rgb(38,38,38)';
|
||||||
|
|
||||||
|
var options = {
|
||||||
|
series: {
|
||||||
|
gauges: {
|
||||||
|
gauge: {
|
||||||
|
min: panel.gauge.minValue,
|
||||||
|
max: panel.gauge.maxValue,
|
||||||
|
background: { color: bgColor },
|
||||||
|
border: { color: null },
|
||||||
|
shadow: { show: false },
|
||||||
|
width: 38
|
||||||
|
},
|
||||||
|
frame: { show: false },
|
||||||
|
label: { show: false },
|
||||||
|
layout: { margin: 0 },
|
||||||
|
cell: { border: { width: 0 } },
|
||||||
|
threshold: {
|
||||||
|
values: thresholds,
|
||||||
|
label: {
|
||||||
|
show: panel.gauge.thresholdLabels,
|
||||||
|
margin: 8,
|
||||||
|
font: { size: 18 }
|
||||||
|
},
|
||||||
|
width: 8
|
||||||
|
},
|
||||||
|
value: {
|
||||||
|
color: panel.colorValue ? getColorForValue(data, data.valueRounded) : null,
|
||||||
|
formatter: function () { return data.valueFormated; },
|
||||||
|
font: { size: getGaugeFontSize() }
|
||||||
|
},
|
||||||
|
show: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
elem.append(plotCanvas);
|
||||||
|
|
||||||
|
var plotSeries = {
|
||||||
|
data: [[0, data.valueRounded]]
|
||||||
|
};
|
||||||
|
|
||||||
|
$.plot(plotCanvas, [plotSeries], options);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getGaugeFontSize() {
|
||||||
|
if (panel.valueFontSize) {
|
||||||
|
var num = parseInt(panel.valueFontSize.substring(0, panel.valueFontSize.length - 1));
|
||||||
|
return 30 * (num / 100);
|
||||||
|
} else {
|
||||||
|
return 30;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function addSparkline() {
|
function addSparkline() {
|
||||||
var width = elem.width() + 20;
|
var width = elem.width() + 20;
|
||||||
if (width < 30) {
|
if (width < 30) {
|
||||||
@ -331,11 +419,10 @@ class SingleStatCtrl extends MetricsPanelCtrl {
|
|||||||
|
|
||||||
function render() {
|
function render() {
|
||||||
if (!ctrl.data) { return; }
|
if (!ctrl.data) { return; }
|
||||||
|
|
||||||
data = ctrl.data;
|
data = ctrl.data;
|
||||||
setElementHeight();
|
setElementHeight();
|
||||||
|
|
||||||
var body = getBigValueHtml();
|
var body = panel.gauge.show ? '' : getBigValueHtml();
|
||||||
|
|
||||||
if (panel.colorBackground && !isNaN(data.valueRounded)) {
|
if (panel.colorBackground && !isNaN(data.valueRounded)) {
|
||||||
var color = getColorForValue(data, data.valueRounded);
|
var color = getColorForValue(data, data.valueRounded);
|
||||||
@ -358,6 +445,10 @@ class SingleStatCtrl extends MetricsPanelCtrl {
|
|||||||
addSparkline();
|
addSparkline();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (panel.gauge.show) {
|
||||||
|
addGauge();
|
||||||
|
}
|
||||||
|
|
||||||
elem.toggleClass('pointer', panel.links.length > 0);
|
elem.toggleClass('pointer', panel.links.length > 0);
|
||||||
|
|
||||||
if (panel.links.length > 0) {
|
if (panel.links.length > 0) {
|
||||||
|
@ -10,33 +10,6 @@ import {transformDataToTable} from './transformers';
|
|||||||
import {tablePanelEditor} from './editor';
|
import {tablePanelEditor} from './editor';
|
||||||
import {TableRenderer} from './renderer';
|
import {TableRenderer} from './renderer';
|
||||||
|
|
||||||
var panelDefaults = {
|
|
||||||
targets: [{}],
|
|
||||||
transform: 'timeseries_to_columns',
|
|
||||||
pageSize: null,
|
|
||||||
showHeader: true,
|
|
||||||
styles: [
|
|
||||||
{
|
|
||||||
type: 'date',
|
|
||||||
pattern: 'Time',
|
|
||||||
dateFormat: 'YYYY-MM-DD HH:mm:ss',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
unit: 'short',
|
|
||||||
type: 'number',
|
|
||||||
decimals: 2,
|
|
||||||
colors: ["rgba(245, 54, 54, 0.9)", "rgba(237, 129, 40, 0.89)", "rgba(50, 172, 45, 0.97)"],
|
|
||||||
colorMode: null,
|
|
||||||
pattern: '/.*/',
|
|
||||||
thresholds: [],
|
|
||||||
}
|
|
||||||
],
|
|
||||||
columns: [],
|
|
||||||
scroll: true,
|
|
||||||
fontSize: '100%',
|
|
||||||
sort: {col: 0, desc: true},
|
|
||||||
};
|
|
||||||
|
|
||||||
class TablePanelCtrl extends MetricsPanelCtrl {
|
class TablePanelCtrl extends MetricsPanelCtrl {
|
||||||
static templateUrl = 'module.html';
|
static templateUrl = 'module.html';
|
||||||
|
|
||||||
@ -44,6 +17,33 @@ class TablePanelCtrl extends MetricsPanelCtrl {
|
|||||||
dataRaw: any;
|
dataRaw: any;
|
||||||
table: any;
|
table: any;
|
||||||
|
|
||||||
|
panelDefaults = {
|
||||||
|
targets: [{}],
|
||||||
|
transform: 'timeseries_to_columns',
|
||||||
|
pageSize: null,
|
||||||
|
showHeader: true,
|
||||||
|
styles: [
|
||||||
|
{
|
||||||
|
type: 'date',
|
||||||
|
pattern: 'Time',
|
||||||
|
dateFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
unit: 'short',
|
||||||
|
type: 'number',
|
||||||
|
decimals: 2,
|
||||||
|
colors: ["rgba(245, 54, 54, 0.9)", "rgba(237, 129, 40, 0.89)", "rgba(50, 172, 45, 0.97)"],
|
||||||
|
colorMode: null,
|
||||||
|
pattern: '/.*/',
|
||||||
|
thresholds: [],
|
||||||
|
}
|
||||||
|
],
|
||||||
|
columns: [],
|
||||||
|
scroll: true,
|
||||||
|
fontSize: '100%',
|
||||||
|
sort: {col: 0, desc: true},
|
||||||
|
};
|
||||||
|
|
||||||
/** @ngInject */
|
/** @ngInject */
|
||||||
constructor($scope, $injector, private annotationsSrv) {
|
constructor($scope, $injector, private annotationsSrv) {
|
||||||
super($scope, $injector);
|
super($scope, $injector);
|
||||||
@ -56,7 +56,7 @@ class TablePanelCtrl extends MetricsPanelCtrl {
|
|||||||
delete this.panel.fields;
|
delete this.panel.fields;
|
||||||
}
|
}
|
||||||
|
|
||||||
_.defaults(this.panel, panelDefaults);
|
_.defaults(this.panel, this.panelDefaults);
|
||||||
|
|
||||||
this.events.on('data-received', this.onDataReceived.bind(this));
|
this.events.on('data-received', this.onDataReceived.bind(this));
|
||||||
this.events.on('data-error', this.onDataError.bind(this));
|
this.events.on('data-error', this.onDataError.bind(this));
|
||||||
|
@ -30,7 +30,7 @@ export class TableRenderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (_.isArray(v)) {
|
if (_.isArray(v)) {
|
||||||
v = v.join(', ');
|
v = v.join(', ');
|
||||||
}
|
}
|
||||||
|
|
||||||
return v;
|
return v;
|
||||||
|
@ -3,23 +3,21 @@
|
|||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import {PanelCtrl} from 'app/plugins/sdk';
|
import {PanelCtrl} from 'app/plugins/sdk';
|
||||||
|
|
||||||
// Set and populate defaults
|
|
||||||
var panelDefaults = {
|
|
||||||
mode : "markdown", // 'html', 'markdown', 'text'
|
|
||||||
content : "# title",
|
|
||||||
};
|
|
||||||
|
|
||||||
export class TextPanelCtrl extends PanelCtrl {
|
export class TextPanelCtrl extends PanelCtrl {
|
||||||
static templateUrl = `public/app/plugins/panel/text/module.html`;
|
static templateUrl = `public/app/plugins/panel/text/module.html`;
|
||||||
|
|
||||||
remarkable: any;
|
remarkable: any;
|
||||||
content: string;
|
content: string;
|
||||||
|
// Set and populate defaults
|
||||||
|
panelDefaults = {
|
||||||
|
mode : "markdown", // 'html', 'markdown', 'text'
|
||||||
|
content : "# title",
|
||||||
|
};
|
||||||
/** @ngInject */
|
/** @ngInject */
|
||||||
constructor($scope, $injector, private templateSrv, private $sce) {
|
constructor($scope, $injector, private templateSrv, private $sce) {
|
||||||
super($scope, $injector);
|
super($scope, $injector);
|
||||||
|
|
||||||
_.defaults(this.panel, panelDefaults);
|
_.defaults(this.panel, this.panelDefaults);
|
||||||
|
|
||||||
this.events.on('init-edit-mode', this.onInitEditMode.bind(this));
|
this.events.on('init-edit-mode', this.onInitEditMode.bind(this));
|
||||||
this.events.on('refresh', this.onRender.bind(this));
|
this.events.on('refresh', this.onRender.bind(this));
|
||||||
|
@ -27,7 +27,8 @@ System.config({
|
|||||||
"jquery.flot.stackpercent": "vendor/flot/jquery.flot.stackpercent",
|
"jquery.flot.stackpercent": "vendor/flot/jquery.flot.stackpercent",
|
||||||
"jquery.flot.time": "vendor/flot/jquery.flot.time",
|
"jquery.flot.time": "vendor/flot/jquery.flot.time",
|
||||||
"jquery.flot.crosshair": "vendor/flot/jquery.flot.crosshair",
|
"jquery.flot.crosshair": "vendor/flot/jquery.flot.crosshair",
|
||||||
"jquery.flot.fillbelow": "vendor/flot/jquery.flot.fillbelow"
|
"jquery.flot.fillbelow": "vendor/flot/jquery.flot.fillbelow",
|
||||||
|
"jquery.flot.gauge": "vendor/flot/jquery.flot.gauge"
|
||||||
},
|
},
|
||||||
|
|
||||||
packages: {
|
packages: {
|
||||||
|
@ -67,18 +67,20 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Links within the dropdown menu
|
// Links within the dropdown menu
|
||||||
> li > a {
|
> li {
|
||||||
display: block;
|
> a {
|
||||||
padding: 3px 20px 3px 15px;
|
display: block;
|
||||||
clear: both;
|
padding: 3px 20px 3px 15px;
|
||||||
font-weight: normal;
|
clear: both;
|
||||||
line-height: $line-height-base;
|
font-weight: normal;
|
||||||
color: $dropdownLinkColor;
|
line-height: $line-height-base;
|
||||||
white-space: nowrap;
|
color: $dropdownLinkColor;
|
||||||
|
white-space: nowrap;
|
||||||
|
|
||||||
i {
|
i {
|
||||||
padding-right: 5px;
|
padding-right: 5px;
|
||||||
color: $link-color-disabled;
|
color: $link-color-disabled;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -85,7 +85,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.graph-legend-table {
|
.graph-legend-table {
|
||||||
overflow-y: scroll;
|
overflow-y: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
|
|
||||||
.graph-legend-series {
|
.graph-legend-series {
|
||||||
display: table-row;
|
display: table-row;
|
||||||
|
@ -4,4 +4,5 @@
|
|||||||
margin: 0 0 $spacer $spacer * 1.5;
|
margin: 0 0 $spacer $spacer * 1.5;
|
||||||
}
|
}
|
||||||
li {line-height: 2;}
|
li {line-height: 2;}
|
||||||
|
a { color: $external-link-color; }
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@ describe("Emitter", () => {
|
|||||||
expect(sub2Called).to.be(true);
|
expect(sub2Called).to.be(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it.only('should handle errors', () => {
|
it('should handle errors', () => {
|
||||||
var events = new Emitter();
|
var events = new Emitter();
|
||||||
var sub1Called = 0;
|
var sub1Called = 0;
|
||||||
var sub2Called = 0;
|
var sub2Called = 0;
|
||||||
|
@ -99,6 +99,11 @@ define([
|
|||||||
var target = _templateSrv.replace('this.$test.filters', {}, 'glob');
|
var target = _templateSrv.replace('this.$test.filters', {}, 'glob');
|
||||||
expect(target).to.be('this.*.filters');
|
expect(target).to.be('this.*.filters');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should not escape custom all value', function() {
|
||||||
|
var target = _templateSrv.replace('this.$test', {}, 'regex');
|
||||||
|
expect(target).to.be('this.*');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('lucene format', function() {
|
describe('lucene format', function() {
|
||||||
@ -127,7 +132,7 @@ define([
|
|||||||
|
|
||||||
it('multi value and regex format should render regex string', function() {
|
it('multi value and regex format should render regex string', function() {
|
||||||
var result = _templateSrv.formatValue(['test.','test2'], 'regex');
|
var result = _templateSrv.formatValue(['test.','test2'], 'regex');
|
||||||
expect(result).to.be('test\\.|test2');
|
expect(result).to.be('(test\\.|test2)');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('multi value and pipe should render pipe string', function() {
|
it('multi value and pipe should render pipe string', function() {
|
||||||
|
@ -280,7 +280,7 @@ define([
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should add All option with custom value', function() {
|
it('should add All option with custom value', function() {
|
||||||
expect(scenario.variable.options[0].value).to.be('*');
|
expect(scenario.variable.options[0].value).to.be('$__all');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -35,7 +35,8 @@
|
|||||||
"jquery.flot.stackpercent": "vendor/flot/jquery.flot.stackpercent",
|
"jquery.flot.stackpercent": "vendor/flot/jquery.flot.stackpercent",
|
||||||
"jquery.flot.time": "vendor/flot/jquery.flot.time",
|
"jquery.flot.time": "vendor/flot/jquery.flot.time",
|
||||||
"jquery.flot.crosshair": "vendor/flot/jquery.flot.crosshair",
|
"jquery.flot.crosshair": "vendor/flot/jquery.flot.crosshair",
|
||||||
"jquery.flot.fillbelow": "vendor/flot/jquery.flot.fillbelow"
|
"jquery.flot.fillbelow": "vendor/flot/jquery.flot.fillbelow",
|
||||||
|
"jquery.flot.gauge": "vendor/flot/jquery.flot.gauge"
|
||||||
},
|
},
|
||||||
|
|
||||||
packages: {
|
packages: {
|
||||||
|
960
public/vendor/flot/jquery.flot.gauge.js
vendored
Normal file
960
public/vendor/flot/jquery.flot.gauge.js
vendored
Normal file
@ -0,0 +1,960 @@
|
|||||||
|
/*!
|
||||||
|
* jquery.flot.gauge v1.1.0 *
|
||||||
|
*
|
||||||
|
* Flot plugin for rendering gauge charts.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2015 @toyoty99.
|
||||||
|
* Licensed under the MIT license.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @module flot.gauge
|
||||||
|
*/
|
||||||
|
(function($) {
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gauge class
|
||||||
|
*
|
||||||
|
* @class Gauge
|
||||||
|
*/
|
||||||
|
var Gauge = (function() {
|
||||||
|
/**
|
||||||
|
* context of canvas
|
||||||
|
*
|
||||||
|
* @property context
|
||||||
|
* @type Object
|
||||||
|
*/
|
||||||
|
var context;
|
||||||
|
/**
|
||||||
|
* placeholder of canvas
|
||||||
|
*
|
||||||
|
* @property placeholder
|
||||||
|
* @type Object
|
||||||
|
*/
|
||||||
|
var placeholder;
|
||||||
|
/**
|
||||||
|
* options of plot
|
||||||
|
*
|
||||||
|
* @property options
|
||||||
|
* @type Object
|
||||||
|
*/
|
||||||
|
var options;
|
||||||
|
/**
|
||||||
|
* options of gauge
|
||||||
|
*
|
||||||
|
* @property gaugeOptions
|
||||||
|
* @type Object
|
||||||
|
*/
|
||||||
|
var gaugeOptions;
|
||||||
|
/**
|
||||||
|
* data series
|
||||||
|
*
|
||||||
|
* @property series
|
||||||
|
* @type Array
|
||||||
|
*/
|
||||||
|
var series;
|
||||||
|
/**
|
||||||
|
* logger
|
||||||
|
*
|
||||||
|
* @property logger
|
||||||
|
* @type Object
|
||||||
|
*/
|
||||||
|
var logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* constructor
|
||||||
|
*
|
||||||
|
* @class Gauge
|
||||||
|
* @constructor
|
||||||
|
* @param {Object} gaugeOptions gauge options
|
||||||
|
*/
|
||||||
|
var Gauge = function(plot, ctx) {
|
||||||
|
context = ctx;
|
||||||
|
placeholder = plot.getPlaceholder();
|
||||||
|
options = plot.getOptions();
|
||||||
|
gaugeOptions = options.series.gauges;
|
||||||
|
series = plot.getData();
|
||||||
|
logger = getLogger(gaugeOptions.debug);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* calculate layout
|
||||||
|
*
|
||||||
|
* @method calculateLayout
|
||||||
|
* @return the calculated layout properties
|
||||||
|
*/
|
||||||
|
Gauge.prototype.calculateLayout = function() {
|
||||||
|
|
||||||
|
var canvasWidth = placeholder.width();
|
||||||
|
var canvasHeight = placeholder.height();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// calculate cell size
|
||||||
|
var columns = Math.min(series.length, gaugeOptions.layout.columns);
|
||||||
|
var rows = Math.ceil(series.length / columns);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var margin = gaugeOptions.layout.margin;
|
||||||
|
var hMargin = gaugeOptions.layout.hMargin;
|
||||||
|
var vMargin = gaugeOptions.layout.vMargin;
|
||||||
|
var cellWidth = (canvasWidth - (margin * 2) - (hMargin * (columns - 1))) / columns;
|
||||||
|
var cellHeight = (canvasHeight - (margin * 2) - (vMargin * (rows - 1))) / rows;
|
||||||
|
if (gaugeOptions.layout.square) {
|
||||||
|
var cell = Math.min(cellWidth, cellHeight);
|
||||||
|
cellWidth = cell;
|
||||||
|
cellHeight = cell;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// calculate 'auto' values
|
||||||
|
calculateAutoValues(gaugeOptions, cellWidth);
|
||||||
|
|
||||||
|
// calculate maximum radius
|
||||||
|
var cellMargin = gaugeOptions.cell.margin;
|
||||||
|
var labelMargin = 0;
|
||||||
|
var labelFontSize = 0;
|
||||||
|
if (gaugeOptions.label.show) {
|
||||||
|
labelMargin = gaugeOptions.label.margin;
|
||||||
|
labelFontSize = gaugeOptions.label.font.size;
|
||||||
|
}
|
||||||
|
var valueMargin = 0;
|
||||||
|
var valueFontSize = 0;
|
||||||
|
if (gaugeOptions.value.show) {
|
||||||
|
valueMargin = gaugeOptions.value.margin;
|
||||||
|
valueFontSize = gaugeOptions.value.font.size;
|
||||||
|
}
|
||||||
|
var thresholdWidth = 0;
|
||||||
|
if (gaugeOptions.threshold.show) {
|
||||||
|
thresholdWidth = gaugeOptions.threshold.width;
|
||||||
|
}
|
||||||
|
var thresholdLabelMargin = 0;
|
||||||
|
var thresholdLabelFontSize = 0;
|
||||||
|
if (gaugeOptions.threshold.label.show) {
|
||||||
|
thresholdLabelMargin = gaugeOptions.threshold.label.margin;
|
||||||
|
thresholdLabelFontSize = gaugeOptions.threshold.label.font.size;
|
||||||
|
}
|
||||||
|
|
||||||
|
var maxRadiusH = (cellWidth / 2) - cellMargin - thresholdWidth - (thresholdLabelMargin * 2) - thresholdLabelFontSize;
|
||||||
|
|
||||||
|
var startAngle = gaugeOptions.gauge.startAngle;
|
||||||
|
var endAngle = gaugeOptions.gauge.endAngle;
|
||||||
|
var dAngle = (endAngle - startAngle) / 100;
|
||||||
|
var heightRatioV = -1;
|
||||||
|
for (var a = startAngle; a < endAngle; a += dAngle) {
|
||||||
|
heightRatioV = Math.max(heightRatioV, Math.sin(toRad(a)));
|
||||||
|
}
|
||||||
|
heightRatioV = Math.max(heightRatioV, Math.sin(toRad(endAngle)));
|
||||||
|
var outerRadiusV = (cellHeight - (cellMargin * 2) - (labelMargin * 2) - labelFontSize) / (1 + heightRatioV);
|
||||||
|
if (outerRadiusV * heightRatioV < valueMargin + (valueFontSize / 2)) {
|
||||||
|
outerRadiusV = cellHeight - (cellMargin * 2) - (labelMargin * 2) - labelFontSize - valueMargin - (valueFontSize / 2);
|
||||||
|
}
|
||||||
|
var maxRadiusV = outerRadiusV - (thresholdLabelMargin * 2) - thresholdLabelFontSize - thresholdWidth;
|
||||||
|
|
||||||
|
var radius = Math.min(maxRadiusH, maxRadiusV);
|
||||||
|
|
||||||
|
|
||||||
|
var width = gaugeOptions.gauge.width;
|
||||||
|
if (width >= radius) {
|
||||||
|
width = Math.max(3, radius / 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var outerRadius = (thresholdLabelMargin * 2) + thresholdLabelFontSize + thresholdWidth + radius;
|
||||||
|
var gaugeOuterHeight = Math.max(outerRadius * (1 + heightRatioV), outerRadius + valueMargin + (valueFontSize / 2));
|
||||||
|
|
||||||
|
return {
|
||||||
|
canvasWidth: canvasWidth,
|
||||||
|
canvasHeight: canvasHeight,
|
||||||
|
margin: margin,
|
||||||
|
hMargin: hMargin,
|
||||||
|
vMargin: vMargin,
|
||||||
|
columns: columns,
|
||||||
|
rows: rows,
|
||||||
|
cellWidth: cellWidth,
|
||||||
|
cellHeight: cellHeight,
|
||||||
|
cellMargin: cellMargin,
|
||||||
|
labelMargin: labelMargin,
|
||||||
|
labelFontSize: labelFontSize,
|
||||||
|
valueMargin: valueMargin,
|
||||||
|
valueFontSize: valueFontSize,
|
||||||
|
width: width,
|
||||||
|
radius: radius,
|
||||||
|
thresholdWidth: thresholdWidth,
|
||||||
|
thresholdLabelMargin: thresholdLabelMargin,
|
||||||
|
thresholdLabelFontSize: thresholdLabelFontSize,
|
||||||
|
gaugeOuterHeight: gaugeOuterHeight
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* calculate the values which are set as 'auto'
|
||||||
|
*
|
||||||
|
* @method calculateAutoValues
|
||||||
|
* @param {Object} gaugeOptionsi the options of the gauge
|
||||||
|
* @param {Number} cellWidth the width of cell
|
||||||
|
*/
|
||||||
|
function calculateAutoValues(gaugeOptionsi, cellWidth) {
|
||||||
|
|
||||||
|
if (gaugeOptionsi.gauge.width === "auto") {
|
||||||
|
gaugeOptionsi.gauge.width = Math.max(5, cellWidth / 8);
|
||||||
|
}
|
||||||
|
if (gaugeOptionsi.label.margin === "auto") {
|
||||||
|
gaugeOptionsi.label.margin = Math.max(1, cellWidth / 20);
|
||||||
|
}
|
||||||
|
if (gaugeOptionsi.label.font.size === "auto") {
|
||||||
|
gaugeOptionsi.label.font.size = Math.max(5, cellWidth / 8);
|
||||||
|
}
|
||||||
|
if (gaugeOptionsi.value.margin === "auto") {
|
||||||
|
gaugeOptionsi.value.margin = Math.max(1, cellWidth / 30);
|
||||||
|
}
|
||||||
|
if (gaugeOptionsi.value.font.size === "auto") {
|
||||||
|
gaugeOptionsi.value.font.size = Math.max(5, cellWidth / 9);
|
||||||
|
}
|
||||||
|
if (gaugeOptionsi.threshold.width === "auto") {
|
||||||
|
gaugeOptionsi.threshold.width = Math.max(3, cellWidth / 100);
|
||||||
|
}
|
||||||
|
if (gaugeOptionsi.threshold.label.margin === "auto") {
|
||||||
|
gaugeOptionsi.threshold.label.margin = Math.max(3, cellWidth / 40);
|
||||||
|
}
|
||||||
|
if (gaugeOptionsi.threshold.label.font.size === "auto") {
|
||||||
|
gaugeOptionsi.threshold.label.font.size = Math.max(5, cellWidth / 15);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
Gauge.prototype.calculateAutoValues = calculateAutoValues;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* calculate the layout of the cell inside
|
||||||
|
*
|
||||||
|
* @method calculateCellLayout
|
||||||
|
* @param {Object} gaugeOptionsi the options of the gauge
|
||||||
|
* @param {Number} cellWidth the width of cell
|
||||||
|
* @param {Number} i the index of the series
|
||||||
|
* @return the calculated cell layout properties
|
||||||
|
*/
|
||||||
|
Gauge.prototype.calculateCellLayout = function(gaugeOptionsi, layout, i) {
|
||||||
|
|
||||||
|
// calculate top, left and center
|
||||||
|
var c = col(layout.columns, i);
|
||||||
|
var r = row(layout.columns, i);
|
||||||
|
var x = layout.margin + (layout.cellWidth + layout.hMargin) * c;
|
||||||
|
var y = layout.margin + (layout.cellHeight + layout.vMargin) * r;
|
||||||
|
var cx = x + (layout.cellWidth / 2);
|
||||||
|
var cy = y + layout.cellMargin + (layout.labelMargin * 2) + layout.labelFontSize + layout.thresholdWidth
|
||||||
|
+ layout.thresholdLabelFontSize + (layout.thresholdLabelMargin * 2) + layout.radius;
|
||||||
|
var blank = layout.cellHeight - (layout.cellMargin * 2) - (layout.labelMargin * 2) - layout.labelFontSize - layout.gaugeOuterHeight;
|
||||||
|
var offsetY = 0;
|
||||||
|
if (gaugeOptionsi.cell.vAlign === "middle") {
|
||||||
|
offsetY = (blank / 2);
|
||||||
|
} else if (gaugeOptionsi.cell.vAlign === "bottom") {
|
||||||
|
offsetY = blank;
|
||||||
|
}
|
||||||
|
cy += offsetY;
|
||||||
|
|
||||||
|
return {
|
||||||
|
col: c,
|
||||||
|
row: r,
|
||||||
|
x: x,
|
||||||
|
y: y,
|
||||||
|
offsetY: offsetY,
|
||||||
|
cellWidth: layout.cellWidth,
|
||||||
|
cellHeight: layout.cellHeight,
|
||||||
|
cellMargin: layout.cellMargin,
|
||||||
|
cx: cx,
|
||||||
|
cy: cy
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* draw the background of chart
|
||||||
|
*
|
||||||
|
* @method drawBackground
|
||||||
|
* @param {Object} layout the layout properties
|
||||||
|
*/
|
||||||
|
Gauge.prototype.drawBackground = function(layout) {
|
||||||
|
|
||||||
|
if (!gaugeOptions.frame.show) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
context.save();
|
||||||
|
context.strokeStyle = options.grid.borderColor;
|
||||||
|
context.lineWidth = options.grid.borderWidth;
|
||||||
|
context.strokeRect(0, 0, layout.canvasWidth, layout.canvasHeight);
|
||||||
|
if (options.grid.backgroundColor) {
|
||||||
|
context.fillStyle = options.grid.backgroundColor;
|
||||||
|
context.fillRect(0, 0, layout.canvasWidth, layout.canvasHeight);
|
||||||
|
}
|
||||||
|
context.restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* draw the background of cell
|
||||||
|
*
|
||||||
|
* @method drawCellBackground
|
||||||
|
* @param {Object} gaugeOptionsi the options of the gauge
|
||||||
|
* @param {Object} cellLayout the cell layout properties
|
||||||
|
*/
|
||||||
|
Gauge.prototype.drawCellBackground = function(gaugeOptionsi, cellLayout) {
|
||||||
|
|
||||||
|
context.save();
|
||||||
|
if (gaugeOptionsi.cell.border && gaugeOptionsi.cell.border.show && gaugeOptionsi.cell.border.color && gaugeOptionsi.cell.border.width) {
|
||||||
|
context.strokeStyle = gaugeOptionsi.cell.border.color;
|
||||||
|
context.lineWidth = gaugeOptionsi.cell.border.width;
|
||||||
|
context.strokeRect(cellLayout.x, cellLayout.y, cellLayout.cellWidth, cellLayout.cellHeight);
|
||||||
|
}
|
||||||
|
if (gaugeOptionsi.cell.background && gaugeOptionsi.cell.background.color) {
|
||||||
|
context.fillStyle = gaugeOptionsi.cell.background.color;
|
||||||
|
context.fillRect(cellLayout.x, cellLayout.y, cellLayout.cellWidth, cellLayout.cellHeight);
|
||||||
|
}
|
||||||
|
context.restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* draw the gauge
|
||||||
|
*
|
||||||
|
* @method drawGauge
|
||||||
|
* @param {Object} gaugeOptionsi the options of the gauge
|
||||||
|
* @param {Object} layout the layout properties
|
||||||
|
* @param {Object} cellLayout the cell layout properties
|
||||||
|
* @param {String} label the label of data
|
||||||
|
* @param {Number} data the value of the gauge
|
||||||
|
*/
|
||||||
|
Gauge.prototype.drawGauge = function(gaugeOptionsi, layout, cellLayout, label, data) {
|
||||||
|
|
||||||
|
|
||||||
|
var blur = gaugeOptionsi.gauge.shadow.show ? gaugeOptionsi.gauge.shadow.blur : 0;
|
||||||
|
|
||||||
|
|
||||||
|
// draw gauge frame
|
||||||
|
drawArcWithShadow(
|
||||||
|
cellLayout.cx, // center x
|
||||||
|
cellLayout.cy, // center y
|
||||||
|
layout.radius,
|
||||||
|
layout.width,
|
||||||
|
toRad(gaugeOptionsi.gauge.startAngle),
|
||||||
|
toRad(gaugeOptionsi.gauge.endAngle),
|
||||||
|
gaugeOptionsi.gauge.border.color, // line color
|
||||||
|
gaugeOptionsi.gauge.border.width, // line width
|
||||||
|
gaugeOptionsi.gauge.background.color, // fill color
|
||||||
|
blur);
|
||||||
|
|
||||||
|
// draw gauge
|
||||||
|
var c1 = getColor(gaugeOptionsi, data);
|
||||||
|
var a2 = calculateAngle(gaugeOptionsi, layout, data);
|
||||||
|
drawArcWithShadow(
|
||||||
|
cellLayout.cx, // center x
|
||||||
|
cellLayout.cy, // center y
|
||||||
|
layout.radius - 1,
|
||||||
|
layout.width - 2,
|
||||||
|
toRad(gaugeOptionsi.gauge.startAngle),
|
||||||
|
toRad(a2),
|
||||||
|
c1, // line color
|
||||||
|
1, // line width
|
||||||
|
c1, // fill color
|
||||||
|
blur);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* decide the color of the data from the threshold options
|
||||||
|
*
|
||||||
|
* @method getColor
|
||||||
|
* @private
|
||||||
|
* @param {Object} gaugeOptionsi the options of the gauge
|
||||||
|
* @param {Number} data the value of the gauge
|
||||||
|
*/
|
||||||
|
function getColor(gaugeOptionsi, data) {
|
||||||
|
var color;
|
||||||
|
for (var i = 0; i < gaugeOptionsi.threshold.values.length; i++) {
|
||||||
|
var threshold = gaugeOptionsi.threshold.values[i];
|
||||||
|
color = threshold.color;
|
||||||
|
if (data <= threshold.value) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* calculate the angle of the data
|
||||||
|
*
|
||||||
|
* @method calculateAngle
|
||||||
|
* @private
|
||||||
|
* @param {Object} gaugeOptionsi the options of the gauge
|
||||||
|
* @param {Object} layout the layout properties
|
||||||
|
* @param {Number} data the value of the gauge
|
||||||
|
*/
|
||||||
|
function calculateAngle(gaugeOptionsi, layout, data) {
|
||||||
|
var a =
|
||||||
|
gaugeOptionsi.gauge.startAngle
|
||||||
|
+ (gaugeOptionsi.gauge.endAngle - gaugeOptionsi.gauge.startAngle)
|
||||||
|
* ((data - gaugeOptionsi.gauge.min) / (gaugeOptionsi.gauge.max - gaugeOptionsi.gauge.min));
|
||||||
|
|
||||||
|
if (a < gaugeOptionsi.gauge.startAngle) {
|
||||||
|
a = gaugeOptionsi.gauge.startAngle;
|
||||||
|
} else if (a > gaugeOptionsi.gauge.endAngle) {
|
||||||
|
a = gaugeOptionsi.gauge.endAngle;
|
||||||
|
}
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* draw the arc of the threshold
|
||||||
|
*
|
||||||
|
* @method drawThreshold
|
||||||
|
* @param {Object} gaugeOptionsi the options of the gauge
|
||||||
|
* @param {Object} layout the layout properties
|
||||||
|
* @param {Object} cellLayout the cell layout properties
|
||||||
|
*/
|
||||||
|
Gauge.prototype.drawThreshold = function(gaugeOptionsi, layout, cellLayout) {
|
||||||
|
|
||||||
|
var a1 = gaugeOptionsi.gauge.startAngle;
|
||||||
|
for (var i = 0; i < gaugeOptionsi.threshold.values.length; i++) {
|
||||||
|
var threshold = gaugeOptionsi.threshold.values[i];
|
||||||
|
c1 = threshold.color;
|
||||||
|
a2 = calculateAngle(gaugeOptionsi, layout, threshold.value);
|
||||||
|
drawArc(
|
||||||
|
context,
|
||||||
|
cellLayout.cx, // center x
|
||||||
|
cellLayout.cy, // center y
|
||||||
|
layout.radius + layout.thresholdWidth,
|
||||||
|
layout.thresholdWidth - 2,
|
||||||
|
toRad(a1),
|
||||||
|
toRad(a2),
|
||||||
|
c1, // line color
|
||||||
|
1, // line width
|
||||||
|
c1); // fill color
|
||||||
|
a1 = a2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* draw an arc with a shadow
|
||||||
|
*
|
||||||
|
* @method drawArcWithShadow
|
||||||
|
* @private
|
||||||
|
* @param {Number} cx the x position of the center
|
||||||
|
* @param {Number} cy the y position of the center
|
||||||
|
* @param {Number} r the radius of an arc
|
||||||
|
* @param {Number} w the width of an arc
|
||||||
|
* @param {Number} rd1 the start angle of an arc in radians
|
||||||
|
* @param {Number} rd2 the end angle of an arc in radians
|
||||||
|
* @param {String} lc the color of a line
|
||||||
|
* @param {Number} lw the widht of a line
|
||||||
|
* @param {String} fc the fill color of an arc
|
||||||
|
* @param {Number} blur the shdow blur
|
||||||
|
*/
|
||||||
|
function drawArcWithShadow(cx, cy, r, w, rd1, rd2, lc, lw, fc, blur) {
|
||||||
|
if (rd1 === rd2) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
context.save();
|
||||||
|
|
||||||
|
drawArc(context, cx, cy, r, w, rd1, rd2, lc, lw, fc);
|
||||||
|
|
||||||
|
if (blur) {
|
||||||
|
drawArc(context, cx, cy, r, w, rd1, rd2);
|
||||||
|
context.clip();
|
||||||
|
context.shadowOffsetX = 0;
|
||||||
|
context.shadowOffsetY = 0;
|
||||||
|
context.shadowBlur = 10;
|
||||||
|
context.shadowColor = "gray";
|
||||||
|
drawArc(context, cx, cy, r + 1, w + 2, rd1, rd2, lc, 1);
|
||||||
|
}
|
||||||
|
context.restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* draw the label of the gauge
|
||||||
|
*
|
||||||
|
* @method drawLable
|
||||||
|
* @param {Object} gaugeOptionsi the options of the gauge
|
||||||
|
* @param {Object} layout the layout properties
|
||||||
|
* @param {Object} cellLayout the cell layout properties
|
||||||
|
* @param {Number} i the index of the series
|
||||||
|
* @param {Object} item the item of the series
|
||||||
|
*/
|
||||||
|
Gauge.prototype.drawLable = function(gaugeOptionsi, layout, cellLayout, i, item) {
|
||||||
|
|
||||||
|
drawText(
|
||||||
|
cellLayout.cx,
|
||||||
|
cellLayout.y + cellLayout.cellMargin + layout.labelMargin + cellLayout.offsetY,
|
||||||
|
"flotGagueLabel" + i,
|
||||||
|
gaugeOptionsi.label.formatter ? gaugeOptionsi.label.formatter(item.label, item.data[0][1]) : text,
|
||||||
|
gaugeOptionsi.label);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* draw the value of the gauge
|
||||||
|
*
|
||||||
|
* @method drawValue
|
||||||
|
* @param {Object} gaugeOptionsi the options of the gauge
|
||||||
|
* @param {Object} layout the layout properties
|
||||||
|
* @param {Object} cellLayout the cell layout properties
|
||||||
|
* @param {Number} i the index of the series
|
||||||
|
* @param {Object} item the item of the series
|
||||||
|
*/
|
||||||
|
Gauge.prototype.drawValue = function(gaugeOptionsi, layout, cellLayout, i, item) {
|
||||||
|
|
||||||
|
drawText(
|
||||||
|
cellLayout.cx,
|
||||||
|
cellLayout.cy - (gaugeOptionsi.value.font.size / 2),
|
||||||
|
"flotGagueValue" + i,
|
||||||
|
gaugeOptionsi.value.formatter ? gaugeOptionsi.value.formatter(item.label, item.data[0][1]) : text,
|
||||||
|
gaugeOptionsi.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* draw the values of the threshold
|
||||||
|
*
|
||||||
|
* @method drawThresholdValues
|
||||||
|
* @param {Object} gaugeOptionsi the options of the gauge
|
||||||
|
* @param {Object} layout the layout properties
|
||||||
|
* @param {Object} cellLayout the cell layout properties
|
||||||
|
* @param {Number} i the index of the series
|
||||||
|
*/
|
||||||
|
Gauge.prototype.drawThresholdValues = function(gaugeOptionsi, layout, cellLayout, i) {
|
||||||
|
|
||||||
|
// min, max
|
||||||
|
drawThresholdValue(gaugeOptionsi, layout, cellLayout, "Min" + i, gaugeOptionsi.gauge.min, gaugeOptionsi.gauge.startAngle);
|
||||||
|
drawThresholdValue(gaugeOptionsi, layout, cellLayout, "Max" + i, gaugeOptionsi.gauge.max, gaugeOptionsi.gauge.endAngle);
|
||||||
|
// threshold values
|
||||||
|
for (var j = 0; j < gaugeOptionsi.threshold.values.length; j++) {
|
||||||
|
var threshold = gaugeOptionsi.threshold.values[j];
|
||||||
|
if (threshold.value > gaugeOptionsi.gauge.min && threshold.value < gaugeOptionsi.gauge.max) {
|
||||||
|
var a = calculateAngle(gaugeOptionsi, layout, threshold.value);
|
||||||
|
drawThresholdValue(gaugeOptionsi, layout, cellLayout, i + "_" + j, threshold.value, a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* draw the value of the threshold
|
||||||
|
*
|
||||||
|
* @method drawThresholdValue
|
||||||
|
* @param {Object} gaugeOptionsi the options of the gauge
|
||||||
|
* @param {Object} layout the layout properties
|
||||||
|
* @param {Object} cellLayout the cell layout properties
|
||||||
|
* @param {Number} i the index of the series
|
||||||
|
* @param {Number} value the value of the threshold
|
||||||
|
* @param {Number} a the angle of the value drawn
|
||||||
|
*/
|
||||||
|
function drawThresholdValue(gaugeOptionsi, layout, cellLayout, i, value, a) {
|
||||||
|
drawText(
|
||||||
|
cellLayout.cx
|
||||||
|
+ ((layout.thresholdLabelMargin + (layout.thresholdLabelFontSize / 2) + layout.radius)
|
||||||
|
* Math.cos(toRad(a))),
|
||||||
|
cellLayout.cy
|
||||||
|
+ ((layout.thresholdLabelMargin + (layout.thresholdLabelFontSize / 2) + layout.radius)
|
||||||
|
* Math.sin(toRad(a))),
|
||||||
|
"flotGagueThresholdValue" + i,
|
||||||
|
gaugeOptionsi.threshold.label.formatter ? gaugeOptionsi.threshold.label.formatter(value) : value,
|
||||||
|
gaugeOptionsi.threshold.label,
|
||||||
|
a);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* draw a text
|
||||||
|
*
|
||||||
|
* the textOptions is assumed as follows:
|
||||||
|
*
|
||||||
|
* textOptions: {
|
||||||
|
* background: {
|
||||||
|
* color: null,
|
||||||
|
* opacity: 0
|
||||||
|
* },
|
||||||
|
* font: {
|
||||||
|
* size: "auto"
|
||||||
|
* family: "\"MS ゴシック\",sans-serif"
|
||||||
|
* },
|
||||||
|
* color: null
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* @method drawText
|
||||||
|
* @private
|
||||||
|
* @param {Number} x the x position of the text drawn (left top)
|
||||||
|
* @param {Number} y the y position of the text drawn (left top)
|
||||||
|
* @param {String} id the id of the dom element
|
||||||
|
* @param {String} text the text drawn
|
||||||
|
* @param {Object} textOptions the option of the text
|
||||||
|
* @param {Number} [a] the angle of the value drawn
|
||||||
|
*/
|
||||||
|
function drawText(x, y, id, text, textOptions, a) {
|
||||||
|
var span = $("." + id, placeholder);
|
||||||
|
var exists = span.length;
|
||||||
|
if (!exists) {
|
||||||
|
span = $("<span></span>")
|
||||||
|
span.attr("id", id);
|
||||||
|
span.css("position", "absolute");
|
||||||
|
span.css("top", y + "px");
|
||||||
|
if (textOptions.font.size) {
|
||||||
|
span.css("font-size", textOptions.font.size + "px");
|
||||||
|
}
|
||||||
|
if (textOptions.font.family) {
|
||||||
|
span.css("font-family", textOptions.font.family);
|
||||||
|
}
|
||||||
|
if (textOptions.color) {
|
||||||
|
span.css("color", textOptions.color);
|
||||||
|
}
|
||||||
|
if (textOptions.background.color) {
|
||||||
|
span.css("background-color", textOptions.background.color);
|
||||||
|
}
|
||||||
|
if (textOptions.background.opacity) {
|
||||||
|
span.css("opacity", textOptions.background.opacity);
|
||||||
|
}
|
||||||
|
placeholder.append(span);
|
||||||
|
}
|
||||||
|
span.text(text);
|
||||||
|
// after append, readjust the left position
|
||||||
|
span.css("left", x + "px"); // for redraw, resetting the left position is needed here
|
||||||
|
span.css("left", (parseInt(span.css("left")) - (span.width()/ 2)) + "px");
|
||||||
|
|
||||||
|
// at last, set angle
|
||||||
|
if (!exists && a) {
|
||||||
|
span.css("top", (parseInt(span.css("top")) - (span.height()/ 2)) + "px");
|
||||||
|
span.css("transform", "rotate(" + ((180 * a) + 90) + "deg)"); // not supported for ie8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Gauge;
|
||||||
|
})();
|
||||||
|
/**
|
||||||
|
* get a instance of Logger
|
||||||
|
*
|
||||||
|
* @method getLogger
|
||||||
|
* @for flot.gauge
|
||||||
|
* @private
|
||||||
|
* @param {Object} debugOptions the options of debug
|
||||||
|
*/
|
||||||
|
function getLogger(debugOptions) {
|
||||||
|
return typeof Logger !== "undefined" ? new Logger(debugOptions) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* calculate the index of columns for the specified data
|
||||||
|
*
|
||||||
|
* @method col
|
||||||
|
* @for flot.gauge
|
||||||
|
* @param {Number} columns the number of columns
|
||||||
|
* @param {Number} i the index of the series
|
||||||
|
* @return the index of columns
|
||||||
|
*/
|
||||||
|
function col(columns, i) {
|
||||||
|
return i % columns;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* calculate the index of rows for the specified data
|
||||||
|
*
|
||||||
|
* @method row
|
||||||
|
* @for flot.gauge
|
||||||
|
* @param {Number} columns the number of rows
|
||||||
|
* @param {Number} i the index of the series
|
||||||
|
* @return the index of rows
|
||||||
|
*/
|
||||||
|
function row(columns, i) {
|
||||||
|
return Math.floor(i / columns);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* calculate the angle in radians
|
||||||
|
*
|
||||||
|
* internally, use a number without PI (0 - 2).
|
||||||
|
* so, in this function, multiply PI
|
||||||
|
*
|
||||||
|
* @method toRad
|
||||||
|
* @for flot.gauge
|
||||||
|
* @param {Number} a the number of angle without PI
|
||||||
|
* @return the angle in radians
|
||||||
|
*/
|
||||||
|
function toRad(a) {
|
||||||
|
return a * Math.PI;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* draw an arc
|
||||||
|
*
|
||||||
|
* @method drawArc
|
||||||
|
* @for flot.gauge
|
||||||
|
* @param {Object} context the context of canvas
|
||||||
|
* @param {Number} cx the x position of the center
|
||||||
|
* @param {Number} cy the y position of the center
|
||||||
|
* @param {Number} r the radius of an arc
|
||||||
|
* @param {Number} w the width of an arc
|
||||||
|
* @param {Number} rd1 the start angle of an arc in radians
|
||||||
|
* @param {Number} rd2 the end angle of an arc in radians
|
||||||
|
* @param {String} lc the color of a line
|
||||||
|
* @param {Number} lw the widht of a line
|
||||||
|
* @param {String} fc the fill color of an arc
|
||||||
|
*/
|
||||||
|
function drawArc(context, cx, cy, r, w, rd1, rd2, lc, lw, fc) {
|
||||||
|
if (rd1 === rd2) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var counterClockwise = false;
|
||||||
|
context.save();
|
||||||
|
context.beginPath();
|
||||||
|
context.arc(cx, cy, r, rd1, rd2, counterClockwise);
|
||||||
|
context.lineTo(cx + (r - w) * Math.cos(rd2),
|
||||||
|
cy + (r - w) * Math.sin(rd2));
|
||||||
|
context.arc(cx, cy, r - w, rd2, rd1, !counterClockwise);
|
||||||
|
context.closePath();
|
||||||
|
if (lw) {
|
||||||
|
context.lineWidth = lw;
|
||||||
|
}
|
||||||
|
if (lc) {
|
||||||
|
context.strokeStyle = lc;
|
||||||
|
context.stroke();
|
||||||
|
}
|
||||||
|
if (fc) {
|
||||||
|
context.fillStyle = fc;
|
||||||
|
context.fill();
|
||||||
|
}
|
||||||
|
context.restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* initialize plugin
|
||||||
|
*
|
||||||
|
* @method init
|
||||||
|
* @for flot.gauge
|
||||||
|
* @private
|
||||||
|
* @param {Object} plot a instance of plot
|
||||||
|
*/
|
||||||
|
function init (plot) {
|
||||||
|
// add processOptions hook
|
||||||
|
plot.hooks.processOptions.push(function(plot, options) {
|
||||||
|
var logger = getLogger(options.series.gauges.debug);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// turn 'grid' and 'legend' off
|
||||||
|
if (options.series.gauges.show) {
|
||||||
|
options.grid.show = false;
|
||||||
|
options.legend.show = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// sort threshold
|
||||||
|
var thresholds = options.series.gauges.threshold.values;
|
||||||
|
|
||||||
|
thresholds.sort(function(a, b) {
|
||||||
|
if (a.value < b.value) {
|
||||||
|
return -1;
|
||||||
|
} else if (a.value > b.value) {
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
// add draw hook
|
||||||
|
plot.hooks.draw.push(function(plot, context) {
|
||||||
|
var options = plot.getOptions();
|
||||||
|
var gaugeOptions = options.series.gauges;
|
||||||
|
|
||||||
|
var logger = getLogger(gaugeOptions.debug);
|
||||||
|
|
||||||
|
|
||||||
|
if (!gaugeOptions.show) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var series = plot.getData();
|
||||||
|
|
||||||
|
if (!series || !series.length) {
|
||||||
|
return; // if no series were passed
|
||||||
|
}
|
||||||
|
|
||||||
|
var gauge = new Gauge(plot, context);
|
||||||
|
|
||||||
|
// calculate layout
|
||||||
|
var layout = gauge.calculateLayout();
|
||||||
|
|
||||||
|
// debug layout
|
||||||
|
if (gaugeOptions.debug.layout) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// draw background
|
||||||
|
gauge.drawBackground(layout)
|
||||||
|
|
||||||
|
// draw cells (label, gauge, value, threshold)
|
||||||
|
for (var i = 0; i < series.length; i++) {
|
||||||
|
var item = series[i];
|
||||||
|
|
||||||
|
var gaugeOptionsi = $.extend({}, gaugeOptions, item.gauges);
|
||||||
|
if (item.gauges) {
|
||||||
|
// re-calculate 'auto' values
|
||||||
|
gauge.calculateAutoValues(gaugeOptionsi, layout.cellWidth);
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculate cell layout
|
||||||
|
var cellLayout = gauge.calculateCellLayout(gaugeOptionsi, layout, i);
|
||||||
|
|
||||||
|
// draw cell background
|
||||||
|
gauge.drawCellBackground(gaugeOptionsi, cellLayout)
|
||||||
|
// debug layout
|
||||||
|
if (gaugeOptionsi.debug.layout) {
|
||||||
|
|
||||||
|
}
|
||||||
|
// draw label
|
||||||
|
if (gaugeOptionsi.label.show) {
|
||||||
|
gauge.drawLable(gaugeOptionsi, layout, cellLayout, i, item);
|
||||||
|
}
|
||||||
|
// draw gauge
|
||||||
|
gauge.drawGauge(gaugeOptionsi, layout, cellLayout, item.label, item.data[0][1]);
|
||||||
|
// draw threshold
|
||||||
|
if (gaugeOptionsi.threshold.show) {
|
||||||
|
gauge.drawThreshold(gaugeOptionsi, layout, cellLayout);
|
||||||
|
}
|
||||||
|
if (gaugeOptionsi.threshold.label.show) {
|
||||||
|
gauge.drawThresholdValues(gaugeOptionsi, layout, cellLayout, i)
|
||||||
|
}
|
||||||
|
// draw value
|
||||||
|
if (gaugeOptionsi.value.show) {
|
||||||
|
gauge.drawValue(gaugeOptionsi, layout, cellLayout, i, item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [defaults description]
|
||||||
|
*
|
||||||
|
* @property defaults
|
||||||
|
* @type {Object}
|
||||||
|
*/
|
||||||
|
var defaults = {
|
||||||
|
series: {
|
||||||
|
gauges: {
|
||||||
|
debug: {
|
||||||
|
log: false,
|
||||||
|
layout: false,
|
||||||
|
alert: false
|
||||||
|
},
|
||||||
|
show: false,
|
||||||
|
layout: {
|
||||||
|
margin: 5,
|
||||||
|
columns: 3,
|
||||||
|
hMargin: 5,
|
||||||
|
vMargin: 5,
|
||||||
|
square: false
|
||||||
|
},
|
||||||
|
frame: {
|
||||||
|
show: true
|
||||||
|
},
|
||||||
|
cell: {
|
||||||
|
background: {
|
||||||
|
color: null
|
||||||
|
},
|
||||||
|
border: {
|
||||||
|
show: true,
|
||||||
|
color: "black",
|
||||||
|
width: 1
|
||||||
|
},
|
||||||
|
margin: 5,
|
||||||
|
vAlign: "middle" // 'top' or 'middle' or 'bottom'
|
||||||
|
},
|
||||||
|
gauge: {
|
||||||
|
width: "auto", // a specified number, or 'auto'
|
||||||
|
startAngle: 0.9, // 0 - 2 factor of the radians
|
||||||
|
endAngle: 2.1, // 0 - 2 factor of the radians
|
||||||
|
min: 0,
|
||||||
|
max: 100,
|
||||||
|
background: {
|
||||||
|
color: "white"
|
||||||
|
},
|
||||||
|
border: {
|
||||||
|
color: "lightgray",
|
||||||
|
width: 2
|
||||||
|
},
|
||||||
|
shadow: {
|
||||||
|
show: true,
|
||||||
|
blur: 5
|
||||||
|
}
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
show: true,
|
||||||
|
margin: "auto", // a specified number, or 'auto'
|
||||||
|
background: {
|
||||||
|
color: null,
|
||||||
|
opacity: 0
|
||||||
|
},
|
||||||
|
font: {
|
||||||
|
size: "auto", // a specified number, or 'auto'
|
||||||
|
family: "sans-serif"
|
||||||
|
},
|
||||||
|
color: null,
|
||||||
|
formatter: function(label, value) {
|
||||||
|
return label;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
value: {
|
||||||
|
show: true,
|
||||||
|
margin: "auto", // a specified number, or 'auto'
|
||||||
|
background: {
|
||||||
|
color: null,
|
||||||
|
opacity: 0
|
||||||
|
},
|
||||||
|
font: {
|
||||||
|
size: "auto", // a specified number, or 'auto'
|
||||||
|
family: "sans-serif"
|
||||||
|
},
|
||||||
|
color: null,
|
||||||
|
formatter: function(label, value) {
|
||||||
|
return parseInt(value);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
threshold: {
|
||||||
|
show: true,
|
||||||
|
width: "auto", // a specified number, or 'auto'
|
||||||
|
label: {
|
||||||
|
show: true,
|
||||||
|
margin: "auto", // a specified number, or 'auto'
|
||||||
|
background: {
|
||||||
|
color: null,
|
||||||
|
opacity: 0
|
||||||
|
},
|
||||||
|
font: {
|
||||||
|
size: "auto", // a specified number, or 'auto'
|
||||||
|
family: ",sans-serif"
|
||||||
|
},
|
||||||
|
color: null,
|
||||||
|
formatter: function(value) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
values: [
|
||||||
|
{
|
||||||
|
value: 50,
|
||||||
|
color: "lightgreen"
|
||||||
|
}, {
|
||||||
|
value: 80,
|
||||||
|
color: "yellow"
|
||||||
|
}, {
|
||||||
|
value: 100,
|
||||||
|
color: "red"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// register the gauge plugin
|
||||||
|
$.plot.plugins.push({
|
||||||
|
init: init,
|
||||||
|
options: defaults,
|
||||||
|
name: "gauge",
|
||||||
|
version: "1.1.0"
|
||||||
|
});
|
||||||
|
|
||||||
|
})(jQuery);
|
@ -25,6 +25,19 @@ module.exports = function(grunt) {
|
|||||||
'typescript:build'
|
'typescript:build'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
grunt.registerTask('test', ['default', 'karma:test']);
|
grunt.registerTask('test', ['default', 'karma:test', 'no-only-tests']);
|
||||||
|
|
||||||
|
grunt.registerTask('no-only-tests', function() {
|
||||||
|
var files = grunt.file.expand('public/**/*_specs\.ts', 'public/**/*_specs\.js');
|
||||||
|
|
||||||
|
files.forEach(function(spec) {
|
||||||
|
var rows = grunt.file.read(spec).split('\n');
|
||||||
|
rows.forEach(function(row) {
|
||||||
|
if (row.indexOf('.only(') > 0) {
|
||||||
|
grunt.log.errorlns(row);
|
||||||
|
grunt.fail.warn('found only statement in test: ' + spec)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user