Просмотр исходного кода

Merge branch 'query-part-refactor'

Conflicts:
	public/app/plugins/datasource/influxdb/query_part.ts
Torkel Ödegaard 9 лет назад
Родитель
Сommit
79986e5593

+ 123 - 0
public/app/core/components/query_part/query_part.ts

@@ -0,0 +1,123 @@
+///<reference path="../../../headers/common.d.ts" />
+
+import _ from 'lodash';
+
+export class QueryPartDef {
+  type: string;
+  params: any[];
+  defaultParams: any[];
+  renderer: any;
+  category: any;
+  addStrategy: any;
+
+  constructor(options: any) {
+    this.type = options.type;
+    this.params = options.params;
+    this.defaultParams = options.defaultParams;
+    this.renderer = options.renderer;
+    this.category = options.category;
+    this.addStrategy = options.addStrategy;
+  }
+}
+
+export class QueryPart {
+  part: any;
+  def: QueryPartDef;
+  params: any[];
+  text: string;
+
+  constructor(part: any, def: any) {
+    this.part = part;
+    this.def = def;
+    if (!this.def) {
+      throw {message: 'Could not find query part ' + part.type};
+    }
+
+    part.params = part.params || _.clone(this.def.defaultParams);
+    this.params = part.params;
+    this.updateText();
+  }
+
+  render(innerExpr: string) {
+    return this.def.renderer(this, innerExpr);
+  }
+
+  hasMultipleParamsInString (strValue, index) {
+    if (strValue.indexOf(',') === -1) {
+      return false;
+    }
+
+    return this.def.params[index + 1] && this.def.params[index + 1].optional;
+  }
+
+  updateParam (strValue, index) {
+    // handle optional parameters
+    // if string contains ',' and next param is optional, split and update both
+    if (this.hasMultipleParamsInString(strValue, index)) {
+      _.each(strValue.split(','), function(partVal: string, idx) {
+        this.updateParam(partVal.trim(), idx);
+      }, this);
+      return;
+    }
+
+    if (strValue === '' && this.def.params[index].optional) {
+      this.params.splice(index, 1);
+    } else {
+      this.params[index] = strValue;
+    }
+
+    this.part.params = this.params;
+    this.updateText();
+  }
+
+  updateText() {
+    if (this.params.length === 0) {
+      this.text = this.def.type + '()';
+      return;
+    }
+
+    var text = this.def.type + '(';
+    text += this.params.join(', ');
+    text += ')';
+    this.text = text;
+  }
+}
+
+export function functionRenderer(part, innerExpr) {
+  var str = part.def.type + '(';
+  var parameters = _.map(part.params, (value, index) => {
+    var paramType = part.def.params[index];
+    if (paramType.type === 'time') {
+      if (value === 'auto') {
+        value = '$interval';
+      }
+    }
+    if (paramType.quote === 'single') {
+      return "'" + value + "'";
+    } else if (paramType.quote === 'double') {
+      return '"' + value + '"';
+    }
+
+    return value;
+  });
+
+  if (innerExpr) {
+    parameters.unshift(innerExpr);
+  }
+  return str + parameters.join(', ') + ')';
+}
+
+
+export function suffixRenderer(part, innerExpr) {
+  return innerExpr + ' ' + part.params[0];
+}
+
+export function identityRenderer(part, innerExpr) {
+  return part.params[0];
+}
+
+export function quotedIdentityRenderer(part, innerExpr) {
+  return '"' + part.params[0] + '"';
+}
+
+

+ 183 - 0
public/app/core/components/query_part/query_part_editor.ts

