diff --git a/public/app/core/directives/plugin_component.ts b/public/app/core/directives/plugin_component.ts
index 3b18ac62acf..d4722c6d2f5 100644
--- a/public/app/core/directives/plugin_component.ts
+++ b/public/app/core/directives/plugin_component.ts
@@ -37,7 +37,7 @@ function pluginDirectiveLoader($compile, datasourceSrv) {
name: 'metrics-query-editor-' + ds.meta.id,
bindings: {target: "=", panelCtrl: "=", datasource: "="},
attrs: {"target": "target", "panel-ctrl": "ctrl", datasource: "datasource"},
- Component: dsModule.MetricsQueryEditor
+ Component: dsModule.QueryCtrl
};
});
});
diff --git a/public/app/features/dashboard/dashboardSrv.js b/public/app/features/dashboard/dashboardSrv.js
index ad45ce64a70..2f64fec9be1 100644
--- a/public/app/features/dashboard/dashboardSrv.js
+++ b/public/app/features/dashboard/dashboardSrv.js
@@ -194,6 +194,16 @@ function (angular, $, _, moment) {
moment.utc(date).fromNow();
};
+ p.getNextQueryLetter = function(panel) {
+ var letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
+
+ return _.find(letters, function(refId) {
+ return _.every(panel.targets, function(other) {
+ return other.refId !== refId;
+ });
+ });
+ };
+
p._updateSchema = function(old) {
var i, j, k;
var oldVersion = this.schemaVersion;
diff --git a/public/app/features/panel/all.js b/public/app/features/panel/all.js
index c03089bbf9b..96a119c8b48 100644
--- a/public/app/features/panel/all.js
+++ b/public/app/features/panel/all.js
@@ -3,6 +3,6 @@ define([
'./panel_directive',
'./solo_panel_ctrl',
'./panel_loader',
- './query_editor',
+ './query_ctrl',
'./panel_editor_tab',
], function () {});
diff --git a/public/app/features/panel/panel.ts b/public/app/features/panel/panel.ts
index 3f98b77ff1b..634591801b6 100644
--- a/public/app/features/panel/panel.ts
+++ b/public/app/features/panel/panel.ts
@@ -5,11 +5,11 @@ import config from 'app/core/config';
import {PanelCtrl} from './panel_ctrl';
import {MetricsPanelCtrl} from './metrics_panel_ctrl';
import {PanelDirective} from './panel_directive';
-import {QueryEditorCtrl} from './query_editor';
+import {QueryCtrl} from './query_ctrl';
export {
PanelCtrl,
MetricsPanelCtrl,
PanelDirective,
- QueryEditorCtrl,
+ QueryCtrl,
}
diff --git a/public/app/features/panel/query_editor.ts b/public/app/features/panel/query_ctrl.ts
similarity index 86%
rename from public/app/features/panel/query_editor.ts
rename to public/app/features/panel/query_ctrl.ts
index a186266ecaf..5227febed2f 100644
--- a/public/app/features/panel/query_editor.ts
+++ b/public/app/features/panel/query_ctrl.ts
@@ -3,7 +3,7 @@
import angular from 'angular';
import _ from 'lodash';
-export class QueryEditorCtrl {
+export class QueryCtrl {
target: any;
datasource: any;
panelCtrl: any;
@@ -27,22 +27,26 @@ export class QueryEditorCtrl {
});
}
- removeDataQuery(query) {
- this.panel.targets = _.without(this.panel.targets, query);
+ removeQuery() {
+ this.panel.targets = _.without(this.panel.targets, this.target);
this.panelCtrl.refresh();
};
- duplicateDataQuery(query) {
- var clone = angular.copy(query);
+ duplicateQuery() {
+ var clone = angular.copy(this.target);
clone.refId = this.getNextQueryLetter();
this.panel.targets.push(clone);
}
- moveDataQuery(direction) {
+ moveQuery(direction) {
var index = _.indexOf(this.panel.targets, this.target);
_.move(this.panel.targets, index, index + direction);
}
+ refresh() {
+ this.panelCtrl.refresh();
+ }
+
toggleHideQuery() {
this.target.hide = !this.target.hide;
this.panelCtrl.refresh();
diff --git a/public/app/plugins/datasource/graphite/datasource.d.ts b/public/app/plugins/datasource/graphite/datasource.d.ts
deleted file mode 100644
index a50d7ca49cc..00000000000
--- a/public/app/plugins/datasource/graphite/datasource.d.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-declare var Datasource: any;
-export default Datasource;
-
diff --git a/public/app/plugins/datasource/graphite/datasource.js b/public/app/plugins/datasource/graphite/datasource.js
deleted file mode 100644
index 3a169eaae4e..00000000000
--- a/public/app/plugins/datasource/graphite/datasource.js
+++ /dev/null
@@ -1,296 +0,0 @@
-define([
- 'angular',
- 'lodash',
- 'jquery',
- 'app/core/config',
- 'app/core/utils/datemath',
- './query_ctrl',
- './func_editor',
- './add_graphite_func',
-],
-function (angular, _, $, config, dateMath) {
- 'use strict';
-
- /** @ngInject */
- function GraphiteDatasource(instanceSettings, $q, backendSrv, templateSrv) {
- this.basicAuth = instanceSettings.basicAuth;
- this.url = instanceSettings.url;
- this.name = instanceSettings.name;
- this.cacheTimeout = instanceSettings.cacheTimeout;
- this.withCredentials = instanceSettings.withCredentials;
- this.render_method = instanceSettings.render_method || 'POST';
-
- this.query = function(options) {
- try {
- var graphOptions = {
- from: this.translateTime(options.rangeRaw.from, false),
- until: this.translateTime(options.rangeRaw.to, true),
- targets: options.targets,
- format: options.format,
- cacheTimeout: options.cacheTimeout || this.cacheTimeout,
- maxDataPoints: options.maxDataPoints,
- };
-
- var params = this.buildGraphiteParams(graphOptions, options.scopedVars);
- if (params.length === 0) {
- return $q.when([]);
- }
-
- if (options.format === 'png') {
- return $q.when(this.url + '/render' + '?' + params.join('&'));
- }
-
- var httpOptions = { method: this.render_method, url: '/render' };
-
- if (httpOptions.method === 'GET') {
- httpOptions.url = httpOptions.url + '?' + params.join('&');
- }
- else {
- httpOptions.data = params.join('&');
- httpOptions.headers = { 'Content-Type': 'application/x-www-form-urlencoded' };
- }
-
- return this.doGraphiteRequest(httpOptions).then(this.convertDataPointsToMs);
- }
- catch(err) {
- return $q.reject(err);
- }
- };
-
- this.convertDataPointsToMs = function(result) {
- if (!result || !result.data) { return []; }
- for (var i = 0; i < result.data.length; i++) {
- var series = result.data[i];
- for (var y = 0; y < series.datapoints.length; y++) {
- series.datapoints[y][1] *= 1000;
- }
- }
- return result;
- };
-
- this.annotationQuery = function(options) {
- // Graphite metric as annotation
- if (options.annotation.target) {
- var target = templateSrv.replace(options.annotation.target);
- var graphiteQuery = {
- rangeRaw: options.rangeRaw,
- targets: [{ target: target }],
- format: 'json',
- maxDataPoints: 100
- };
-
- return this.query(graphiteQuery)
- .then(function(result) {
- var list = [];
-
- for (var i = 0; i < result.data.length; i++) {
- var target = result.data[i];
-
- for (var y = 0; y < target.datapoints.length; y++) {
- var datapoint = target.datapoints[y];
- if (!datapoint[0]) { continue; }
-
- list.push({
- annotation: options.annotation,
- time: datapoint[1],
- title: target.target
- });
- }
- }
-
- return list;
- });
- }
- // Graphite event as annotation
- else {
- var tags = templateSrv.replace(options.annotation.tags);
- return this.events({range: options.rangeRaw, tags: tags}).then(function(results) {
- var list = [];
- for (var i = 0; i < results.data.length; i++) {
- var e = results.data[i];
-
- list.push({
- annotation: options.annotation,
- time: e.when * 1000,
- title: e.what,
- tags: e.tags,
- text: e.data
- });
- }
- return list;
- });
- }
- };
-
- this.events = function(options) {
- try {
- var tags = '';
- if (options.tags) {
- tags = '&tags=' + options.tags;
- }
-
- return this.doGraphiteRequest({
- method: 'GET',
- url: '/events/get_data?from=' + this.translateTime(options.range.from, false) +
- '&until=' + this.translateTime(options.range.to, true) + tags,
- });
- }
- catch(err) {
- return $q.reject(err);
- }
- };
-
- this.translateTime = function(date, roundUp) {
- if (_.isString(date)) {
- if (date === 'now') {
- return 'now';
- }
- else if (date.indexOf('now-') >= 0 && date.indexOf('/') === -1) {
- date = date.substring(3);
- date = date.replace('m', 'min');
- date = date.replace('M', 'mon');
- return date;
- }
- date = dateMath.parse(date, roundUp);
- }
-
- // graphite' s from filter is exclusive
- // here we step back one minute in order
- // to guarantee that we get all the data that
- // exists for the specified range
- if (roundUp) {
- if (date.get('s')) {
- date.add(1, 'm');
- }
- }
- else if (roundUp === false) {
- if (date.get('s')) {
- date.subtract(1, 'm');
- }
- }
-
- return date.unix();
- };
-
- this.metricFindQuery = function(query) {
- var interpolated;
- try {
- interpolated = encodeURIComponent(templateSrv.replace(query));
- }
- catch(err) {
- return $q.reject(err);
- }
-
- return this.doGraphiteRequest({method: 'GET', url: '/metrics/find/?query=' + interpolated })
- .then(function(results) {
- return _.map(results.data, function(metric) {
- return {
- text: metric.text,
- expandable: metric.expandable ? true : false
- };
- });
- });
- };
-
- this.testDatasource = function() {
- return this.metricFindQuery('*').then(function () {
- return { status: "success", message: "Data source is working", title: "Success" };
- });
- };
-
- this.listDashboards = function(query) {
- return this.doGraphiteRequest({ method: 'GET', url: '/dashboard/find/', params: {query: query || ''} })
- .then(function(results) {
- return results.data.dashboards;
- });
- };
-
- this.loadDashboard = function(dashName) {
- return this.doGraphiteRequest({method: 'GET', url: '/dashboard/load/' + encodeURIComponent(dashName) });
- };
-
- this.doGraphiteRequest = function(options) {
- if (this.basicAuth || this.withCredentials) {
- options.withCredentials = true;
- }
- if (this.basicAuth) {
- options.headers = options.headers || {};
- options.headers.Authorization = this.basicAuth;
- }
-
- options.url = this.url + options.url;
- options.inspect = { type: 'graphite' };
-
- return backendSrv.datasourceRequest(options);
- };
-
- this._seriesRefLetters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
-
- this.buildGraphiteParams = function(options, scopedVars) {
- var graphite_options = ['from', 'until', 'rawData', 'format', 'maxDataPoints', 'cacheTimeout'];
- var clean_options = [], targets = {};
- var target, targetValue, i;
- var regex = /\#([A-Z])/g;
- var intervalFormatFixRegex = /'(\d+)m'/gi;
- var hasTargets = false;
-
- if (options.format !== 'png') {
- options['format'] = 'json';
- }
-
- function fixIntervalFormat(match) {
- return match.replace('m', 'min').replace('M', 'mon');
- }
-
- for (i = 0; i < options.targets.length; i++) {
- target = options.targets[i];
- if (!target.target) {
- continue;
- }
-
- if (!target.refId) {
- target.refId = this._seriesRefLetters[i];
- }
-
- targetValue = templateSrv.replace(target.target, scopedVars);
- targetValue = targetValue.replace(intervalFormatFixRegex, fixIntervalFormat);
- targets[target.refId] = targetValue;
- }
-
- function nestedSeriesRegexReplacer(match, g1) {
- return targets[g1];
- }
-
- for (i = 0; i < options.targets.length; i++) {
- target = options.targets[i];
- if (!target.target) {
- continue;
- }
-
- targetValue = targets[target.refId];
- targetValue = targetValue.replace(regex, nestedSeriesRegexReplacer);
- targets[target.refId] = targetValue;
-
- if (!target.hide) {
- hasTargets = true;
- clean_options.push("target=" + encodeURIComponent(targetValue));
- }
- }
-
- _.each(options, function (value, key) {
- if ($.inArray(key, graphite_options) === -1) { return; }
- if (value) {
- clean_options.push(key + "=" + encodeURIComponent(value));
- }
- });
-
- if (!hasTargets) {
- return [];
- }
-
- return clean_options;
- };
- }
-
- return GraphiteDatasource;
-});
diff --git a/public/app/plugins/datasource/graphite/datasource.ts b/public/app/plugins/datasource/graphite/datasource.ts
new file mode 100644
index 00000000000..c57e5fcfd26
--- /dev/null
+++ b/public/app/plugins/datasource/graphite/datasource.ts
@@ -0,0 +1,281 @@
+///
+
+import angular from 'angular';
+import _ from 'lodash';
+import moment from 'moment';
+
+import * as dateMath from 'app/core/utils/datemath';
+
+/** @ngInject */
+export function GraphiteDatasource(instanceSettings, $q, backendSrv, templateSrv) {
+ this.basicAuth = instanceSettings.basicAuth;
+ this.url = instanceSettings.url;
+ this.name = instanceSettings.name;
+ this.cacheTimeout = instanceSettings.cacheTimeout;
+ this.withCredentials = instanceSettings.withCredentials;
+ this.render_method = instanceSettings.render_method || 'POST';
+
+ this.query = function(options) {
+ try {
+ var graphOptions = {
+ from: this.translateTime(options.rangeRaw.from, false),
+ until: this.translateTime(options.rangeRaw.to, true),
+ targets: options.targets,
+ format: options.format,
+ cacheTimeout: options.cacheTimeout || this.cacheTimeout,
+ maxDataPoints: options.maxDataPoints,
+ };
+
+ var params = this.buildGraphiteParams(graphOptions, options.scopedVars);
+ if (params.length === 0) {
+ return $q.when([]);
+ }
+
+ if (options.format === 'png') {
+ return $q.when(this.url + '/render' + '?' + params.join('&'));
+ }
+
+ var httpOptions: any = {method: this.render_method, url: '/render'};
+
+ if (httpOptions.method === 'GET') {
+ httpOptions.url = httpOptions.url + '?' + params.join('&');
+ } else {
+ httpOptions.data = params.join('&');
+ httpOptions.headers = { 'Content-Type': 'application/x-www-form-urlencoded' };
+ }
+
+ return this.doGraphiteRequest(httpOptions).then(this.convertDataPointsToMs);
+ } catch (err) {
+ return $q.reject(err);
+ }
+ };
+
+ this.convertDataPointsToMs = function(result) {
+ if (!result || !result.data) { return []; }
+ for (var i = 0; i < result.data.length; i++) {
+ var series = result.data[i];
+ for (var y = 0; y < series.datapoints.length; y++) {
+ series.datapoints[y][1] *= 1000;
+ }
+ }
+ return result;
+ };
+
+ this.annotationQuery = function(options) {
+ // Graphite metric as annotation
+ if (options.annotation.target) {
+ var target = templateSrv.replace(options.annotation.target);
+ var graphiteQuery = {
+ rangeRaw: options.rangeRaw,
+ targets: [{ target: target }],
+ format: 'json',
+ maxDataPoints: 100
+ };
+
+ return this.query(graphiteQuery)
+ .then(function(result) {
+ var list = [];
+
+ for (var i = 0; i < result.data.length; i++) {
+ var target = result.data[i];
+
+ for (var y = 0; y < target.datapoints.length; y++) {
+ var datapoint = target.datapoints[y];
+ if (!datapoint[0]) { continue; }
+
+ list.push({
+ annotation: options.annotation,
+ time: datapoint[1],
+ title: target.target
+ });
+ }
+ }
+
+ return list;
+ });
+ } else {
+ // Graphite event as annotation
+ var tags = templateSrv.replace(options.annotation.tags);
+ return this.events({range: options.rangeRaw, tags: tags}).then(function(results) {
+ var list = [];
+ for (var i = 0; i < results.data.length; i++) {
+ var e = results.data[i];
+
+ list.push({
+ annotation: options.annotation,
+ time: e.when * 1000,
+ title: e.what,
+ tags: e.tags,
+ text: e.data
+ });
+ }
+ return list;
+ });
+ }
+ };
+
+ this.events = function(options) {
+ try {
+ var tags = '';
+ if (options.tags) {
+ tags = '&tags=' + options.tags;
+ }
+
+ return this.doGraphiteRequest({
+ method: 'GET',
+ url: '/events/get_data?from=' + this.translateTime(options.range.from, false) +
+ '&until=' + this.translateTime(options.range.to, true) + tags,
+ });
+ } catch (err) {
+ return $q.reject(err);
+ }
+ };
+
+ this.translateTime = function(date, roundUp) {
+ if (_.isString(date)) {
+ if (date === 'now') {
+ return 'now';
+ } else if (date.indexOf('now-') >= 0 && date.indexOf('/') === -1) {
+ date = date.substring(3);
+ date = date.replace('m', 'min');
+ date = date.replace('M', 'mon');
+ return date;
+ }
+ date = dateMath.parse(date, roundUp);
+ }
+
+ // graphite' s from filter is exclusive
+ // here we step back one minute in order
+ // to guarantee that we get all the data that
+ // exists for the specified range
+ if (roundUp) {
+ if (date.get('s')) {
+ date.add(1, 'm');
+ }
+ } else if (roundUp === false) {
+ if (date.get('s')) {
+ date.subtract(1, 'm');
+ }
+ }
+
+ return date.unix();
+ };
+
+ this.metricFindQuery = function(query) {
+ var interpolated;
+ try {
+ interpolated = encodeURIComponent(templateSrv.replace(query));
+ } catch (err) {
+ return $q.reject(err);
+ }
+
+ return this.doGraphiteRequest({method: 'GET', url: '/metrics/find/?query=' + interpolated })
+ .then(function(results) {
+ return _.map(results.data, function(metric) {
+ return {
+ text: metric.text,
+ expandable: metric.expandable ? true : false
+ };
+ });
+ });
+ };
+
+ this.testDatasource = function() {
+ return this.metricFindQuery('*').then(function () {
+ return { status: "success", message: "Data source is working", title: "Success" };
+ });
+ };
+
+ this.listDashboards = function(query) {
+ return this.doGraphiteRequest({ method: 'GET', url: '/dashboard/find/', params: {query: query || ''} })
+ .then(function(results) {
+ return results.data.dashboards;
+ });
+ };
+
+ this.loadDashboard = function(dashName) {
+ return this.doGraphiteRequest({method: 'GET', url: '/dashboard/load/' + encodeURIComponent(dashName) });
+ };
+
+ this.doGraphiteRequest = function(options) {
+ if (this.basicAuth || this.withCredentials) {
+ options.withCredentials = true;
+ }
+ if (this.basicAuth) {
+ options.headers = options.headers || {};
+ options.headers.Authorization = this.basicAuth;
+ }
+
+ options.url = this.url + options.url;
+ options.inspect = { type: 'graphite' };
+
+ return backendSrv.datasourceRequest(options);
+ };
+
+ this._seriesRefLetters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
+
+ this.buildGraphiteParams = function(options, scopedVars) {
+ var graphite_options = ['from', 'until', 'rawData', 'format', 'maxDataPoints', 'cacheTimeout'];
+ var clean_options = [], targets = {};
+ var target, targetValue, i;
+ var regex = /\#([A-Z])/g;
+ var intervalFormatFixRegex = /'(\d+)m'/gi;
+ var hasTargets = false;
+
+ if (options.format !== 'png') {
+ options['format'] = 'json';
+ }
+
+ function fixIntervalFormat(match) {
+ return match.replace('m', 'min').replace('M', 'mon');
+ }
+
+ for (i = 0; i < options.targets.length; i++) {
+ target = options.targets[i];
+ if (!target.target) {
+ continue;
+ }
+
+ if (!target.refId) {
+ target.refId = this._seriesRefLetters[i];
+ }
+
+ targetValue = templateSrv.replace(target.target, scopedVars);
+ targetValue = targetValue.replace(intervalFormatFixRegex, fixIntervalFormat);
+ targets[target.refId] = targetValue;
+ }
+
+ function nestedSeriesRegexReplacer(match, g1) {
+ return targets[g1];
+ }
+
+ for (i = 0; i < options.targets.length; i++) {
+ target = options.targets[i];
+ if (!target.target) {
+ continue;
+ }
+
+ targetValue = targets[target.refId];
+ targetValue = targetValue.replace(regex, nestedSeriesRegexReplacer);
+ targets[target.refId] = targetValue;
+
+ if (!target.hide) {
+ hasTargets = true;
+ clean_options.push("target=" + encodeURIComponent(targetValue));
+ }
+ }
+
+ _.each(options, function (value, key) {
+ if (_.indexOf(graphite_options, key) === -1) { return; }
+ if (value) {
+ clean_options.push(key + "=" + encodeURIComponent(value));
+ }
+ });
+
+ if (!hasTargets) {
+ return [];
+ }
+
+ return clean_options;
+ };
+}
diff --git a/public/app/plugins/datasource/graphite/lexer.js b/public/app/plugins/datasource/graphite/lexer.js
deleted file mode 100644
index 2b8affd04e2..00000000000
--- a/public/app/plugins/datasource/graphite/lexer.js
+++ /dev/null
@@ -1,682 +0,0 @@
-define([
- 'lodash'
-], function(_) {
- 'use strict';
-
- // This is auto generated from the unicode tables.
- // The tables are at:
- // http://www.fileformat.info/info/unicode/category/Lu/list.htm
- // http://www.fileformat.info/info/unicode/category/Ll/list.htm
- // http://www.fileformat.info/info/unicode/category/Lt/list.htm
- // http://www.fileformat.info/info/unicode/category/Lm/list.htm
- // http://www.fileformat.info/info/unicode/category/Lo/list.htm
- // http://www.fileformat.info/info/unicode/category/Nl/list.htm
-
- var unicodeLetterTable = [
- 170, 170, 181, 181, 186, 186, 192, 214,
- 216, 246, 248, 705, 710, 721, 736, 740, 748, 748, 750, 750,
- 880, 884, 886, 887, 890, 893, 902, 902, 904, 906, 908, 908,
- 910, 929, 931, 1013, 1015, 1153, 1162, 1319, 1329, 1366,
- 1369, 1369, 1377, 1415, 1488, 1514, 1520, 1522, 1568, 1610,
- 1646, 1647, 1649, 1747, 1749, 1749, 1765, 1766, 1774, 1775,
- 1786, 1788, 1791, 1791, 1808, 1808, 1810, 1839, 1869, 1957,
- 1969, 1969, 1994, 2026, 2036, 2037, 2042, 2042, 2048, 2069,
- 2074, 2074, 2084, 2084, 2088, 2088, 2112, 2136, 2308, 2361,
- 2365, 2365, 2384, 2384, 2392, 2401, 2417, 2423, 2425, 2431,
- 2437, 2444, 2447, 2448, 2451, 2472, 2474, 2480, 2482, 2482,
- 2486, 2489, 2493, 2493, 2510, 2510, 2524, 2525, 2527, 2529,
- 2544, 2545, 2565, 2570, 2575, 2576, 2579, 2600, 2602, 2608,
- 2610, 2611, 2613, 2614, 2616, 2617, 2649, 2652, 2654, 2654,
- 2674, 2676, 2693, 2701, 2703, 2705, 2707, 2728, 2730, 2736,
- 2738, 2739, 2741, 2745, 2749, 2749, 2768, 2768, 2784, 2785,
- 2821, 2828, 2831, 2832, 2835, 2856, 2858, 2864, 2866, 2867,
- 2869, 2873, 2877, 2877, 2908, 2909, 2911, 2913, 2929, 2929,
- 2947, 2947, 2949, 2954, 2958, 2960, 2962, 2965, 2969, 2970,
- 2972, 2972, 2974, 2975, 2979, 2980, 2984, 2986, 2990, 3001,
- 3024, 3024, 3077, 3084, 3086, 3088, 3090, 3112, 3114, 3123,
- 3125, 3129, 3133, 3133, 3160, 3161, 3168, 3169, 3205, 3212,
- 3214, 3216, 3218, 3240, 3242, 3251, 3253, 3257, 3261, 3261,
- 3294, 3294, 3296, 3297, 3313, 3314, 3333, 3340, 3342, 3344,
- 3346, 3386, 3389, 3389, 3406, 3406, 3424, 3425, 3450, 3455,
- 3461, 3478, 3482, 3505, 3507, 3515, 3517, 3517, 3520, 3526,
- 3585, 3632, 3634, 3635, 3648, 3654, 3713, 3714, 3716, 3716,
- 3719, 3720, 3722, 3722, 3725, 3725, 3732, 3735, 3737, 3743,
- 3745, 3747, 3749, 3749, 3751, 3751, 3754, 3755, 3757, 3760,
- 3762, 3763, 3773, 3773, 3776, 3780, 3782, 3782, 3804, 3805,
- 3840, 3840, 3904, 3911, 3913, 3948, 3976, 3980, 4096, 4138,
- 4159, 4159, 4176, 4181, 4186, 4189, 4193, 4193, 4197, 4198,
- 4206, 4208, 4213, 4225, 4238, 4238, 4256, 4293, 4304, 4346,
- 4348, 4348, 4352, 4680, 4682, 4685, 4688, 4694, 4696, 4696,
- 4698, 4701, 4704, 4744, 4746, 4749, 4752, 4784, 4786, 4789,
- 4792, 4798, 4800, 4800, 4802, 4805, 4808, 4822, 4824, 4880,
- 4882, 4885, 4888, 4954, 4992, 5007, 5024, 5108, 5121, 5740,
- 5743, 5759, 5761, 5786, 5792, 5866, 5870, 5872, 5888, 5900,
- 5902, 5905, 5920, 5937, 5952, 5969, 5984, 5996, 5998, 6000,
- 6016, 6067, 6103, 6103, 6108, 6108, 6176, 6263, 6272, 6312,
- 6314, 6314, 6320, 6389, 6400, 6428, 6480, 6509, 6512, 6516,
- 6528, 6571, 6593, 6599, 6656, 6678, 6688, 6740, 6823, 6823,
- 6917, 6963, 6981, 6987, 7043, 7072, 7086, 7087, 7104, 7141,
- 7168, 7203, 7245, 7247, 7258, 7293, 7401, 7404, 7406, 7409,
- 7424, 7615, 7680, 7957, 7960, 7965, 7968, 8005, 8008, 8013,
- 8016, 8023, 8025, 8025, 8027, 8027, 8029, 8029, 8031, 8061,
- 8064, 8116, 8118, 8124, 8126, 8126, 8130, 8132, 8134, 8140,
- 8144, 8147, 8150, 8155, 8160, 8172, 8178, 8180, 8182, 8188,
- 8305, 8305, 8319, 8319, 8336, 8348, 8450, 8450, 8455, 8455,
- 8458, 8467, 8469, 8469, 8473, 8477, 8484, 8484, 8486, 8486,
- 8488, 8488, 8490, 8493, 8495, 8505, 8508, 8511, 8517, 8521,
- 8526, 8526, 8544, 8584, 11264, 11310, 11312, 11358,
- 11360, 11492, 11499, 11502, 11520, 11557, 11568, 11621,
- 11631, 11631, 11648, 11670, 11680, 11686, 11688, 11694,
- 11696, 11702, 11704, 11710, 11712, 11718, 11720, 11726,
- 11728, 11734, 11736, 11742, 11823, 11823, 12293, 12295,
- 12321, 12329, 12337, 12341, 12344, 12348, 12353, 12438,
- 12445, 12447, 12449, 12538, 12540, 12543, 12549, 12589,
- 12593, 12686, 12704, 12730, 12784, 12799, 13312, 13312,
- 19893, 19893, 19968, 19968, 40907, 40907, 40960, 42124,
- 42192, 42237, 42240, 42508, 42512, 42527, 42538, 42539,
- 42560, 42606, 42623, 42647, 42656, 42735, 42775, 42783,
- 42786, 42888, 42891, 42894, 42896, 42897, 42912, 42921,
- 43002, 43009, 43011, 43013, 43015, 43018, 43020, 43042,
- 43072, 43123, 43138, 43187, 43250, 43255, 43259, 43259,
- 43274, 43301, 43312, 43334, 43360, 43388, 43396, 43442,
- 43471, 43471, 43520, 43560, 43584, 43586, 43588, 43595,
- 43616, 43638, 43642, 43642, 43648, 43695, 43697, 43697,
- 43701, 43702, 43705, 43709, 43712, 43712, 43714, 43714,
- 43739, 43741, 43777, 43782, 43785, 43790, 43793, 43798,
- 43808, 43814, 43816, 43822, 43968, 44002, 44032, 44032,
- 55203, 55203, 55216, 55238, 55243, 55291, 63744, 64045,
- 64048, 64109, 64112, 64217, 64256, 64262, 64275, 64279,
- 64285, 64285, 64287, 64296, 64298, 64310, 64312, 64316,
- 64318, 64318, 64320, 64321, 64323, 64324, 64326, 64433,
- 64467, 64829, 64848, 64911, 64914, 64967, 65008, 65019,
- 65136, 65140, 65142, 65276, 65313, 65338, 65345, 65370,
- 65382, 65470, 65474, 65479, 65482, 65487, 65490, 65495,
- 65498, 65500, 65536, 65547, 65549, 65574, 65576, 65594,
- 65596, 65597, 65599, 65613, 65616, 65629, 65664, 65786,
- 65856, 65908, 66176, 66204, 66208, 66256, 66304, 66334,
- 66352, 66378, 66432, 66461, 66464, 66499, 66504, 66511,
- 66513, 66517, 66560, 66717, 67584, 67589, 67592, 67592,
- 67594, 67637, 67639, 67640, 67644, 67644, 67647, 67669,
- 67840, 67861, 67872, 67897, 68096, 68096, 68112, 68115,
- 68117, 68119, 68121, 68147, 68192, 68220, 68352, 68405,
- 68416, 68437, 68448, 68466, 68608, 68680, 69635, 69687,
- 69763, 69807, 73728, 74606, 74752, 74850, 77824, 78894,
- 92160, 92728, 110592, 110593, 119808, 119892, 119894, 119964,
- 119966, 119967, 119970, 119970, 119973, 119974, 119977, 119980,
- 119982, 119993, 119995, 119995, 119997, 120003, 120005, 120069,
- 120071, 120074, 120077, 120084, 120086, 120092, 120094, 120121,
- 120123, 120126, 120128, 120132, 120134, 120134, 120138, 120144,
- 120146, 120485, 120488, 120512, 120514, 120538, 120540, 120570,
- 120572, 120596, 120598, 120628, 120630, 120654, 120656, 120686,
- 120688, 120712, 120714, 120744, 120746, 120770, 120772, 120779,
- 131072, 131072, 173782, 173782, 173824, 173824, 177972, 177972,
- 177984, 177984, 178205, 178205, 194560, 195101
- ];
-
- var identifierStartTable = [];
-
- for (var i = 0; i < 128; i++) {
- identifierStartTable[i] =
- i >= 48 && i <= 57 || // 0-9
- i === 36 || // $
- i === 126 || // ~
- i === 124 || // |
- i >= 65 && i <= 90 || // A-Z
- i === 95 || // _
- i === 45 || // -
- i === 42 || // *
- i === 58 || // :
- i === 91 || // templateStart [
- i === 93 || // templateEnd ]
- i === 63 || // ?
- i === 37 || // %
- i === 35 || // #
- i === 61 || // =
- i >= 97 && i <= 122; // a-z
- }
-
- var identifierPartTable = [];
-
- for (var i2 = 0; i2 < 128; i2++) {
- identifierPartTable[i2] =
- identifierStartTable[i2] || // $, _, A-Z, a-z
- i2 >= 48 && i2 <= 57; // 0-9
- }
-
- function Lexer(expression) {
- this.input = expression;
- this.char = 1;
- this.from = 1;
- }
-
- Lexer.prototype = {
-
- peek: function (i) {
- return this.input.charAt(i || 0);
- },
-
- skip: function (i) {
- i = i || 1;
- this.char += i;
- this.input = this.input.slice(i);
- },
-
- tokenize: function() {
- var list = [];
- var token;
- while (token = this.next()) {
- list.push(token);
- }
- return list;
- },
-
- next: function() {
- this.from = this.char;
-
- // Move to the next non-space character.
- var start;
- if (/\s/.test(this.peek())) {
- start = this.char;
-
- while (/\s/.test(this.peek())) {
- this.from += 1;
- this.skip();
- }
-
- if (this.peek() === "") { // EOL
- return null;
- }
- }
-
- var match = this.scanStringLiteral();
- if (match) {
- return match;
- }
-
- match =
- this.scanPunctuator() ||
- this.scanNumericLiteral() ||
- this.scanIdentifier() ||
- this.scanTemplateSequence();
-
- if (match) {
- this.skip(match.value.length);
- return match;
- }
-
- // No token could be matched, give up.
- return null;
- },
-
- scanTemplateSequence: function() {
- if (this.peek() === '[' && this.peek(1) === '[') {
- return {
- type: 'templateStart',
- value: '[[',
- pos: this.char
- };
- }
-
- if (this.peek() === ']' && this.peek(1) === ']') {
- return {
- type: 'templateEnd',
- value: '[[',
- pos: this.char
- };
- }
-
- return null;
- },
-
- /*
- * Extract a JavaScript identifier out of the next sequence of
- * characters or return 'null' if its not possible. In addition,
- * to Identifier this method can also produce BooleanLiteral
- * (true/false) and NullLiteral (null).
- */
- scanIdentifier: function() {
- var id = "";
- var index = 0;
- var type, char;
-
- // Detects any character in the Unicode categories "Uppercase
- // letter (Lu)", "Lowercase letter (Ll)", "Titlecase letter
- // (Lt)", "Modifier letter (Lm)", "Other letter (Lo)", or
- // "Letter number (Nl)".
- //
- // Both approach and unicodeLetterTable were borrowed from
- // Google's Traceur.
-
- function isUnicodeLetter(code) {
- for (var i = 0; i < unicodeLetterTable.length;) {
- if (code < unicodeLetterTable[i++]) {
- return false;
- }
-
- if (code <= unicodeLetterTable[i++]) {
- return true;
- }
- }
-
- return false;
- }
-
- function isHexDigit(str) {
- return (/^[0-9a-fA-F]$/).test(str);
- }
-
- var readUnicodeEscapeSequence = _.bind(function () {
- /*jshint validthis:true */
- index += 1;
-
- if (this.peek(index) !== "u") {
- return null;
- }
-
- var ch1 = this.peek(index + 1);
- var ch2 = this.peek(index + 2);
- var ch3 = this.peek(index + 3);
- var ch4 = this.peek(index + 4);
- var code;
-
- if (isHexDigit(ch1) && isHexDigit(ch2) && isHexDigit(ch3) && isHexDigit(ch4)) {
- code = parseInt(ch1 + ch2 + ch3 + ch4, 16);
-
- if (isUnicodeLetter(code)) {
- index += 5;
- return "\\u" + ch1 + ch2 + ch3 + ch4;
- }
-
- return null;
- }
-
- return null;
- }, this);
-
- var getIdentifierStart = _.bind(function () {
- /*jshint validthis:true */
- var chr = this.peek(index);
- var code = chr.charCodeAt(0);
-
- if (chr === '*') {
- index += 1;
- return chr;
- }
-
- if (code === 92) {
- return readUnicodeEscapeSequence();
- }
-
- if (code < 128) {
- if (identifierStartTable[code]) {
- index += 1;
- return chr;
- }
-
- return null;
- }
-
- if (isUnicodeLetter(code)) {
- index += 1;
- return chr;
- }
-
- return null;
- }, this);
-
- var getIdentifierPart = _.bind(function () {
- /*jshint validthis:true */
- var chr = this.peek(index);
- var code = chr.charCodeAt(0);
-
- if (code === 92) {
- return readUnicodeEscapeSequence();
- }
-
- if (code < 128) {
- if (identifierPartTable[code]) {
- index += 1;
- return chr;
- }
-
- return null;
- }
-
- if (isUnicodeLetter(code)) {
- index += 1;
- return chr;
- }
-
- return null;
- }, this);
-
- char = getIdentifierStart();
- if (char === null) {
- return null;
- }
-
- id = char;
- for (;;) {
- char = getIdentifierPart();
-
- if (char === null) {
- break;
- }
-
- id += char;
- }
-
- switch (id) {
- case 'true': {
- type = 'bool';
- break;
- }
- case 'false': {
- type = 'bool';
- break;
- }
- default:
- type = "identifier";
- }
-
- return {
- type: type,
- value: id,
- pos: this.char
- };
-
- },
-
- /*
- * Extract a numeric literal out of the next sequence of
- * characters or return 'null' if its not possible. This method
- * supports all numeric literals described in section 7.8.3
- * of the EcmaScript 5 specification.
- *
- * This method's implementation was heavily influenced by the
- * scanNumericLiteral function in the Esprima parser's source code.
- */
- scanNumericLiteral: function () {
- var index = 0;
- var value = "";
- var length = this.input.length;
- var char = this.peek(index);
- var bad;
-
- function isDecimalDigit(str) {
- return (/^[0-9]$/).test(str);
- }
-
- function isOctalDigit(str) {
- return (/^[0-7]$/).test(str);
- }
-
- function isHexDigit(str) {
- return (/^[0-9a-fA-F]$/).test(str);
- }
-
- function isIdentifierStart(ch) {
- return (ch === "$") || (ch === "_") || (ch === "\\") ||
- (ch >= "a" && ch <= "z") || (ch >= "A" && ch <= "Z");
- }
-
- // handle negative num literals
- if (char === '-') {
- value += char;
- index += 1;
- char = this.peek(index);
- }
-
- // Numbers must start either with a decimal digit or a point.
- if (char !== "." && !isDecimalDigit(char)) {
- return null;
- }
-
- if (char !== ".") {
- value += this.peek(index);
- index += 1;
- char = this.peek(index);
-
- if (value === "0") {
- // Base-16 numbers.
- if (char === "x" || char === "X") {
- index += 1;
- value += char;
-
- while (index < length) {
- char = this.peek(index);
- if (!isHexDigit(char)) {
- break;
- }
- value += char;
- index += 1;
- }
-
- if (value.length <= 2) { // 0x
- return {
- type: 'number',
- value: value,
- isMalformed: true,
- pos: this.char
- };
- }
-
- if (index < length) {
- char = this.peek(index);
- if (isIdentifierStart(char)) {
- return null;
- }
- }
-
- return {
- type: 'number',
- value: value,
- base: 16,
- isMalformed: false,
- pos: this.char
- };
- }
-
- // Base-8 numbers.
- if (isOctalDigit(char)) {
- index += 1;
- value += char;
- bad = false;
-
- while (index < length) {
- char = this.peek(index);
-
- // Numbers like '019' (note the 9) are not valid octals
- // but we still parse them and mark as malformed.
-
- if (isDecimalDigit(char)) {
- bad = true;
- } else if (!isOctalDigit(char)) {
- break;
- }
- value += char;
- index += 1;
- }
-
- if (index < length) {
- char = this.peek(index);
- if (isIdentifierStart(char)) {
- return null;
- }
- }
-
- return {
- type: 'number',
- value: value,
- base: 8,
- isMalformed: false
- };
- }
-
- // Decimal numbers that start with '0' such as '09' are illegal
- // but we still parse them and return as malformed.
-
- if (isDecimalDigit(char)) {
- index += 1;
- value += char;
- }
- }
-
- while (index < length) {
- char = this.peek(index);
- if (!isDecimalDigit(char)) {
- break;
- }
- value += char;
- index += 1;
- }
- }
-
- // Decimal digits.
-
- if (char === ".") {
- value += char;
- index += 1;
-
- while (index < length) {
- char = this.peek(index);
- if (!isDecimalDigit(char)) {
- break;
- }
- value += char;
- index += 1;
- }
- }
-
- // Exponent part.
-
- if (char === "e" || char === "E") {
- value += char;
- index += 1;
- char = this.peek(index);
-
- if (char === "+" || char === "-") {
- value += this.peek(index);
- index += 1;
- }
-
- char = this.peek(index);
- if (isDecimalDigit(char)) {
- value += char;
- index += 1;
-
- while (index < length) {
- char = this.peek(index);
- if (!isDecimalDigit(char)) {
- break;
- }
- value += char;
- index += 1;
- }
- } else {
- return null;
- }
- }
-
- if (index < length) {
- char = this.peek(index);
- if (!this.isPunctuator(char)) {
- return null;
- }
- }
-
- return {
- type: 'number',
- value: value,
- base: 10,
- pos: this.char,
- isMalformed: !isFinite(value)
- };
- },
-
- isPunctuator: function (ch1) {
- switch (ch1) {
- case ".":
- case "(":
- case ")":
- case ",":
- case "{":
- case "}":
- return true;
- }
-
- return false;
- },
-
- scanPunctuator: function () {
- var ch1 = this.peek();
-
- if (this.isPunctuator(ch1)) {
- return {
- type: ch1,
- value: ch1,
- pos: this.char
- };
- }
-
- return null;
- },
-
- /*
- * Extract a string out of the next sequence of characters and/or
- * lines or return 'null' if its not possible. Since strings can
- * span across multiple lines this method has to move the char
- * pointer.
- *
- * This method recognizes pseudo-multiline JavaScript strings:
- *
- * var str = "hello\
- * world";
- */
- scanStringLiteral: function () {
- /*jshint loopfunc:true */
- var quote = this.peek();
-
- // String must start with a quote.
- if (quote !== "\"" && quote !== "'") {
- return null;
- }
-
- var value = "";
-
- this.skip();
-
- while (this.peek() !== quote) {
- if (this.peek() === "") { // End Of Line
- return {
- type: 'string',
- value: value,
- isUnclosed: true,
- quote: quote,
- pos: this.char
- };
- }
-
- var char = this.peek();
- var jump = 1; // A length of a jump, after we're done
- // parsing this character.
-
- value += char;
- this.skip(jump);
- }
-
- this.skip();
- return {
- type: 'string',
- value: value,
- isUnclosed: false,
- quote: quote,
- pos: this.char
- };
- },
-
- };
-
- return Lexer;
-
-});
diff --git a/public/app/plugins/datasource/graphite/lexer.ts b/public/app/plugins/datasource/graphite/lexer.ts
new file mode 100644
index 00000000000..1835921d40b
--- /dev/null
+++ b/public/app/plugins/datasource/graphite/lexer.ts
@@ -0,0 +1,678 @@
+///
+
+import _ from 'lodash';
+
+// This is auto generated from the unicode tables.
+// The tables are at:
+// http://www.fileformat.info/info/unicode/category/Lu/list.htm
+// http://www.fileformat.info/info/unicode/category/Ll/list.htm
+// http://www.fileformat.info/info/unicode/category/Lt/list.htm
+// http://www.fileformat.info/info/unicode/category/Lm/list.htm
+// http://www.fileformat.info/info/unicode/category/Lo/list.htm
+// http://www.fileformat.info/info/unicode/category/Nl/list.htm
+
+var unicodeLetterTable = [
+ 170, 170, 181, 181, 186, 186, 192, 214,
+ 216, 246, 248, 705, 710, 721, 736, 740, 748, 748, 750, 750,
+ 880, 884, 886, 887, 890, 893, 902, 902, 904, 906, 908, 908,
+ 910, 929, 931, 1013, 1015, 1153, 1162, 1319, 1329, 1366,
+ 1369, 1369, 1377, 1415, 1488, 1514, 1520, 1522, 1568, 1610,
+ 1646, 1647, 1649, 1747, 1749, 1749, 1765, 1766, 1774, 1775,
+ 1786, 1788, 1791, 1791, 1808, 1808, 1810, 1839, 1869, 1957,
+ 1969, 1969, 1994, 2026, 2036, 2037, 2042, 2042, 2048, 2069,
+ 2074, 2074, 2084, 2084, 2088, 2088, 2112, 2136, 2308, 2361,
+ 2365, 2365, 2384, 2384, 2392, 2401, 2417, 2423, 2425, 2431,
+ 2437, 2444, 2447, 2448, 2451, 2472, 2474, 2480, 2482, 2482,
+ 2486, 2489, 2493, 2493, 2510, 2510, 2524, 2525, 2527, 2529,
+ 2544, 2545, 2565, 2570, 2575, 2576, 2579, 2600, 2602, 2608,
+ 2610, 2611, 2613, 2614, 2616, 2617, 2649, 2652, 2654, 2654,
+ 2674, 2676, 2693, 2701, 2703, 2705, 2707, 2728, 2730, 2736,
+ 2738, 2739, 2741, 2745, 2749, 2749, 2768, 2768, 2784, 2785,
+ 2821, 2828, 2831, 2832, 2835, 2856, 2858, 2864, 2866, 2867,
+ 2869, 2873, 2877, 2877, 2908, 2909, 2911, 2913, 2929, 2929,
+ 2947, 2947, 2949, 2954, 2958, 2960, 2962, 2965, 2969, 2970,
+ 2972, 2972, 2974, 2975, 2979, 2980, 2984, 2986, 2990, 3001,
+ 3024, 3024, 3077, 3084, 3086, 3088, 3090, 3112, 3114, 3123,
+ 3125, 3129, 3133, 3133, 3160, 3161, 3168, 3169, 3205, 3212,
+ 3214, 3216, 3218, 3240, 3242, 3251, 3253, 3257, 3261, 3261,
+ 3294, 3294, 3296, 3297, 3313, 3314, 3333, 3340, 3342, 3344,
+ 3346, 3386, 3389, 3389, 3406, 3406, 3424, 3425, 3450, 3455,
+ 3461, 3478, 3482, 3505, 3507, 3515, 3517, 3517, 3520, 3526,
+ 3585, 3632, 3634, 3635, 3648, 3654, 3713, 3714, 3716, 3716,
+ 3719, 3720, 3722, 3722, 3725, 3725, 3732, 3735, 3737, 3743,
+ 3745, 3747, 3749, 3749, 3751, 3751, 3754, 3755, 3757, 3760,
+ 3762, 3763, 3773, 3773, 3776, 3780, 3782, 3782, 3804, 3805,
+ 3840, 3840, 3904, 3911, 3913, 3948, 3976, 3980, 4096, 4138,
+ 4159, 4159, 4176, 4181, 4186, 4189, 4193, 4193, 4197, 4198,
+ 4206, 4208, 4213, 4225, 4238, 4238, 4256, 4293, 4304, 4346,
+ 4348, 4348, 4352, 4680, 4682, 4685, 4688, 4694, 4696, 4696,
+ 4698, 4701, 4704, 4744, 4746, 4749, 4752, 4784, 4786, 4789,
+ 4792, 4798, 4800, 4800, 4802, 4805, 4808, 4822, 4824, 4880,
+ 4882, 4885, 4888, 4954, 4992, 5007, 5024, 5108, 5121, 5740,
+ 5743, 5759, 5761, 5786, 5792, 5866, 5870, 5872, 5888, 5900,
+ 5902, 5905, 5920, 5937, 5952, 5969, 5984, 5996, 5998, 6000,
+ 6016, 6067, 6103, 6103, 6108, 6108, 6176, 6263, 6272, 6312,
+ 6314, 6314, 6320, 6389, 6400, 6428, 6480, 6509, 6512, 6516,
+ 6528, 6571, 6593, 6599, 6656, 6678, 6688, 6740, 6823, 6823,
+ 6917, 6963, 6981, 6987, 7043, 7072, 7086, 7087, 7104, 7141,
+ 7168, 7203, 7245, 7247, 7258, 7293, 7401, 7404, 7406, 7409,
+ 7424, 7615, 7680, 7957, 7960, 7965, 7968, 8005, 8008, 8013,
+ 8016, 8023, 8025, 8025, 8027, 8027, 8029, 8029, 8031, 8061,
+ 8064, 8116, 8118, 8124, 8126, 8126, 8130, 8132, 8134, 8140,
+ 8144, 8147, 8150, 8155, 8160, 8172, 8178, 8180, 8182, 8188,
+ 8305, 8305, 8319, 8319, 8336, 8348, 8450, 8450, 8455, 8455,
+ 8458, 8467, 8469, 8469, 8473, 8477, 8484, 8484, 8486, 8486,
+ 8488, 8488, 8490, 8493, 8495, 8505, 8508, 8511, 8517, 8521,
+ 8526, 8526, 8544, 8584, 11264, 11310, 11312, 11358,
+ 11360, 11492, 11499, 11502, 11520, 11557, 11568, 11621,
+ 11631, 11631, 11648, 11670, 11680, 11686, 11688, 11694,
+ 11696, 11702, 11704, 11710, 11712, 11718, 11720, 11726,
+ 11728, 11734, 11736, 11742, 11823, 11823, 12293, 12295,
+ 12321, 12329, 12337, 12341, 12344, 12348, 12353, 12438,
+ 12445, 12447, 12449, 12538, 12540, 12543, 12549, 12589,
+ 12593, 12686, 12704, 12730, 12784, 12799, 13312, 13312,
+ 19893, 19893, 19968, 19968, 40907, 40907, 40960, 42124,
+ 42192, 42237, 42240, 42508, 42512, 42527, 42538, 42539,
+ 42560, 42606, 42623, 42647, 42656, 42735, 42775, 42783,
+ 42786, 42888, 42891, 42894, 42896, 42897, 42912, 42921,
+ 43002, 43009, 43011, 43013, 43015, 43018, 43020, 43042,
+ 43072, 43123, 43138, 43187, 43250, 43255, 43259, 43259,
+ 43274, 43301, 43312, 43334, 43360, 43388, 43396, 43442,
+ 43471, 43471, 43520, 43560, 43584, 43586, 43588, 43595,
+ 43616, 43638, 43642, 43642, 43648, 43695, 43697, 43697,
+ 43701, 43702, 43705, 43709, 43712, 43712, 43714, 43714,
+ 43739, 43741, 43777, 43782, 43785, 43790, 43793, 43798,
+ 43808, 43814, 43816, 43822, 43968, 44002, 44032, 44032,
+ 55203, 55203, 55216, 55238, 55243, 55291, 63744, 64045,
+ 64048, 64109, 64112, 64217, 64256, 64262, 64275, 64279,
+ 64285, 64285, 64287, 64296, 64298, 64310, 64312, 64316,
+ 64318, 64318, 64320, 64321, 64323, 64324, 64326, 64433,
+ 64467, 64829, 64848, 64911, 64914, 64967, 65008, 65019,
+ 65136, 65140, 65142, 65276, 65313, 65338, 65345, 65370,
+ 65382, 65470, 65474, 65479, 65482, 65487, 65490, 65495,
+ 65498, 65500, 65536, 65547, 65549, 65574, 65576, 65594,
+ 65596, 65597, 65599, 65613, 65616, 65629, 65664, 65786,
+ 65856, 65908, 66176, 66204, 66208, 66256, 66304, 66334,
+ 66352, 66378, 66432, 66461, 66464, 66499, 66504, 66511,
+ 66513, 66517, 66560, 66717, 67584, 67589, 67592, 67592,
+ 67594, 67637, 67639, 67640, 67644, 67644, 67647, 67669,
+ 67840, 67861, 67872, 67897, 68096, 68096, 68112, 68115,
+ 68117, 68119, 68121, 68147, 68192, 68220, 68352, 68405,
+ 68416, 68437, 68448, 68466, 68608, 68680, 69635, 69687,
+ 69763, 69807, 73728, 74606, 74752, 74850, 77824, 78894,
+ 92160, 92728, 110592, 110593, 119808, 119892, 119894, 119964,
+ 119966, 119967, 119970, 119970, 119973, 119974, 119977, 119980,
+ 119982, 119993, 119995, 119995, 119997, 120003, 120005, 120069,
+ 120071, 120074, 120077, 120084, 120086, 120092, 120094, 120121,
+ 120123, 120126, 120128, 120132, 120134, 120134, 120138, 120144,
+ 120146, 120485, 120488, 120512, 120514, 120538, 120540, 120570,
+ 120572, 120596, 120598, 120628, 120630, 120654, 120656, 120686,
+ 120688, 120712, 120714, 120744, 120746, 120770, 120772, 120779,
+ 131072, 131072, 173782, 173782, 173824, 173824, 177972, 177972,
+ 177984, 177984, 178205, 178205, 194560, 195101
+];
+
+var identifierStartTable = [];
+
+for (var i = 0; i < 128; i++) {
+ identifierStartTable[i] =
+ i >= 48 && i <= 57 || // 0-9
+ i === 36 || // $
+ i === 126 || // ~
+ i === 124 || // |
+ i >= 65 && i <= 90 || // A-Z
+ i === 95 || // _
+ i === 45 || // -
+ i === 42 || // *
+ i === 58 || // :
+ i === 91 || // templateStart [
+ i === 93 || // templateEnd ]
+ i === 63 || // ?
+ i === 37 || // %
+ i === 35 || // #
+ i === 61 || // =
+ i >= 97 && i <= 122; // a-z
+}
+
+var identifierPartTable = [];
+
+for (var i2 = 0; i2 < 128; i2++) {
+ identifierPartTable[i2] =
+ identifierStartTable[i2] || // $, _, A-Z, a-z
+ i2 >= 48 && i2 <= 57; // 0-9
+}
+
+export function Lexer(expression) {
+ this.input = expression;
+ this.char = 1;
+ this.from = 1;
+}
+
+Lexer.prototype = {
+
+ peek: function (i) {
+ return this.input.charAt(i || 0);
+ },
+
+ skip: function (i) {
+ i = i || 1;
+ this.char += i;
+ this.input = this.input.slice(i);
+ },
+
+ tokenize: function() {
+ var list = [];
+ var token;
+ while (token = this.next()) {
+ list.push(token);
+ }
+ return list;
+ },
+
+ next: function() {
+ this.from = this.char;
+
+ // Move to the next non-space character.
+ var start;
+ if (/\s/.test(this.peek())) {
+ start = this.char;
+
+ while (/\s/.test(this.peek())) {
+ this.from += 1;
+ this.skip();
+ }
+
+ if (this.peek() === "") { // EOL
+ return null;
+ }
+ }
+
+ var match = this.scanStringLiteral();
+ if (match) {
+ return match;
+ }
+
+ match =
+ this.scanPunctuator() ||
+ this.scanNumericLiteral() ||
+ this.scanIdentifier() ||
+ this.scanTemplateSequence();
+
+ if (match) {
+ this.skip(match.value.length);
+ return match;
+ }
+
+ // No token could be matched, give up.
+ return null;
+ },
+
+ scanTemplateSequence: function() {
+ if (this.peek() === '[' && this.peek(1) === '[') {
+ return {
+ type: 'templateStart',
+ value: '[[',
+ pos: this.char
+ };
+ }
+
+ if (this.peek() === ']' && this.peek(1) === ']') {
+ return {
+ type: 'templateEnd',
+ value: '[[',
+ pos: this.char
+ };
+ }
+
+ return null;
+ },
+
+ /*
+ * Extract a JavaScript identifier out of the next sequence of
+ * characters or return 'null' if its not possible. In addition,
+ * to Identifier this method can also produce BooleanLiteral
+ * (true/false) and NullLiteral (null).
+ */
+ scanIdentifier: function() {
+ var id = "";
+ var index = 0;
+ var type, char;
+
+ // Detects any character in the Unicode categories "Uppercase
+ // letter (Lu)", "Lowercase letter (Ll)", "Titlecase letter
+ // (Lt)", "Modifier letter (Lm)", "Other letter (Lo)", or
+ // "Letter number (Nl)".
+ //
+ // Both approach and unicodeLetterTable were borrowed from
+ // Google's Traceur.
+
+ function isUnicodeLetter(code) {
+ for (var i = 0; i < unicodeLetterTable.length;) {
+ if (code < unicodeLetterTable[i++]) {
+ return false;
+ }
+
+ if (code <= unicodeLetterTable[i++]) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ function isHexDigit(str) {
+ return (/^[0-9a-fA-F]$/).test(str);
+ }
+
+ var readUnicodeEscapeSequence = _.bind(function () {
+ /*jshint validthis:true */
+ index += 1;
+
+ if (this.peek(index) !== "u") {
+ return null;
+ }
+
+ var ch1 = this.peek(index + 1);
+ var ch2 = this.peek(index + 2);
+ var ch3 = this.peek(index + 3);
+ var ch4 = this.peek(index + 4);
+ var code;
+
+ if (isHexDigit(ch1) && isHexDigit(ch2) && isHexDigit(ch3) && isHexDigit(ch4)) {
+ code = parseInt(ch1 + ch2 + ch3 + ch4, 16);
+
+ if (isUnicodeLetter(code)) {
+ index += 5;
+ return "\\u" + ch1 + ch2 + ch3 + ch4;
+ }
+
+ return null;
+ }
+
+ return null;
+ }, this);
+
+ var getIdentifierStart = _.bind(function () {
+ /*jshint validthis:true */
+ var chr = this.peek(index);
+ var code = chr.charCodeAt(0);
+
+ if (chr === '*') {
+ index += 1;
+ return chr;
+ }
+
+ if (code === 92) {
+ return readUnicodeEscapeSequence();
+ }
+
+ if (code < 128) {
+ if (identifierStartTable[code]) {
+ index += 1;
+ return chr;
+ }
+
+ return null;
+ }
+
+ if (isUnicodeLetter(code)) {
+ index += 1;
+ return chr;
+ }
+
+ return null;
+ }, this);
+
+ var getIdentifierPart = _.bind(function () {
+ /*jshint validthis:true */
+ var chr = this.peek(index);
+ var code = chr.charCodeAt(0);
+
+ if (code === 92) {
+ return readUnicodeEscapeSequence();
+ }
+
+ if (code < 128) {
+ if (identifierPartTable[code]) {
+ index += 1;
+ return chr;
+ }
+
+ return null;
+ }
+
+ if (isUnicodeLetter(code)) {
+ index += 1;
+ return chr;
+ }
+
+ return null;
+ }, this);
+
+ char = getIdentifierStart();
+ if (char === null) {
+ return null;
+ }
+
+ id = char;
+ for (;;) {
+ char = getIdentifierPart();
+
+ if (char === null) {
+ break;
+ }
+
+ id += char;
+ }
+
+ switch (id) {
+ case 'true': {
+ type = 'bool';
+ break;
+ }
+ case 'false': {
+ type = 'bool';
+ break;
+ }
+ default:
+ type = "identifier";
+ }
+
+ return {
+ type: type,
+ value: id,
+ pos: this.char
+ };
+
+ },
+
+ /*
+ * Extract a numeric literal out of the next sequence of
+ * characters or return 'null' if its not possible. This method
+ * supports all numeric literals described in section 7.8.3
+ * of the EcmaScript 5 specification.
+ *
+ * This method's implementation was heavily influenced by the
+ * scanNumericLiteral function in the Esprima parser's source code.
+ */
+ scanNumericLiteral: function (): any {
+ var index = 0;
+ var value = "";
+ var length = this.input.length;
+ var char = this.peek(index);
+ var bad;
+
+ function isDecimalDigit(str) {
+ return (/^[0-9]$/).test(str);
+ }
+
+ function isOctalDigit(str) {
+ return (/^[0-7]$/).test(str);
+ }
+
+ function isHexDigit(str) {
+ return (/^[0-9a-fA-F]$/).test(str);
+ }
+
+ function isIdentifierStart(ch) {
+ return (ch === "$") || (ch === "_") || (ch === "\\") ||
+ (ch >= "a" && ch <= "z") || (ch >= "A" && ch <= "Z");
+ }
+
+ // handle negative num literals
+ if (char === '-') {
+ value += char;
+ index += 1;
+ char = this.peek(index);
+ }
+
+ // Numbers must start either with a decimal digit or a point.
+ if (char !== "." && !isDecimalDigit(char)) {
+ return null;
+ }
+
+ if (char !== ".") {
+ value += this.peek(index);
+ index += 1;
+ char = this.peek(index);
+
+ if (value === "0") {
+ // Base-16 numbers.
+ if (char === "x" || char === "X") {
+ index += 1;
+ value += char;
+
+ while (index < length) {
+ char = this.peek(index);
+ if (!isHexDigit(char)) {
+ break;
+ }
+ value += char;
+ index += 1;
+ }
+
+ if (value.length <= 2) { // 0x
+ return {
+ type: 'number',
+ value: value,
+ isMalformed: true,
+ pos: this.char
+ };
+ }
+
+ if (index < length) {
+ char = this.peek(index);
+ if (isIdentifierStart(char)) {
+ return null;
+ }
+ }
+
+ return {
+ type: 'number',
+ value: value,
+ base: 16,
+ isMalformed: false,
+ pos: this.char
+ };
+ }
+
+ // Base-8 numbers.
+ if (isOctalDigit(char)) {
+ index += 1;
+ value += char;
+ bad = false;
+
+ while (index < length) {
+ char = this.peek(index);
+
+ // Numbers like '019' (note the 9) are not valid octals
+ // but we still parse them and mark as malformed.
+
+ if (isDecimalDigit(char)) {
+ bad = true;
+ } else if (!isOctalDigit(char)) {
+ break;
+ }
+ value += char;
+ index += 1;
+ }
+
+ if (index < length) {
+ char = this.peek(index);
+ if (isIdentifierStart(char)) {
+ return null;
+ }
+ }
+
+ return {
+ type: 'number',
+ value: value,
+ base: 8,
+ isMalformed: false
+ };
+ }
+
+ // Decimal numbers that start with '0' such as '09' are illegal
+ // but we still parse them and return as malformed.
+
+ if (isDecimalDigit(char)) {
+ index += 1;
+ value += char;
+ }
+ }
+
+ while (index < length) {
+ char = this.peek(index);
+ if (!isDecimalDigit(char)) {
+ break;
+ }
+ value += char;
+ index += 1;
+ }
+ }
+
+ // Decimal digits.
+
+ if (char === ".") {
+ value += char;
+ index += 1;
+
+ while (index < length) {
+ char = this.peek(index);
+ if (!isDecimalDigit(char)) {
+ break;
+ }
+ value += char;
+ index += 1;
+ }
+ }
+
+ // Exponent part.
+
+ if (char === "e" || char === "E") {
+ value += char;
+ index += 1;
+ char = this.peek(index);
+
+ if (char === "+" || char === "-") {
+ value += this.peek(index);
+ index += 1;
+ }
+
+ char = this.peek(index);
+ if (isDecimalDigit(char)) {
+ value += char;
+ index += 1;
+
+ while (index < length) {
+ char = this.peek(index);
+ if (!isDecimalDigit(char)) {
+ break;
+ }
+ value += char;
+ index += 1;
+ }
+ } else {
+ return null;
+ }
+ }
+
+ if (index < length) {
+ char = this.peek(index);
+ if (!this.isPunctuator(char)) {
+ return null;
+ }
+ }
+
+ return {
+ type: 'number',
+ value: value,
+ base: 10,
+ pos: this.char,
+ isMalformed: !isFinite(+value)
+ };
+ },
+
+ isPunctuator: function (ch1) {
+ switch (ch1) {
+ case ".":
+ case "(":
+ case ")":
+ case ",":
+ case "{":
+ case "}":
+ return true;
+ }
+
+ return false;
+ },
+
+ scanPunctuator: function () {
+ var ch1 = this.peek();
+
+ if (this.isPunctuator(ch1)) {
+ return {
+ type: ch1,
+ value: ch1,
+ pos: this.char
+ };
+ }
+
+ return null;
+ },
+
+ /*
+ * Extract a string out of the next sequence of characters and/or
+ * lines or return 'null' if its not possible. Since strings can
+ * span across multiple lines this method has to move the char
+ * pointer.
+ *
+ * This method recognizes pseudo-multiline JavaScript strings:
+ *
+ * var str = "hello\
+ * world";
+ */
+ scanStringLiteral: function () {
+ /*jshint loopfunc:true */
+ var quote = this.peek();
+
+ // String must start with a quote.
+ if (quote !== "\"" && quote !== "'") {
+ return null;
+ }
+
+ var value = "";
+
+ this.skip();
+
+ while (this.peek() !== quote) {
+ if (this.peek() === "") { // End Of Line
+ return {
+ type: 'string',
+ value: value,
+ isUnclosed: true,
+ quote: quote,
+ pos: this.char
+ };
+ }
+
+ var char = this.peek();
+ var jump = 1; // A length of a jump, after we're done
+ // parsing this character.
+
+ value += char;
+ this.skip(jump);
+ }
+
+ this.skip();
+ return {
+ type: 'string',
+ value: value,
+ isUnclosed: false,
+ quote: quote,
+ pos: this.char
+ };
+ },
+
+ };
+
diff --git a/public/app/plugins/datasource/graphite/module.js b/public/app/plugins/datasource/graphite/module.js
deleted file mode 100644
index a1d4ed5fc2b..00000000000
--- a/public/app/plugins/datasource/graphite/module.js
+++ /dev/null
@@ -1,38 +0,0 @@
-define([
- './datasource',
-],
-function (GraphiteDatasource) {
- 'use strict';
-
- function metricsQueryEditor() {
- return {
- controller: 'GraphiteQueryCtrl',
- templateUrl: 'public/app/plugins/datasource/graphite/partials/query.editor.html'
- };
- }
-
- function metricsQueryOptions() {
- return {templateUrl: 'public/app/plugins/datasource/graphite/partials/query.options.html'};
- }
-
- function annotationsQueryEditor() {
- return {templateUrl: 'public/app/plugins/datasource/graphite/partials/annotations.editor.html'};
- }
-
- function configView() {
- return {templateUrl: 'public/app/plugins/datasource/graphite/partials/config.html'};
- }
-
- function ConfigView() {
- }
- ConfigView.templateUrl = 'public/app/plugins/datasource/graphite/partials/config.html';
-
- return {
- Datasource: GraphiteDatasource,
- configView: configView,
- annotationsQueryEditor: annotationsQueryEditor,
- metricsQueryEditor: metricsQueryEditor,
- metricsQueryOptions: metricsQueryOptions,
- ConfigView: ConfigView
- };
-});
diff --git a/public/app/plugins/datasource/graphite/module.ts b/public/app/plugins/datasource/graphite/module.ts
new file mode 100644
index 00000000000..abab560aafd
--- /dev/null
+++ b/public/app/plugins/datasource/graphite/module.ts
@@ -0,0 +1,51 @@
+import {GraphiteDatasource} from './datasource';
+import {GraphiteQueryCtrl} from './query_ctrl';
+
+class GraphiteConfigView {
+ static templateUrl = 'public/app/plugins/datasource/graphite/partials/config.html';
+}
+
+export {
+ GraphiteDatasource as Datasource,
+ GraphiteQueryCtrl as QueryCtrl,
+ GraphiteConfigView as ConfigView
+};
+
+// define([
+// './datasource',
+// ],
+// function (GraphiteDatasource) {
+// 'use strict';
+//
+// function metricsQueryEditor() {
+// return {
+// controller: 'GraphiteQueryCtrl',
+// templateUrl: 'public/app/plugins/datasource/graphite/partials/query.editor.html'
+// };
+// }
+//
+// function metricsQueryOptions() {
+// return {templateUrl: 'public/app/plugins/datasource/graphite/partials/query.options.html'};
+// }
+//
+// function annotationsQueryEditor() {
+// return {templateUrl: 'public/app/plugins/datasource/graphite/partials/annotations.editor.html'};
+// }
+//
+// function configView() {
+// return {templateUrl: 'public/app/plugins/datasource/graphite/partials/config.html'};
+// }
+//
+// function ConfigView() {
+// }
+// ConfigView.templateUrl = 'public/app/plugins/datasource/graphite/partials/config.html';
+//
+// return {
+// Datasource: GraphiteDatasource,
+// configView: configView,
+// annotationsQueryEditor: annotationsQueryEditor,
+// metricsQueryEditor: metricsQueryEditor,
+// metricsQueryOptions: metricsQueryOptions,
+// ConfigView: ConfigView
+// };
+// });
diff --git a/public/app/plugins/datasource/graphite/parser.js b/public/app/plugins/datasource/graphite/parser.js
deleted file mode 100644
index 43fd148f1ea..00000000000
--- a/public/app/plugins/datasource/graphite/parser.js
+++ /dev/null
@@ -1,265 +0,0 @@
-define([
- './lexer'
-], function (Lexer) {
- 'use strict';
-
- function Parser(expression) {
- this.expression = expression;
- this.lexer = new Lexer(expression);
- this.tokens = this.lexer.tokenize();
- this.index = 0;
- }
-
- Parser.prototype = {
-
- getAst: function () {
- return this.start();
- },
-
- start: function () {
- try {
- return this.functionCall() || this.metricExpression();
- }
- catch (e) {
- return {
- type: 'error',
- message: e.message,
- pos: e.pos
- };
- }
- },
-
- curlyBraceSegment: function() {
- if (this.match('identifier', '{') || this.match('{')) {
-
- var curlySegment = "";
-
- while (!this.match('') && !this.match('}')) {
- curlySegment += this.consumeToken().value;
- }
-
- if (!this.match('}')) {
- this.errorMark("Expected closing '}'");
- }
-
- curlySegment += this.consumeToken().value;
-
- // if curly segment is directly followed by identifier
- // include it in the segment
- if (this.match('identifier')) {
- curlySegment += this.consumeToken().value;
- }
-
- return {
- type: 'segment',
- value: curlySegment
- };
- }
- else {
- return null;
- }
- },
-
- metricSegment: function() {
- var curly = this.curlyBraceSegment();
- if (curly) {
- return curly;
- }
-
- if (this.match('identifier') || this.match('number')) {
- // hack to handle float numbers in metric segments
- var parts = this.consumeToken().value.split('.');
- if (parts.length === 2) {
- this.tokens.splice(this.index, 0, { type: '.' });
- this.tokens.splice(this.index + 1, 0, { type: 'number', value: parts[1] });
- }
-
- return {
- type: 'segment',
- value: parts[0]
- };
- }
-
- if (!this.match('templateStart')) {
- this.errorMark('Expected metric identifier');
- }
-
- this.consumeToken();
-
- if (!this.match('identifier')) {
- this.errorMark('Expected identifier after templateStart');
- }
-
- var node = {
- type: 'template',
- value: this.consumeToken().value
- };
-
- if (!this.match('templateEnd')) {
- this.errorMark('Expected templateEnd');
- }
-
- this.consumeToken();
- return node;
- },
-
- metricExpression: function() {
- if (!this.match('templateStart') &&
- !this.match('identifier') &&
- !this.match('number') &&
- !this.match('{')) {
- return null;
- }
-
- var node = {
- type: 'metric',
- segments: []
- };
-
- node.segments.push(this.metricSegment());
-
- while (this.match('.')) {
- this.consumeToken();
-
- var segment = this.metricSegment();
- if (!segment) {
- this.errorMark('Expected metric identifier');
- }
-
- node.segments.push(segment);
- }
-
- return node;
- },
-
- functionCall: function() {
- if (!this.match('identifier', '(')) {
- return null;
- }
-
- var node = {
- type: 'function',
- name: this.consumeToken().value,
- };
-
- // consume left parenthesis
- this.consumeToken();
-
- node.params = this.functionParameters();
-
- if (!this.match(')')) {
- this.errorMark('Expected closing parenthesis');
- }
-
- this.consumeToken();
-
- return node;
- },
-
- boolExpression: function() {
- if (!this.match('bool')) {
- return null;
- }
-
- return {
- type: 'bool',
- value: this.consumeToken().value === 'true',
- };
- },
-
- functionParameters: function () {
- if (this.match(')') || this.match('')) {
- return [];
- }
-
- var param =
- this.functionCall() ||
- this.numericLiteral() ||
- this.seriesRefExpression() ||
- this.boolExpression() ||
- this.metricExpression() ||
- this.stringLiteral();
-
- if (!this.match(',')) {
- return [param];
- }
-
- this.consumeToken();
- return [param].concat(this.functionParameters());
- },
-
- seriesRefExpression: function() {
- if (!this.match('identifier')) {
- return null;
- }
-
- var value = this.tokens[this.index].value;
- if (!value.match(/\#[A-Z]/)) {
- return null;
- }
-
- var token = this.consumeToken();
-
- return {
- type: 'series-ref',
- value: token.value
- };
- },
-
- numericLiteral: function () {
- if (!this.match('number')) {
- return null;
- }
-
- return {
- type: 'number',
- value: parseFloat(this.consumeToken().value)
- };
- },
-
- stringLiteral: function () {
- if (!this.match('string')) {
- return null;
- }
-
- var token = this.consumeToken();
- if (token.isUnclosed) {
- throw { message: 'Unclosed string parameter', pos: token.pos };
- }
-
- return {
- type: 'string',
- value: token.value
- };
- },
-
- errorMark: function(text) {
- var currentToken = this.tokens[this.index];
- var type = currentToken ? currentToken.type : 'end of string';
- throw {
- message: text + " instead found " + type,
- pos: currentToken ? currentToken.pos : this.lexer.char
- };
- },
-
- // returns token value and incre
- consumeToken: function() {
- this.index++;
- return this.tokens[this.index - 1];
- },
-
- matchToken: function(type, index) {
- var token = this.tokens[this.index + index];
- return (token === undefined && type === '') ||
- token && token.type === type;
- },
-
- match: function(token1, token2) {
- return this.matchToken(token1, 0) &&
- (!token2 || this.matchToken(token2, 1));
- },
-
- };
-
- return Parser;
-});
diff --git a/public/app/plugins/datasource/graphite/parser.ts b/public/app/plugins/datasource/graphite/parser.ts
new file mode 100644
index 00000000000..bfafdda7815
--- /dev/null
+++ b/public/app/plugins/datasource/graphite/parser.ts
@@ -0,0 +1,258 @@
+
+import {Lexer} from './lexer';
+
+export function Parser(expression) {
+ this.expression = expression;
+ this.lexer = new Lexer(expression);
+ this.tokens = this.lexer.tokenize();
+ this.index = 0;
+}
+
+Parser.prototype = {
+
+ getAst: function () {
+ return this.start();
+ },
+
+ start: function () {
+ try {
+ return this.functionCall() || this.metricExpression();
+ } catch (e) {
+ return {
+ type: 'error',
+ message: e.message,
+ pos: e.pos
+ };
+ }
+ },
+
+ curlyBraceSegment: function() {
+ if (this.match('identifier', '{') || this.match('{')) {
+
+ var curlySegment = "";
+
+ while (!this.match('') && !this.match('}')) {
+ curlySegment += this.consumeToken().value;
+ }
+
+ if (!this.match('}')) {
+ this.errorMark("Expected closing '}'");
+ }
+
+ curlySegment += this.consumeToken().value;
+
+ // if curly segment is directly followed by identifier
+ // include it in the segment
+ if (this.match('identifier')) {
+ curlySegment += this.consumeToken().value;
+ }
+
+ return {
+ type: 'segment',
+ value: curlySegment
+ };
+ } else {
+ return null;
+ }
+ },
+
+ metricSegment: function() {
+ var curly = this.curlyBraceSegment();
+ if (curly) {
+ return curly;
+ }
+
+ if (this.match('identifier') || this.match('number')) {
+ // hack to handle float numbers in metric segments
+ var parts = this.consumeToken().value.split('.');
+ if (parts.length === 2) {
+ this.tokens.splice(this.index, 0, { type: '.' });
+ this.tokens.splice(this.index + 1, 0, { type: 'number', value: parts[1] });
+ }
+
+ return {
+ type: 'segment',
+ value: parts[0]
+ };
+ }
+
+ if (!this.match('templateStart')) {
+ this.errorMark('Expected metric identifier');
+ }
+
+ this.consumeToken();
+
+ if (!this.match('identifier')) {
+ this.errorMark('Expected identifier after templateStart');
+ }
+
+ var node = {
+ type: 'template',
+ value: this.consumeToken().value
+ };
+
+ if (!this.match('templateEnd')) {
+ this.errorMark('Expected templateEnd');
+ }
+
+ this.consumeToken();
+ return node;
+ },
+
+ metricExpression: function() {
+ if (!this.match('templateStart') &&
+ !this.match('identifier') &&
+ !this.match('number') &&
+ !this.match('{')) {
+ return null;
+ }
+
+ var node = {
+ type: 'metric',
+ segments: []
+ };
+
+ node.segments.push(this.metricSegment());
+
+ while (this.match('.')) {
+ this.consumeToken();
+
+ var segment = this.metricSegment();
+ if (!segment) {
+ this.errorMark('Expected metric identifier');
+ }
+
+ node.segments.push(segment);
+ }
+
+ return node;
+ },
+
+ functionCall: function() {
+ if (!this.match('identifier', '(')) {
+ return null;
+ }
+
+ var node: any = {
+ type: 'function',
+ name: this.consumeToken().value,
+ };
+
+ // consume left parenthesis
+ this.consumeToken();
+
+ node.params = this.functionParameters();
+
+ if (!this.match(')')) {
+ this.errorMark('Expected closing parenthesis');
+ }
+
+ this.consumeToken();
+
+ return node;
+ },
+
+ boolExpression: function() {
+ if (!this.match('bool')) {
+ return null;
+ }
+
+ return {
+ type: 'bool',
+ value: this.consumeToken().value === 'true',
+ };
+ },
+
+ functionParameters: function () {
+ if (this.match(')') || this.match('')) {
+ return [];
+ }
+
+ var param =
+ this.functionCall() ||
+ this.numericLiteral() ||
+ this.seriesRefExpression() ||
+ this.boolExpression() ||
+ this.metricExpression() ||
+ this.stringLiteral();
+
+ if (!this.match(',')) {
+ return [param];
+ }
+
+ this.consumeToken();
+ return [param].concat(this.functionParameters());
+ },
+
+ seriesRefExpression: function() {
+ if (!this.match('identifier')) {
+ return null;
+ }
+
+ var value = this.tokens[this.index].value;
+ if (!value.match(/\#[A-Z]/)) {
+ return null;
+ }
+
+ var token = this.consumeToken();
+
+ return {
+ type: 'series-ref',
+ value: token.value
+ };
+ },
+
+ numericLiteral: function () {
+ if (!this.match('number')) {
+ return null;
+ }
+
+ return {
+ type: 'number',
+ value: parseFloat(this.consumeToken().value)
+ };
+ },
+
+ stringLiteral: function () {
+ if (!this.match('string')) {
+ return null;
+ }
+
+ var token = this.consumeToken();
+ if (token.isUnclosed) {
+ throw { message: 'Unclosed string parameter', pos: token.pos };
+ }
+
+ return {
+ type: 'string',
+ value: token.value
+ };
+ },
+
+ errorMark: function(text) {
+ var currentToken = this.tokens[this.index];
+ var type = currentToken ? currentToken.type : 'end of string';
+ throw {
+ message: text + " instead found " + type,
+ pos: currentToken ? currentToken.pos : this.lexer.char
+ };
+ },
+
+ // returns token value and incre
+ consumeToken: function() {
+ this.index++;
+ return this.tokens[this.index - 1];
+ },
+
+ matchToken: function(type, index) {
+ var token = this.tokens[this.index + index];
+ return (token === undefined && type === '') ||
+ token && token.type === type;
+ },
+
+ match: function(token1, token2) {
+ return this.matchToken(token1, 0) &&
+ (!token2 || this.matchToken(token2, 1));
+ },
+};
+
diff --git a/public/app/plugins/datasource/graphite/partials/query.editor.html b/public/app/plugins/datasource/graphite/partials/query.editor.html
index 9cb2b454132..2557dfbf0a2 100755
--- a/public/app/plugins/datasource/graphite/partials/query.editor.html
+++ b/public/app/plugins/datasource/graphite/partials/query.editor.html
@@ -1,15 +1,15 @@
-
+
@@ -45,24 +45,24 @@
-
+
-