Merge branch 'master' into postgres-query-builder
This commit is contained in:
@@ -9,7 +9,7 @@ watch_dirs = [
|
||||
"$WORKDIR/public/views",
|
||||
"$WORKDIR/conf",
|
||||
]
|
||||
watch_exts = [".go", ".ini", ".toml"]
|
||||
watch_exts = [".go", ".ini", ".toml", ".template.html"]
|
||||
build_delay = 1500
|
||||
cmds = [
|
||||
["go", "run", "build.go", "-dev", "build-server"],
|
||||
|
||||
@@ -104,6 +104,7 @@ jobs:
|
||||
- run:
|
||||
name: yarn install
|
||||
command: 'yarn install --pure-lockfile --no-progress'
|
||||
no_output_timeout: 15m
|
||||
- save_cache:
|
||||
key: dependency-cache-{{ checksum "yarn.lock" }}
|
||||
paths:
|
||||
|
||||
-13
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"disallowImplicitTypeConversion": ["string"],
|
||||
"disallowKeywords": ["with"],
|
||||
"disallowMultipleLineBreaks": true,
|
||||
"disallowMixedSpacesAndTabs": true,
|
||||
"disallowTrailingWhitespace": true,
|
||||
"requireSpacesInFunctionExpression": {
|
||||
"beforeOpeningCurlyBrace": true
|
||||
},
|
||||
"disallowSpacesInsideArrayBrackets": true,
|
||||
"disallowSpacesInsideParentheses": true,
|
||||
"validateIndentation": 2
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
{
|
||||
"browser": true,
|
||||
"esversion": 6,
|
||||
"bitwise":false,
|
||||
"curly": true,
|
||||
"eqnull": true,
|
||||
"strict": false,
|
||||
"devel": true,
|
||||
"eqeqeq": true,
|
||||
"forin": false,
|
||||
"immed": true,
|
||||
"supernew": true,
|
||||
"expr": true,
|
||||
"indent": 2,
|
||||
"latedef": false,
|
||||
"newcap": true,
|
||||
"noarg": true,
|
||||
"noempty": true,
|
||||
"undef": true,
|
||||
"boss": true,
|
||||
"trailing": true,
|
||||
"laxbreak": true,
|
||||
"laxcomma": true,
|
||||
"sub": true,
|
||||
"unused": true,
|
||||
"maxdepth": 6,
|
||||
"maxlen": 140,
|
||||
|
||||
"globals": {
|
||||
"System": true,
|
||||
"Promise": true,
|
||||
"define": true,
|
||||
"require": true,
|
||||
"Chromath": false,
|
||||
"setImmediate": true
|
||||
}
|
||||
}
|
||||
+11
-2
@@ -5,13 +5,16 @@
|
||||
* **LDAP**: Define Grafana Admin permission in ldap group mappings [#2469](https://github.com/grafana/grafana/issues/2496), PR [#12622](https://github.com/grafana/grafana/issues/12622)
|
||||
* **Cloudwatch**: CloudWatch GetMetricData support [#11487](https://github.com/grafana/grafana/issues/11487), thx [@mtanda](https://github.com/mtanda)
|
||||
* **Configuration**: Allow auto-assigning users to specific organization (other than Main. Org) [#1823](https://github.com/grafana/grafana/issues/1823) [#12801](https://github.com/grafana/grafana/issues/12801), thx [@gzzo](https://github.com/gzzo) and [@ofosos](https://github.com/ofosos)
|
||||
* **Profile**: List teams that the user is member of in current/active organization [#12476](https://github.com/grafana/grafana/issues/12476)
|
||||
* **LDAP**: Client certificates support [#12805](https://github.com/grafana/grafana/issues/12805), thx [@nyxi](https://github.com/nyxi)
|
||||
* **Postgres**: TimescaleDB support, e.g. use `time_bucket` for grouping by time when option enabled [#12680](https://github.com/grafana/grafana/pull/12680), thx [svenklemm](https://github.com/svenklemm)
|
||||
|
||||
### Minor
|
||||
|
||||
* **Api**: Delete nonexistent datasource should return 404 [#12313](https://github.com/grafana/grafana/issues/12313), thx [@AustinWinstanley](https://github.com/AustinWinstanley)
|
||||
* **Dashboard**: Fix selecting current dashboard from search should not reload dashboard [#12248](https://github.com/grafana/grafana/issues/12248)
|
||||
* **Dashboard**: Use uid when linking to dashboards internally in a dashboard [#10705](https://github.com/grafana/grafana/issues/10705)
|
||||
* **Singlestat**: Make colorization of prefix and postfix optional in singlestat [#11892](https://github.com/grafana/grafana/pull/11892), thx [@ApsOps](https://github.com/ApsOps)
|
||||
* **Table**: Make table sorting stable when null values exist [#12362](https://github.com/grafana/grafana/pull/12362), thx [@bz2](https://github.com/bz2)
|
||||
* **Prometheus**: Fix graph panel bar width issue in aligned prometheus queries [#12379](https://github.com/grafana/grafana/issues/12379)
|
||||
* **Prometheus**: Heatmap - fix unhandled error when some points are missing [#12484](https://github.com/grafana/grafana/issues/12484)
|
||||
* **Prometheus**: Add $__interval, $__interval_ms, $__range, $__range_s & $__range_ms support for dashboard and template queries [#12597](https://github.com/grafana/grafana/issues/12597) [#12882](https://github.com/grafana/grafana/issues/12882), thx [@roidelapluie](https://github.com/roidelapluie)
|
||||
@@ -25,7 +28,7 @@
|
||||
* **Postgres**: Escape ssl mode parameter in connectionstring [#12644](https://github.com/grafana/grafana/issues/12644), thx [@yogyrahmawan](https://github.com/yogyrahmawan)
|
||||
* **Github OAuth**: Allow changes of user info at Github to be synched to Grafana when signing in [#11818](https://github.com/grafana/grafana/issues/11818), thx [@rwaweber](https://github.com/rwaweber)
|
||||
* **Alerting**: Fix diff and percent_diff reducers [#11563](https://github.com/grafana/grafana/issues/11563), thx [@jessetane](https://github.com/jessetane)
|
||||
* **Units**: Polish złoty currency [#12691](https://github.com/grafana/grafana/pull/12691), thx [@mwegrzynek](https://github.com/mwegrzynek)
|
||||
* **Alerting**: Fix rendering timeout which could cause notifications to not be sent due to rendering timing out [#12151](https://github.com/grafana/grafana/issues/12151)
|
||||
* **Cloudwatch**: Improved error handling [#12489](https://github.com/grafana/grafana/issues/12489), thx [@mtanda](https://github.com/mtanda)
|
||||
* **Cloudwatch**: AppSync metrics and dimensions [#12300](https://github.com/grafana/grafana/issues/12300), thx [@franciscocpg](https://github.com/franciscocpg)
|
||||
* **Cloudwatch**: Direct Connect metrics and dimensions [#12762](https://github.com/grafana/grafana/pulls/12762), thx [@mindriot88](https://github.com/mindriot88)
|
||||
@@ -33,14 +36,20 @@
|
||||
* **Cloudwatch**: Add new Redshift metrics and dimensions [#12063](https://github.com/grafana/grafana/pulls/12063), thx [@A21z](https://github.com/A21z)
|
||||
* **Table**: Adjust header contrast for the light theme [#12668](https://github.com/grafana/grafana/issues/12668)
|
||||
* **Table**: Fix link color when using light theme and thresholds in use [#12766](https://github.com/grafana/grafana/issues/12766)
|
||||
om/grafana/grafana/issues/12668)
|
||||
* **Table**: Fix for useless horizontal scrollbar for table panel [#9964](https://github.com/grafana/grafana/issues/9964)
|
||||
* **Table**: Make table sorting stable when null values exist [#12362](https://github.com/grafana/grafana/pull/12362), thx [@bz2](https://github.com/bz2)
|
||||
* **Elasticsearch**: For alerting/backend, support having index name to the right of pattern in index pattern [#12731](https://github.com/grafana/grafana/issues/12731)
|
||||
* **OAuth**: Fix overriding tls_skip_verify_insecure using environment variable [#12747](https://github.com/grafana/grafana/issues/12747), thx [@jangaraj](https://github.com/jangaraj)
|
||||
* **Units**: Change units to include characters for power of 2 and 3 [#12744](https://github.com/grafana/grafana/pull/12744), thx [@Worty](https://github.com/Worty)
|
||||
* **Units**: Polish złoty currency [#12691](https://github.com/grafana/grafana/pull/12691), thx [@mwegrzynek](https://github.com/mwegrzynek)
|
||||
* **Graph**: Option to hide series from tooltip [#3341](https://github.com/grafana/grafana/issues/3341), thx [@mtanda](https://github.com/mtanda)
|
||||
* **UI**: Fix iOS home screen "app" icon and Windows 10 app experience [#12752](https://github.com/grafana/grafana/issues/12752), thx [@andig](https://github.com/andig)
|
||||
* **Datasource**: Fix UI issue with secret fields after updating datasource [#11270](https://github.com/grafana/grafana/issues/11270)
|
||||
* **Plugins**: Convert URL-like text to links in plugins readme [#12843](https://github.com/grafana/grafana/pull/12843), thx [pgiraud](https://github.com/pgiraud)
|
||||
* **Docker**: Make it possible to set a specific plugin url [#12861](https://github.com/grafana/grafana/pull/12861), thx [ClementGautier](https://github.com/ClementGautier)
|
||||
* **Graphite**: Fix for quoting of int function parameters (when using variables) [#11927](https://github.com/grafana/grafana/pull/11927)
|
||||
* **InfluxDB**: Support timeFilter in query templating for InfluxDB [#12598](https://github.com/grafana/grafana/pull/12598), thx [kichristensen](https://github.com/kichristensen)
|
||||
|
||||
### Breaking changes
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* jshint node:true */
|
||||
'use strict';
|
||||
module.exports = function (grunt) {
|
||||
var os = require('os');
|
||||
|
||||
@@ -315,6 +315,9 @@ api_url =
|
||||
team_ids =
|
||||
allowed_organizations =
|
||||
tls_skip_verify_insecure = false
|
||||
tls_client_cert =
|
||||
tls_client_key =
|
||||
tls_client_ca =
|
||||
|
||||
#################################### Basic Auth ##########################
|
||||
[auth.basic]
|
||||
|
||||
@@ -15,6 +15,9 @@ start_tls = false
|
||||
ssl_skip_verify = false
|
||||
# set to the path to your root CA certificate or leave unset to use system defaults
|
||||
# root_ca_cert = "/path/to/certificate.crt"
|
||||
# Authentication against LDAP servers requiring client certificates
|
||||
# client_cert = "/path/to/client.crt"
|
||||
# client_key = "/path/to/client.key"
|
||||
|
||||
# Search user bind dn
|
||||
bind_dn = "cn=admin,dc=grafana,dc=org"
|
||||
|
||||
@@ -272,6 +272,10 @@ log_queries =
|
||||
;api_url = https://foo.bar/user
|
||||
;team_ids =
|
||||
;allowed_organizations =
|
||||
;tls_skip_verify_insecure = false
|
||||
;tls_client_cert =
|
||||
;tls_client_key =
|
||||
;tls_client_ca =
|
||||
|
||||
#################################### Grafana.com Auth ####################
|
||||
[auth.grafana_com]
|
||||
|
||||
@@ -31,6 +31,7 @@ Name | Description
|
||||
*User* | Database user's login/username
|
||||
*Password* | Database user's password
|
||||
*SSL Mode* | This option determines whether or with what priority a secure SSL TCP/IP connection will be negotiated with the server.
|
||||
*TimescaleDB* | With this option enabled Grafana will use TimescaleDB features, e.g. use ```time_bucket``` for grouping by time (only available in Grafana 5.3+).
|
||||
|
||||
### Database User Permissions (Important!)
|
||||
|
||||
@@ -289,4 +290,5 @@ datasources:
|
||||
password: "Password!"
|
||||
jsonData:
|
||||
sslmode: "disable" # disable/require/verify-ca/verify-full
|
||||
timescaledb: false
|
||||
```
|
||||
|
||||
@@ -363,6 +363,39 @@ Content-Type: application/json
|
||||
]
|
||||
```
|
||||
|
||||
## Teams that the actual User is member of
|
||||
|
||||
`GET /api/user/teams`
|
||||
|
||||
Return a list of all teams that the current user is member of.
|
||||
|
||||
**Example Request**:
|
||||
|
||||
```http
|
||||
GET /api/user/teams HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
|
||||
```http
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
[
|
||||
{
|
||||
"id": 1,
|
||||
"orgId": 1,
|
||||
"name": "MyTestTeam",
|
||||
"email": "",
|
||||
"avatarUrl": "\/avatar\/3f49c15916554246daa714b9bd0ee398",
|
||||
"memberCount": 1
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
## Star a dashboard
|
||||
|
||||
`POST /api/user/stars/dashboard/:dashboardId`
|
||||
|
||||
@@ -40,6 +40,9 @@ start_tls = false
|
||||
ssl_skip_verify = false
|
||||
# set to the path to your root CA certificate or leave unset to use system defaults
|
||||
# root_ca_cert = "/path/to/certificate.crt"
|
||||
# Authentication against LDAP servers requiring client certificates
|
||||
# client_cert = "/path/to/client.crt"
|
||||
# client_key = "/path/to/client.key"
|
||||
|
||||
# Search user bind dn
|
||||
bind_dn = "cn=admin,dc=grafana,dc=org"
|
||||
|
||||
@@ -45,9 +45,7 @@
|
||||
"grunt-contrib-concat": "^1.0.1",
|
||||
"grunt-contrib-copy": "~1.0.0",
|
||||
"grunt-contrib-cssmin": "~1.0.2",
|
||||
"grunt-contrib-jshint": "~1.1.0",
|
||||
"grunt-exec": "^1.0.1",
|
||||
"grunt-jscs": "3.0.1",
|
||||
"grunt-karma": "~2.0.0",
|
||||
"grunt-notify": "^0.4.5",
|
||||
"grunt-postcss": "^0.8.0",
|
||||
@@ -60,7 +58,6 @@
|
||||
"html-webpack-plugin": "^3.2.0",
|
||||
"husky": "^0.14.3",
|
||||
"jest": "^22.0.4",
|
||||
"jshint-stylish": "~2.2.1",
|
||||
"karma": "1.7.0",
|
||||
"karma-chrome-launcher": "~2.2.0",
|
||||
"karma-expect": "~1.1.3",
|
||||
|
||||
@@ -120,6 +120,7 @@ func (hs *HTTPServer) registerRoutes() {
|
||||
userRoute.Put("/", bind(m.UpdateUserCommand{}), Wrap(UpdateSignedInUser))
|
||||
userRoute.Post("/using/:id", Wrap(UserSetUsingOrg))
|
||||
userRoute.Get("/orgs", Wrap(GetSignedInUserOrgList))
|
||||
userRoute.Get("/teams", Wrap(GetSignedInUserTeamList))
|
||||
|
||||
userRoute.Post("/stars/dashboard/:id", Wrap(StarDashboard))
|
||||
userRoute.Delete("/stars/dashboard/:id", Wrap(UnstarDashboard))
|
||||
|
||||
@@ -111,6 +111,21 @@ func GetSignedInUserOrgList(c *m.ReqContext) Response {
|
||||
return getUserOrgList(c.UserId)
|
||||
}
|
||||
|
||||
// GET /api/user/teams
|
||||
func GetSignedInUserTeamList(c *m.ReqContext) Response {
|
||||
query := m.GetTeamsByUserQuery{OrgId: c.OrgId, UserId: c.UserId}
|
||||
|
||||
if err := bus.Dispatch(&query); err != nil {
|
||||
return Error(500, "Failed to get user teams", err)
|
||||
}
|
||||
|
||||
for _, team := range query.Result {
|
||||
team.AvatarUrl = dtos.GetGravatarUrlWithDefault(team.Email, team.Name)
|
||||
}
|
||||
|
||||
return JSON(200, query.Result)
|
||||
}
|
||||
|
||||
// GET /api/user/:id/orgs
|
||||
func GetUserOrgList(c *m.ReqContext) Response {
|
||||
return getUserOrgList(c.ParamsInt64(":id"))
|
||||
|
||||
@@ -59,6 +59,13 @@ func (a *ldapAuther) Dial() error {
|
||||
}
|
||||
}
|
||||
}
|
||||
var clientCert tls.Certificate
|
||||
if a.server.ClientCert != "" && a.server.ClientKey != "" {
|
||||
clientCert, err = tls.LoadX509KeyPair(a.server.ClientCert, a.server.ClientKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for _, host := range strings.Split(a.server.Host, " ") {
|
||||
address := fmt.Sprintf("%s:%d", host, a.server.Port)
|
||||
if a.server.UseSSL {
|
||||
@@ -67,6 +74,9 @@ func (a *ldapAuther) Dial() error {
|
||||
ServerName: host,
|
||||
RootCAs: certPool,
|
||||
}
|
||||
if len(clientCert.Certificate) > 0 {
|
||||
tlsCfg.Certificates = append(tlsCfg.Certificates, clientCert)
|
||||
}
|
||||
if a.server.StartTLS {
|
||||
a.conn, err = ldap.Dial("tcp", address)
|
||||
if err == nil {
|
||||
|
||||
@@ -21,6 +21,8 @@ type LdapServerConf struct {
|
||||
StartTLS bool `toml:"start_tls"`
|
||||
SkipVerifySSL bool `toml:"ssl_skip_verify"`
|
||||
RootCACert string `toml:"root_ca_cert"`
|
||||
ClientCert string `toml:"client_cert"`
|
||||
ClientKey string `toml:"client_key"`
|
||||
BindDN string `toml:"bind_dn"`
|
||||
BindPassword string `toml:"bind_password"`
|
||||
Attr LdapAttributeMap `toml:"attributes"`
|
||||
|
||||
@@ -3,7 +3,6 @@ package alerting
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
||||
@@ -81,7 +80,7 @@ func (n *notificationService) uploadImage(context *EvalContext) (err error) {
|
||||
renderOpts := rendering.Opts{
|
||||
Width: 1000,
|
||||
Height: 500,
|
||||
Timeout: time.Second * 30,
|
||||
Timeout: alertTimeout / 2,
|
||||
OrgId: context.Rule.OrgId,
|
||||
OrgRole: m.ROLE_ADMIN,
|
||||
}
|
||||
|
||||
@@ -14,12 +14,13 @@ const rsIdentifier = `([_a-zA-Z0-9]+)`
|
||||
const sExpr = `\$` + rsIdentifier + `\(([^\)]*)\)`
|
||||
|
||||
type postgresMacroEngine struct {
|
||||
timeRange *tsdb.TimeRange
|
||||
query *tsdb.Query
|
||||
timeRange *tsdb.TimeRange
|
||||
query *tsdb.Query
|
||||
timescaledb bool
|
||||
}
|
||||
|
||||
func newPostgresMacroEngine() tsdb.SqlMacroEngine {
|
||||
return &postgresMacroEngine{}
|
||||
func newPostgresMacroEngine(timescaledb bool) tsdb.SqlMacroEngine {
|
||||
return &postgresMacroEngine{timescaledb: timescaledb}
|
||||
}
|
||||
|
||||
func (m *postgresMacroEngine) Interpolate(query *tsdb.Query, timeRange *tsdb.TimeRange, sql string) (string, error) {
|
||||
@@ -118,7 +119,12 @@ func (m *postgresMacroEngine) evaluateMacro(name string, args []string) (string,
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
return fmt.Sprintf("floor(extract(epoch from %s)/%v)*%v", args[0], interval.Seconds(), interval.Seconds()), nil
|
||||
|
||||
if m.timescaledb {
|
||||
return fmt.Sprintf("time_bucket('%vs',%s)", interval.Seconds(), args[0]), nil
|
||||
} else {
|
||||
return fmt.Sprintf("floor(extract(epoch from %s)/%v)*%v", args[0], interval.Seconds(), interval.Seconds()), nil
|
||||
}
|
||||
case "__timeGroupAlias":
|
||||
tg, err := m.evaluateMacro("__timeGroup", args)
|
||||
if err == nil {
|
||||
|
||||
@@ -12,7 +12,10 @@ import (
|
||||
|
||||
func TestMacroEngine(t *testing.T) {
|
||||
Convey("MacroEngine", t, func() {
|
||||
engine := newPostgresMacroEngine()
|
||||
timescaledbEnabled := false
|
||||
engine := newPostgresMacroEngine(timescaledbEnabled)
|
||||
timescaledbEnabled = true
|
||||
engineTS := newPostgresMacroEngine(timescaledbEnabled)
|
||||
query := &tsdb.Query{}
|
||||
|
||||
Convey("Given a time range between 2018-04-12 00:00 and 2018-04-12 00:05", func() {
|
||||
@@ -83,6 +86,22 @@ func TestMacroEngine(t *testing.T) {
|
||||
So(sql2, ShouldEqual, sql+" AS \"time\"")
|
||||
})
|
||||
|
||||
Convey("interpolate __timeGroup function with TimescaleDB enabled", func() {
|
||||
|
||||
sql, err := engineTS.Interpolate(query, timeRange, "GROUP BY $__timeGroup(time_column,'5m')")
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
So(sql, ShouldEqual, "GROUP BY time_bucket('300s',time_column)")
|
||||
})
|
||||
|
||||
Convey("interpolate __timeGroup function with spaces between args and TimescaleDB enabled", func() {
|
||||
|
||||
sql, err := engineTS.Interpolate(query, timeRange, "GROUP BY $__timeGroup(time_column , '5m')")
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
So(sql, ShouldEqual, "GROUP BY time_bucket('300s',time_column)")
|
||||
})
|
||||
|
||||
Convey("interpolate __timeTo function", func() {
|
||||
sql, err := engine.Interpolate(query, timeRange, "select $__timeTo(time_column)")
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
@@ -32,7 +32,9 @@ func newPostgresQueryEndpoint(datasource *models.DataSource) (tsdb.TsdbQueryEndp
|
||||
log: logger,
|
||||
}
|
||||
|
||||
return tsdb.NewSqlQueryEndpoint(&config, &rowTransformer, newPostgresMacroEngine(), logger)
|
||||
timescaledb := datasource.JsonData.Get("timescaledb").MustBool(false)
|
||||
|
||||
return tsdb.NewSqlQueryEndpoint(&config, &rowTransformer, newPostgresMacroEngine(timescaledb), logger)
|
||||
}
|
||||
|
||||
func generateConnectionString(datasource *models.DataSource) string {
|
||||
|
||||
@@ -27,7 +27,7 @@ import (
|
||||
// use to verify that the generated data are vizualized as expected, see
|
||||
// devenv/README.md for setup instructions.
|
||||
func TestPostgres(t *testing.T) {
|
||||
// change to true to run the MySQL tests
|
||||
// change to true to run the PostgreSQL tests
|
||||
runPostgresTests := false
|
||||
// runPostgresTests := true
|
||||
|
||||
|
||||
@@ -69,8 +69,9 @@ export class TeamMembers extends React.Component<Props, State> {
|
||||
|
||||
render() {
|
||||
const { newTeamMember, isAdding } = this.state;
|
||||
const members = this.props.team.members.values();
|
||||
const members = this.props.team.filteredMembers;
|
||||
const newTeamMemberValue = newTeamMember && newTeamMember.id.toString();
|
||||
const { team } = this.props;
|
||||
|
||||
return (
|
||||
<div>
|
||||
@@ -81,7 +82,7 @@ export class TeamMembers extends React.Component<Props, State> {
|
||||
type="text"
|
||||
className="gf-form-input"
|
||||
placeholder="Search members"
|
||||
value={''}
|
||||
value={team.search}
|
||||
onChange={this.onSearchQueryChange}
|
||||
/>
|
||||
<i className="gf-form-input-icon fa fa-search" />
|
||||
|
||||
@@ -25,6 +25,7 @@ export class HelpCtrl {
|
||||
{ keys: ['d', 'k'], description: 'Toggle kiosk mode (hides top nav)' },
|
||||
{ keys: ['d', 'E'], description: 'Expand all rows' },
|
||||
{ keys: ['d', 'C'], description: 'Collapse all rows' },
|
||||
{ keys: ['d', 'a'], description: 'Toggle auto fit panels (experimental feature)' },
|
||||
{ keys: ['mod+o'], description: 'Toggle shared graph crosshair' },
|
||||
],
|
||||
'Focused Panel': [
|
||||
|
||||
@@ -15,14 +15,7 @@ export class KeybindingSrv {
|
||||
timepickerOpen = false;
|
||||
|
||||
/** @ngInject */
|
||||
constructor(
|
||||
private $rootScope,
|
||||
private $location,
|
||||
private datasourceSrv,
|
||||
private timeSrv,
|
||||
private contextSrv,
|
||||
private $route
|
||||
) {
|
||||
constructor(private $rootScope, private $location, private datasourceSrv, private timeSrv, private contextSrv) {
|
||||
// clear out all shortcuts on route change
|
||||
$rootScope.$on('$routeChangeSuccess', () => {
|
||||
Mousetrap.reset();
|
||||
@@ -269,10 +262,8 @@ export class KeybindingSrv {
|
||||
|
||||
//Autofit panels
|
||||
this.bind('d a', () => {
|
||||
this.$location.search('autofitpanels', this.$location.search().autofitpanels ? null : true);
|
||||
//Force reload
|
||||
|
||||
this.$route.reload();
|
||||
// this has to be a full page reload
|
||||
window.location.href = window.location.href + '&autofitpanels';
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,120 +2,118 @@ import angular from 'angular';
|
||||
import config from 'app/core/config';
|
||||
import moment from 'moment';
|
||||
|
||||
export class ShareModalCtrl {
|
||||
/** @ngInject */
|
||||
constructor($scope, $rootScope, $location, $timeout, timeSrv, templateSrv, linkSrv) {
|
||||
$scope.options = {
|
||||
forCurrent: true,
|
||||
includeTemplateVars: true,
|
||||
theme: 'current',
|
||||
};
|
||||
$scope.editor = { index: $scope.tabIndex || 0 };
|
||||
/** @ngInject */
|
||||
export function ShareModalCtrl($scope, $rootScope, $location, $timeout, timeSrv, templateSrv, linkSrv) {
|
||||
$scope.options = {
|
||||
forCurrent: true,
|
||||
includeTemplateVars: true,
|
||||
theme: 'current',
|
||||
};
|
||||
$scope.editor = { index: $scope.tabIndex || 0 };
|
||||
|
||||
$scope.init = function() {
|
||||
$scope.modeSharePanel = $scope.panel ? true : false;
|
||||
$scope.init = function() {
|
||||
$scope.modeSharePanel = $scope.panel ? true : false;
|
||||
|
||||
$scope.tabs = [{ title: 'Link', src: 'shareLink.html' }];
|
||||
$scope.tabs = [{ title: 'Link', src: 'shareLink.html' }];
|
||||
|
||||
if ($scope.modeSharePanel) {
|
||||
$scope.modalTitle = 'Share Panel';
|
||||
$scope.tabs.push({ title: 'Embed', src: 'shareEmbed.html' });
|
||||
} else {
|
||||
$scope.modalTitle = 'Share';
|
||||
}
|
||||
if ($scope.modeSharePanel) {
|
||||
$scope.modalTitle = 'Share Panel';
|
||||
$scope.tabs.push({ title: 'Embed', src: 'shareEmbed.html' });
|
||||
} else {
|
||||
$scope.modalTitle = 'Share';
|
||||
}
|
||||
|
||||
if (!$scope.dashboard.meta.isSnapshot) {
|
||||
$scope.tabs.push({ title: 'Snapshot', src: 'shareSnapshot.html' });
|
||||
}
|
||||
if (!$scope.dashboard.meta.isSnapshot) {
|
||||
$scope.tabs.push({ title: 'Snapshot', src: 'shareSnapshot.html' });
|
||||
}
|
||||
|
||||
if (!$scope.dashboard.meta.isSnapshot && !$scope.modeSharePanel) {
|
||||
$scope.tabs.push({ title: 'Export', src: 'shareExport.html' });
|
||||
}
|
||||
if (!$scope.dashboard.meta.isSnapshot && !$scope.modeSharePanel) {
|
||||
$scope.tabs.push({ title: 'Export', src: 'shareExport.html' });
|
||||
}
|
||||
|
||||
$scope.buildUrl();
|
||||
};
|
||||
$scope.buildUrl();
|
||||
};
|
||||
|
||||
$scope.buildUrl = function() {
|
||||
var baseUrl = $location.absUrl();
|
||||
var queryStart = baseUrl.indexOf('?');
|
||||
$scope.buildUrl = function() {
|
||||
var baseUrl = $location.absUrl();
|
||||
var queryStart = baseUrl.indexOf('?');
|
||||
|
||||
if (queryStart !== -1) {
|
||||
baseUrl = baseUrl.substring(0, queryStart);
|
||||
}
|
||||
if (queryStart !== -1) {
|
||||
baseUrl = baseUrl.substring(0, queryStart);
|
||||
}
|
||||
|
||||
var params = angular.copy($location.search());
|
||||
var params = angular.copy($location.search());
|
||||
|
||||
var range = timeSrv.timeRange();
|
||||
params.from = range.from.valueOf();
|
||||
params.to = range.to.valueOf();
|
||||
params.orgId = config.bootData.user.orgId;
|
||||
var range = timeSrv.timeRange();
|
||||
params.from = range.from.valueOf();
|
||||
params.to = range.to.valueOf();
|
||||
params.orgId = config.bootData.user.orgId;
|
||||
|
||||
if ($scope.options.includeTemplateVars) {
|
||||
templateSrv.fillVariableValuesForUrl(params);
|
||||
}
|
||||
if ($scope.options.includeTemplateVars) {
|
||||
templateSrv.fillVariableValuesForUrl(params);
|
||||
}
|
||||
|
||||
if (!$scope.options.forCurrent) {
|
||||
delete params.from;
|
||||
delete params.to;
|
||||
}
|
||||
if (!$scope.options.forCurrent) {
|
||||
delete params.from;
|
||||
delete params.to;
|
||||
}
|
||||
|
||||
if ($scope.options.theme !== 'current') {
|
||||
params.theme = $scope.options.theme;
|
||||
}
|
||||
if ($scope.options.theme !== 'current') {
|
||||
params.theme = $scope.options.theme;
|
||||
}
|
||||
|
||||
if ($scope.modeSharePanel) {
|
||||
params.panelId = $scope.panel.id;
|
||||
params.fullscreen = true;
|
||||
} else {
|
||||
delete params.panelId;
|
||||
delete params.fullscreen;
|
||||
}
|
||||
|
||||
$scope.shareUrl = linkSrv.addParamsToUrl(baseUrl, params);
|
||||
|
||||
var soloUrl = baseUrl.replace(config.appSubUrl + '/dashboard/', config.appSubUrl + '/dashboard-solo/');
|
||||
soloUrl = soloUrl.replace(config.appSubUrl + '/d/', config.appSubUrl + '/d-solo/');
|
||||
if ($scope.modeSharePanel) {
|
||||
params.panelId = $scope.panel.id;
|
||||
params.fullscreen = true;
|
||||
} else {
|
||||
delete params.panelId;
|
||||
delete params.fullscreen;
|
||||
delete params.edit;
|
||||
soloUrl = linkSrv.addParamsToUrl(soloUrl, params);
|
||||
}
|
||||
|
||||
$scope.iframeHtml = '<iframe src="' + soloUrl + '" width="450" height="200" frameborder="0"></iframe>';
|
||||
$scope.shareUrl = linkSrv.addParamsToUrl(baseUrl, params);
|
||||
|
||||
$scope.imageUrl = soloUrl.replace(
|
||||
config.appSubUrl + '/dashboard-solo/',
|
||||
config.appSubUrl + '/render/dashboard-solo/'
|
||||
);
|
||||
$scope.imageUrl = $scope.imageUrl.replace(config.appSubUrl + '/d-solo/', config.appSubUrl + '/render/d-solo/');
|
||||
$scope.imageUrl += '&width=1000&height=500' + $scope.getLocalTimeZone();
|
||||
};
|
||||
var soloUrl = baseUrl.replace(config.appSubUrl + '/dashboard/', config.appSubUrl + '/dashboard-solo/');
|
||||
soloUrl = soloUrl.replace(config.appSubUrl + '/d/', config.appSubUrl + '/d-solo/');
|
||||
delete params.fullscreen;
|
||||
delete params.edit;
|
||||
soloUrl = linkSrv.addParamsToUrl(soloUrl, params);
|
||||
|
||||
// This function will try to return the proper full name of the local timezone
|
||||
// Chrome does not handle the timezone offset (but phantomjs does)
|
||||
$scope.getLocalTimeZone = function() {
|
||||
let utcOffset = '&tz=UTC' + encodeURIComponent(moment().format('Z'));
|
||||
$scope.iframeHtml = '<iframe src="' + soloUrl + '" width="450" height="200" frameborder="0"></iframe>';
|
||||
|
||||
// Older browser does not the internationalization API
|
||||
if (!(<any>window).Intl) {
|
||||
return utcOffset;
|
||||
}
|
||||
$scope.imageUrl = soloUrl.replace(
|
||||
config.appSubUrl + '/dashboard-solo/',
|
||||
config.appSubUrl + '/render/dashboard-solo/'
|
||||
);
|
||||
$scope.imageUrl = $scope.imageUrl.replace(config.appSubUrl + '/d-solo/', config.appSubUrl + '/render/d-solo/');
|
||||
$scope.imageUrl += '&width=1000&height=500' + $scope.getLocalTimeZone();
|
||||
};
|
||||
|
||||
const dateFormat = (<any>window).Intl.DateTimeFormat();
|
||||
if (!dateFormat.resolvedOptions) {
|
||||
return utcOffset;
|
||||
}
|
||||
// This function will try to return the proper full name of the local timezone
|
||||
// Chrome does not handle the timezone offset (but phantomjs does)
|
||||
$scope.getLocalTimeZone = function() {
|
||||
let utcOffset = '&tz=UTC' + encodeURIComponent(moment().format('Z'));
|
||||
|
||||
const options = dateFormat.resolvedOptions();
|
||||
if (!options.timeZone) {
|
||||
return utcOffset;
|
||||
}
|
||||
// Older browser does not the internationalization API
|
||||
if (!(<any>window).Intl) {
|
||||
return utcOffset;
|
||||
}
|
||||
|
||||
return '&tz=' + encodeURIComponent(options.timeZone);
|
||||
};
|
||||
const dateFormat = (<any>window).Intl.DateTimeFormat();
|
||||
if (!dateFormat.resolvedOptions) {
|
||||
return utcOffset;
|
||||
}
|
||||
|
||||
$scope.getShareUrl = function() {
|
||||
return $scope.shareUrl;
|
||||
};
|
||||
}
|
||||
const options = dateFormat.resolvedOptions();
|
||||
if (!options.timeZone) {
|
||||
return utcOffset;
|
||||
}
|
||||
|
||||
return '&tz=' + encodeURIComponent(options.timeZone);
|
||||
};
|
||||
|
||||
$scope.getShareUrl = function() {
|
||||
return $scope.shareUrl;
|
||||
};
|
||||
}
|
||||
|
||||
angular.module('grafana.controllers').controller('ShareModalCtrl', ShareModalCtrl);
|
||||
|
||||
@@ -0,0 +1,150 @@
|
||||
import '../shareModalCtrl';
|
||||
import { ShareModalCtrl } from '../shareModalCtrl';
|
||||
import config from 'app/core/config';
|
||||
import { LinkSrv } from 'app/features/panellinks/link_srv';
|
||||
|
||||
describe('ShareModalCtrl', () => {
|
||||
var ctx = <any>{
|
||||
timeSrv: {
|
||||
timeRange: () => {
|
||||
return { from: new Date(1000), to: new Date(2000) };
|
||||
},
|
||||
},
|
||||
$location: {
|
||||
absUrl: () => 'http://server/#!/test',
|
||||
search: () => {
|
||||
return { from: '', to: '' };
|
||||
},
|
||||
},
|
||||
scope: {
|
||||
dashboard: {
|
||||
meta: {
|
||||
isSnapshot: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
templateSrv: {
|
||||
fillVariableValuesForUrl: () => {},
|
||||
},
|
||||
};
|
||||
|
||||
(<any>window).Intl.DateTimeFormat = () => {
|
||||
return {
|
||||
resolvedOptions: () => {
|
||||
return { timeZone: 'UTC' };
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
config.bootData = {
|
||||
user: {
|
||||
orgId: 1,
|
||||
},
|
||||
};
|
||||
|
||||
ctx.ctrl = new ShareModalCtrl(
|
||||
ctx.scope,
|
||||
{},
|
||||
ctx.$location,
|
||||
{},
|
||||
ctx.timeSrv,
|
||||
ctx.templateSrv,
|
||||
new LinkSrv({}, ctx.stimeSrv)
|
||||
);
|
||||
});
|
||||
|
||||
describe('shareUrl with current time range and panel', () => {
|
||||
it('should generate share url absolute time', () => {
|
||||
ctx.scope.panel = { id: 22 };
|
||||
|
||||
ctx.scope.init();
|
||||
expect(ctx.scope.shareUrl).toBe('http://server/#!/test?from=1000&to=2000&orgId=1&panelId=22&fullscreen');
|
||||
});
|
||||
|
||||
it('should generate render url', () => {
|
||||
ctx.$location.absUrl = () => 'http://dashboards.grafana.com/d/abcdefghi/my-dash';
|
||||
|
||||
ctx.scope.panel = { id: 22 };
|
||||
|
||||
ctx.scope.init();
|
||||
var base = 'http://dashboards.grafana.com/render/d-solo/abcdefghi/my-dash';
|
||||
var params = '?from=1000&to=2000&orgId=1&panelId=22&width=1000&height=500&tz=UTC';
|
||||
expect(ctx.scope.imageUrl).toContain(base + params);
|
||||
});
|
||||
|
||||
it('should generate render url for scripted dashboard', () => {
|
||||
ctx.$location.absUrl = () => 'http://dashboards.grafana.com/dashboard/script/my-dash.js';
|
||||
|
||||
ctx.scope.panel = { id: 22 };
|
||||
|
||||
ctx.scope.init();
|
||||
var base = 'http://dashboards.grafana.com/render/dashboard-solo/script/my-dash.js';
|
||||
var params = '?from=1000&to=2000&orgId=1&panelId=22&width=1000&height=500&tz=UTC';
|
||||
expect(ctx.scope.imageUrl).toContain(base + params);
|
||||
});
|
||||
|
||||
it('should remove panel id when no panel in scope', () => {
|
||||
ctx.$location.absUrl = () => 'http://server/#!/test';
|
||||
ctx.scope.options.forCurrent = true;
|
||||
ctx.scope.panel = null;
|
||||
|
||||
ctx.scope.init();
|
||||
expect(ctx.scope.shareUrl).toBe('http://server/#!/test?from=1000&to=2000&orgId=1');
|
||||
});
|
||||
|
||||
it('should add theme when specified', () => {
|
||||
ctx.scope.options.theme = 'light';
|
||||
ctx.scope.panel = null;
|
||||
|
||||
ctx.scope.init();
|
||||
expect(ctx.scope.shareUrl).toBe('http://server/#!/test?from=1000&to=2000&orgId=1&theme=light');
|
||||
});
|
||||
|
||||
it('should remove fullscreen from image url when is first param in querystring and modeSharePanel is true', () => {
|
||||
ctx.$location.search = () => {
|
||||
return { fullscreen: true, edit: true };
|
||||
};
|
||||
ctx.$location.absUrl = () => 'http://server/#!/test?fullscreen&edit';
|
||||
ctx.scope.modeSharePanel = true;
|
||||
ctx.scope.panel = { id: 1 };
|
||||
|
||||
ctx.scope.buildUrl();
|
||||
|
||||
expect(ctx.scope.shareUrl).toContain('?fullscreen&edit&from=1000&to=2000&orgId=1&panelId=1');
|
||||
expect(ctx.scope.imageUrl).toContain('?from=1000&to=2000&orgId=1&panelId=1&width=1000&height=500&tz=UTC');
|
||||
});
|
||||
|
||||
it('should remove edit from image url when is first param in querystring and modeSharePanel is true', () => {
|
||||
ctx.$location.search = () => {
|
||||
return { edit: true, fullscreen: true };
|
||||
};
|
||||
ctx.$location.absUrl = () => 'http://server/#!/test?edit&fullscreen';
|
||||
ctx.scope.modeSharePanel = true;
|
||||
ctx.scope.panel = { id: 1 };
|
||||
|
||||
ctx.scope.buildUrl();
|
||||
|
||||
expect(ctx.scope.shareUrl).toContain('?edit&fullscreen&from=1000&to=2000&orgId=1&panelId=1');
|
||||
expect(ctx.scope.imageUrl).toContain('?from=1000&to=2000&orgId=1&panelId=1&width=1000&height=500&tz=UTC');
|
||||
});
|
||||
|
||||
it('should include template variables in url', () => {
|
||||
ctx.$location.search = () => {
|
||||
return {};
|
||||
};
|
||||
ctx.$location.absUrl = () => 'http://server/#!/test';
|
||||
ctx.scope.options.includeTemplateVars = true;
|
||||
|
||||
ctx.templateSrv.fillVariableValuesForUrl = function(params) {
|
||||
params['var-app'] = 'mupp';
|
||||
params['var-server'] = 'srv-01';
|
||||
};
|
||||
|
||||
ctx.scope.buildUrl();
|
||||
expect(ctx.scope.shareUrl).toContain(
|
||||
'http://server/#!/test?from=1000&to=2000&orgId=1&var-app=mupp&var-server=srv-01'
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,122 +0,0 @@
|
||||
import { describe, beforeEach, it, expect, sinon, angularMocks } from 'test/lib/common';
|
||||
import helpers from 'test/specs/helpers';
|
||||
import '../shareModalCtrl';
|
||||
import config from 'app/core/config';
|
||||
import 'app/features/panellinks/link_srv';
|
||||
|
||||
describe('ShareModalCtrl', function() {
|
||||
var ctx = new helpers.ControllerTestContext();
|
||||
|
||||
function setTime(range) {
|
||||
ctx.timeSrv.timeRange = sinon.stub().returns(range);
|
||||
}
|
||||
|
||||
beforeEach(function() {
|
||||
config.bootData = {
|
||||
user: {
|
||||
orgId: 1,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
setTime({ from: new Date(1000), to: new Date(2000) });
|
||||
|
||||
beforeEach(angularMocks.module('grafana.controllers'));
|
||||
beforeEach(angularMocks.module('grafana.services'));
|
||||
beforeEach(
|
||||
angularMocks.module(function($compileProvider) {
|
||||
$compileProvider.preAssignBindingsEnabled(true);
|
||||
})
|
||||
);
|
||||
|
||||
beforeEach(ctx.providePhase());
|
||||
|
||||
beforeEach(ctx.createControllerPhase('ShareModalCtrl'));
|
||||
|
||||
describe('shareUrl with current time range and panel', function() {
|
||||
it('should generate share url absolute time', function() {
|
||||
ctx.$location.path('/test');
|
||||
ctx.scope.panel = { id: 22 };
|
||||
|
||||
ctx.scope.init();
|
||||
expect(ctx.scope.shareUrl).to.be('http://server/#!/test?from=1000&to=2000&orgId=1&panelId=22&fullscreen');
|
||||
});
|
||||
|
||||
it('should generate render url', function() {
|
||||
ctx.$location.$$absUrl = 'http://dashboards.grafana.com/d/abcdefghi/my-dash';
|
||||
|
||||
ctx.scope.panel = { id: 22 };
|
||||
|
||||
ctx.scope.init();
|
||||
var base = 'http://dashboards.grafana.com/render/d-solo/abcdefghi/my-dash';
|
||||
var params = '?from=1000&to=2000&orgId=1&panelId=22&width=1000&height=500&tz=UTC';
|
||||
expect(ctx.scope.imageUrl).to.contain(base + params);
|
||||
});
|
||||
|
||||
it('should generate render url for scripted dashboard', function() {
|
||||
ctx.$location.$$absUrl = 'http://dashboards.grafana.com/dashboard/script/my-dash.js';
|
||||
|
||||
ctx.scope.panel = { id: 22 };
|
||||
|
||||
ctx.scope.init();
|
||||
var base = 'http://dashboards.grafana.com/render/dashboard-solo/script/my-dash.js';
|
||||
var params = '?from=1000&to=2000&orgId=1&panelId=22&width=1000&height=500&tz=UTC';
|
||||
expect(ctx.scope.imageUrl).to.contain(base + params);
|
||||
});
|
||||
|
||||
it('should remove panel id when no panel in scope', function() {
|
||||
ctx.$location.path('/test');
|
||||
ctx.scope.options.forCurrent = true;
|
||||
ctx.scope.panel = null;
|
||||
|
||||
ctx.scope.init();
|
||||
expect(ctx.scope.shareUrl).to.be('http://server/#!/test?from=1000&to=2000&orgId=1');
|
||||
});
|
||||
|
||||
it('should add theme when specified', function() {
|
||||
ctx.$location.path('/test');
|
||||
ctx.scope.options.theme = 'light';
|
||||
ctx.scope.panel = null;
|
||||
|
||||
ctx.scope.init();
|
||||
expect(ctx.scope.shareUrl).to.be('http://server/#!/test?from=1000&to=2000&orgId=1&theme=light');
|
||||
});
|
||||
|
||||
it('should remove fullscreen from image url when is first param in querystring and modeSharePanel is true', function() {
|
||||
ctx.$location.url('/test?fullscreen&edit');
|
||||
ctx.scope.modeSharePanel = true;
|
||||
ctx.scope.panel = { id: 1 };
|
||||
|
||||
ctx.scope.buildUrl();
|
||||
|
||||
expect(ctx.scope.shareUrl).to.contain('?fullscreen&edit&from=1000&to=2000&orgId=1&panelId=1');
|
||||
expect(ctx.scope.imageUrl).to.contain('?from=1000&to=2000&orgId=1&panelId=1&width=1000&height=500&tz=UTC');
|
||||
});
|
||||
|
||||
it('should remove edit from image url when is first param in querystring and modeSharePanel is true', function() {
|
||||
ctx.$location.url('/test?edit&fullscreen');
|
||||
ctx.scope.modeSharePanel = true;
|
||||
ctx.scope.panel = { id: 1 };
|
||||
|
||||
ctx.scope.buildUrl();
|
||||
|
||||
expect(ctx.scope.shareUrl).to.contain('?edit&fullscreen&from=1000&to=2000&orgId=1&panelId=1');
|
||||
expect(ctx.scope.imageUrl).to.contain('?from=1000&to=2000&orgId=1&panelId=1&width=1000&height=500&tz=UTC');
|
||||
});
|
||||
|
||||
it('should include template variables in url', function() {
|
||||
ctx.$location.path('/test');
|
||||
ctx.scope.options.includeTemplateVars = true;
|
||||
|
||||
ctx.templateSrv.fillVariableValuesForUrl = function(params) {
|
||||
params['var-app'] = 'mupp';
|
||||
params['var-server'] = 'srv-01';
|
||||
};
|
||||
|
||||
ctx.scope.buildUrl();
|
||||
expect(ctx.scope.shareUrl).to.be(
|
||||
'http://server/#!/test?from=1000&to=2000&orgId=1&var-app=mupp&var-server=srv-01'
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -144,8 +144,8 @@ export class DashLinksContainerCtrl {
|
||||
if (dash.id !== currentDashId) {
|
||||
memo.push({
|
||||
title: dash.title,
|
||||
url: 'dashboard/' + dash.uri,
|
||||
target: link.target,
|
||||
url: dash.url,
|
||||
target: link.target === '_self' ? '' : link.target,
|
||||
icon: 'fa fa-th-large',
|
||||
keepTime: link.keepTime,
|
||||
includeVars: link.includeVars,
|
||||
|
||||
@@ -3,53 +3,74 @@
|
||||
<div class="page-container page-body">
|
||||
<h3 class="page-sub-heading">User Profile</h3>
|
||||
|
||||
<form name="ctrl.userForm" class="gf-form-group">
|
||||
<form name="ctrl.userForm" class="gf-form-group">
|
||||
|
||||
<div class="gf-form max-width-30">
|
||||
<span class="gf-form-label width-8">Name</span>
|
||||
<input class="gf-form-input max-width-22" type="text" required ng-model="ctrl.user.name" >
|
||||
</div>
|
||||
<div class="gf-form max-width-30">
|
||||
<span class="gf-form-label width-8">Email</span>
|
||||
<input class="gf-form-input max-width-22" type="email" ng-readonly="ctrl.readonlyLoginFields" required ng-model="ctrl.user.email">
|
||||
<div class="gf-form max-width-30">
|
||||
<span class="gf-form-label width-8">Name</span>
|
||||
<input class="gf-form-input max-width-22" type="text" required ng-model="ctrl.user.name">
|
||||
</div>
|
||||
<div class="gf-form max-width-30">
|
||||
<span class="gf-form-label width-8">Email</span>
|
||||
<input class="gf-form-input max-width-22" type="email" ng-readonly="ctrl.readonlyLoginFields" required ng-model="ctrl.user.email">
|
||||
<i ng-if="ctrl.readonlyLoginFields" class="fa fa-lock gf-form-icon--right-absolute" bs-tooltip="'Login Details Locked - managed in another system.'"></i>
|
||||
</div>
|
||||
<div class="gf-form max-width-30">
|
||||
<span class="gf-form-label width-8">Username</span>
|
||||
<div class="gf-form max-width-30">
|
||||
<span class="gf-form-label width-8">Username</span>
|
||||
<input class="gf-form-input max-width-22" type="text" ng-readonly="ctrl.readonlyLoginFields" required ng-model="ctrl.user.login">
|
||||
<i ng-if="ctrl.readonlyLoginFields" class="fa fa-lock gf-form-icon--right-absolute" bs-tooltip="'Login Details Locked - managed in another system.'"></i>
|
||||
</div>
|
||||
<div class="gf-form-button-row">
|
||||
<button type="submit" class="btn btn-success" ng-click="ctrl.update()">Save</button>
|
||||
</div>
|
||||
</form>
|
||||
<div class="gf-form-button-row">
|
||||
<button type="submit" class="btn btn-success" ng-click="ctrl.update()">Save</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<prefs-control mode="user"></prefs-control>
|
||||
<prefs-control mode="user"></prefs-control>
|
||||
|
||||
<h3 class="page-heading" ng-show="ctrl.showOrgsList">Organizations</h3>
|
||||
<h3 class="page-heading" ng-show="ctrl.showTeamsList">Teams</h3>
|
||||
<div class="gf-form-group" ng-show="ctrl.showTeamsList">
|
||||
<table class="filter-table form-inline">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>Name</th>
|
||||
<th>Email</th>
|
||||
<th>Members</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="team in ctrl.teams">
|
||||
<td class="width-4 text-center"><img class="filter-table__avatar" src={{team.avatarUrl}}></td>
|
||||
<td>{{team.name}}</td>
|
||||
<td>{{team.email}}</td>
|
||||
<td>{{team.memberCount}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<h3 class="page-heading" ng-show="ctrl.showOrgsList">Organizations</h3>
|
||||
<div class="gf-form-group" ng-show="ctrl.showOrgsList">
|
||||
<table class="filter-table form-inline">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Role</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="org in ctrl.orgs">
|
||||
<td>{{org.name}}</td>
|
||||
<td>{{org.role}}</td>
|
||||
<td class="text-right">
|
||||
<span class="btn btn-primary btn-mini" ng-show="org.orgId === contextSrv.user.orgId">
|
||||
Current
|
||||
</span>
|
||||
<a ng-click="ctrl.setUsingOrg(org)" class="btn btn-inverse btn-mini" ng-show="org.orgId !== contextSrv.user.orgId">
|
||||
Select
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<table class="filter-table form-inline">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Role</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="org in ctrl.orgs">
|
||||
<td>{{org.name}}</td>
|
||||
<td>{{org.role}}</td>
|
||||
<td class="text-right">
|
||||
<span class="btn btn-primary btn-mini" ng-show="org.orgId === contextSrv.user.orgId">
|
||||
Current
|
||||
</span>
|
||||
<a ng-click="ctrl.setUsingOrg(org)" class="btn btn-inverse btn-mini" ng-show="org.orgId !== contextSrv.user.orgId">
|
||||
Select
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
@@ -4,8 +4,10 @@ import { coreModule } from 'app/core/core';
|
||||
export class ProfileCtrl {
|
||||
user: any;
|
||||
old_theme: any;
|
||||
teams: any = [];
|
||||
orgs: any = [];
|
||||
userForm: any;
|
||||
showTeamsList = false;
|
||||
showOrgsList = false;
|
||||
readonlyLoginFields = config.disableLoginForm;
|
||||
navModel: any;
|
||||
@@ -13,6 +15,7 @@ export class ProfileCtrl {
|
||||
/** @ngInject **/
|
||||
constructor(private backendSrv, private contextSrv, private $location, navModelSrv) {
|
||||
this.getUser();
|
||||
this.getUserTeams();
|
||||
this.getUserOrgs();
|
||||
this.navModel = navModelSrv.getNav('profile', 'profile-settings', 0);
|
||||
}
|
||||
@@ -24,6 +27,13 @@ export class ProfileCtrl {
|
||||
});
|
||||
}
|
||||
|
||||
getUserTeams() {
|
||||
this.backendSrv.get('/api/user/teams').then(teams => {
|
||||
this.teams = teams;
|
||||
this.showTeamsList = this.teams.length > 0;
|
||||
});
|
||||
}
|
||||
|
||||
getUserOrgs() {
|
||||
this.backendSrv.get('/api/user/orgs').then(orgs => {
|
||||
this.orgs = orgs;
|
||||
|
||||
@@ -77,6 +77,10 @@ export class LinkSrv {
|
||||
info.target = link.targetBlank ? '_blank' : '_self';
|
||||
info.href = this.templateSrv.replace(link.url || '', scopedVars);
|
||||
info.title = this.templateSrv.replace(link.title || '', scopedVars);
|
||||
} else if (link.url) {
|
||||
info.href = link.url;
|
||||
info.title = this.templateSrv.replace(link.title || '', scopedVars);
|
||||
info.target = link.targetBlank ? '_blank' : '';
|
||||
} else if (link.dashUri) {
|
||||
info.href = 'dashboard/' + link.dashUri + '?';
|
||||
info.title = this.templateSrv.replace(link.title || '', scopedVars);
|
||||
|
||||
@@ -39,7 +39,12 @@ export class PanelLinksEditorCtrl {
|
||||
backendSrv.search({ query: link.dashboard }).then(function(hits) {
|
||||
var dashboard = _.find(hits, { title: link.dashboard });
|
||||
if (dashboard) {
|
||||
link.dashUri = dashboard.uri;
|
||||
if (dashboard.url) {
|
||||
link.url = dashboard.url;
|
||||
} else {
|
||||
// To support legacy url's
|
||||
link.dashUri = dashboard.uri;
|
||||
}
|
||||
link.title = dashboard.title;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -973,13 +973,12 @@ export class FuncInstance {
|
||||
} else if (_.get(_.last(this.def.params), 'multiple')) {
|
||||
paramType = _.get(_.last(this.def.params), 'type');
|
||||
}
|
||||
if (paramType === 'value_or_series') {
|
||||
// param types that should never be quoted
|
||||
if (_.includes(['value_or_series', 'boolean', 'int', 'float', 'node'], paramType)) {
|
||||
return value;
|
||||
}
|
||||
if (paramType === 'boolean' && _.includes(['true', 'false'], value)) {
|
||||
return value;
|
||||
}
|
||||
if (_.includes(['int', 'float', 'int_or_interval', 'node_or_tag', 'node'], paramType) && _.isFinite(+value)) {
|
||||
// param types that might be quoted
|
||||
if (_.includes(['int_or_interval', 'node_or_tag'], paramType) && _.isFinite(+value)) {
|
||||
return _.toString(+value);
|
||||
}
|
||||
return "'" + value + "'";
|
||||
|
||||
@@ -55,6 +55,24 @@ describe('when rendering func instance', function() {
|
||||
expect(func.render('hello')).toEqual("movingMedian(hello, '5min')");
|
||||
});
|
||||
|
||||
it('should never quote boolean paramater', function() {
|
||||
var func = gfunc.createFuncInstance('sortByName');
|
||||
func.params[0] = '$natural';
|
||||
expect(func.render('hello')).toEqual('sortByName(hello, $natural)');
|
||||
});
|
||||
|
||||
it('should never quote int paramater', function() {
|
||||
var func = gfunc.createFuncInstance('maximumAbove');
|
||||
func.params[0] = '$value';
|
||||
expect(func.render('hello')).toEqual('maximumAbove(hello, $value)');
|
||||
});
|
||||
|
||||
it('should never quote node paramater', function() {
|
||||
var func = gfunc.createFuncInstance('aliasByNode');
|
||||
func.params[0] = '$node';
|
||||
expect(func.render('hello')).toEqual('aliasByNode(hello, $node)');
|
||||
});
|
||||
|
||||
it('should handle metric param and int param and string param', function() {
|
||||
var func = gfunc.createFuncInstance('groupByNode');
|
||||
func.params[0] = 5;
|
||||
|
||||
@@ -187,6 +187,11 @@ export default class InfluxDatasource {
|
||||
return this.$q.when({ results: [] });
|
||||
}
|
||||
|
||||
if (options && options.range) {
|
||||
var timeFilter = this.getTimeFilter({ rangeRaw: options.range });
|
||||
query = query.replace('$timeFilter', timeFilter);
|
||||
}
|
||||
|
||||
return this._influxRequest('GET', '/query', { q: query, epoch: 'ms' }, options);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
import InfluxDatasource from '../datasource';
|
||||
import $q from 'q';
|
||||
import { TemplateSrvStub } from 'test/specs/helpers';
|
||||
|
||||
describe('InfluxDataSource', () => {
|
||||
let ctx: any = {
|
||||
backendSrv: {},
|
||||
$q: $q,
|
||||
templateSrv: new TemplateSrvStub(),
|
||||
instanceSettings: { url: 'url', name: 'influxDb', jsonData: {} },
|
||||
};
|
||||
|
||||
beforeEach(function() {
|
||||
ctx.instanceSettings.url = '/api/datasources/proxy/1';
|
||||
ctx.ds = new InfluxDatasource(ctx.instanceSettings, ctx.$q, ctx.backendSrv, ctx.templateSrv);
|
||||
});
|
||||
|
||||
describe('When issuing metricFindQuery', () => {
|
||||
let query = 'SELECT max(value) FROM measurement WHERE $timeFilter';
|
||||
let queryOptions: any = {
|
||||
range: {
|
||||
from: '2018-01-01T00:00:00Z',
|
||||
to: '2018-01-02T00:00:00Z',
|
||||
},
|
||||
};
|
||||
let requestQuery;
|
||||
|
||||
beforeEach(async () => {
|
||||
ctx.backendSrv.datasourceRequest = function(req) {
|
||||
requestQuery = req.params.q;
|
||||
return ctx.$q.when({
|
||||
results: [
|
||||
{
|
||||
series: [
|
||||
{
|
||||
name: 'measurement',
|
||||
columns: ['max'],
|
||||
values: [[1]],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
};
|
||||
|
||||
await ctx.ds.metricFindQuery(query, queryOptions).then(function(_) {});
|
||||
});
|
||||
|
||||
it('should replace $timefilter', () => {
|
||||
expect(requestQuery).toMatch('time >= 1514764800000ms and time <= 1514851200000ms');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -123,25 +123,7 @@ export class PostgresDatasource {
|
||||
}
|
||||
|
||||
testDatasource() {
|
||||
return this.backendSrv
|
||||
.datasourceRequest({
|
||||
url: '/api/tsdb/query',
|
||||
method: 'POST',
|
||||
data: {
|
||||
from: '5m',
|
||||
to: 'now',
|
||||
queries: [
|
||||
{
|
||||
refId: 'A',
|
||||
intervalMs: 1,
|
||||
maxDataPoints: 1,
|
||||
datasourceId: this.id,
|
||||
rawSql: 'SELECT 1',
|
||||
format: 'table',
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
return this.metricFindQuery('SELECT 1', {})
|
||||
.then(res => {
|
||||
return { status: 'success', message: 'Database Connection OK' };
|
||||
})
|
||||
|
||||
@@ -38,6 +38,14 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3 class="page-heading">PostgreSQL details</h3>
|
||||
|
||||
<div class="gf-form-group">
|
||||
<div class="gf-form">
|
||||
<gf-form-switch class="gf-form" label="TimescaleDB" tooltip="Use TimescaleDB features (e.g., time_bucket) in Grafana" label-class="width-9" checked="ctrl.current.jsonData.timescaledb" switch-class="max-width-6"></gf-form-switch>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="gf-form-group">
|
||||
<div class="grafana-info-box">
|
||||
<h5>User Permission</h5>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -13,6 +13,7 @@ import { axesEditorComponent } from './axes_editor';
|
||||
class GraphCtrl extends MetricsPanelCtrl {
|
||||
static template = template;
|
||||
|
||||
renderError: boolean;
|
||||
hiddenSeries: any = {};
|
||||
seriesList: any = [];
|
||||
dataList: any = [];
|
||||
|
||||
@@ -0,0 +1,518 @@
|
||||
jest.mock('app/features/annotations/all', () => ({
|
||||
EventManager: function() {
|
||||
return {
|
||||
on: () => {},
|
||||
addFlotEvents: () => {},
|
||||
};
|
||||
},
|
||||
}));
|
||||
|
||||
jest.mock('app/core/core', () => ({
|
||||
coreModule: {
|
||||
directive: () => {},
|
||||
},
|
||||
appEvents: {
|
||||
on: () => {},
|
||||
},
|
||||
}));
|
||||
|
||||
import '../module';
|
||||
import { GraphCtrl } from '../module';
|
||||
import { MetricsPanelCtrl } from 'app/features/panel/metrics_panel_ctrl';
|
||||
import { PanelCtrl } from 'app/features/panel/panel_ctrl';
|
||||
|
||||
import config from 'app/core/config';
|
||||
|
||||
import TimeSeries from 'app/core/time_series2';
|
||||
import moment from 'moment';
|
||||
import $ from 'jquery';
|
||||
import { graphDirective } from '../graph';
|
||||
|
||||
let ctx = <any>{};
|
||||
let ctrl;
|
||||
let scope = {
|
||||
ctrl: {},
|
||||
range: {
|
||||
from: moment([2015, 1, 1]),
|
||||
to: moment([2015, 11, 20]),
|
||||
},
|
||||
$on: () => {},
|
||||
};
|
||||
let link;
|
||||
|
||||
describe('grafanaGraph', function() {
|
||||
const setupCtx = (beforeRender?) => {
|
||||
config.bootData = {
|
||||
user: {
|
||||
lightTheme: false,
|
||||
},
|
||||
};
|
||||
GraphCtrl.prototype = <any>{
|
||||
...MetricsPanelCtrl.prototype,
|
||||
...PanelCtrl.prototype,
|
||||
...GraphCtrl.prototype,
|
||||
height: 200,
|
||||
panel: {
|
||||
events: {
|
||||
on: () => {},
|
||||
},
|
||||
legend: {},
|
||||
grid: {},
|
||||
yaxes: [
|
||||
{
|
||||
min: null,
|
||||
max: null,
|
||||
format: 'short',
|
||||
logBase: 1,
|
||||
},
|
||||
{
|
||||
min: null,
|
||||
max: null,
|
||||
format: 'short',
|
||||
logBase: 1,
|
||||
},
|
||||
],
|
||||
thresholds: [],
|
||||
xaxis: {},
|
||||
seriesOverrides: [],
|
||||
tooltip: {
|
||||
shared: true,
|
||||
},
|
||||
},
|
||||
renderingCompleted: jest.fn(),
|
||||
hiddenSeries: {},
|
||||
dashboard: {
|
||||
getTimezone: () => 'browser',
|
||||
},
|
||||
range: {
|
||||
from: moment([2015, 1, 1, 10]),
|
||||
to: moment([2015, 1, 1, 22]),
|
||||
},
|
||||
};
|
||||
|
||||
ctx.data = [];
|
||||
ctx.data.push(
|
||||
new TimeSeries({
|
||||
datapoints: [[1, 1], [2, 2]],
|
||||
alias: 'series1',
|
||||
})
|
||||
);
|
||||
ctx.data.push(
|
||||
new TimeSeries({
|
||||
datapoints: [[10, 1], [20, 2]],
|
||||
alias: 'series2',
|
||||
})
|
||||
);
|
||||
|
||||
ctrl = new GraphCtrl(
|
||||
{
|
||||
$on: () => {},
|
||||
},
|
||||
{
|
||||
get: () => {},
|
||||
},
|
||||
{}
|
||||
);
|
||||
|
||||
$.plot = ctrl.plot = jest.fn();
|
||||
scope.ctrl = ctrl;
|
||||
|
||||
link = graphDirective({}, {}, {}).link(scope, { width: () => 500, mouseleave: () => {}, bind: () => {} });
|
||||
if (typeof beforeRender === 'function') {
|
||||
beforeRender();
|
||||
}
|
||||
link.data = ctx.data;
|
||||
|
||||
//Emulate functions called by event listeners
|
||||
link.buildFlotPairs(link.data);
|
||||
link.render_panel();
|
||||
ctx.plotData = ctrl.plot.mock.calls[0][1];
|
||||
|
||||
ctx.plotOptions = ctrl.plot.mock.calls[0][2];
|
||||
};
|
||||
|
||||
describe('simple lines options', () => {
|
||||
beforeEach(() => {
|
||||
setupCtx(() => {
|
||||
ctrl.panel.lines = true;
|
||||
ctrl.panel.fill = 5;
|
||||
ctrl.panel.linewidth = 3;
|
||||
ctrl.panel.steppedLine = true;
|
||||
});
|
||||
});
|
||||
|
||||
it('should configure plot with correct options', () => {
|
||||
expect(ctx.plotOptions.series.lines.show).toBe(true);
|
||||
expect(ctx.plotOptions.series.lines.fill).toBe(0.5);
|
||||
expect(ctx.plotOptions.series.lines.lineWidth).toBe(3);
|
||||
expect(ctx.plotOptions.series.lines.steps).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('sorting stacked series as legend. disabled', () => {
|
||||
beforeEach(() => {
|
||||
setupCtx(() => {
|
||||
ctrl.panel.legend.sort = undefined;
|
||||
ctrl.panel.stack = false;
|
||||
});
|
||||
});
|
||||
|
||||
it('should not modify order of time series', () => {
|
||||
expect(ctx.plotData[0].alias).toBe('series1');
|
||||
expect(ctx.plotData[1].alias).toBe('series2');
|
||||
});
|
||||
});
|
||||
|
||||
describe('sorting stacked series as legend. min descending order', () => {
|
||||
beforeEach(() => {
|
||||
setupCtx(() => {
|
||||
ctrl.panel.legend.sort = 'min';
|
||||
ctrl.panel.legend.sortDesc = true;
|
||||
ctrl.panel.stack = true;
|
||||
});
|
||||
});
|
||||
it('highest value should be first', () => {
|
||||
expect(ctx.plotData[0].alias).toBe('series2');
|
||||
expect(ctx.plotData[1].alias).toBe('series1');
|
||||
});
|
||||
});
|
||||
|
||||
describe('sorting stacked series as legend. min ascending order', () => {
|
||||
beforeEach(() => {
|
||||
setupCtx(() => {
|
||||
ctrl.panel.legend.sort = 'min';
|
||||
ctrl.panel.legend.sortDesc = false;
|
||||
ctrl.panel.stack = true;
|
||||
});
|
||||
});
|
||||
it('lowest value should be first', () => {
|
||||
expect(ctx.plotData[0].alias).toBe('series1');
|
||||
expect(ctx.plotData[1].alias).toBe('series2');
|
||||
});
|
||||
});
|
||||
|
||||
describe('sorting stacked series as legend. stacking disabled', () => {
|
||||
beforeEach(() => {
|
||||
setupCtx(() => {
|
||||
ctrl.panel.legend.sort = 'min';
|
||||
ctrl.panel.legend.sortDesc = true;
|
||||
ctrl.panel.stack = false;
|
||||
});
|
||||
});
|
||||
|
||||
it('highest value should be first', () => {
|
||||
expect(ctx.plotData[0].alias).toBe('series1');
|
||||
expect(ctx.plotData[1].alias).toBe('series2');
|
||||
});
|
||||
});
|
||||
|
||||
describe('sorting stacked series as legend. current descending order', () => {
|
||||
beforeEach(() => {
|
||||
setupCtx(() => {
|
||||
ctrl.panel.legend.sort = 'current';
|
||||
ctrl.panel.legend.sortDesc = true;
|
||||
ctrl.panel.stack = true;
|
||||
});
|
||||
});
|
||||
|
||||
it('highest last value should be first', () => {
|
||||
expect(ctx.plotData[0].alias).toBe('series2');
|
||||
expect(ctx.plotData[1].alias).toBe('series1');
|
||||
});
|
||||
});
|
||||
|
||||
describe('when logBase is log 10', () => {
|
||||
beforeEach(() => {
|
||||
setupCtx(() => {
|
||||
ctx.data[0] = new TimeSeries({
|
||||
datapoints: [[2000, 1], [0.002, 2], [0, 3], [-1, 4]],
|
||||
alias: 'seriesAutoscale',
|
||||
});
|
||||
ctx.data[0].yaxis = 1;
|
||||
ctx.data[1] = new TimeSeries({
|
||||
datapoints: [[2000, 1], [0.002, 2], [0, 3], [-1, 4]],
|
||||
alias: 'seriesFixedscale',
|
||||
});
|
||||
ctx.data[1].yaxis = 2;
|
||||
ctrl.panel.yaxes[0].logBase = 10;
|
||||
|
||||
ctrl.panel.yaxes[1].logBase = 10;
|
||||
ctrl.panel.yaxes[1].min = '0.05';
|
||||
ctrl.panel.yaxes[1].max = '1500';
|
||||
});
|
||||
});
|
||||
|
||||
it('should apply axis transform, autoscaling (if necessary) and ticks', function() {
|
||||
var axisAutoscale = ctx.plotOptions.yaxes[0];
|
||||
expect(axisAutoscale.transform(100)).toBe(2);
|
||||
expect(axisAutoscale.inverseTransform(-3)).toBeCloseTo(0.001);
|
||||
expect(axisAutoscale.min).toBeCloseTo(0.001);
|
||||
expect(axisAutoscale.max).toBe(10000);
|
||||
expect(axisAutoscale.ticks.length).toBeCloseTo(8);
|
||||
expect(axisAutoscale.ticks[0]).toBeCloseTo(0.001);
|
||||
if (axisAutoscale.ticks.length === 7) {
|
||||
expect(axisAutoscale.ticks[axisAutoscale.ticks.length - 1]).toBeCloseTo(1000);
|
||||
} else {
|
||||
expect(axisAutoscale.ticks[axisAutoscale.ticks.length - 1]).toBe(10000);
|
||||
}
|
||||
|
||||
var axisFixedscale = ctx.plotOptions.yaxes[1];
|
||||
expect(axisFixedscale.min).toBe(0.05);
|
||||
expect(axisFixedscale.max).toBe(1500);
|
||||
expect(axisFixedscale.ticks.length).toBe(5);
|
||||
expect(axisFixedscale.ticks[0]).toBe(0.1);
|
||||
expect(axisFixedscale.ticks[4]).toBe(1000);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when logBase is log 10 and data points contain only zeroes', () => {
|
||||
beforeEach(() => {
|
||||
setupCtx(() => {
|
||||
ctrl.panel.yaxes[0].logBase = 10;
|
||||
ctx.data[0] = new TimeSeries({
|
||||
datapoints: [[0, 1], [0, 2], [0, 3], [0, 4]],
|
||||
alias: 'seriesAutoscale',
|
||||
});
|
||||
ctx.data[0].yaxis = 1;
|
||||
});
|
||||
});
|
||||
|
||||
it('should not set min and max and should create some fake ticks', function() {
|
||||
var axisAutoscale = ctx.plotOptions.yaxes[0];
|
||||
expect(axisAutoscale.transform(100)).toBe(2);
|
||||
expect(axisAutoscale.inverseTransform(-3)).toBeCloseTo(0.001);
|
||||
expect(axisAutoscale.min).toBe(undefined);
|
||||
expect(axisAutoscale.max).toBe(undefined);
|
||||
expect(axisAutoscale.ticks.length).toBe(2);
|
||||
expect(axisAutoscale.ticks[0]).toBe(1);
|
||||
expect(axisAutoscale.ticks[1]).toBe(2);
|
||||
});
|
||||
});
|
||||
|
||||
// y-min set 0 is a special case for log scale,
|
||||
// this approximates it by setting min to 0.1
|
||||
describe('when logBase is log 10 and y-min is set to 0 and auto min is > 0.1', () => {
|
||||
beforeEach(() => {
|
||||
setupCtx(() => {
|
||||
ctrl.panel.yaxes[0].logBase = 10;
|
||||
ctrl.panel.yaxes[0].min = '0';
|
||||
ctx.data[0] = new TimeSeries({
|
||||
datapoints: [[2000, 1], [4, 2], [500, 3], [3000, 4]],
|
||||
alias: 'seriesAutoscale',
|
||||
});
|
||||
ctx.data[0].yaxis = 1;
|
||||
});
|
||||
});
|
||||
it('should set min to 0.1 and add a tick for 0.1', function() {
|
||||
var axisAutoscale = ctx.plotOptions.yaxes[0];
|
||||
expect(axisAutoscale.transform(100)).toBe(2);
|
||||
expect(axisAutoscale.inverseTransform(-3)).toBeCloseTo(0.001);
|
||||
expect(axisAutoscale.min).toBe(0.1);
|
||||
expect(axisAutoscale.max).toBe(10000);
|
||||
expect(axisAutoscale.ticks.length).toBe(6);
|
||||
expect(axisAutoscale.ticks[0]).toBe(0.1);
|
||||
expect(axisAutoscale.ticks[5]).toBe(10000);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when logBase is log 2 and y-min is set to 0 and num of ticks exceeds max', () => {
|
||||
beforeEach(() => {
|
||||
setupCtx(() => {
|
||||
const heightForApprox5Ticks = 125;
|
||||
ctrl.height = heightForApprox5Ticks;
|
||||
ctrl.panel.yaxes[0].logBase = 2;
|
||||
ctrl.panel.yaxes[0].min = '0';
|
||||
ctx.data[0] = new TimeSeries({
|
||||
datapoints: [[2000, 1], [4, 2], [500, 3], [3000, 4], [10000, 5], [100000, 6]],
|
||||
alias: 'seriesAutoscale',
|
||||
});
|
||||
ctx.data[0].yaxis = 1;
|
||||
});
|
||||
});
|
||||
|
||||
it('should regenerate ticks so that if fits on the y-axis', function() {
|
||||
var axisAutoscale = ctx.plotOptions.yaxes[0];
|
||||
expect(axisAutoscale.min).toBe(0.1);
|
||||
expect(axisAutoscale.ticks.length).toBe(8);
|
||||
expect(axisAutoscale.ticks[0]).toBe(0.1);
|
||||
expect(axisAutoscale.ticks[7]).toBe(262144);
|
||||
expect(axisAutoscale.max).toBe(262144);
|
||||
});
|
||||
|
||||
it('should set axis max to be max tick value', function() {
|
||||
expect(ctx.plotOptions.yaxes[0].max).toBe(262144);
|
||||
});
|
||||
});
|
||||
|
||||
describe('dashed lines options', () => {
|
||||
beforeEach(() => {
|
||||
setupCtx(() => {
|
||||
ctrl.panel.lines = true;
|
||||
ctrl.panel.linewidth = 2;
|
||||
ctrl.panel.dashes = true;
|
||||
});
|
||||
});
|
||||
|
||||
it('should configure dashed plot with correct options', function() {
|
||||
expect(ctx.plotOptions.series.lines.show).toBe(true);
|
||||
expect(ctx.plotOptions.series.dashes.lineWidth).toBe(2);
|
||||
expect(ctx.plotOptions.series.dashes.show).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('should use timeStep for barWidth', () => {
|
||||
beforeEach(() => {
|
||||
setupCtx(() => {
|
||||
ctrl.panel.bars = true;
|
||||
ctx.data[0] = new TimeSeries({
|
||||
datapoints: [[1, 10], [2, 20]],
|
||||
alias: 'series1',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should set barWidth', function() {
|
||||
expect(ctx.plotOptions.series.bars.barWidth).toBe(1 / 1.5);
|
||||
});
|
||||
});
|
||||
|
||||
describe('series option overrides, fill & points', () => {
|
||||
beforeEach(() => {
|
||||
setupCtx(() => {
|
||||
ctrl.panel.lines = true;
|
||||
ctrl.panel.fill = 5;
|
||||
ctx.data[0].zindex = 10;
|
||||
ctx.data[1].alias = 'test';
|
||||
ctx.data[1].lines = { fill: 0.001 };
|
||||
ctx.data[1].points = { show: true };
|
||||
});
|
||||
});
|
||||
|
||||
it('should match second series and fill zero, and enable points', function() {
|
||||
expect(ctx.plotOptions.series.lines.fill).toBe(0.5);
|
||||
expect(ctx.plotData[1].lines.fill).toBe(0.001);
|
||||
expect(ctx.plotData[1].points.show).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('should order series order according to zindex', () => {
|
||||
beforeEach(() => {
|
||||
setupCtx(() => {
|
||||
ctx.data[1].zindex = 1;
|
||||
ctx.data[0].zindex = 10;
|
||||
});
|
||||
});
|
||||
|
||||
it('should move zindex 2 last', function() {
|
||||
expect(ctx.plotData[0].alias).toBe('series2');
|
||||
expect(ctx.plotData[1].alias).toBe('series1');
|
||||
});
|
||||
});
|
||||
|
||||
describe('when series is hidden', () => {
|
||||
beforeEach(() => {
|
||||
setupCtx(() => {
|
||||
ctrl.hiddenSeries = { series2: true };
|
||||
});
|
||||
});
|
||||
|
||||
it('should remove datapoints and disable stack', function() {
|
||||
expect(ctx.plotData[0].alias).toBe('series1');
|
||||
expect(ctx.plotData[1].data.length).toBe(0);
|
||||
expect(ctx.plotData[1].stack).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when stack and percent', () => {
|
||||
beforeEach(() => {
|
||||
setupCtx(() => {
|
||||
ctrl.panel.percentage = true;
|
||||
ctrl.panel.stack = true;
|
||||
});
|
||||
});
|
||||
|
||||
it('should show percentage', function() {
|
||||
var axis = ctx.plotOptions.yaxes[0];
|
||||
expect(axis.tickFormatter(100, axis)).toBe('100%');
|
||||
});
|
||||
});
|
||||
|
||||
describe('when panel too narrow to show x-axis dates in same granularity as wide panels', () => {
|
||||
//Set width to 10px
|
||||
describe('and the range is less than 24 hours', function() {
|
||||
beforeEach(() => {
|
||||
setupCtx(() => {
|
||||
ctrl.range.from = moment([2015, 1, 1, 10]);
|
||||
ctrl.range.to = moment([2015, 1, 1, 22]);
|
||||
});
|
||||
});
|
||||
|
||||
it('should format dates as hours minutes', function() {
|
||||
var axis = ctx.plotOptions.xaxis;
|
||||
expect(axis.timeformat).toBe('%H:%M');
|
||||
});
|
||||
});
|
||||
|
||||
describe('and the range is less than one year', function() {
|
||||
beforeEach(() => {
|
||||
setupCtx(() => {
|
||||
ctrl.range.from = moment([2015, 1, 1]);
|
||||
ctrl.range.to = moment([2015, 11, 20]);
|
||||
});
|
||||
});
|
||||
|
||||
it('should format dates as month days', function() {
|
||||
var axis = ctx.plotOptions.xaxis;
|
||||
expect(axis.timeformat).toBe('%m/%d');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when graph is histogram, and enable stack', () => {
|
||||
beforeEach(() => {
|
||||
setupCtx(() => {
|
||||
ctrl.panel.xaxis.mode = 'histogram';
|
||||
ctrl.panel.stack = true;
|
||||
ctrl.hiddenSeries = {};
|
||||
ctx.data[0] = new TimeSeries({
|
||||
datapoints: [[100, 1], [100, 2], [200, 3], [300, 4]],
|
||||
alias: 'series1',
|
||||
});
|
||||
ctx.data[1] = new TimeSeries({
|
||||
datapoints: [[100, 1], [100, 2], [200, 3], [300, 4]],
|
||||
alias: 'series2',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should calculate correct histogram', function() {
|
||||
expect(ctx.plotData[0].data[0][0]).toBe(100);
|
||||
expect(ctx.plotData[0].data[0][1]).toBe(2);
|
||||
expect(ctx.plotData[1].data[0][0]).toBe(100);
|
||||
expect(ctx.plotData[1].data[0][1]).toBe(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when graph is histogram, and some series are hidden', () => {
|
||||
beforeEach(() => {
|
||||
setupCtx(() => {
|
||||
ctrl.panel.xaxis.mode = 'histogram';
|
||||
ctrl.panel.stack = false;
|
||||
ctrl.hiddenSeries = { series2: true };
|
||||
ctx.data[0] = new TimeSeries({
|
||||
datapoints: [[100, 1], [100, 2], [200, 3], [300, 4]],
|
||||
alias: 'series1',
|
||||
});
|
||||
ctx.data[1] = new TimeSeries({
|
||||
datapoints: [[100, 1], [100, 2], [200, 3], [300, 4]],
|
||||
alias: 'series2',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should calculate correct histogram', function() {
|
||||
expect(ctx.plotData[0].data[0][0]).toBe(100);
|
||||
expect(ctx.plotData[0].data[0][1]).toBe(2);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,454 +0,0 @@
|
||||
import { describe, beforeEach, it, sinon, expect, angularMocks } from '../../../../../test/lib/common';
|
||||
|
||||
import '../module';
|
||||
import angular from 'angular';
|
||||
import $ from 'jquery';
|
||||
import helpers from 'test/specs/helpers';
|
||||
import TimeSeries from 'app/core/time_series2';
|
||||
import moment from 'moment';
|
||||
import { Emitter } from 'app/core/core';
|
||||
|
||||
describe('grafanaGraph', function() {
|
||||
beforeEach(angularMocks.module('grafana.core'));
|
||||
|
||||
function graphScenario(desc, func, elementWidth = 500) {
|
||||
describe(desc, () => {
|
||||
var ctx: any = {};
|
||||
|
||||
ctx.setup = setupFunc => {
|
||||
beforeEach(
|
||||
angularMocks.module($provide => {
|
||||
$provide.value('timeSrv', new helpers.TimeSrvStub());
|
||||
})
|
||||
);
|
||||
|
||||
beforeEach(
|
||||
angularMocks.inject(($rootScope, $compile) => {
|
||||
var ctrl: any = {
|
||||
height: 200,
|
||||
panel: {
|
||||
events: new Emitter(),
|
||||
legend: {},
|
||||
grid: {},
|
||||
yaxes: [
|
||||
{
|
||||
min: null,
|
||||
max: null,
|
||||
format: 'short',
|
||||
logBase: 1,
|
||||
},
|
||||
{
|
||||
min: null,
|
||||
max: null,
|
||||
format: 'short',
|
||||
logBase: 1,
|
||||
},
|
||||
],
|
||||
thresholds: [],
|
||||
xaxis: {},
|
||||
seriesOverrides: [],
|
||||
tooltip: {
|
||||
shared: true,
|
||||
},
|
||||
},
|
||||
renderingCompleted: sinon.spy(),
|
||||
hiddenSeries: {},
|
||||
dashboard: {
|
||||
getTimezone: sinon.stub().returns('browser'),
|
||||
},
|
||||
range: {
|
||||
from: moment([2015, 1, 1, 10]),
|
||||
to: moment([2015, 1, 1, 22]),
|
||||
},
|
||||
};
|
||||
|
||||
var scope = $rootScope.$new();
|
||||
scope.ctrl = ctrl;
|
||||
scope.ctrl.events = ctrl.panel.events;
|
||||
|
||||
$rootScope.onAppEvent = sinon.spy();
|
||||
|
||||
ctx.data = [];
|
||||
ctx.data.push(
|
||||
new TimeSeries({
|
||||
datapoints: [[1, 1], [2, 2]],
|
||||
alias: 'series1',
|
||||
})
|
||||
);
|
||||
ctx.data.push(
|
||||
new TimeSeries({
|
||||
datapoints: [[10, 1], [20, 2]],
|
||||
alias: 'series2',
|
||||
})
|
||||
);
|
||||
|
||||
setupFunc(ctrl, ctx.data);
|
||||
|
||||
var element = angular.element("<div style='width:" + elementWidth + "px' grafana-graph><div>");
|
||||
$compile(element)(scope);
|
||||
scope.$digest();
|
||||
|
||||
$.plot = ctx.plotSpy = sinon.spy();
|
||||
ctrl.events.emit('render', ctx.data);
|
||||
ctrl.events.emit('render-legend');
|
||||
ctrl.events.emit('legend-rendering-complete');
|
||||
ctx.plotData = ctx.plotSpy.getCall(0).args[1];
|
||||
ctx.plotOptions = ctx.plotSpy.getCall(0).args[2];
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
func(ctx);
|
||||
});
|
||||
}
|
||||
|
||||
graphScenario('simple lines options', ctx => {
|
||||
ctx.setup(ctrl => {
|
||||
ctrl.panel.lines = true;
|
||||
ctrl.panel.fill = 5;
|
||||
ctrl.panel.linewidth = 3;
|
||||
ctrl.panel.steppedLine = true;
|
||||
});
|
||||
|
||||
it('should configure plot with correct options', () => {
|
||||
expect(ctx.plotOptions.series.lines.show).to.be(true);
|
||||
expect(ctx.plotOptions.series.lines.fill).to.be(0.5);
|
||||
expect(ctx.plotOptions.series.lines.lineWidth).to.be(3);
|
||||
expect(ctx.plotOptions.series.lines.steps).to.be(true);
|
||||
});
|
||||
});
|
||||
|
||||
graphScenario('sorting stacked series as legend. disabled', ctx => {
|
||||
ctx.setup(ctrl => {
|
||||
ctrl.panel.legend.sort = undefined;
|
||||
ctrl.panel.stack = false;
|
||||
});
|
||||
|
||||
it('should not modify order of time series', () => {
|
||||
expect(ctx.plotData[0].alias).to.be('series1');
|
||||
expect(ctx.plotData[1].alias).to.be('series2');
|
||||
});
|
||||
});
|
||||
|
||||
graphScenario('sorting stacked series as legend. min descending order', ctx => {
|
||||
ctx.setup(ctrl => {
|
||||
ctrl.panel.legend.sort = 'min';
|
||||
ctrl.panel.legend.sortDesc = true;
|
||||
ctrl.panel.stack = true;
|
||||
});
|
||||
|
||||
it('highest value should be first', () => {
|
||||
expect(ctx.plotData[0].alias).to.be('series2');
|
||||
expect(ctx.plotData[1].alias).to.be('series1');
|
||||
});
|
||||
});
|
||||
|
||||
graphScenario('sorting stacked series as legend. min ascending order', ctx => {
|
||||
ctx.setup((ctrl, data) => {
|
||||
ctrl.panel.legend.sort = 'min';
|
||||
ctrl.panel.legend.sortDesc = false;
|
||||
ctrl.panel.stack = true;
|
||||
});
|
||||
|
||||
it('lowest value should be first', () => {
|
||||
expect(ctx.plotData[0].alias).to.be('series1');
|
||||
expect(ctx.plotData[1].alias).to.be('series2');
|
||||
});
|
||||
});
|
||||
|
||||
graphScenario('sorting stacked series as legend. stacking disabled', ctx => {
|
||||
ctx.setup(ctrl => {
|
||||
ctrl.panel.legend.sort = 'min';
|
||||
ctrl.panel.legend.sortDesc = true;
|
||||
ctrl.panel.stack = false;
|
||||
});
|
||||
|
||||
it('highest value should be first', () => {
|
||||
expect(ctx.plotData[0].alias).to.be('series1');
|
||||
expect(ctx.plotData[1].alias).to.be('series2');
|
||||
});
|
||||
});
|
||||
|
||||
graphScenario('sorting stacked series as legend. current descending order', ctx => {
|
||||
ctx.setup(ctrl => {
|
||||
ctrl.panel.legend.sort = 'current';
|
||||
ctrl.panel.legend.sortDesc = true;
|
||||
ctrl.panel.stack = true;
|
||||
});
|
||||
|
||||
it('highest last value should be first', () => {
|
||||
expect(ctx.plotData[0].alias).to.be('series2');
|
||||
expect(ctx.plotData[1].alias).to.be('series1');
|
||||
});
|
||||
});
|
||||
|
||||
graphScenario('when logBase is log 10', function(ctx) {
|
||||
ctx.setup(function(ctrl, data) {
|
||||
ctrl.panel.yaxes[0].logBase = 10;
|
||||
data[0] = new TimeSeries({
|
||||
datapoints: [[2000, 1], [0.002, 2], [0, 3], [-1, 4]],
|
||||
alias: 'seriesAutoscale',
|
||||
});
|
||||
data[0].yaxis = 1;
|
||||
ctrl.panel.yaxes[1].logBase = 10;
|
||||
ctrl.panel.yaxes[1].min = '0.05';
|
||||
ctrl.panel.yaxes[1].max = '1500';
|
||||
data[1] = new TimeSeries({
|
||||
datapoints: [[2000, 1], [0.002, 2], [0, 3], [-1, 4]],
|
||||
alias: 'seriesFixedscale',
|
||||
});
|
||||
data[1].yaxis = 2;
|
||||
});
|
||||
|
||||
it('should apply axis transform, autoscaling (if necessary) and ticks', function() {
|
||||
var axisAutoscale = ctx.plotOptions.yaxes[0];
|
||||
expect(axisAutoscale.transform(100)).to.be(2);
|
||||
expect(axisAutoscale.inverseTransform(-3)).to.within(0.00099999999, 0.00100000001);
|
||||
expect(axisAutoscale.min).to.within(0.00099999999, 0.00100000001);
|
||||
expect(axisAutoscale.max).to.be(10000);
|
||||
expect(axisAutoscale.ticks.length).to.within(7, 8);
|
||||
expect(axisAutoscale.ticks[0]).to.within(0.00099999999, 0.00100000001);
|
||||
if (axisAutoscale.ticks.length === 7) {
|
||||
expect(axisAutoscale.ticks[axisAutoscale.ticks.length - 1]).to.within(999.9999, 1000.0001);
|
||||
} else {
|
||||
expect(axisAutoscale.ticks[axisAutoscale.ticks.length - 1]).to.be(10000);
|
||||
}
|
||||
|
||||
var axisFixedscale = ctx.plotOptions.yaxes[1];
|
||||
expect(axisFixedscale.min).to.be(0.05);
|
||||
expect(axisFixedscale.max).to.be(1500);
|
||||
expect(axisFixedscale.ticks.length).to.be(5);
|
||||
expect(axisFixedscale.ticks[0]).to.be(0.1);
|
||||
expect(axisFixedscale.ticks[4]).to.be(1000);
|
||||
});
|
||||
});
|
||||
|
||||
graphScenario('when logBase is log 10 and data points contain only zeroes', function(ctx) {
|
||||
ctx.setup(function(ctrl, data) {
|
||||
ctrl.panel.yaxes[0].logBase = 10;
|
||||
data[0] = new TimeSeries({
|
||||
datapoints: [[0, 1], [0, 2], [0, 3], [0, 4]],
|
||||
alias: 'seriesAutoscale',
|
||||
});
|
||||
data[0].yaxis = 1;
|
||||
});
|
||||
|
||||
it('should not set min and max and should create some fake ticks', function() {
|
||||
var axisAutoscale = ctx.plotOptions.yaxes[0];
|
||||
expect(axisAutoscale.transform(100)).to.be(2);
|
||||
expect(axisAutoscale.inverseTransform(-3)).to.within(0.00099999999, 0.00100000001);
|
||||
expect(axisAutoscale.min).to.be(undefined);
|
||||
expect(axisAutoscale.max).to.be(undefined);
|
||||
expect(axisAutoscale.ticks.length).to.be(2);
|
||||
expect(axisAutoscale.ticks[0]).to.be(1);
|
||||
expect(axisAutoscale.ticks[1]).to.be(2);
|
||||
});
|
||||
});
|
||||
|
||||
// y-min set 0 is a special case for log scale,
|
||||
// this approximates it by setting min to 0.1
|
||||
graphScenario('when logBase is log 10 and y-min is set to 0 and auto min is > 0.1', function(ctx) {
|
||||
ctx.setup(function(ctrl, data) {
|
||||
ctrl.panel.yaxes[0].logBase = 10;
|
||||
ctrl.panel.yaxes[0].min = '0';
|
||||
data[0] = new TimeSeries({
|
||||
datapoints: [[2000, 1], [4, 2], [500, 3], [3000, 4]],
|
||||
alias: 'seriesAutoscale',
|
||||
});
|
||||
data[0].yaxis = 1;
|
||||
});
|
||||
|
||||
it('should set min to 0.1 and add a tick for 0.1', function() {
|
||||
var axisAutoscale = ctx.plotOptions.yaxes[0];
|
||||
expect(axisAutoscale.transform(100)).to.be(2);
|
||||
expect(axisAutoscale.inverseTransform(-3)).to.within(0.00099999999, 0.00100000001);
|
||||
expect(axisAutoscale.min).to.be(0.1);
|
||||
expect(axisAutoscale.max).to.be(10000);
|
||||
expect(axisAutoscale.ticks.length).to.be(6);
|
||||
expect(axisAutoscale.ticks[0]).to.be(0.1);
|
||||
expect(axisAutoscale.ticks[5]).to.be(10000);
|
||||
});
|
||||
});
|
||||
|
||||
graphScenario('when logBase is log 2 and y-min is set to 0 and num of ticks exceeds max', function(ctx) {
|
||||
ctx.setup(function(ctrl, data) {
|
||||
const heightForApprox5Ticks = 125;
|
||||
ctrl.height = heightForApprox5Ticks;
|
||||
ctrl.panel.yaxes[0].logBase = 2;
|
||||
ctrl.panel.yaxes[0].min = '0';
|
||||
data[0] = new TimeSeries({
|
||||
datapoints: [[2000, 1], [4, 2], [500, 3], [3000, 4], [10000, 5], [100000, 6]],
|
||||
alias: 'seriesAutoscale',
|
||||
});
|
||||
data[0].yaxis = 1;
|
||||
});
|
||||
|
||||
it('should regenerate ticks so that if fits on the y-axis', function() {
|
||||
var axisAutoscale = ctx.plotOptions.yaxes[0];
|
||||
expect(axisAutoscale.min).to.be(0.1);
|
||||
expect(axisAutoscale.ticks.length).to.be(8);
|
||||
expect(axisAutoscale.ticks[0]).to.be(0.1);
|
||||
expect(axisAutoscale.ticks[7]).to.be(262144);
|
||||
expect(axisAutoscale.max).to.be(262144);
|
||||
});
|
||||
|
||||
it('should set axis max to be max tick value', function() {
|
||||
expect(ctx.plotOptions.yaxes[0].max).to.be(262144);
|
||||
});
|
||||
});
|
||||
|
||||
graphScenario('dashed lines options', function(ctx) {
|
||||
ctx.setup(function(ctrl) {
|
||||
ctrl.panel.lines = true;
|
||||
ctrl.panel.linewidth = 2;
|
||||
ctrl.panel.dashes = true;
|
||||
});
|
||||
|
||||
it('should configure dashed plot with correct options', function() {
|
||||
expect(ctx.plotOptions.series.lines.show).to.be(true);
|
||||
expect(ctx.plotOptions.series.dashes.lineWidth).to.be(2);
|
||||
expect(ctx.plotOptions.series.dashes.show).to.be(true);
|
||||
});
|
||||
});
|
||||
|
||||
graphScenario('should use timeStep for barWidth', function(ctx) {
|
||||
ctx.setup(function(ctrl, data) {
|
||||
ctrl.panel.bars = true;
|
||||
data[0] = new TimeSeries({
|
||||
datapoints: [[1, 10], [2, 20]],
|
||||
alias: 'series1',
|
||||
});
|
||||
});
|
||||
|
||||
it('should set barWidth', function() {
|
||||
expect(ctx.plotOptions.series.bars.barWidth).to.be(1 / 1.5);
|
||||
});
|
||||
});
|
||||
|
||||
graphScenario('series option overrides, fill & points', function(ctx) {
|
||||
ctx.setup(function(ctrl, data) {
|
||||
ctrl.panel.lines = true;
|
||||
ctrl.panel.fill = 5;
|
||||
data[0].zindex = 10;
|
||||
data[1].alias = 'test';
|
||||
data[1].lines = { fill: 0.001 };
|
||||
data[1].points = { show: true };
|
||||
});
|
||||
|
||||
it('should match second series and fill zero, and enable points', function() {
|
||||
expect(ctx.plotOptions.series.lines.fill).to.be(0.5);
|
||||
expect(ctx.plotData[1].lines.fill).to.be(0.001);
|
||||
expect(ctx.plotData[1].points.show).to.be(true);
|
||||
});
|
||||
});
|
||||
|
||||
graphScenario('should order series order according to zindex', function(ctx) {
|
||||
ctx.setup(function(ctrl, data) {
|
||||
data[1].zindex = 1;
|
||||
data[0].zindex = 10;
|
||||
});
|
||||
|
||||
it('should move zindex 2 last', function() {
|
||||
expect(ctx.plotData[0].alias).to.be('series2');
|
||||
expect(ctx.plotData[1].alias).to.be('series1');
|
||||
});
|
||||
});
|
||||
|
||||
graphScenario('when series is hidden', function(ctx) {
|
||||
ctx.setup(function(ctrl) {
|
||||
ctrl.hiddenSeries = { series2: true };
|
||||
});
|
||||
|
||||
it('should remove datapoints and disable stack', function() {
|
||||
expect(ctx.plotData[0].alias).to.be('series1');
|
||||
expect(ctx.plotData[1].data.length).to.be(0);
|
||||
expect(ctx.plotData[1].stack).to.be(false);
|
||||
});
|
||||
});
|
||||
|
||||
graphScenario('when stack and percent', function(ctx) {
|
||||
ctx.setup(function(ctrl) {
|
||||
ctrl.panel.percentage = true;
|
||||
ctrl.panel.stack = true;
|
||||
});
|
||||
|
||||
it('should show percentage', function() {
|
||||
var axis = ctx.plotOptions.yaxes[0];
|
||||
expect(axis.tickFormatter(100, axis)).to.be('100%');
|
||||
});
|
||||
});
|
||||
|
||||
graphScenario(
|
||||
'when panel too narrow to show x-axis dates in same granularity as wide panels',
|
||||
function(ctx) {
|
||||
describe('and the range is less than 24 hours', function() {
|
||||
ctx.setup(function(ctrl) {
|
||||
ctrl.range.from = moment([2015, 1, 1, 10]);
|
||||
ctrl.range.to = moment([2015, 1, 1, 22]);
|
||||
});
|
||||
|
||||
it('should format dates as hours minutes', function() {
|
||||
var axis = ctx.plotOptions.xaxis;
|
||||
expect(axis.timeformat).to.be('%H:%M');
|
||||
});
|
||||
});
|
||||
|
||||
describe('and the range is less than one year', function() {
|
||||
ctx.setup(function(scope) {
|
||||
scope.range.from = moment([2015, 1, 1]);
|
||||
scope.range.to = moment([2015, 11, 20]);
|
||||
});
|
||||
|
||||
it('should format dates as month days', function() {
|
||||
var axis = ctx.plotOptions.xaxis;
|
||||
expect(axis.timeformat).to.be('%m/%d');
|
||||
});
|
||||
});
|
||||
},
|
||||
10
|
||||
);
|
||||
|
||||
graphScenario('when graph is histogram, and enable stack', function(ctx) {
|
||||
ctx.setup(function(ctrl, data) {
|
||||
ctrl.panel.xaxis.mode = 'histogram';
|
||||
ctrl.panel.stack = true;
|
||||
ctrl.hiddenSeries = {};
|
||||
data[0] = new TimeSeries({
|
||||
datapoints: [[100, 1], [100, 2], [200, 3], [300, 4]],
|
||||
alias: 'series1',
|
||||
});
|
||||
data[1] = new TimeSeries({
|
||||
datapoints: [[100, 1], [100, 2], [200, 3], [300, 4]],
|
||||
alias: 'series2',
|
||||
});
|
||||
});
|
||||
|
||||
it('should calculate correct histogram', function() {
|
||||
expect(ctx.plotData[0].data[0][0]).to.be(100);
|
||||
expect(ctx.plotData[0].data[0][1]).to.be(2);
|
||||
expect(ctx.plotData[1].data[0][0]).to.be(100);
|
||||
expect(ctx.plotData[1].data[0][1]).to.be(2);
|
||||
});
|
||||
});
|
||||
|
||||
graphScenario('when graph is histogram, and some series are hidden', function(ctx) {
|
||||
ctx.setup(function(ctrl, data) {
|
||||
ctrl.panel.xaxis.mode = 'histogram';
|
||||
ctrl.panel.stack = false;
|
||||
ctrl.hiddenSeries = { series2: true };
|
||||
data[0] = new TimeSeries({
|
||||
datapoints: [[100, 1], [100, 2], [200, 3], [300, 4]],
|
||||
alias: 'series1',
|
||||
});
|
||||
data[1] = new TimeSeries({
|
||||
datapoints: [[100, 1], [100, 2], [200, 3], [300, 4]],
|
||||
alias: 'series2',
|
||||
});
|
||||
});
|
||||
|
||||
it('should calculate correct histogram', function() {
|
||||
expect(ctx.plotData[0].data[0][0]).to.be(100);
|
||||
expect(ctx.plotData[0].data[0][1]).to.be(2);
|
||||
});
|
||||
});
|
||||
});
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,320 +0,0 @@
|
||||
import { describe, beforeEach, it, sinon, expect, angularMocks } from '../../../../../test/lib/common';
|
||||
|
||||
import '../module';
|
||||
import angular from 'angular';
|
||||
import $ from 'jquery';
|
||||
import helpers from 'test/specs/helpers';
|
||||
import TimeSeries from 'app/core/time_series2';
|
||||
import moment from 'moment';
|
||||
import { Emitter } from 'app/core/core';
|
||||
import rendering from '../rendering';
|
||||
import { convertToHeatMap, convertToCards, histogramToHeatmap, calculateBucketSize } from '../heatmap_data_converter';
|
||||
|
||||
describe('grafanaHeatmap', function() {
|
||||
beforeEach(angularMocks.module('grafana.core'));
|
||||
|
||||
function heatmapScenario(desc, func, elementWidth = 500) {
|
||||
describe(desc, function() {
|
||||
var ctx: any = {};
|
||||
|
||||
ctx.setup = function(setupFunc) {
|
||||
beforeEach(
|
||||
angularMocks.module(function($provide) {
|
||||
$provide.value('timeSrv', new helpers.TimeSrvStub());
|
||||
})
|
||||
);
|
||||
|
||||
beforeEach(
|
||||
angularMocks.inject(function($rootScope, $compile) {
|
||||
var ctrl: any = {
|
||||
colorSchemes: [
|
||||
{
|
||||
name: 'Oranges',
|
||||
value: 'interpolateOranges',
|
||||
invert: 'dark',
|
||||
},
|
||||
{ name: 'Reds', value: 'interpolateReds', invert: 'dark' },
|
||||
],
|
||||
events: new Emitter(),
|
||||
height: 200,
|
||||
panel: {
|
||||
heatmap: {},
|
||||
cards: {
|
||||
cardPadding: null,
|
||||
cardRound: null,
|
||||
},
|
||||
color: {
|
||||
mode: 'spectrum',
|
||||
cardColor: '#b4ff00',
|
||||
colorScale: 'linear',
|
||||
exponent: 0.5,
|
||||
colorScheme: 'interpolateOranges',
|
||||
fillBackground: false,
|
||||
},
|
||||
legend: {
|
||||
show: false,
|
||||
},
|
||||
xBucketSize: 1000,
|
||||
xBucketNumber: null,
|
||||
yBucketSize: 1,
|
||||
yBucketNumber: null,
|
||||
xAxis: {
|
||||
show: true,
|
||||
},
|
||||
yAxis: {
|
||||
show: true,
|
||||
format: 'short',
|
||||
decimals: null,
|
||||
logBase: 1,
|
||||
splitFactor: null,
|
||||
min: null,
|
||||
max: null,
|
||||
removeZeroValues: false,
|
||||
},
|
||||
tooltip: {
|
||||
show: true,
|
||||
seriesStat: false,
|
||||
showHistogram: false,
|
||||
},
|
||||
highlightCards: true,
|
||||
},
|
||||
renderingCompleted: sinon.spy(),
|
||||
hiddenSeries: {},
|
||||
dashboard: {
|
||||
getTimezone: sinon.stub().returns('utc'),
|
||||
},
|
||||
range: {
|
||||
from: moment.utc('01 Mar 2017 10:00:00', 'DD MMM YYYY HH:mm:ss'),
|
||||
to: moment.utc('01 Mar 2017 11:00:00', 'DD MMM YYYY HH:mm:ss'),
|
||||
},
|
||||
};
|
||||
|
||||
var scope = $rootScope.$new();
|
||||
scope.ctrl = ctrl;
|
||||
|
||||
ctx.series = [];
|
||||
ctx.series.push(
|
||||
new TimeSeries({
|
||||
datapoints: [[1, 1422774000000], [2, 1422774060000]],
|
||||
alias: 'series1',
|
||||
})
|
||||
);
|
||||
ctx.series.push(
|
||||
new TimeSeries({
|
||||
datapoints: [[2, 1422774000000], [3, 1422774060000]],
|
||||
alias: 'series2',
|
||||
})
|
||||
);
|
||||
|
||||
ctx.data = {
|
||||
heatmapStats: {
|
||||
min: 1,
|
||||
max: 3,
|
||||
minLog: 1,
|
||||
},
|
||||
xBucketSize: ctrl.panel.xBucketSize,
|
||||
yBucketSize: ctrl.panel.yBucketSize,
|
||||
};
|
||||
|
||||
setupFunc(ctrl, ctx);
|
||||
|
||||
let logBase = ctrl.panel.yAxis.logBase;
|
||||
let bucketsData;
|
||||
if (ctrl.panel.dataFormat === 'tsbuckets') {
|
||||
bucketsData = histogramToHeatmap(ctx.series);
|
||||
} else {
|
||||
bucketsData = convertToHeatMap(ctx.series, ctx.data.yBucketSize, ctx.data.xBucketSize, logBase);
|
||||
}
|
||||
ctx.data.buckets = bucketsData;
|
||||
|
||||
let { cards, cardStats } = convertToCards(bucketsData);
|
||||
ctx.data.cards = cards;
|
||||
ctx.data.cardStats = cardStats;
|
||||
|
||||
let elemHtml = `
|
||||
<div class="heatmap-wrapper">
|
||||
<div class="heatmap-canvas-wrapper">
|
||||
<div class="heatmap-panel" style='width:${elementWidth}px'></div>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
var element = angular.element(elemHtml);
|
||||
$compile(element)(scope);
|
||||
scope.$digest();
|
||||
|
||||
ctrl.data = ctx.data;
|
||||
ctx.element = element;
|
||||
rendering(scope, $(element), [], ctrl);
|
||||
ctrl.events.emit('render');
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
func(ctx);
|
||||
});
|
||||
}
|
||||
|
||||
heatmapScenario('default options', function(ctx) {
|
||||
ctx.setup(function(ctrl) {
|
||||
ctrl.panel.yAxis.logBase = 1;
|
||||
});
|
||||
|
||||
it('should draw correct Y axis', function() {
|
||||
var yTicks = getTicks(ctx.element, '.axis-y');
|
||||
expect(yTicks).to.eql(['1', '2', '3']);
|
||||
});
|
||||
|
||||
it('should draw correct X axis', function() {
|
||||
var xTicks = getTicks(ctx.element, '.axis-x');
|
||||
let expectedTicks = [
|
||||
formatTime('01 Mar 2017 10:00:00'),
|
||||
formatTime('01 Mar 2017 10:15:00'),
|
||||
formatTime('01 Mar 2017 10:30:00'),
|
||||
formatTime('01 Mar 2017 10:45:00'),
|
||||
formatTime('01 Mar 2017 11:00:00'),
|
||||
];
|
||||
expect(xTicks).to.eql(expectedTicks);
|
||||
});
|
||||
});
|
||||
|
||||
heatmapScenario('when logBase is 2', function(ctx) {
|
||||
ctx.setup(function(ctrl) {
|
||||
ctrl.panel.yAxis.logBase = 2;
|
||||
});
|
||||
|
||||
it('should draw correct Y axis', function() {
|
||||
var yTicks = getTicks(ctx.element, '.axis-y');
|
||||
expect(yTicks).to.eql(['1', '2', '4']);
|
||||
});
|
||||
});
|
||||
|
||||
heatmapScenario('when logBase is 10', function(ctx) {
|
||||
ctx.setup(function(ctrl, ctx) {
|
||||
ctrl.panel.yAxis.logBase = 10;
|
||||
|
||||
ctx.series.push(
|
||||
new TimeSeries({
|
||||
datapoints: [[10, 1422774000000], [20, 1422774060000]],
|
||||
alias: 'series3',
|
||||
})
|
||||
);
|
||||
ctx.data.heatmapStats.max = 20;
|
||||
});
|
||||
|
||||
it('should draw correct Y axis', function() {
|
||||
var yTicks = getTicks(ctx.element, '.axis-y');
|
||||
expect(yTicks).to.eql(['1', '10', '100']);
|
||||
});
|
||||
});
|
||||
|
||||
heatmapScenario('when logBase is 32', function(ctx) {
|
||||
ctx.setup(function(ctrl) {
|
||||
ctrl.panel.yAxis.logBase = 32;
|
||||
|
||||
ctx.series.push(
|
||||
new TimeSeries({
|
||||
datapoints: [[10, 1422774000000], [100, 1422774060000]],
|
||||
alias: 'series3',
|
||||
})
|
||||
);
|
||||
ctx.data.heatmapStats.max = 100;
|
||||
});
|
||||
|
||||
it('should draw correct Y axis', function() {
|
||||
var yTicks = getTicks(ctx.element, '.axis-y');
|
||||
expect(yTicks).to.eql(['1', '32', '1.0 K']);
|
||||
});
|
||||
});
|
||||
|
||||
heatmapScenario('when logBase is 1024', function(ctx) {
|
||||
ctx.setup(function(ctrl) {
|
||||
ctrl.panel.yAxis.logBase = 1024;
|
||||
|
||||
ctx.series.push(
|
||||
new TimeSeries({
|
||||
datapoints: [[2000, 1422774000000], [300000, 1422774060000]],
|
||||
alias: 'series3',
|
||||
})
|
||||
);
|
||||
ctx.data.heatmapStats.max = 300000;
|
||||
});
|
||||
|
||||
it('should draw correct Y axis', function() {
|
||||
var yTicks = getTicks(ctx.element, '.axis-y');
|
||||
expect(yTicks).to.eql(['1', '1 K', '1.0 Mil']);
|
||||
});
|
||||
});
|
||||
|
||||
heatmapScenario('when Y axis format set to "none"', function(ctx) {
|
||||
ctx.setup(function(ctrl) {
|
||||
ctrl.panel.yAxis.logBase = 1;
|
||||
ctrl.panel.yAxis.format = 'none';
|
||||
ctx.data.heatmapStats.max = 10000;
|
||||
});
|
||||
|
||||
it('should draw correct Y axis', function() {
|
||||
var yTicks = getTicks(ctx.element, '.axis-y');
|
||||
expect(yTicks).to.eql(['0', '2000', '4000', '6000', '8000', '10000', '12000']);
|
||||
});
|
||||
});
|
||||
|
||||
heatmapScenario('when Y axis format set to "second"', function(ctx) {
|
||||
ctx.setup(function(ctrl) {
|
||||
ctrl.panel.yAxis.logBase = 1;
|
||||
ctrl.panel.yAxis.format = 's';
|
||||
ctx.data.heatmapStats.max = 3600;
|
||||
});
|
||||
|
||||
it('should draw correct Y axis', function() {
|
||||
var yTicks = getTicks(ctx.element, '.axis-y');
|
||||
expect(yTicks).to.eql(['0 ns', '17 min', '33 min', '50 min', '1.11 hour']);
|
||||
});
|
||||
});
|
||||
|
||||
heatmapScenario('when data format is Time series buckets', function(ctx) {
|
||||
ctx.setup(function(ctrl, ctx) {
|
||||
ctrl.panel.dataFormat = 'tsbuckets';
|
||||
|
||||
const series = [
|
||||
{
|
||||
alias: '1',
|
||||
datapoints: [[1000, 1422774000000], [200000, 1422774060000]],
|
||||
},
|
||||
{
|
||||
alias: '2',
|
||||
datapoints: [[3000, 1422774000000], [400000, 1422774060000]],
|
||||
},
|
||||
{
|
||||
alias: '3',
|
||||
datapoints: [[2000, 1422774000000], [300000, 1422774060000]],
|
||||
},
|
||||
];
|
||||
ctx.series = series.map(s => new TimeSeries(s));
|
||||
|
||||
ctx.data.tsBuckets = series.map(s => s.alias).concat('');
|
||||
ctx.data.yBucketSize = 1;
|
||||
let xBucketBoundSet = series[0].datapoints.map(dp => dp[1]);
|
||||
ctx.data.xBucketSize = calculateBucketSize(xBucketBoundSet);
|
||||
});
|
||||
|
||||
it('should draw correct Y axis', function() {
|
||||
var yTicks = getTicks(ctx.element, '.axis-y');
|
||||
expect(yTicks).to.eql(['1', '2', '3', '']);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function getTicks(element, axisSelector) {
|
||||
return element
|
||||
.find(axisSelector)
|
||||
.find('text')
|
||||
.map(function() {
|
||||
return this.textContent;
|
||||
})
|
||||
.get();
|
||||
}
|
||||
|
||||
function formatTime(timeStr) {
|
||||
let format = 'HH:mm';
|
||||
return moment.utc(timeStr, 'DD MMM YYYY HH:mm:ss').format(format);
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
<table class="table-panel-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th ng-repeat="col in ctrl.table.columns" ng-hide="col.hidden">
|
||||
<th ng-repeat="col in ctrl.table.columns" ng-if="!col.hidden">
|
||||
<div class="table-panel-table-header-inner pointer" ng-click="ctrl.toggleColumnSort(col, $index)">
|
||||
{{col.title}}
|
||||
<span class="table-panel-table-header-controls" ng-if="col.sort">
|
||||
|
||||
@@ -238,6 +238,10 @@ export class TableRenderer {
|
||||
column.hidden = false;
|
||||
}
|
||||
|
||||
if (column.hidden === true) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (column.style && column.style.preserveFormat) {
|
||||
cellClasses.push('table-panel-cell-pre');
|
||||
}
|
||||
|
||||
@@ -34,7 +34,9 @@ export class LoadDashboardCtrl {
|
||||
const url = locationUtil.stripBaseFromUrl(result.meta.url);
|
||||
|
||||
if (url !== $location.path()) {
|
||||
// replace url to not create additional history items and then return so that initDashboard below isn't executed multiple times.
|
||||
$location.path(url).replace();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
<base href="[[.AppSubUrl]]/" />
|
||||
|
||||
<link rel="stylesheet" href="public/build/grafana.[[ .Theme ]].css?v[[ .BuildVersion ]]">
|
||||
<link rel="stylesheet" href="public/build/grafana.[[ .Theme ]].css?v[[ .BuildVersion ]]+[[ .BuildCommit ]]">
|
||||
|
||||
<link rel="icon" type="image/png" href="public/img/fav32.png">
|
||||
<link rel="mask-icon" href="public/img/grafana_mask_icon.svg" color="#F05A28">
|
||||
@@ -107,12 +107,12 @@
|
||||
<iframe src="//www.googletagmanager.com/ns.html?id=[[.GoogleTagManagerId]]" height="0" width="0" style="display:none;visibility:hidden"></iframe>
|
||||
</noscript>
|
||||
<script>(function (w, d, s, l, i) {
|
||||
w[l] = w[l] || []; w[l].push({ 'gtm.start': new Date().getTime(), event: 'gtm.js' }); var f = d.getElementsByTagName(s)[0],
|
||||
j = d.createElement(s), dl = l != 'dataLayer' ? '&l=' + l : ''; j.async = true; j.src = '//www.googletagmanager.com/gtm.js?id=' + i + dl; f.parentNode.insertBefore(j, f);
|
||||
w[l] = w[l] || []; w[l].push({ 'gtm.start': new Date().getTime(), event: 'gtm.js' }); var f = d.getElementsByTagName(s)[0],
|
||||
j = d.createElement(s), dl = l != 'dataLayer' ? '&l=' + l : ''; j.async = true; j.src = '//www.googletagmanager.com/gtm.js?id=' + i + dl; f.parentNode.insertBefore(j, f);
|
||||
})(window, document, 'script', 'dataLayer', '[[.GoogleTagManagerId]]');</script>
|
||||
<!-- End Google Tag Manager -->
|
||||
[[end]]
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</html>
|
||||
@@ -9,8 +9,6 @@ module.exports = function(grunt) {
|
||||
]);
|
||||
|
||||
grunt.registerTask('test', [
|
||||
'jscs',
|
||||
'jshint',
|
||||
'sasslint',
|
||||
'exec:tslint',
|
||||
"exec:jest",
|
||||
@@ -19,8 +17,6 @@ module.exports = function(grunt) {
|
||||
]);
|
||||
|
||||
grunt.registerTask('precommit', [
|
||||
'jscs',
|
||||
'jshint',
|
||||
'sasslint',
|
||||
'exec:tslint',
|
||||
'no-only-tests'
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
module.exports = function(config) {
|
||||
return {
|
||||
src: [
|
||||
'Gruntfile.js',
|
||||
'<%= srcDir %>/app/**/*.js',
|
||||
'<%= srcDir %>/plugin/**/*.js',
|
||||
'!<%= srcDir %>/app/dashboards/*'
|
||||
],
|
||||
options: {
|
||||
config: ".jscs.json",
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
/*
|
||||
"requireCurlyBraces": ["if", "else", "for", "while", "do", "try", "catch"],
|
||||
"requireSpaceAfterKeywords": ["if", "else", "for", "while", "do", "switch", "return", "try", "catch"],
|
||||
"disallowLeftStickedOperators": ["?", "+", "-", "/", "*", "=", "==", "===", "!=", "!==", ">", ">=", "<", "<="],
|
||||
"disallowRightStickedOperators": ["?", "+", "/", "*", ":", "=", "==", "===", "!=", "!==", ">", ">=", "<", "<="],
|
||||
"requireRightStickedOperators": ["!"],
|
||||
"requireLeftStickedOperators": [","],
|
||||
*/
|
||||
@@ -1,20 +0,0 @@
|
||||
module.exports = function(config) {
|
||||
return {
|
||||
source: {
|
||||
files: {
|
||||
src: ['Gruntfile.js', '<%= srcDir %>/app/**/*.js'],
|
||||
}
|
||||
},
|
||||
options: {
|
||||
jshintrc: true,
|
||||
reporter: require('jshint-stylish'),
|
||||
ignores: [
|
||||
'node_modules/*',
|
||||
'dist/*',
|
||||
'sample/*',
|
||||
'<%= srcDir %>/vendor/*',
|
||||
'<%= srcDir %>/app/dashboards/*'
|
||||
]
|
||||
}
|
||||
};
|
||||
};
|
||||
@@ -1,45 +0,0 @@
|
||||
module.exports = function(config) {
|
||||
return {
|
||||
// copy source to temp, we will minify in place for the dist build
|
||||
everything_but_less_to_temp: {
|
||||
cwd: '<%= srcDir %>',
|
||||
expand: true,
|
||||
src: ['**/*', '!**/*.less'],
|
||||
dest: '<%= tempDir %>'
|
||||
},
|
||||
|
||||
public_to_gen: {
|
||||
cwd: '<%= srcDir %>',
|
||||
expand: true,
|
||||
src: ['**/*', '!**/*.less'],
|
||||
dest: '<%= genDir %>'
|
||||
},
|
||||
|
||||
node_modules: {
|
||||
cwd: './node_modules',
|
||||
expand: true,
|
||||
src: [
|
||||
'ace-builds/src-noconflict/**/*',
|
||||
'eventemitter3/*.js',
|
||||
'systemjs/dist/*.js',
|
||||
'es6-promise/**/*',
|
||||
'es6-shim/*.js',
|
||||
'reflect-metadata/*.js',
|
||||
'reflect-metadata/*.ts',
|
||||
'reflect-metadata/*.d.ts',
|
||||
'rxjs/**/*',
|
||||
'tether/**/*',
|
||||
'tether-drop/**/*',
|
||||
'tether-drop/**/*',
|
||||
'remarkable/dist/*',
|
||||
'remarkable/dist/*',
|
||||
'virtual-scroll/**/*',
|
||||
'mousetrap/**/*',
|
||||
'twemoji/2/twemoji.amd*',
|
||||
'twemoji/2/svg/*.svg',
|
||||
],
|
||||
dest: '<%= srcDir %>/vendor/npm'
|
||||
}
|
||||
|
||||
};
|
||||
};
|
||||
@@ -414,10 +414,6 @@ JSONStream@^1.3.2:
|
||||
jsonparse "^1.2.0"
|
||||
through ">=2.2.7 <3"
|
||||
|
||||
JSV@^4.0.x:
|
||||
version "4.0.2"
|
||||
resolved "https://registry.yarnpkg.com/JSV/-/JSV-4.0.2.tgz#d077f6825571f82132f9dffaed587b4029feff57"
|
||||
|
||||
abab@^1.0.4:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/abab/-/abab-1.0.4.tgz#5faad9c2c07f60dd76770f71cf025b62a63cfd4e"
|
||||
@@ -869,10 +865,6 @@ async-limiter@~1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.0.tgz#78faed8c3d074ab81f22b4e985d79e8738f720f8"
|
||||
|
||||
async@0.2.x, async@~0.2.6, async@~0.2.9:
|
||||
version "0.2.10"
|
||||
resolved "https://registry.yarnpkg.com/async/-/async-0.2.10.tgz#b6bbe0b0674b9d719708ca38de8c237cb526c3d1"
|
||||
|
||||
async@^1.4.0, async@^1.5.0, async@^1.5.2, async@~1.5.2:
|
||||
version "1.5.2"
|
||||
resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a"
|
||||
@@ -883,6 +875,10 @@ async@^2.0.0, async@^2.1.4, async@^2.4.1, async@^2.6.0:
|
||||
dependencies:
|
||||
lodash "^4.17.10"
|
||||
|
||||
async@~0.2.6:
|
||||
version "0.2.10"
|
||||
resolved "https://registry.yarnpkg.com/async/-/async-0.2.10.tgz#b6bbe0b0674b9d719708ca38de8c237cb526c3d1"
|
||||
|
||||
asynckit@^0.4.0:
|
||||
version "0.4.0"
|
||||
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
|
||||
@@ -1564,7 +1560,7 @@ babel-types@^6.18.0, babel-types@^6.19.0, babel-types@^6.24.1, babel-types@^6.26
|
||||
lodash "^4.17.4"
|
||||
to-fast-properties "^1.0.3"
|
||||
|
||||
babylon@^6.17.3, babylon@^6.18.0, babylon@^6.8.1:
|
||||
babylon@^6.17.3, babylon@^6.18.0:
|
||||
version "6.18.0"
|
||||
resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.18.0.tgz#af2f3b88fa6f5c1e4c634d1a0f8eac4f55b395e3"
|
||||
|
||||
@@ -1626,10 +1622,6 @@ bcrypt-pbkdf@^1.0.0:
|
||||
dependencies:
|
||||
tweetnacl "^0.14.3"
|
||||
|
||||
beeper@^1.1.0:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/beeper/-/beeper-1.1.1.tgz#e6d5ea8c5dad001304a70b22638447f69cb2f809"
|
||||
|
||||
better-assert@~1.0.0:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/better-assert/-/better-assert-1.0.2.tgz#40866b9e1b9e0b55b481894311e68faffaebc522"
|
||||
@@ -2109,7 +2101,7 @@ center-align@^0.1.1:
|
||||
align-text "^0.1.3"
|
||||
lazy-cache "^1.0.3"
|
||||
|
||||
chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3, chalk@~1.1.0, chalk@~1.1.1:
|
||||
chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3, chalk@~1.1.1:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98"
|
||||
dependencies:
|
||||
@@ -2315,7 +2307,7 @@ cli-table2@^0.2.0, cli-table2@~0.2.0:
|
||||
optionalDependencies:
|
||||
colors "^1.1.2"
|
||||
|
||||
cli-table@^0.3.1, cli-table@~0.3.1:
|
||||
cli-table@^0.3.1:
|
||||
version "0.3.1"
|
||||
resolved "https://registry.yarnpkg.com/cli-table/-/cli-table-0.3.1.tgz#f53b05266a8b1a0b934b3d0821e6e2dc5914ae23"
|
||||
dependencies:
|
||||
@@ -2332,13 +2324,6 @@ cli-width@^2.0.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639"
|
||||
|
||||
cli@~1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/cli/-/cli-1.0.1.tgz#22817534f24bfa4950c34d532d48ecbc621b8c14"
|
||||
dependencies:
|
||||
exit "0.1.2"
|
||||
glob "^7.1.1"
|
||||
|
||||
clipboard@^1.7.1:
|
||||
version "1.7.1"
|
||||
resolved "https://registry.yarnpkg.com/clipboard/-/clipboard-1.7.1.tgz#360d6d6946e99a7a1fef395e42ba92b5e9b5a16b"
|
||||
@@ -2490,10 +2475,6 @@ colors@0.5.x:
|
||||
version "0.5.1"
|
||||
resolved "https://registry.yarnpkg.com/colors/-/colors-0.5.1.tgz#7d0023eaeb154e8ee9fce75dcb923d0ed1667774"
|
||||
|
||||
colors@0.6.x:
|
||||
version "0.6.2"
|
||||
resolved "https://registry.yarnpkg.com/colors/-/colors-0.6.2.tgz#2423fe6678ac0c5dae8852e5d0e5be08c997abcc"
|
||||
|
||||
colors@1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/colors/-/colors-1.0.3.tgz#0433f44d809680fdeb60ed260f1b0c262e82a40b"
|
||||
@@ -2539,7 +2520,7 @@ commander@2.8.x:
|
||||
dependencies:
|
||||
graceful-readlink ">= 1.0.0"
|
||||
|
||||
commander@2.9.x, commander@~2.9.0:
|
||||
commander@2.9.x:
|
||||
version "2.9.0"
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-2.9.0.tgz#9c99094176e12240cb22d6c5146098400fe0f7d4"
|
||||
dependencies:
|
||||
@@ -2549,12 +2530,6 @@ commander@~2.13.0:
|
||||
version "2.13.0"
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-2.13.0.tgz#6964bca67685df7c1f1430c584f07d7597885b9c"
|
||||
|
||||
comment-parser@^0.3.1:
|
||||
version "0.3.2"
|
||||
resolved "https://registry.yarnpkg.com/comment-parser/-/comment-parser-0.3.2.tgz#3c03f0776b86a36dfd9a0a2c97c6307f332082fe"
|
||||
dependencies:
|
||||
readable-stream "^2.0.4"
|
||||
|
||||
commondir@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b"
|
||||
@@ -2660,7 +2635,7 @@ connect@^3.6.0:
|
||||
parseurl "~1.3.2"
|
||||
utils-merge "1.0.1"
|
||||
|
||||
console-browserify@1.1.x, console-browserify@^1.1.0:
|
||||
console-browserify@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.1.0.tgz#f0241c45730a9fc6323b206dbf38edc741d0bb10"
|
||||
dependencies:
|
||||
@@ -2978,14 +2953,6 @@ csstype@^2.2.0:
|
||||
version "2.5.3"
|
||||
resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.5.3.tgz#2504152e6e1cc59b32098b7f5d6a63f16294c1f7"
|
||||
|
||||
cst@^0.4.3:
|
||||
version "0.4.10"
|
||||
resolved "https://registry.yarnpkg.com/cst/-/cst-0.4.10.tgz#9c05c825290a762f0a85c0aabb8c0fe035ae8516"
|
||||
dependencies:
|
||||
babel-runtime "^6.9.2"
|
||||
babylon "^6.8.1"
|
||||
source-map-support "^0.4.0"
|
||||
|
||||
currently-unhandled@^0.4.1:
|
||||
version "0.4.1"
|
||||
resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea"
|
||||
@@ -2996,10 +2963,6 @@ custom-event@~1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/custom-event/-/custom-event-1.0.1.tgz#5d02a46850adf1b4a317946a3928fccb5bfd0425"
|
||||
|
||||
cycle@1.0.x:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/cycle/-/cycle-1.0.3.tgz#21e80b2be8580f98b468f379430662b046c34ad2"
|
||||
|
||||
cyclist@~0.2.2:
|
||||
version "0.2.2"
|
||||
resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-0.2.2.tgz#1b33792e11e914a2fd6d6ed6447464444e5fa640"
|
||||
@@ -3324,7 +3287,7 @@ dedent@^0.7.0:
|
||||
version "0.7.0"
|
||||
resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c"
|
||||
|
||||
deep-equal@*, deep-equal@^1.0.1:
|
||||
deep-equal@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5"
|
||||
|
||||
@@ -3604,12 +3567,6 @@ domhandler@2.1:
|
||||
dependencies:
|
||||
domelementtype "1"
|
||||
|
||||
domhandler@2.3:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.3.0.tgz#2de59a0822d5027fabff6f032c2b25a2a8abe738"
|
||||
dependencies:
|
||||
domelementtype "1"
|
||||
|
||||
domhandler@^2.3.0:
|
||||
version "2.4.2"
|
||||
resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.4.2.tgz#8805097e933d65e85546f726d60f5eb88b44f803"
|
||||
@@ -3622,7 +3579,7 @@ domutils@1.1:
|
||||
dependencies:
|
||||
domelementtype "1"
|
||||
|
||||
domutils@1.5, domutils@1.5.1:
|
||||
domutils@1.5.1:
|
||||
version "1.5.1"
|
||||
resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf"
|
||||
dependencies:
|
||||
@@ -3813,10 +3770,6 @@ ent@~2.2.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/ent/-/ent-2.2.0.tgz#e964219325a21d05f44466a2f686ed6ce5f5dd1d"
|
||||
|
||||
entities@1.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/entities/-/entities-1.0.0.tgz#b2987aa3821347fcde642b24fdfc9e4fb712bf26"
|
||||
|
||||
entities@^1.1.1, entities@~1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.1.tgz#6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0"
|
||||
@@ -4181,7 +4134,7 @@ exit-hook@^1.0.0:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/exit-hook/-/exit-hook-1.1.1.tgz#f05ca233b48c05d54fff07765df8507e95c02ff8"
|
||||
|
||||
exit@0.1.2, exit@0.1.x, exit@^0.1.2, exit@~0.1.1, exit@~0.1.2:
|
||||
exit@^0.1.2, exit@~0.1.1:
|
||||
version "0.1.2"
|
||||
resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c"
|
||||
|
||||
@@ -4362,10 +4315,6 @@ extsprintf@^1.2.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f"
|
||||
|
||||
eyes@0.1.x:
|
||||
version "0.1.8"
|
||||
resolved "https://registry.yarnpkg.com/eyes/-/eyes-0.1.8.tgz#62cf120234c683785d902348a800ef3e0cc20bc0"
|
||||
|
||||
fast-deep-equal@^1.0.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz#c053477817c86b51daa853c81e059b733d023614"
|
||||
@@ -4945,9 +4894,9 @@ glob@7.1.2, glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.1.1, glob@^7.1.2, glo
|
||||
once "^1.3.0"
|
||||
path-is-absolute "^1.0.0"
|
||||
|
||||
glob@^5.0.1, glob@~5.0.0:
|
||||
version "5.0.15"
|
||||
resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1"
|
||||
glob@^6.0.4:
|
||||
version "6.0.4"
|
||||
resolved "https://registry.yarnpkg.com/glob/-/glob-6.0.4.tgz#0f08860f6a155127b2fadd4f9ce24b1aab6e4d22"
|
||||
dependencies:
|
||||
inflight "^1.0.4"
|
||||
inherits "2"
|
||||
@@ -4955,9 +4904,9 @@ glob@^5.0.1, glob@~5.0.0:
|
||||
once "^1.3.0"
|
||||
path-is-absolute "^1.0.0"
|
||||
|
||||
glob@^6.0.4:
|
||||
version "6.0.4"
|
||||
resolved "https://registry.yarnpkg.com/glob/-/glob-6.0.4.tgz#0f08860f6a155127b2fadd4f9ce24b1aab6e4d22"
|
||||
glob@~5.0.0:
|
||||
version "5.0.15"
|
||||
resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1"
|
||||
dependencies:
|
||||
inflight "^1.0.4"
|
||||
inherits "2"
|
||||
@@ -5199,27 +5148,10 @@ grunt-contrib-cssmin@~1.0.2:
|
||||
clean-css "~3.4.2"
|
||||
maxmin "^1.1.0"
|
||||
|
||||
grunt-contrib-jshint@~1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/grunt-contrib-jshint/-/grunt-contrib-jshint-1.1.0.tgz#369d909b2593c40e8be79940b21340850c7939ac"
|
||||
dependencies:
|
||||
chalk "^1.1.1"
|
||||
hooker "^0.2.3"
|
||||
jshint "~2.9.4"
|
||||
|
||||
grunt-exec@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/grunt-exec/-/grunt-exec-1.0.1.tgz#e5d53a39c5f346901305edee5c87db0f2af999c4"
|
||||
|
||||
grunt-jscs@3.0.1:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/grunt-jscs/-/grunt-jscs-3.0.1.tgz#1fae50e3e955df9e3a9d9425aec22accae008092"
|
||||
dependencies:
|
||||
hooker "~0.2.3"
|
||||
jscs "~3.0.5"
|
||||
lodash "~4.6.1"
|
||||
vow "~0.4.1"
|
||||
|
||||
grunt-karma@~2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/grunt-karma/-/grunt-karma-2.0.0.tgz#753583d115dfdc055fe57e58f96d6b3c7e612118"
|
||||
@@ -5530,7 +5462,7 @@ homedir-polyfill@^1.0.1:
|
||||
dependencies:
|
||||
parse-passwd "^1.0.0"
|
||||
|
||||
hooker@^0.2.3, hooker@~0.2.3:
|
||||
hooker@~0.2.3:
|
||||
version "0.2.3"
|
||||
resolved "https://registry.yarnpkg.com/hooker/-/hooker-0.2.3.tgz#b834f723cc4a242aa65963459df6d984c5d3d959"
|
||||
|
||||
@@ -5614,16 +5546,6 @@ html-webpack-plugin@^3.2.0:
|
||||
toposort "^1.0.0"
|
||||
util.promisify "1.0.0"
|
||||
|
||||
htmlparser2@3.8.3, htmlparser2@3.8.x:
|
||||
version "3.8.3"
|
||||
resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.8.3.tgz#996c28b191516a8be86501a7d79757e5c70c1068"
|
||||
dependencies:
|
||||
domelementtype "1"
|
||||
domhandler "2.3"
|
||||
domutils "1.5"
|
||||
entities "1.0"
|
||||
readable-stream "1.1"
|
||||
|
||||
htmlparser2@^3.9.1:
|
||||
version "3.9.2"
|
||||
resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.9.2.tgz#1bdf87acca0f3f9e53fa4fcceb0f4b4cbb00b338"
|
||||
@@ -5739,10 +5661,6 @@ husky@^0.14.3:
|
||||
normalize-path "^1.0.0"
|
||||
strip-indent "^2.0.0"
|
||||
|
||||
i@0.3.x:
|
||||
version "0.3.6"
|
||||
resolved "https://registry.yarnpkg.com/i/-/i-0.3.6.tgz#d96c92732076f072711b6b10fd7d4f65ad8ee23d"
|
||||
|
||||
iconv-lite@0.4, iconv-lite@0.4.23, iconv-lite@^0.4.17, iconv-lite@^0.4.4, iconv-lite@~0.4.13:
|
||||
version "0.4.23"
|
||||
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.23.tgz#297871f63be507adcfbfca715d0cd0eed84e9a63"
|
||||
@@ -5838,10 +5756,6 @@ inflight@^1.0.4, inflight@~1.0.6:
|
||||
once "^1.3.0"
|
||||
wrappy "1"
|
||||
|
||||
inherit@^2.2.2:
|
||||
version "2.2.6"
|
||||
resolved "https://registry.yarnpkg.com/inherit/-/inherit-2.2.6.tgz#f1614b06c8544e8128e4229c86347db73ad9788d"
|
||||
|
||||
inherits@2, inherits@2.0.3, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
|
||||
@@ -5938,10 +5852,6 @@ ipaddr.js@1.6.0:
|
||||
version "1.6.0"
|
||||
resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.6.0.tgz#e3fa357b773da619f26e95f049d055c72796f86b"
|
||||
|
||||
irregular-plurals@^1.0.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/irregular-plurals/-/irregular-plurals-1.4.0.tgz#2ca9b033651111855412f16be5d77c62a458a766"
|
||||
|
||||
is-absolute-url@^2.0.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-2.1.0.tgz#50530dfb84fcc9aa7dbe7852e83a37b93b9f2aa6"
|
||||
@@ -6348,7 +6258,7 @@ isomorphic-fetch@^2.1.1:
|
||||
node-fetch "^1.0.1"
|
||||
whatwg-fetch ">=0.10.0"
|
||||
|
||||
isstream@0.1.x, isstream@~0.1.2:
|
||||
isstream@~0.1.2:
|
||||
version "0.1.2"
|
||||
resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
|
||||
|
||||
@@ -6748,14 +6658,6 @@ js-yaml@^3.4.3, js-yaml@^3.4.6, js-yaml@^3.5.1, js-yaml@^3.5.4, js-yaml@^3.7.0,
|
||||
argparse "^1.0.7"
|
||||
esprima "^4.0.0"
|
||||
|
||||
js-yaml@~3.4.0:
|
||||
version "3.4.6"
|
||||
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.4.6.tgz#6be1b23f6249f53d293370fd4d1aaa63ce1b4eb0"
|
||||
dependencies:
|
||||
argparse "^1.0.2"
|
||||
esprima "^2.6.0"
|
||||
inherit "^2.2.2"
|
||||
|
||||
js-yaml@~3.5.2:
|
||||
version "3.5.5"
|
||||
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.5.5.tgz#0377c38017cabc7322b0d1fbcd25a491641f2fbe"
|
||||
@@ -6814,54 +6716,6 @@ jscodeshift@^0.5.0:
|
||||
temp "^0.8.1"
|
||||
write-file-atomic "^1.2.0"
|
||||
|
||||
jscs-jsdoc@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/jscs-jsdoc/-/jscs-jsdoc-2.0.0.tgz#f53ebce029aa3125bd88290ba50d64d4510a4871"
|
||||
dependencies:
|
||||
comment-parser "^0.3.1"
|
||||
jsdoctypeparser "~1.2.0"
|
||||
|
||||
jscs-preset-wikimedia@~1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/jscs-preset-wikimedia/-/jscs-preset-wikimedia-1.0.1.tgz#a6a5fa5967fd67a5d609038e1c794eaf41d4233d"
|
||||
|
||||
jscs@~3.0.5:
|
||||
version "3.0.7"
|
||||
resolved "https://registry.yarnpkg.com/jscs/-/jscs-3.0.7.tgz#7141b4dff5b86e32d0e99d764b836767c30d201a"
|
||||
dependencies:
|
||||
chalk "~1.1.0"
|
||||
cli-table "~0.3.1"
|
||||
commander "~2.9.0"
|
||||
cst "^0.4.3"
|
||||
estraverse "^4.1.0"
|
||||
exit "~0.1.2"
|
||||
glob "^5.0.1"
|
||||
htmlparser2 "3.8.3"
|
||||
js-yaml "~3.4.0"
|
||||
jscs-jsdoc "^2.0.0"
|
||||
jscs-preset-wikimedia "~1.0.0"
|
||||
jsonlint "~1.6.2"
|
||||
lodash "~3.10.0"
|
||||
minimatch "~3.0.0"
|
||||
natural-compare "~1.2.2"
|
||||
pathval "~0.1.1"
|
||||
prompt "~0.2.14"
|
||||
reserved-words "^0.1.1"
|
||||
resolve "^1.1.6"
|
||||
strip-bom "^2.0.0"
|
||||
strip-json-comments "~1.0.2"
|
||||
to-double-quotes "^2.0.0"
|
||||
to-single-quotes "^2.0.0"
|
||||
vow "~0.4.8"
|
||||
vow-fs "~0.3.4"
|
||||
xmlbuilder "^3.1.0"
|
||||
|
||||
jsdoctypeparser@~1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/jsdoctypeparser/-/jsdoctypeparser-1.2.0.tgz#e7dedc153a11849ffc5141144ae86a7ef0c25392"
|
||||
dependencies:
|
||||
lodash "^3.7.0"
|
||||
|
||||
jsdom@^11.5.1:
|
||||
version "11.11.0"
|
||||
resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-11.11.0.tgz#df486efad41aee96c59ad7a190e2449c7eb1110e"
|
||||
@@ -6901,30 +6755,6 @@ jsesc@^1.3.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-1.3.0.tgz#46c3fec8c1892b12b0833db9bc7622176dbab34b"
|
||||
|
||||
jshint-stylish@~2.2.1:
|
||||
version "2.2.1"
|
||||
resolved "https://registry.yarnpkg.com/jshint-stylish/-/jshint-stylish-2.2.1.tgz#242082a2c035ae03fd81044e0570cc4208cf6e61"
|
||||
dependencies:
|
||||
beeper "^1.1.0"
|
||||
chalk "^1.0.0"
|
||||
log-symbols "^1.0.0"
|
||||
plur "^2.1.0"
|
||||
string-length "^1.0.0"
|
||||
text-table "^0.2.0"
|
||||
|
||||
jshint@~2.9.4:
|
||||
version "2.9.5"
|
||||
resolved "https://registry.yarnpkg.com/jshint/-/jshint-2.9.5.tgz#1e7252915ce681b40827ee14248c46d34e9aa62c"
|
||||
dependencies:
|
||||
cli "~1.0.0"
|
||||
console-browserify "1.1.x"
|
||||
exit "0.1.x"
|
||||
htmlparser2 "3.8.x"
|
||||
lodash "3.7.x"
|
||||
minimatch "~3.0.2"
|
||||
shelljs "0.3.x"
|
||||
strip-json-comments "1.0.x"
|
||||
|
||||
json-buffer@3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898"
|
||||
@@ -6981,13 +6811,6 @@ jsonify@~0.0.0:
|
||||
version "0.0.0"
|
||||
resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73"
|
||||
|
||||
jsonlint@~1.6.2:
|
||||
version "1.6.3"
|
||||
resolved "https://registry.yarnpkg.com/jsonlint/-/jsonlint-1.6.3.tgz#cb5e31efc0b78291d0d862fbef05900adf212988"
|
||||
dependencies:
|
||||
JSV "^4.0.x"
|
||||
nomnom "^1.5.x"
|
||||
|
||||
jsonparse@^1.2.0:
|
||||
version "1.3.1"
|
||||
resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280"
|
||||
@@ -7497,11 +7320,7 @@ lodash.without@~4.4.0:
|
||||
version "4.4.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash.without/-/lodash.without-4.4.0.tgz#3cd4574a00b67bae373a94b748772640507b7aac"
|
||||
|
||||
lodash@3.7.x:
|
||||
version "3.7.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.7.0.tgz#3678bd8ab995057c07ade836ed2ef087da811d45"
|
||||
|
||||
lodash@^3.10.1, lodash@^3.5.0, lodash@^3.6.0, lodash@^3.7.0, lodash@^3.8.0, lodash@~3.10.0:
|
||||
lodash@^3.10.1, lodash@^3.6.0, lodash@^3.8.0:
|
||||
version "3.10.1"
|
||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6"
|
||||
|
||||
@@ -7513,11 +7332,7 @@ lodash@~4.3.0:
|
||||
version "4.3.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.3.0.tgz#efd9c4a6ec53f3b05412429915c3e4824e4d25a4"
|
||||
|
||||
lodash@~4.6.1:
|
||||
version "4.6.1"
|
||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.6.1.tgz#df00c1164ad236b183cfc3887a5e8d38cc63cbbc"
|
||||
|
||||
log-symbols@^1.0.0, log-symbols@^1.0.2:
|
||||
log-symbols@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-1.0.2.tgz#376ff7b58ea3086a0f09facc74617eca501e1a18"
|
||||
dependencies:
|
||||
@@ -7990,7 +7805,7 @@ mixin-object@^2.0.1:
|
||||
for-in "^0.1.3"
|
||||
is-extendable "^0.1.1"
|
||||
|
||||
mkdirp@0.5.1, mkdirp@0.5.x, mkdirp@0.x.x, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkdirp@~0.5.1:
|
||||
mkdirp@0.5.1, mkdirp@0.5.x, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkdirp@~0.5.1:
|
||||
version "0.5.1"
|
||||
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
|
||||
dependencies:
|
||||
@@ -8121,20 +7936,12 @@ natural-compare@^1.4.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
|
||||
|
||||
natural-compare@~1.2.2:
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.2.2.tgz#1f96d60e3141cac1b6d05653ce0daeac763af6aa"
|
||||
|
||||
ncname@1.0.x:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/ncname/-/ncname-1.0.0.tgz#5b57ad18b1ca092864ef62b0b1ed8194f383b71c"
|
||||
dependencies:
|
||||
xml-char-classes "^1.0.0"
|
||||
|
||||
ncp@0.4.x:
|
||||
version "0.4.2"
|
||||
resolved "https://registry.yarnpkg.com/ncp/-/ncp-0.4.2.tgz#abcc6cbd3ec2ed2a729ff6e7c1fa8f01784a8574"
|
||||
|
||||
nearley@^2.7.10:
|
||||
version "2.13.0"
|
||||
resolved "https://registry.yarnpkg.com/nearley/-/nearley-2.13.0.tgz#6e7b0f4e68bfc3e74c99eaef2eda39e513143439"
|
||||
@@ -8359,7 +8166,7 @@ node-sass@^4.7.2:
|
||||
stdout-stream "^1.4.0"
|
||||
"true-case-path" "^1.0.2"
|
||||
|
||||
nomnom@^1.5.x, nomnom@^1.8.1:
|
||||
nomnom@^1.8.1:
|
||||
version "1.8.1"
|
||||
resolved "https://registry.yarnpkg.com/nomnom/-/nomnom-1.8.1.tgz#2151f722472ba79e50a76fc125bb8c8f2e4dc2a7"
|
||||
dependencies:
|
||||
@@ -9207,10 +9014,6 @@ path-type@^3.0.0:
|
||||
dependencies:
|
||||
pify "^3.0.0"
|
||||
|
||||
pathval@~0.1.1:
|
||||
version "0.1.1"
|
||||
resolved "https://registry.yarnpkg.com/pathval/-/pathval-0.1.1.tgz#08f911cdca9cce5942880da7817bc0b723b66d82"
|
||||
|
||||
pbkdf2@^3.0.3:
|
||||
version "3.0.16"
|
||||
resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.16.tgz#7404208ec6b01b62d85bf83853a8064f8d9c2a5c"
|
||||
@@ -9273,20 +9076,6 @@ pkg-up@^1.0.0:
|
||||
dependencies:
|
||||
find-up "^1.0.0"
|
||||
|
||||
pkginfo@0.3.x:
|
||||
version "0.3.1"
|
||||
resolved "https://registry.yarnpkg.com/pkginfo/-/pkginfo-0.3.1.tgz#5b29f6a81f70717142e09e765bbeab97b4f81e21"
|
||||
|
||||
pkginfo@0.x.x:
|
||||
version "0.4.1"
|
||||
resolved "https://registry.yarnpkg.com/pkginfo/-/pkginfo-0.4.1.tgz#b5418ef0439de5425fc4995042dced14fb2a84ff"
|
||||
|
||||
plur@^2.1.0:
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/plur/-/plur-2.1.2.tgz#7482452c1a0f508e3e344eaec312c91c29dc655a"
|
||||
dependencies:
|
||||
irregular-plurals "^1.0.0"
|
||||
|
||||
pluralize@^1.2.1:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-1.2.1.tgz#d1a21483fd22bb41e58a12fa3421823140897c45"
|
||||
@@ -9810,16 +9599,6 @@ promise@^7.1.1:
|
||||
dependencies:
|
||||
asap "~2.0.3"
|
||||
|
||||
prompt@~0.2.14:
|
||||
version "0.2.14"
|
||||
resolved "https://registry.yarnpkg.com/prompt/-/prompt-0.2.14.tgz#57754f64f543fd7b0845707c818ece618f05ffdc"
|
||||
dependencies:
|
||||
pkginfo "0.x.x"
|
||||
read "1.0.x"
|
||||
revalidator "0.1.x"
|
||||
utile "0.2.x"
|
||||
winston "0.8.x"
|
||||
|
||||
promzard@^0.3.0:
|
||||
version "0.3.0"
|
||||
resolved "https://registry.yarnpkg.com/promzard/-/promzard-0.3.0.tgz#26a5d6ee8c7dee4cb12208305acfb93ba382a9ee"
|
||||
@@ -10304,7 +10083,7 @@ read-pkg@^3.0.0:
|
||||
normalize-package-data "^2.3.2"
|
||||
path-type "^3.0.0"
|
||||
|
||||
read@1, read@1.0.x, read@~1.0.1, read@~1.0.7:
|
||||
read@1, read@~1.0.1, read@~1.0.7:
|
||||
version "1.0.7"
|
||||
resolved "https://registry.yarnpkg.com/read/-/read-1.0.7.tgz#b3da19bd052431a97671d44a42634adf710b40c4"
|
||||
dependencies:
|
||||
@@ -10331,15 +10110,6 @@ readable-stream@1.0, readable-stream@~1.0.2:
|
||||
isarray "0.0.1"
|
||||
string_decoder "~0.10.x"
|
||||
|
||||
readable-stream@1.1:
|
||||
version "1.1.13"
|
||||
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.13.tgz#f6eef764f514c89e2b9e23146a75ba106756d23e"
|
||||
dependencies:
|
||||
core-util-is "~1.0.0"
|
||||
inherits "~2.0.1"
|
||||
isarray "0.0.1"
|
||||
string_decoder "~0.10.x"
|
||||
|
||||
readable-stream@~1.1.10:
|
||||
version "1.1.14"
|
||||
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9"
|
||||
@@ -10660,10 +10430,6 @@ requires-port@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
|
||||
|
||||
reserved-words@^0.1.1:
|
||||
version "0.1.2"
|
||||
resolved "https://registry.yarnpkg.com/reserved-words/-/reserved-words-0.1.2.tgz#00a0940f98cd501aeaaac316411d9adc52b31ab1"
|
||||
|
||||
resolve-cwd@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a"
|
||||
@@ -10754,17 +10520,13 @@ retry@^0.12.0:
|
||||
version "0.12.0"
|
||||
resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b"
|
||||
|
||||
revalidator@0.1.x:
|
||||
version "0.1.8"
|
||||
resolved "https://registry.yarnpkg.com/revalidator/-/revalidator-0.1.8.tgz#fece61bfa0c1b52a206bd6b18198184bdd523a3b"
|
||||
|
||||
right-align@^0.1.1:
|
||||
version "0.1.3"
|
||||
resolved "https://registry.yarnpkg.com/right-align/-/right-align-0.1.3.tgz#61339b722fe6a3515689210d24e14c96148613ef"
|
||||
dependencies:
|
||||
align-text "^0.1.1"
|
||||
|
||||
rimraf@2, rimraf@2.x.x, rimraf@^2.2.8, rimraf@^2.4.4, rimraf@^2.5.1, rimraf@^2.5.2, rimraf@^2.5.4, rimraf@^2.6.0, rimraf@^2.6.1, rimraf@^2.6.2, rimraf@~2.6.2:
|
||||
rimraf@2, rimraf@^2.2.8, rimraf@^2.4.4, rimraf@^2.5.1, rimraf@^2.5.2, rimraf@^2.5.4, rimraf@^2.6.0, rimraf@^2.6.1, rimraf@^2.6.2, rimraf@~2.6.2:
|
||||
version "2.6.2"
|
||||
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.2.tgz#2ed8150d24a16ea8651e6d6ef0f47c4158ce7a36"
|
||||
dependencies:
|
||||
@@ -11116,10 +10878,6 @@ shell-quote@^1.6.1:
|
||||
array-reduce "~0.0.0"
|
||||
jsonify "~0.0.0"
|
||||
|
||||
shelljs@0.3.x:
|
||||
version "0.3.0"
|
||||
resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.3.0.tgz#3596e6307a781544f591f37da618360f31db57b1"
|
||||
|
||||
shelljs@^0.6.0:
|
||||
version "0.6.1"
|
||||
resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.6.1.tgz#ec6211bed1920442088fe0f70b2837232ed2c8a8"
|
||||
@@ -11432,7 +11190,7 @@ source-map-resolve@^0.5.0:
|
||||
source-map-url "^0.4.0"
|
||||
urix "^0.1.0"
|
||||
|
||||
source-map-support@^0.4.0, source-map-support@^0.4.15:
|
||||
source-map-support@^0.4.15:
|
||||
version "0.4.18"
|
||||
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.18.tgz#0286a6de8be42641338594e97ccea75f0a2c585f"
|
||||
dependencies:
|
||||
@@ -11555,10 +11313,6 @@ stack-parser@^0.0.1:
|
||||
version "0.0.1"
|
||||
resolved "https://registry.yarnpkg.com/stack-parser/-/stack-parser-0.0.1.tgz#7d3b63a17887e9e2c2bf55dbd3318fe34a39d1e7"
|
||||
|
||||
stack-trace@0.0.x:
|
||||
version "0.0.10"
|
||||
resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0"
|
||||
|
||||
stack-utils@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-1.0.1.tgz#d4f33ab54e8e38778b0ca5cfd3b3afb12db68620"
|
||||
@@ -11649,12 +11403,6 @@ strict-uri-encode@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546"
|
||||
|
||||
string-length@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/string-length/-/string-length-1.0.1.tgz#56970fb1c38558e9e70b728bf3de269ac45adfac"
|
||||
dependencies:
|
||||
strip-ansi "^3.0.0"
|
||||
|
||||
string-length@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/string-length/-/string-length-2.0.0.tgz#d40dbb686a3ace960c1cffca562bf2c45f8363ed"
|
||||
@@ -11766,7 +11514,7 @@ strip-indent@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-2.0.0.tgz#5ef8db295d01e6ed6cbf7aab96998d7822527b68"
|
||||
|
||||
strip-json-comments@1.0.x, strip-json-comments@~1.0.1, strip-json-comments@~1.0.2:
|
||||
strip-json-comments@~1.0.1:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-1.0.4.tgz#1e15fbcac97d3ee99bf2d73b4c656b082bbafb91"
|
||||
|
||||
@@ -12029,10 +11777,6 @@ to-buffer@^1.1.0:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/to-buffer/-/to-buffer-1.1.1.tgz#493bd48f62d7c43fcded313a03dcadb2e1213a80"
|
||||
|
||||
to-double-quotes@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/to-double-quotes/-/to-double-quotes-2.0.0.tgz#aaf231d6fa948949f819301bbab4484d8588e4a7"
|
||||
|
||||
to-fast-properties@^1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47"
|
||||
@@ -12059,10 +11803,6 @@ to-regex@^3.0.1, to-regex@^3.0.2:
|
||||
regex-not "^1.0.2"
|
||||
safe-regex "^1.1.0"
|
||||
|
||||
to-single-quotes@^2.0.0:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/to-single-quotes/-/to-single-quotes-2.0.1.tgz#7cc29151f0f5f2c41946f119f5932fe554170125"
|
||||
|
||||
toposort@^1.0.0:
|
||||
version "1.0.7"
|
||||
resolved "https://registry.yarnpkg.com/toposort/-/toposort-1.0.7.tgz#2e68442d9f64ec720b8cc89e6443ac6caa950029"
|
||||
@@ -12527,25 +12267,10 @@ utila@~0.4:
|
||||
version "0.4.0"
|
||||
resolved "https://registry.yarnpkg.com/utila/-/utila-0.4.0.tgz#8a16a05d445657a3aea5eecc5b12a4fa5379772c"
|
||||
|
||||
utile@0.2.x:
|
||||
version "0.2.1"
|
||||
resolved "https://registry.yarnpkg.com/utile/-/utile-0.2.1.tgz#930c88e99098d6220834c356cbd9a770522d90d7"
|
||||
dependencies:
|
||||
async "~0.2.9"
|
||||
deep-equal "*"
|
||||
i "0.3.x"
|
||||
mkdirp "0.x.x"
|
||||
ncp "0.4.x"
|
||||
rimraf "2.x.x"
|
||||
|
||||
utils-merge@1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
|
||||
|
||||
uuid@^2.0.2:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-2.0.3.tgz#67e2e863797215530dff318e5bf9dcebfd47b21a"
|
||||
|
||||
uuid@^3.0.0, uuid@^3.0.1, uuid@^3.1.0, uuid@^3.2.1:
|
||||
version "3.2.1"
|
||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.2.1.tgz#12c528bb9d58d0b9265d9a2f6f0fe8be17ff1f14"
|
||||
@@ -12623,25 +12348,6 @@ void-elements@^2.0.0:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-2.0.1.tgz#c066afb582bb1cb4128d60ea92392e94d5e9dbec"
|
||||
|
||||
vow-fs@~0.3.4:
|
||||
version "0.3.6"
|
||||
resolved "https://registry.yarnpkg.com/vow-fs/-/vow-fs-0.3.6.tgz#2d4c59be22e2bf2618ddf597ab4baa923be7200d"
|
||||
dependencies:
|
||||
glob "^7.0.5"
|
||||
uuid "^2.0.2"
|
||||
vow "^0.4.7"
|
||||
vow-queue "^0.4.1"
|
||||
|
||||
vow-queue@^0.4.1:
|
||||
version "0.4.3"
|
||||
resolved "https://registry.yarnpkg.com/vow-queue/-/vow-queue-0.4.3.tgz#4ba8f64b56e9212c0dbe57f1405aeebd54cce78d"
|
||||
dependencies:
|
||||
vow "^0.4.17"
|
||||
|
||||
vow@^0.4.17, vow@^0.4.7, vow@~0.4.1, vow@~0.4.8:
|
||||
version "0.4.17"
|
||||
resolved "https://registry.yarnpkg.com/vow/-/vow-0.4.17.tgz#b16e08fae58c52f3ebc6875f2441b26a92682904"
|
||||
|
||||
vue-parser@^1.1.5:
|
||||
version "1.1.6"
|
||||
resolved "https://registry.yarnpkg.com/vue-parser/-/vue-parser-1.1.6.tgz#3063c8431795664ebe429c23b5506899706e6355"
|
||||
@@ -12960,18 +12666,6 @@ window-size@0.1.0:
|
||||
version "0.1.0"
|
||||
resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d"
|
||||
|
||||
winston@0.8.x:
|
||||
version "0.8.3"
|
||||
resolved "https://registry.yarnpkg.com/winston/-/winston-0.8.3.tgz#64b6abf4cd01adcaefd5009393b1d8e8bec19db0"
|
||||
dependencies:
|
||||
async "0.2.x"
|
||||
colors "0.6.x"
|
||||
cycle "1.0.x"
|
||||
eyes "0.1.x"
|
||||
isstream "0.1.x"
|
||||
pkginfo "0.3.x"
|
||||
stack-trace "0.0.x"
|
||||
|
||||
wordwrap@0.0.2:
|
||||
version "0.0.2"
|
||||
resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f"
|
||||
@@ -13053,12 +12747,6 @@ xml-name-validator@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a"
|
||||
|
||||
xmlbuilder@^3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-3.1.0.tgz#2c86888f2d4eade850fa38ca7f7223f7209516e1"
|
||||
dependencies:
|
||||
lodash "^3.5.0"
|
||||
|
||||
xmlhttprequest-ssl@1.5.3:
|
||||
version "1.5.3"
|
||||
resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.3.tgz#185a888c04eca46c3e4070d99f7b49de3528992d"
|
||||
|
||||
Reference in New Issue
Block a user