diff --git a/src/app/directives/graphiteFuncEditor.js b/src/app/directives/graphiteFuncEditor.js
index 10516f1ca7c..7767d097c04 100644
--- a/src/app/directives/graphiteFuncEditor.js
+++ b/src/app/directives/graphiteFuncEditor.js
@@ -29,6 +29,8 @@ function (angular, _, $) {
var $funcControls = $(funcControlsTemplate);
var func = $scope.func;
var funcDef = func.def;
+ var scheduledRelink = false;
+ var paramCountAtLink = 0;
function clickFuncParam(paramIndex) {
/*jshint validthis:true */
@@ -51,17 +53,33 @@ function (angular, _, $) {
}
}
+ function scheduledRelinkIfNeeded() {
+ if (paramCountAtLink === func.params.length) {
+ return;
+ }
+
+ if (!scheduledRelink) {
+ scheduledRelink = true;
+ setTimeout(function() {
+ relink();
+ scheduledRelink = false;
+ }, 200);
+ }
+ }
+
function inputBlur(paramIndex) {
/*jshint validthis:true */
var $input = $(this);
var $link = $input.prev();
- if ($input.val() !== '') {
+ if ($input.val() !== '' || func.def.params[paramIndex].optional) {
$link.text($input.val());
-
+
func.updateParam($input.val(), paramIndex);
- $scope.$apply($scope.targetChanged);
+ scheduledRelinkIfNeeded();
+
+ $scope.$apply($scope.targetChanged);
}
$input.hide();
@@ -129,9 +147,19 @@ function (angular, _, $) {
$funcLink.appendTo(elem);
_.each(funcDef.params, function(param, index) {
+ if (param.optional && !func.params[index]) {
+ return;
+ }
+
+ if (index > 0) {
+ $(', ').appendTo(elem);
+ }
+
var $paramLink = $('' + func.params[index] + '');
var $input = $(paramTemplate);
+ paramCountAtLink++;
+
$paramLink.appendTo(elem);
$input.appendTo(elem);
@@ -140,10 +168,6 @@ function (angular, _, $) {
$input.keypress(_.partial(inputKeyPress, index));
$paramLink.click(_.partial(clickFuncParam, index));
- if (index !== funcDef.params.length - 1) {
- $(', ').appendTo(elem);
- }
-
if (funcDef.params[index].options) {
addTypeahead($input, index);
}
@@ -200,10 +224,16 @@ function (angular, _, $) {
});
}
- addElementsAndCompile();
- ifJustAddedFocusFistParam();
- registerFuncControlsToggle();
- registerFuncControlsActions();
+ function relink() {
+ elem.children().remove();
+
+ addElementsAndCompile();
+ ifJustAddedFocusFistParam();
+ registerFuncControlsToggle();
+ registerFuncControlsActions();
+ }
+
+ relink();
}
};
diff --git a/src/app/services/graphite/gfunc.js b/src/app/services/graphite/gfunc.js
index 9902e7c15d6..dadfb736e93 100644
--- a/src/app/services/graphite/gfunc.js
+++ b/src/app/services/graphite/gfunc.js
@@ -132,7 +132,10 @@ function (_) {
addFuncDef({
name: 'aliasByNode',
category: categories.Special,
- params: [ { name: "node", type: "int", options: [0,1,2,3,4,5,6,7,8,9,10,12] } ],
+ params: [
+ { name: "node", type: "int", options: [0,1,2,3,4,5,6,7,8,9,10,12] },
+ { name: "node", type: "int", options: [0,-1,-2,-3,-4,-5,-6,-7], optional: true },
+ ],
defaultParams: [3]
});
@@ -340,13 +343,33 @@ function (_) {
return str + parameters.join(',') + ')';
};
- FuncInstance.prototype.updateParam = function(strValue, index) {
- if (this.def.params[index].type === 'int') {
+ FuncInstance.prototype._hasMultipleParamsInString = function(strValue, index) {
+ if (strValue.indexOf(',') === -1) {
+ return false;
+ }
+
+ return this.def.params[index + 1] && this.def.params[index + 1].optional;
+ };
+
+ FuncInstance.prototype.updateParam = function(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, idx) {
+ this.updateParam(partVal.trim(), idx);
+ }, this);
+ return;
+ }
+
+ if (strValue === '' && this.def.params[index].optional) {
+ this.params.splice(index, 1);
+ }
+ else if (this.def.params[index].type === 'int') {
this.params[index] = parseInt(strValue, 10);
}
else {
this.params[index] = strValue;
- }
+ }
this.updateText();
};
@@ -359,6 +382,10 @@ function (_) {
var text = this.def.name + '(';
_.each(this.def.params, function(param, index) {
+ if (param.optional && this.params[index] === undefined) {
+ return;
+ }
+
text += this.params[index] + ', ';
}, this);
text = text.substring(0, text.length - 2);
diff --git a/src/test/specs/gfunc-specs.js b/src/test/specs/gfunc-specs.js
index b66c05fe08c..942f098fdea 100644
--- a/src/test/specs/gfunc-specs.js
+++ b/src/test/specs/gfunc-specs.js
@@ -57,12 +57,47 @@ define([
});
describe('when requesting function categories', function() {
-
it('should return function categories', function() {
var catIndex = gfunc.getCategories();
expect(catIndex.Special.length).to.be.greaterThan(8);
});
+ });
+
+ describe('when updating func param', function() {
+ it('should update param value and update text representation', function() {
+ var func = gfunc.createFuncInstance('summarize');
+ func.updateParam('1h', 0);
+ expect(func.params[0]).to.be('1h');
+ expect(func.text).to.be('summarize(1h, sum)');
+ });
+
});
+ describe('when updating func param with optional second parameter', function() {
+ it('should update value and text', function() {
+ var func = gfunc.createFuncInstance('aliasByNode');
+ func.updateParam('1', 0);
+ expect(func.params[0]).to.be(1);
+ });
+
+ it('should slit text and put value in second param', function() {
+ var func = gfunc.createFuncInstance('aliasByNode');
+ func.updateParam('4,-5', 0);
+ expect(func.params[0]).to.be(4);
+ expect(func.params[1]).to.be(-5);
+ expect(func.text).to.be('aliasByNode(4, -5)');
+ });
+
+ it('should remove second param when empty string is set', function() {
+ var func = gfunc.createFuncInstance('aliasByNode');
+ func.updateParam('4,-5', 0);
+ func.updateParam('', 1);
+ expect(func.params[0]).to.be(4);
+ expect(func.params[1]).to.be(undefined);
+ expect(func.text).to.be('aliasByNode(4)');
+ });
+ });
+
});
+