@@ -0,0 +1,183 @@
+///<reference path="../../../headers/common.d.ts" />
+
+import _ from 'lodash';
+import $ from 'jquery';
+import coreModule from 'app/core/core_module';
+
+var template = `
+<div class="tight-form-func-controls">
+  <span class="pointer fa fa-remove" ng-click="removeActionInternal()"></span>
+</div>
+
+<a ng-click="toggleControls()" class="query-part-name">{{part.def.type}}</a>
+<span>(</span><span class="query-part-parameters"></span><span>)</span>
+`;
+
+  /** @ngInject */
+export function queryPartEditorDirective($compile, templateSrv) {
+
+  var paramTemplate = '<input type="text" style="display:none"' +
+    ' class="input-mini tight-form-func-param"></input>';
+  return {
+    restrict: 'E',
+    template: template,
+    scope: {
+      part: "=",
+      removeAction: "&",
+      partUpdated: "&",
+      getOptions: "&",
+    },
+    link: function postLink($scope, elem) {
+      var part = $scope.part;
+      var partDef = part.def;
+      var $paramsContainer = elem.find('.query-part-parameters');
+      var $controlsContainer = elem.find('.tight-form-func-controls');
+
+      function clickFuncParam(paramIndex) {
+        /*jshint validthis:true */
+        var $link = $(this);
+        var $input = $link.next();
+
+        $input.val(part.params[paramIndex]);
+        $input.css('width', ($link.width() + 16) + 'px');
+
+        $link.hide();
+        $input.show();
+        $input.focus();
+        $input.select();
+
+        var typeahead = $input.data('typeahead');
+        if (typeahead) {
+          $input.val('');
+          typeahead.lookup();
+        }
+      }
+
+      function inputBlur(paramIndex) {
+        /*jshint validthis:true */
+        var $input = $(this);
+        var $link = $input.prev();
+        var newValue = $input.val();
+
+        if (newValue !== '' || part.def.params[paramIndex].optional) {
+          $link.html(templateSrv.highlightVariablesAsHtml(newValue));
+
+          part.updateParam($input.val(), paramIndex);
+          $scope.$apply($scope.partUpdated);
+        }
+
+        $input.hide();
+        $link.show();
+      }
+
+      function inputKeyPress(paramIndex, e) {
+        /*jshint validthis:true */
+        if (e.which === 13) {
+          inputBlur.call(this, paramIndex);
+        }
+      }
+
+      function inputKeyDown() {
+        /*jshint validthis:true */
+        this.style.width = (3 + this.value.length) * 8 + 'px';
+      }
+
+      function addTypeahead($input, param, paramIndex) {
+        if (!param.options && !param.dynamicLookup) {
+          return;
+        }
+
+        var typeaheadSource = function (query, callback) {
+          if (param.options) { return param.options; }
+
+          $scope.$apply(function() {
+            $scope.getOptions().then(function(result) {
+              var dynamicOptions = _.map(result, function(op) { return op.value; });
+              callback(dynamicOptions);
+            });
+          });
+        };
+
+        $input.attr('data-provide', 'typeahead');
+        var options = param.options;
+        if (param.type === 'int') {
+          options = _.map(options, function(val) { return val.toString(); });
+        }
+
+        $input.typeahead({
+          source: typeaheadSource,
+          minLength: 0,
+          items: 1000,
+          updater: function (value) {
+            setTimeout(function() {
+              inputBlur.call($input[0], paramIndex);
+            }, 0);
+            return value;
+          }
+        });
+
+        var typeahead = $input.data('typeahead');
+        typeahead.lookup = function () {
+          this.query = this.$element.val() || '';
+          var items = this.source(this.query, $.proxy(this.process, this));
+          return items ? this.process(items) : items;
+        };
+      }
+
+      $scope.toggleControls = function() {
+        var targetDiv = elem.closest('.tight-form');
+
+        if (elem.hasClass('show-function-controls')) {
+          elem.removeClass('show-function-controls');
+          targetDiv.removeClass('has-open-function');
+          $controlsContainer.hide();
+          return;
+        }
+
+        elem.addClass('show-function-controls');
+        targetDiv.addClass('has-open-function');
+        $controlsContainer.show();
+      };
+
+      $scope.removeActionInternal = function() {
+        $scope.toggleControls();
+        $scope.removeAction();
+      };
+
+      function addElementsAndCompile() {
+        _.each(partDef.params, function(param, index) {
+          if (param.optional && part.params.length <= index) {
+            return;
+          }
+
+          if (index > 0) {
+            $('<span>, </span>').appendTo($paramsContainer);
+          }
+
+          var paramValue = templateSrv.highlightVariablesAsHtml(part.params[index]);
+          var $paramLink = $('<a class="graphite-func-param-link pointer">' + paramValue + '</a>');
+          var $input = $(paramTemplate);
+
+          $paramLink.appendTo($paramsContainer);
+          $input.appendTo($paramsContainer);
+
+          $input.blur(_.partial(inputBlur, index));
+          $input.keyup(inputKeyDown);
+          $input.keypress(_.partial(inputKeyPress, index));
+          $paramLink.click(_.partial(clickFuncParam, index));
+
+          addTypeahead($input, param, index);
+        });
+      }
+
+      function relink() {
+        $paramsContainer.empty();
+        addElementsAndCompile();
+      }
+
+      relink();
+    }
+  };
+}
+
+coreModule.directive('queryPartEditor', queryPartEditorDirective);

