func_editor.js 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. define([
  2. 'angular',
  3. 'lodash',
  4. 'jquery',
  5. ],
  6. function (angular, _, $) {
  7. 'use strict';
  8. angular
  9. .module('grafana.directives')
  10. .directive('graphiteFuncEditor', function($compile, templateSrv) {
  11. var funcSpanTemplate = '<a ng-click="">{{func.def.name}}</a><span>(</span>';
  12. var paramTemplate = '<input type="text" style="display:none"' +
  13. ' class="input-mini tight-form-func-param"></input>';
  14. var funcControlsTemplate =
  15. '<div class="tight-form-func-controls">' +
  16. '<span class="pointer fa fa-arrow-left"></span>' +
  17. '<span class="pointer fa fa-question-circle"></span>' +
  18. '<span class="pointer fa fa-remove" ></span>' +
  19. '<span class="pointer fa fa-arrow-right"></span>' +
  20. '</div>';
  21. return {
  22. restrict: 'A',
  23. link: function postLink($scope, elem) {
  24. var $funcLink = $(funcSpanTemplate);
  25. var $funcControls = $(funcControlsTemplate);
  26. var ctrl = $scope.ctrl;
  27. var func = $scope.func;
  28. var funcDef = func.def;
  29. var scheduledRelink = false;
  30. var paramCountAtLink = 0;
  31. function clickFuncParam(paramIndex) {
  32. /*jshint validthis:true */
  33. var $link = $(this);
  34. var $input = $link.next();
  35. $input.val(func.params[paramIndex]);
  36. $input.css('width', ($link.width() + 16) + 'px');
  37. $link.hide();
  38. $input.show();
  39. $input.focus();
  40. $input.select();
  41. var typeahead = $input.data('typeahead');
  42. if (typeahead) {
  43. $input.val('');
  44. typeahead.lookup();
  45. }
  46. }
  47. function scheduledRelinkIfNeeded() {
  48. if (paramCountAtLink === func.params.length) {
  49. return;
  50. }
  51. if (!scheduledRelink) {
  52. scheduledRelink = true;
  53. setTimeout(function() {
  54. relink();
  55. scheduledRelink = false;
  56. }, 200);
  57. }
  58. }
  59. function inputBlur(paramIndex) {
  60. /*jshint validthis:true */
  61. var $input = $(this);
  62. var $link = $input.prev();
  63. var newValue = $input.val();
  64. if (newValue !== '' || func.def.params[paramIndex].optional) {
  65. $link.html(templateSrv.highlightVariablesAsHtml(newValue));
  66. func.updateParam($input.val(), paramIndex);
  67. scheduledRelinkIfNeeded();
  68. $scope.$apply(function() {
  69. ctrl.targetChanged();
  70. });
  71. $input.hide();
  72. $link.show();
  73. }
  74. }
  75. function inputKeyPress(paramIndex, e) {
  76. /*jshint validthis:true */
  77. if(e.which === 13) {
  78. inputBlur.call(this, paramIndex);
  79. }
  80. }
  81. function inputKeyDown() {
  82. /*jshint validthis:true */
  83. this.style.width = (3 + this.value.length) * 8 + 'px';
  84. }
  85. function addTypeahead($input, paramIndex) {
  86. $input.attr('data-provide', 'typeahead');
  87. var options = funcDef.params[paramIndex].options;
  88. if (funcDef.params[paramIndex].type === 'int') {
  89. options = _.map(options, function(val) { return val.toString(); });
  90. }
  91. $input.typeahead({
  92. source: options,
  93. minLength: 0,
  94. items: 20,
  95. updater: function (value) {
  96. setTimeout(function() {
  97. inputBlur.call($input[0], paramIndex);
  98. }, 0);
  99. return value;
  100. }
  101. });
  102. var typeahead = $input.data('typeahead');
  103. typeahead.lookup = function () {
  104. this.query = this.$element.val() || '';
  105. return this.process(this.source);
  106. };
  107. }
  108. function toggleFuncControls() {
  109. var targetDiv = elem.closest('.tight-form');
  110. if (elem.hasClass('show-function-controls')) {
  111. elem.removeClass('show-function-controls');
  112. targetDiv.removeClass('has-open-function');
  113. $funcControls.hide();
  114. return;
  115. }
  116. elem.addClass('show-function-controls');
  117. targetDiv.addClass('has-open-function');
  118. $funcControls.show();
  119. }
  120. function addElementsAndCompile() {
  121. $funcControls.appendTo(elem);
  122. $funcLink.appendTo(elem);
  123. _.each(funcDef.params, function(param, index) {
  124. if (param.optional && func.params.length <= index) {
  125. return;
  126. }
  127. if (index > 0) {
  128. $('<span>, </span>').appendTo(elem);
  129. }
  130. var paramValue = templateSrv.highlightVariablesAsHtml(func.params[index]);
  131. var $paramLink = $('<a ng-click="" class="graphite-func-param-link">' + paramValue + '</a>');
  132. var $input = $(paramTemplate);
  133. paramCountAtLink++;
  134. $paramLink.appendTo(elem);
  135. $input.appendTo(elem);
  136. $input.blur(_.partial(inputBlur, index));
  137. $input.keyup(inputKeyDown);
  138. $input.keypress(_.partial(inputKeyPress, index));
  139. $paramLink.click(_.partial(clickFuncParam, index));
  140. if (funcDef.params[index].options) {
  141. addTypeahead($input, index);
  142. }
  143. });
  144. $('<span>)</span>').appendTo(elem);
  145. $compile(elem.contents())($scope);
  146. }
  147. function ifJustAddedFocusFistParam() {
  148. if ($scope.func.added) {
  149. $scope.func.added = false;
  150. setTimeout(function() {
  151. elem.find('.graphite-func-param-link').first().click();
  152. }, 10);
  153. }
  154. }
  155. function registerFuncControlsToggle() {
  156. $funcLink.click(toggleFuncControls);
  157. }
  158. function registerFuncControlsActions() {
  159. $funcControls.click(function(e) {
  160. var $target = $(e.target);
  161. if ($target.hasClass('fa-remove')) {
  162. toggleFuncControls();
  163. $scope.$apply(function() {
  164. ctrl.removeFunction($scope.func);
  165. });
  166. return;
  167. }
  168. if ($target.hasClass('fa-arrow-left')) {
  169. $scope.$apply(function() {
  170. _.move(ctrl.queryModel.functions, $scope.$index, $scope.$index - 1);
  171. ctrl.targetChanged();
  172. });
  173. return;
  174. }
  175. if ($target.hasClass('fa-arrow-right')) {
  176. $scope.$apply(function() {
  177. _.move(ctrl.queryModel.functions, $scope.$index, $scope.$index + 1);
  178. ctrl.targetChanged();
  179. });
  180. return;
  181. }
  182. if ($target.hasClass('fa-question-circle')) {
  183. window.open("http://graphite.readthedocs.org/en/latest/functions.html#graphite.render.functions." + funcDef.name,'_blank');
  184. return;
  185. }
  186. });
  187. }
  188. function relink() {
  189. elem.children().remove();
  190. addElementsAndCompile();
  191. ifJustAddedFocusFistParam();
  192. registerFuncControlsToggle();
  193. registerFuncControlsActions();
  194. }
  195. relink();
  196. }
  197. };
  198. });
  199. });