graphiteFuncEditor.js 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. define([
  2. 'angular',
  3. 'underscore',
  4. 'jquery',
  5. ],
  6. function (angular, _, $) {
  7. 'use strict';
  8. angular
  9. .module('kibana.directives')
  10. .directive('graphiteFuncEditor', function($compile) {
  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 grafana-function-param-input"></input>';
  14. var funcControlsTemplate =
  15. '<div class="graphite-func-controls">' +
  16. '<span class="pointer icon-arrow-left"></span>' +
  17. '<span class="pointer icon-info-sign"></span>' +
  18. '<span class="pointer icon-remove" ></span>' +
  19. '<span class="pointer icon-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. if ($input.val() !== '' || func.def.params[paramIndex].optional) {
  63. $link.text($input.val());
  64. func.updateParam($input.val(), paramIndex);
  65. scheduledRelinkIfNeeded();
  66. $scope.$apply($scope.targetChanged);
  67. }
  68. $input.hide();
  69. $link.show();
  70. }
  71. function inputKeyPress(paramIndex, e) {
  72. /*jshint validthis:true */
  73. if(e.which === 13) {
  74. inputBlur.call(this, paramIndex);
  75. }
  76. }
  77. function inputKeyDown() {
  78. /*jshint validthis:true */
  79. this.style.width = (3 + this.value.length) * 8 + 'px';
  80. }
  81. function addTypeahead($input, paramIndex) {
  82. $input.attr('data-provide', 'typeahead');
  83. var options = funcDef.params[paramIndex].options;
  84. if (funcDef.params[paramIndex].type === 'int') {
  85. options = _.map(options, function(val) { return val.toString(); } );
  86. }
  87. $input.typeahead({
  88. source: options,
  89. minLength: 0,
  90. items: 20,
  91. updater: function (value) {
  92. setTimeout(function() {
  93. inputBlur.call($input[0], paramIndex);
  94. }, 0);
  95. return value;
  96. }
  97. });
  98. var typeahead = $input.data('typeahead');
  99. typeahead.lookup = function () {
  100. this.query = this.$element.val() || '';
  101. return this.process(this.source);
  102. };
  103. }
  104. function toggleFuncControls() {
  105. var targetDiv = elem.closest('.grafana-target-inner');
  106. if (elem.hasClass('show-function-controls')) {
  107. elem.removeClass('show-function-controls');
  108. targetDiv.removeClass('has-open-function');
  109. $funcControls.hide();
  110. return;
  111. }
  112. elem.addClass('show-function-controls');
  113. targetDiv.addClass('has-open-function');
  114. $funcControls.show();
  115. }
  116. function addElementsAndCompile() {
  117. $funcControls.appendTo(elem);
  118. $funcLink.appendTo(elem);
  119. _.each(funcDef.params, function(param, index) {
  120. if (param.optional && !func.params[index]) {
  121. return;
  122. }
  123. if (index > 0) {
  124. $('<span>, </span>').appendTo(elem);
  125. }
  126. var $paramLink = $('<a ng-click="" class="graphite-func-param-link">' + func.params[index] + '</a>');
  127. var $input = $(paramTemplate);
  128. paramCountAtLink++;
  129. $paramLink.appendTo(elem);
  130. $input.appendTo(elem);
  131. $input.blur(_.partial(inputBlur, index));
  132. $input.keyup(inputKeyDown);
  133. $input.keypress(_.partial(inputKeyPress, index));
  134. $paramLink.click(_.partial(clickFuncParam, index));
  135. if (funcDef.params[index].options) {
  136. addTypeahead($input, index);
  137. }
  138. });
  139. $('<span>)</span>').appendTo(elem);
  140. $compile(elem.contents())($scope);
  141. }
  142. function ifJustAddedFocusFistParam() {
  143. if ($scope.func.added) {
  144. $scope.func.added = false;
  145. setTimeout(function() {
  146. elem.find('.graphite-func-param-link').first().click();
  147. }, 10);
  148. }
  149. }
  150. function registerFuncControlsToggle() {
  151. $funcLink.click(toggleFuncControls);
  152. }
  153. function registerFuncControlsActions() {
  154. $funcControls.click(function(e) {
  155. var $target = $(e.target);
  156. if ($target.hasClass('icon-remove')) {
  157. toggleFuncControls();
  158. $scope.$apply(function() {
  159. $scope.removeFunction($scope.func);
  160. });
  161. return;
  162. }
  163. if ($target.hasClass('icon-arrow-left')) {
  164. $scope.$apply(function() {
  165. _.move($scope.functions, $scope.$index, $scope.$index - 1);
  166. });
  167. return;
  168. }
  169. if ($target.hasClass('icon-arrow-right')) {
  170. $scope.$apply(function() {
  171. _.move($scope.functions, $scope.$index, $scope.$index + 1);
  172. });
  173. return;
  174. }
  175. if ($target.hasClass('icon-info-sign')) {
  176. window.open("http://graphite.readthedocs.org/en/latest/functions.html#graphite.render.functions." + funcDef.name,'_blank');
  177. return;
  178. }
  179. });
  180. }
  181. function relink() {
  182. elem.children().remove();
  183. addElementsAndCompile();
  184. ifJustAddedFocusFistParam();
  185. registerFuncControlsToggle();
  186. registerFuncControlsActions();
  187. }
  188. relink();
  189. }
  190. };
  191. });
  192. });