+ 2 - 0
public/app/core/core.ts

@@ -33,6 +33,7 @@ import {Emitter} from './utils/emitter';
 import {layoutSelector} from './components/layout_selector/layout_selector';
 import {layoutSelector} from './components/layout_selector/layout_selector';
 import {switchDirective} from './components/switch';
 import {switchDirective} from './components/switch';
 import {dashboardSelector} from './components/dashboard_selector';
 import {dashboardSelector} from './components/dashboard_selector';
+import {queryPartEditorDirective} from './components/query_part/query_part_editor';
 import 'app/core/controllers/all';
 import 'app/core/controllers/all';
 import 'app/core/services/all';
 import 'app/core/services/all';
 import 'app/core/routes/routes';
 import 'app/core/routes/routes';
@@ -56,4 +57,5 @@ export {
   Emitter,
   Emitter,
   appEvents,
   appEvents,
   dashboardSelector,
   dashboardSelector,
+  queryPartEditorDirective,
 };
 };

+ 4 - 4
public/app/plugins/datasource/influxdb/partials/query.editor.html

@@ -35,13 +35,13 @@
 			</div>
 			</div>
 
 
 			<div class="gf-form" ng-repeat="part in selectParts">
 			<div class="gf-form" ng-repeat="part in selectParts">
-				<influx-query-part-editor
+				<query-part-editor
 														class="gf-form-label query-part"
 														class="gf-form-label query-part"
 														part="part"
 														part="part"
 														remove-action="ctrl.removeSelectPart(selectParts, part)"
 														remove-action="ctrl.removeSelectPart(selectParts, part)"
 														part-updated="ctrl.selectPartUpdated(selectParts, part)"
 														part-updated="ctrl.selectPartUpdated(selectParts, part)"
 														get-options="ctrl.getPartOptions(part)">
 														get-options="ctrl.getPartOptions(part)">
-				</influx-query-part-editor>
+				</query-part-editor>
 			</div>
 			</div>
 
 
 			<div class="gf-form">
 			<div class="gf-form">
@@ -62,12 +62,12 @@
 					<span>GROUP BY</span>
 					<span>GROUP BY</span>
 				</label>
 				</label>
 
 
-				<influx-query-part-editor
+				<query-part-editor
 								ng-repeat="part in ctrl.queryModel.groupByParts"
 								ng-repeat="part in ctrl.queryModel.groupByParts"
 								part="part"
 								part="part"
 								class="gf-form-label query-part"
 								class="gf-form-label query-part"
 								remove-action="ctrl.removeGroupByPart(part, $index)" part-updated="ctrl.refresh();" get-options="ctrl.getPartOptions(part)">
 								remove-action="ctrl.removeGroupByPart(part, $index)" part-updated="ctrl.refresh();" get-options="ctrl.getPartOptions(part)">
-				</influx-query-part-editor>
+				</query-part-editor>
 			</div>
 			</div>
 
 
 			<div class="gf-form">
 			<div class="gf-form">

+ 0 - 5
public/app/plugins/datasource/influxdb/partials/query_part.html

@@ -1,5 +0,0 @@
-<div class="tight-form-func-controls">
-	<span class="pointer fa fa-remove" ng-click="removeActionInternal()" ></span>
-</div>
-
-<a ng-click="toggleControls()" class="query-part-name">{{part.def.type}}</a><span>(</span><span class="query-part-parameters"></span><span>)</span>

+ 0 - 3
public/app/plugins/datasource/influxdb/query_ctrl.ts

@@ -1,8 +1,5 @@
 ///<reference path="../../../headers/common.d.ts" />
 ///<reference path="../../../headers/common.d.ts" />
 
 
-import './query_part_editor';
-import './query_part_editor';
-
 import angular from 'angular';
 import angular from 'angular';
 import _ from 'lodash';
 import _ from 'lodash';
 import InfluxQueryBuilder from './query_builder';
 import InfluxQueryBuilder from './query_builder';

