define([ 'angular', 'lodash', 'jquery', 'rst2html', ], function (angular, _, $, rst2html) { 'use strict'; angular .module('grafana.directives') .directive('graphiteFuncEditor', function($compile, templateSrv) { var funcSpanTemplate = '{{func.def.name}}('; var paramTemplate = ''; var funcControlsTemplate = '
' + '' + '' + '' + '' + '
'; return { restrict: 'A', link: function postLink($scope, elem) { var $funcLink = $(funcSpanTemplate); var $funcControls = $(funcControlsTemplate); var ctrl = $scope.ctrl; var func = $scope.func; var scheduledRelink = false; var paramCountAtLink = 0; function clickFuncParam(paramIndex) { /*jshint validthis:true */ var $link = $(this); var $comma = $link.prev('.comma'); var $input = $link.next(); $input.val(func.params[paramIndex]); $comma.removeClass('last'); $link.hide(); $input.show(); $input.focus(); $input.select(); var typeahead = $input.data('typeahead'); if (typeahead) { $input.val(''); typeahead.lookup(); } } function scheduledRelinkIfNeeded() { if (paramCountAtLink === func.params.length) { return; } if (!scheduledRelink) { scheduledRelink = true; setTimeout(function() { relink(); scheduledRelink = false; }, 200); } } function paramDef(index) { if (index < func.def.params.length) { return func.def.params[index]; } if (_.last(func.def.params).multiple) { return _.assign({}, _.last(func.def.params), {optional: true}); } return {}; } function inputBlur(paramIndex) { /*jshint validthis:true */ var $input = $(this); if ($input.data('typeahead') && $input.data('typeahead').shown) { return; } var $link = $input.prev(); var $comma = $link.prev('.comma'); var newValue = $input.val(); if (newValue !== '' || paramDef(paramIndex).optional) { $link.html(templateSrv.highlightVariablesAsHtml(newValue)); func.updateParam(newValue, paramIndex); scheduledRelinkIfNeeded(); $scope.$apply(function() { ctrl.targetChanged(); }); if ($link.hasClass('last') && newValue === '') { $comma.addClass('last'); } else { $link.removeClass('last'); } $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, paramIndex) { $input.attr('data-provide', 'typeahead'); var options = paramDef(paramIndex).options; if (paramDef(paramIndex).type === 'int') { options = _.map(options, function(val) { return val.toString(); }); } $input.typeahead({ source: options, minLength: 0, items: 20, 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() || ''; return this.process(this.source); }; } function toggleFuncControls() { var targetDiv = elem.closest('.tight-form'); if (elem.hasClass('show-function-controls')) { elem.removeClass('show-function-controls'); targetDiv.removeClass('has-open-function'); $funcControls.hide(); return; } elem.addClass('show-function-controls'); targetDiv.addClass('has-open-function'); $funcControls.show(); } function addElementsAndCompile() { $funcControls.appendTo(elem); $funcLink.appendTo(elem); var defParams = _.clone(func.def.params); var lastParam = _.last(func.def.params); while (func.params.length >= defParams.length && lastParam && lastParam.multiple) { defParams.push(_.assign({}, lastParam, {optional: true})); } _.each(defParams, function(param, index) { if (param.optional && func.params.length < index) { return false; } var paramValue = templateSrv.highlightVariablesAsHtml(func.params[index]); var last = (index >= func.params.length - 1) && param.optional && !paramValue; if (last && param.multiple) { paramValue = '+'; } if (index > 0) { $(', ').appendTo(elem); } var $paramLink = $( '' + (paramValue || ' ') + ''); var $input = $(paramTemplate); $input.attr('placeholder', param.name); paramCountAtLink++; $paramLink.appendTo(elem); $input.appendTo(elem); $input.blur(_.partial(inputBlur, index)); $input.keyup(inputKeyDown); $input.keypress(_.partial(inputKeyPress, index)); $paramLink.click(_.partial(clickFuncParam, index)); if (param.options) { addTypeahead($input, index); } }); $(')').appendTo(elem); $compile(elem.contents())($scope); } function ifJustAddedFocusFirstParam() { if ($scope.func.added) { $scope.func.added = false; setTimeout(function() { elem.find('.graphite-func-param-link').first().click(); }, 10); } } function registerFuncControlsToggle() { $funcLink.click(toggleFuncControls); } function registerFuncControlsActions() { $funcControls.click(function(e) { var $target = $(e.target); if ($target.hasClass('fa-remove')) { toggleFuncControls(); $scope.$apply(function() { ctrl.removeFunction($scope.func); }); return; } if ($target.hasClass('fa-arrow-left')) { $scope.$apply(function() { _.move(ctrl.queryModel.functions, $scope.$index, $scope.$index - 1); ctrl.targetChanged(); }); return; } if ($target.hasClass('fa-arrow-right')) { $scope.$apply(function() { _.move(ctrl.queryModel.functions, $scope.$index, $scope.$index + 1); ctrl.targetChanged(); }); return; } if ($target.hasClass('fa-question-circle')) { if (func.def.description) { alert(rst2html(func.def.description)); } else { window.open( "http://graphite.readthedocs.org/en/latest/functions.html#graphite.render.functions." + func.def.name,'_blank'); } return; } }); } function relink() { elem.children().remove(); addElementsAndCompile(); ifJustAddedFocusFirstParam(); registerFuncControlsToggle(); registerFuncControlsActions(); } relink(); } }; }); });