func_editor.js 7.3 KB

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