+ 46 - 148
public/app/plugins/datasource/influxdb/query_part.ts

@@ -1,6 +1,14 @@
 ///<reference path="../../../headers/common.d.ts" />
 ///<reference path="../../../headers/common.d.ts" />
 
 
 import _ from 'lodash';
 import _ from 'lodash';
+import {
+  QueryPartDef,
+  QueryPart,
+  functionRenderer,
+  suffixRenderer,
+  identityRenderer,
+  quotedIdentityRenderer,
+} from 'app/core/components/query_part/query_part';
 
 
 var index = [];
 var index = [];
 var categories = {
 var categories = {
@@ -12,71 +20,26 @@ var categories = {
   Fields: [],
   Fields: [],
 };
 };
 
 
-var groupByTimeFunctions = [];
-
-class QueryPartDef {
-  type: string;
-  params: any[];
-  defaultParams: any[];
-  renderer: any;
-  category: any;
-  addStrategy: any;
-
-  constructor(options: any) {
-    this.type = options.type;
-    this.params = options.params;
-    this.defaultParams = options.defaultParams;
-    this.renderer = options.renderer;
-    this.category = options.category;
-    this.addStrategy = options.addStrategy;
-  }
-
-  static register(options: any) {
-    index[options.type] = new QueryPartDef(options);
-    options.category.push(index[options.type]);
+function createPart(part): any {
+  var def = index[part.type];
+  if (!def) {
+    throw {message: 'Could not find query part ' + part.type};
   }
   }
-}
 
 
-function functionRenderer(part, innerExpr) {
-  var str = part.def.type + '(';
-  var parameters = _.map(part.params, (value, index) => {
-    var paramType = part.def.params[index];
-    if (paramType.type === 'time') {
-      if (value === 'auto') {
-        value = '$interval';
-      }
-    }
-    if (paramType.quote === 'single') {
-      return "'" + value + "'";
-    } else if (paramType.quote === 'double') {
-      return '"' + value + '"';
-    }
-
-    return value;
-  });
+  return new QueryPart(part, def);
+};
 
 
-  if (innerExpr) {
-    parameters.unshift(innerExpr);
-  }
-  return str + parameters.join(', ') + ')';
+function register(options: any) {
+  index[options.type] = new QueryPartDef(options);
+  options.category.push(index[options.type]);
 }
 }
 
 
+var groupByTimeFunctions = [];
+
 function aliasRenderer(part, innerExpr) {
 function aliasRenderer(part, innerExpr) {
   return innerExpr + ' AS ' + '"' + part.params[0] + '"';
   return innerExpr + ' AS ' + '"' + part.params[0] + '"';
 }
 }
 
 
-function suffixRenderer(part, innerExpr) {
-  return innerExpr + ' ' + part.params[0];
-}
-
-function identityRenderer(part, innerExpr) {
-  return part.params[0];
-}
-
-function quotedIdentityRenderer(part, innerExpr) {
-  return '"' + part.params[0] + '"';
-}
-
 function fieldRenderer(part, innerExpr) {
 function fieldRenderer(part, innerExpr) {
   if (part.params[0] === '*')  {
   if (part.params[0] === '*')  {
     return '*';
     return '*';
@@ -149,13 +112,13 @@ function addAliasStrategy(selectParts, partModel) {
 function addFieldStrategy(selectParts, partModel, query) {
 function addFieldStrategy(selectParts, partModel, query) {
   // copy all parts
   // copy all parts
   var parts = _.map(selectParts, function(part: any) {
   var parts = _.map(selectParts, function(part: any) {
-    return new QueryPart({type: part.def.type, params: _.clone(part.params)});
+    return createPart({type: part.def.type, params: _.clone(part.params)});
   });
   });
 
 
   query.selectModels.push(parts);
   query.selectModels.push(parts);
 }
 }
 
 
-QueryPartDef.register({
+register({
   type: 'field',
   type: 'field',
   addStrategy: addFieldStrategy,
   addStrategy: addFieldStrategy,
   category: categories.Fields,
   category: categories.Fields,
@@ -165,7 +128,7 @@ QueryPartDef.register({
 });
 });
 
 
 // Aggregations
 // Aggregations
-QueryPartDef.register({
+register({
   type: 'count',
   type: 'count',
   addStrategy: replaceAggregationAddStrategy,
   addStrategy: replaceAggregationAddStrategy,
   category: categories.Aggregations,
   category: categories.Aggregations,
@@ -174,7 +137,7 @@ QueryPartDef.register({
   renderer: functionRenderer,
   renderer: functionRenderer,
 });
 });
 
 
-QueryPartDef.register({
+register({
   type: 'distinct',
   type: 'distinct',
   addStrategy: replaceAggregationAddStrategy,
   addStrategy: replaceAggregationAddStrategy,
   category: categories.Aggregations,
   category: categories.Aggregations,
@@ -183,7 +146,7 @@ QueryPartDef.register({
   renderer: functionRenderer,
   renderer: functionRenderer,
 });
 });
 
 
-QueryPartDef.register({
+register({
   type: 'integral',
   type: 'integral',
   addStrategy: replaceAggregationAddStrategy,
   addStrategy: replaceAggregationAddStrategy,
   category: categories.Aggregations,
   category: categories.Aggregations,
@@ -192,7 +155,7 @@ QueryPartDef.register({
   renderer: functionRenderer,
   renderer: functionRenderer,
 });
 });
 
 
-QueryPartDef.register({
+register({
   type: 'mean',
   type: 'mean',
   addStrategy: replaceAggregationAddStrategy,
   addStrategy: replaceAggregationAddStrategy,
   category: categories.Aggregations,
   category: categories.Aggregations,
@@ -201,7 +164,7 @@ QueryPartDef.register({
   renderer: functionRenderer,
   renderer: functionRenderer,
 });
 });
 
 
-QueryPartDef.register({
+register({
   type: 'median',
   type: 'median',
   addStrategy: replaceAggregationAddStrategy,
   addStrategy: replaceAggregationAddStrategy,
   category: categories.Aggregations,
   category: categories.Aggregations,
@@ -210,7 +173,7 @@ QueryPartDef.register({
   renderer: functionRenderer,
   renderer: functionRenderer,
 });
 });
 
 
-QueryPartDef.register({
+register({
   type: 'sum',
   type: 'sum',
   addStrategy: replaceAggregationAddStrategy,
   addStrategy: replaceAggregationAddStrategy,
   category: categories.Aggregations,
   category: categories.Aggregations,
@@ -221,7 +184,7 @@ QueryPartDef.register({
 
 
 // transformations
 // transformations
 
 
-QueryPartDef.register({
+register({
   type: 'derivative',
   type: 'derivative',
   addStrategy: addTransformationStrategy,
   addStrategy: addTransformationStrategy,
   category: categories.Transformations,
   category: categories.Transformations,
@@ -230,7 +193,7 @@ QueryPartDef.register({
   renderer: functionRenderer,
   renderer: functionRenderer,
 });
 });
 
 
-QueryPartDef.register({
+register({
   type: 'spread',
   type: 'spread',
   addStrategy: addTransformationStrategy,
   addStrategy: addTransformationStrategy,
   category: categories.Transformations,
   category: categories.Transformations,
@@ -239,7 +202,7 @@ QueryPartDef.register({
   renderer: functionRenderer,
   renderer: functionRenderer,
 });
 });
 
 
-QueryPartDef.register({
+register({
   type: 'non_negative_derivative',
   type: 'non_negative_derivative',
   addStrategy: addTransformationStrategy,
   addStrategy: addTransformationStrategy,
   category: categories.Transformations,
   category: categories.Transformations,
@@ -248,7 +211,7 @@ QueryPartDef.register({
   renderer: functionRenderer,
   renderer: functionRenderer,
 });
 });
 
 
-QueryPartDef.register({
+register({
   type: 'difference',
   type: 'difference',
   addStrategy: addTransformationStrategy,
   addStrategy: addTransformationStrategy,
   category: categories.Transformations,
   category: categories.Transformations,
@@ -257,7 +220,7 @@ QueryPartDef.register({
   renderer: functionRenderer,
   renderer: functionRenderer,
 });
 });
 
 
-QueryPartDef.register({
+register({
   type: 'moving_average',
   type: 'moving_average',
   addStrategy: addTransformationStrategy,
   addStrategy: addTransformationStrategy,
   category: categories.Transformations,
   category: categories.Transformations,
@@ -266,7 +229,7 @@ QueryPartDef.register({
   renderer: functionRenderer,
   renderer: functionRenderer,
 });
 });
 
 
-QueryPartDef.register({
+register({
   type: 'stddev',
   type: 'stddev',
   addStrategy: addTransformationStrategy,
   addStrategy: addTransformationStrategy,
   category: categories.Transformations,
   category: categories.Transformations,
@@ -275,7 +238,7 @@ QueryPartDef.register({
   renderer: functionRenderer,
   renderer: functionRenderer,
 });
 });
 
 
-QueryPartDef.register({
+register({
   type: 'time',
   type: 'time',
   category: groupByTimeFunctions,
   category: groupByTimeFunctions,
   params: [{ name: "interval", type: "time", options: ['auto', '1s', '10s', '1m', '5m', '10m', '15m', '1h'] }],
   params: [{ name: "interval", type: "time", options: ['auto', '1s', '10s', '1m', '5m', '10m', '15m', '1h'] }],
@@ -283,7 +246,7 @@ QueryPartDef.register({
   renderer: functionRenderer,
   renderer: functionRenderer,
 });
 });
 
 
-QueryPartDef.register({
+register({
   type: 'fill',
   type: 'fill',
   category: groupByTimeFunctions,
   category: groupByTimeFunctions,
   params: [{ name: "fill", type: "string", options: ['none', 'null', '0', 'previous'] }],
   params: [{ name: "fill", type: "string", options: ['none', 'null', '0', 'previous'] }],
@@ -292,7 +255,7 @@ QueryPartDef.register({
 });
 });
 
 
 // Selectors
 // Selectors
-QueryPartDef.register({
+register({
   type: 'bottom',
   type: 'bottom',
   addStrategy: replaceAggregationAddStrategy,
   addStrategy: replaceAggregationAddStrategy,
   category: categories.Selectors,
   category: categories.Selectors,
@@ -301,7 +264,7 @@ QueryPartDef.register({
   renderer: functionRenderer,
   renderer: functionRenderer,
 });
 });
 
 
-QueryPartDef.register({
+register({
   type: 'first',
   type: 'first',
   addStrategy: replaceAggregationAddStrategy,
   addStrategy: replaceAggregationAddStrategy,
   category: categories.Selectors,
   category: categories.Selectors,
@@ -310,7 +273,7 @@ QueryPartDef.register({
   renderer: functionRenderer,
   renderer: functionRenderer,
 });
 });
 
 
-QueryPartDef.register({
+register({
   type: 'last',
   type: 'last',
   addStrategy: replaceAggregationAddStrategy,
   addStrategy: replaceAggregationAddStrategy,
   category: categories.Selectors,
   category: categories.Selectors,
@@ -319,7 +282,7 @@ QueryPartDef.register({
   renderer: functionRenderer,
   renderer: functionRenderer,
 });
 });
 
 
-QueryPartDef.register({
+register({
   type: 'max',
   type: 'max',
   addStrategy: replaceAggregationAddStrategy,
   addStrategy: replaceAggregationAddStrategy,
   category: categories.Selectors,
   category: categories.Selectors,
@@ -328,7 +291,7 @@ QueryPartDef.register({
   renderer: functionRenderer,
   renderer: functionRenderer,
 });
 });
 
 
-QueryPartDef.register({
+register({
   type: 'min',
   type: 'min',
   addStrategy: replaceAggregationAddStrategy,
   addStrategy: replaceAggregationAddStrategy,
   category: categories.Selectors,
   category: categories.Selectors,
@@ -337,7 +300,7 @@ QueryPartDef.register({
   renderer: functionRenderer,
   renderer: functionRenderer,
 });
 });
 
 
-QueryPartDef.register({
+register({
   type: 'percentile',
   type: 'percentile',
   addStrategy: replaceAggregationAddStrategy,
   addStrategy: replaceAggregationAddStrategy,
   category: categories.Selectors,
   category: categories.Selectors,
@@ -346,7 +309,7 @@ QueryPartDef.register({
   renderer: functionRenderer,
   renderer: functionRenderer,
 });
 });
 
 
-QueryPartDef.register({
+register({
   type: 'top',
   type: 'top',
   addStrategy: replaceAggregationAddStrategy,
   addStrategy: replaceAggregationAddStrategy,
   category: categories.Selectors,
   category: categories.Selectors,
@@ -355,7 +318,7 @@ QueryPartDef.register({
   renderer: functionRenderer,
   renderer: functionRenderer,
 });
 });
 
 
-QueryPartDef.register({
+register({
   type: 'tag',
   type: 'tag',
   category: groupByTimeFunctions,
   category: groupByTimeFunctions,
   params: [{name: 'tag', type: 'string', dynamicLookup: true}],
   params: [{name: 'tag', type: 'string', dynamicLookup: true}],
@@ -363,7 +326,7 @@ QueryPartDef.register({
   renderer: fieldRenderer,
   renderer: fieldRenderer,
 });
 });
 
 
-QueryPartDef.register({
+register({
   type: 'math',
   type: 'math',
   addStrategy: addMathStrategy,
   addStrategy: addMathStrategy,
   category: categories.Math,
   category: categories.Math,
@@ -372,7 +335,7 @@ QueryPartDef.register({
   renderer: suffixRenderer,
   renderer: suffixRenderer,
 });
 });
 
 
-QueryPartDef.register({
+register({
   type: 'alias',
   type: 'alias',
   addStrategy: addAliasStrategy,
   addStrategy: addAliasStrategy,
   category: categories.Aliasing,
   category: categories.Aliasing,
@@ -382,74 +345,9 @@ QueryPartDef.register({
   renderer: aliasRenderer,
   renderer: aliasRenderer,
 });
 });
 
 
-class QueryPart {
-  part: any;
-  def: QueryPartDef;
-  params: any[];
-  text: string;
-
-  constructor(part: any) {
-    this.part = part;
-    this.def = index[part.type];
-    if (!this.def) {
-      throw {message: 'Could not find query part ' + part.type};
-    }
-
-    part.params = part.params || _.clone(this.def.defaultParams);
-    this.params = part.params;
-    this.updateText();
-  }
-
-  render(innerExpr: string) {
-    return this.def.renderer(this, innerExpr);
-  }
-
-  hasMultipleParamsInString (strValue, index) {
-    if (strValue.indexOf(',') === -1) {
-      return false;
-    }
-
-    return this.def.params[index + 1] && this.def.params[index + 1].optional;
-  }
-
-  updateParam (strValue, index) {
-    // handle optional parameters
-    // if string contains ',' and next param is optional, split and update both
-    if (this.hasMultipleParamsInString(strValue, index)) {
-      _.each(strValue.split(','), function(partVal: string, idx) {
-        this.updateParam(partVal.trim(), idx);
-      }, this);
-      return;
-    }
-
-    if (strValue === '' && this.def.params[index].optional) {
-      this.params.splice(index, 1);
-    } else {
-      this.params[index] = strValue;
-    }
-
-    this.part.params = this.params;
-    this.updateText();
-  }
-
-  updateText() {
-    if (this.params.length === 0) {
-      this.text = this.def.type + '()';
-      return;
-    }
-
-    var text = this.def.type + '(';
-    text += this.params.join(', ');
-    text += ')';
-    this.text = text;
-  }
-}
 
 
 export default {
 export default {
-  create: function(part): any {
-    return new QueryPart(part);
-  },
-
+  create: createPart,
   getCategories: function() {
   getCategories: function() {
     return categories;
     return categories;
   }
   }

+ 0 - 178
public/app/plugins/datasource/influxdb/query_part_editor.js

@@ -1,178 +0,0 @@
-define([
-  'angular',
-  'lodash',
-  'jquery',
-],
-function (angular, _, $) {
-  'use strict';
-
-  angular
-    .module('grafana.directives')
-    .directive('influxQueryPartEditor', function($compile, templateSrv) {
-
-      var paramTemplate = '<input type="text" style="display:none"' +
-                          ' class="input-mini tight-form-func-param"></input>';
-      return {
-        restrict: 'E',
-        templateUrl: 'public/app/plugins/datasource/influxdb/partials/query_part.html',
-        scope: {
-          part: "=",
-          removeAction: "&",
-          partUpdated: "&",
-          getOptions: "&",
-        },
-        link: function postLink($scope, elem) {
-          var part = $scope.part;
-          var partDef = part.def;
-          var $paramsContainer = elem.find('.query-part-parameters');
-          var $controlsContainer = elem.find('.tight-form-func-controls');
-
-          function clickFuncParam(paramIndex) {
-            /*jshint validthis:true */
-            var $link = $(this);
-            var $input = $link.next();
-
-            $input.val(part.params[paramIndex]);
-            $input.css('width', ($link.width() + 16) + 'px');
-
-            $link.hide();
-            $input.show();
-            $input.focus();
-            $input.select();
-
-            var typeahead = $input.data('typeahead');
-            if (typeahead) {
-              $input.val('');
-              typeahead.lookup();
-            }
-          }
-
-          function inputBlur(paramIndex) {
-            /*jshint validthis:true */
-            var $input = $(this);
-            var $link = $input.prev();
-            var newValue = $input.val();
-
-            if (newValue !== '' || part.def.params[paramIndex].optional) {
-              $link.html(templateSrv.highlightVariablesAsHtml(newValue));
-
-              part.updateParam($input.val(), paramIndex);
-              $scope.$apply($scope.partUpdated);
-            }
-
-            $input.hide();
-            $link.show();
-          }
-
-          function inputKeyPress(paramIndex, e) {
-            /*jshint validthis:true */
-            if(e.which === 13) {
-              inputBlur.call(this, paramIndex);
-            }
-          }
-
-          function inputKeyDown() {
-            /*jshint validthis:true */
-            this.style.width = (3 + this.value.length) * 8 + 'px';
-          }
-
-          function addTypeahead($input, param, paramIndex) {
-            if (!param.options && !param.dynamicLookup) {
-              return;
-            }
-
-            var typeaheadSource = function (query, callback) {
-              if (param.options) { return param.options; }
-
-              $scope.$apply(function() {
-                $scope.getOptions().then(function(result) {
-                  var dynamicOptions = _.map(result, function(op) { return op.value; });
-                  callback(dynamicOptions);
-                });
-              });
-            };
-
-            $input.attr('data-provide', 'typeahead');
-            var options = param.options;
-            if (param.type === 'int') {
-              options = _.map(options, function(val) { return val.toString(); });
-            }
-
-            $input.typeahead({
-              source: typeaheadSource,
-              minLength: 0,
-              items: 1000,
-              updater: function (value) {
-                setTimeout(function() {
-                  inputBlur.call($input[0], paramIndex);
-                }, 0);
-                return value;
-              }
-            });
-
-            var typeahead = $input.data('typeahead');
-            typeahead.lookup = function () {
-              this.query = this.$element.val() || '';
-              var items = this.source(this.query, $.proxy(this.process, this));
-              return items ? this.process(items) : items;
-            };
-          }
-
-          $scope.toggleControls = function() {
-            var targetDiv = elem.closest('.tight-form');
-
-            if (elem.hasClass('show-function-controls')) {
-              elem.removeClass('show-function-controls');
-              targetDiv.removeClass('has-open-function');
-              $controlsContainer.hide();
-              return;
-            }
-
-            elem.addClass('show-function-controls');
-            targetDiv.addClass('has-open-function');
-            $controlsContainer.show();
-          };
-
-          $scope.removeActionInternal = function() {
-            $scope.toggleControls();
-            $scope.removeAction();
-          };
-
-          function addElementsAndCompile() {
-            _.each(partDef.params, function(param, index) {
-              if (param.optional && part.params.length <= index) {
-                return;
-              }
-
-              if (index > 0) {
-                $('<span>, </span>').appendTo($paramsContainer);
-              }
-
-              var paramValue = templateSrv.highlightVariablesAsHtml(part.params[index]);
-              var $paramLink = $('<a class="graphite-func-param-link pointer">' + paramValue + '</a>');
-              var $input = $(paramTemplate);
-
-              $paramLink.appendTo($paramsContainer);
-              $input.appendTo($paramsContainer);
-
-              $input.blur(_.partial(inputBlur, index));
-              $input.keyup(inputKeyDown);
-              $input.keypress(_.partial(inputKeyPress, index));
-              $paramLink.click(_.partial(clickFuncParam, index));
-
-              addTypeahead($input, param, index);
-            });
-          }
-
-          function relink() {
-            $paramsContainer.empty();
-            addElementsAndCompile();
-          }
-
-          relink();
-        }
-      };
-
-    });
-
-});