| 
									
										
										
										
											2018-03-20 12:36:02 +01:00
										 |  |  | import _ from 'lodash'; | 
					
						
							|  |  |  | import $ from 'jquery'; | 
					
						
							| 
									
										
										
										
											2018-10-14 15:39:34 +02:00
										 |  |  | import coreModule from 'app/core/core_module'; | 
					
						
							| 
									
										
										
										
											2019-07-05 16:46:46 +02:00
										 |  |  | import { TemplateSrv } from 'app/features/templating/template_srv'; | 
					
						
							| 
									
										
										
										
											2018-03-20 12:36:02 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-03 19:39:50 +02:00
										 |  |  | /** @ngInject */ | 
					
						
							| 
									
										
										
										
											2019-07-05 16:46:46 +02:00
										 |  |  | export function graphiteFuncEditor($compile: any, templateSrv: TemplateSrv) { | 
					
						
							| 
									
										
										
										
											2019-02-18 17:55:38 +01:00
										 |  |  |   const funcSpanTemplate = `
 | 
					
						
							|  |  |  |     <function-editor | 
					
						
							|  |  |  |       func="func" | 
					
						
							|  |  |  |       onRemove="ctrl.handleRemoveFunction" | 
					
						
							|  |  |  |       onMoveLeft="ctrl.handleMoveLeft" | 
					
						
							| 
									
										
										
										
											2020-06-18 11:35:11 +02:00
										 |  |  |       onMoveRight="ctrl.handleMoveRight"> | 
					
						
							|  |  |  |     </function-editor> | 
					
						
							|  |  |  |     <span>(</span> | 
					
						
							| 
									
										
										
										
											2019-02-18 17:55:38 +01:00
										 |  |  |   `;
 | 
					
						
							| 
									
										
										
										
											2018-03-20 14:43:09 +01:00
										 |  |  |   const paramTemplate = | 
					
						
							|  |  |  |     '<input type="text" style="display:none"' + ' class="input-small tight-form-func-param"></input>'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-20 12:36:02 +01:00
										 |  |  |   return { | 
					
						
							|  |  |  |     restrict: 'A', | 
					
						
							| 
									
										
										
										
											2019-07-05 16:46:46 +02:00
										 |  |  |     link: function postLink($scope: any, elem: JQuery) { | 
					
						
							| 
									
										
										
										
											2018-08-29 14:26:50 +02:00
										 |  |  |       const $funcLink = $(funcSpanTemplate); | 
					
						
							|  |  |  |       const ctrl = $scope.ctrl; | 
					
						
							|  |  |  |       const func = $scope.func; | 
					
						
							| 
									
										
										
										
											2018-08-30 09:03:11 +02:00
										 |  |  |       let scheduledRelink = false; | 
					
						
							|  |  |  |       let paramCountAtLink = 0; | 
					
						
							| 
									
										
										
										
											2019-07-05 16:46:46 +02:00
										 |  |  |       let cancelBlur: any = null; | 
					
						
							| 
									
										
										
										
											2018-03-20 12:36:02 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-05 16:46:46 +02:00
										 |  |  |       ctrl.handleRemoveFunction = (func: any) => { | 
					
						
							| 
									
										
										
										
											2019-02-18 17:55:38 +01:00
										 |  |  |         ctrl.removeFunction(func); | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-05 16:46:46 +02:00
										 |  |  |       ctrl.handleMoveLeft = (func: any) => { | 
					
						
							| 
									
										
										
										
											2019-02-18 17:55:38 +01:00
										 |  |  |         ctrl.moveFunction(func, -1); | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-05 16:46:46 +02:00
										 |  |  |       ctrl.handleMoveRight = (func: any) => { | 
					
						
							| 
									
										
										
										
											2019-02-18 17:55:38 +01:00
										 |  |  |         ctrl.moveFunction(func, 1); | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-05 16:46:46 +02:00
										 |  |  |       function clickFuncParam(this: any, paramIndex: any) { | 
					
						
							| 
									
										
										
										
											2018-08-29 14:26:50 +02:00
										 |  |  |         const $link = $(this); | 
					
						
							|  |  |  |         const $comma = $link.prev('.comma'); | 
					
						
							|  |  |  |         const $input = $link.next(); | 
					
						
							| 
									
										
										
										
											2018-03-20 12:36:02 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         $input.val(func.params[paramIndex]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $comma.removeClass('query-part__last'); | 
					
						
							|  |  |  |         $link.hide(); | 
					
						
							|  |  |  |         $input.show(); | 
					
						
							|  |  |  |         $input.focus(); | 
					
						
							|  |  |  |         $input.select(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-29 14:26:50 +02:00
										 |  |  |         const typeahead = $input.data('typeahead'); | 
					
						
							| 
									
										
										
										
											2018-03-20 12:36:02 +01:00
										 |  |  |         if (typeahead) { | 
					
						
							|  |  |  |           $input.val(''); | 
					
						
							|  |  |  |           typeahead.lookup(); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       function scheduledRelinkIfNeeded() { | 
					
						
							|  |  |  |         if (paramCountAtLink === func.params.length) { | 
					
						
							|  |  |  |           return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (!scheduledRelink) { | 
					
						
							|  |  |  |           scheduledRelink = true; | 
					
						
							| 
									
										
										
										
											2018-09-04 17:02:32 +02:00
										 |  |  |           setTimeout(() => { | 
					
						
							| 
									
										
										
										
											2018-03-20 12:36:02 +01:00
										 |  |  |             relink(); | 
					
						
							|  |  |  |             scheduledRelink = false; | 
					
						
							|  |  |  |           }, 200); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-05 16:46:46 +02:00
										 |  |  |       function paramDef(index: number) { | 
					
						
							| 
									
										
										
										
											2018-03-20 12:36:02 +01:00
										 |  |  |         if (index < func.def.params.length) { | 
					
						
							|  |  |  |           return func.def.params[index]; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2019-04-15 12:11:52 +02:00
										 |  |  |         if ((_.last(func.def.params) as any).multiple) { | 
					
						
							| 
									
										
										
										
											2018-03-20 12:36:02 +01:00
										 |  |  |           return _.assign({}, _.last(func.def.params), { optional: true }); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         return {}; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-05 16:46:46 +02:00
										 |  |  |       function switchToLink(inputElem: HTMLElement, paramIndex: any) { | 
					
						
							| 
									
										
										
										
											2018-08-29 14:26:50 +02:00
										 |  |  |         const $input = $(inputElem); | 
					
						
							| 
									
										
										
										
											2018-03-20 12:36:02 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         clearTimeout(cancelBlur); | 
					
						
							|  |  |  |         cancelBlur = null; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-29 14:26:50 +02:00
										 |  |  |         const $link = $input.prev(); | 
					
						
							|  |  |  |         const $comma = $link.prev('.comma'); | 
					
						
							|  |  |  |         const newValue = $input.val(); | 
					
						
							| 
									
										
										
										
											2018-03-20 12:36:02 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         // remove optional empty params
 | 
					
						
							|  |  |  |         if (newValue !== '' || paramDef(paramIndex).optional) { | 
					
						
							|  |  |  |           func.updateParam(newValue, paramIndex); | 
					
						
							|  |  |  |           $link.html(newValue ? templateSrv.highlightVariablesAsHtml(newValue) : ' '); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         scheduledRelinkIfNeeded(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-04 17:02:32 +02:00
										 |  |  |         $scope.$apply(() => { | 
					
						
							| 
									
										
										
										
											2018-03-20 12:36:02 +01:00
										 |  |  |           ctrl.targetChanged(); | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if ($link.hasClass('query-part__last') && newValue === '') { | 
					
						
							|  |  |  |           $comma.addClass('query-part__last'); | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |           $link.removeClass('query-part__last'); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $input.hide(); | 
					
						
							|  |  |  |         $link.show(); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       // this = input element
 | 
					
						
							| 
									
										
										
										
											2019-07-05 16:46:46 +02:00
										 |  |  |       function inputBlur(this: any, paramIndex: any) { | 
					
						
							| 
									
										
										
										
											2018-08-29 14:26:50 +02:00
										 |  |  |         const inputElem = this; | 
					
						
							| 
									
										
										
										
											2018-03-20 12:36:02 +01:00
										 |  |  |         // happens long before the click event on the typeahead options
 | 
					
						
							|  |  |  |         // need to have long delay because the blur
 | 
					
						
							| 
									
										
										
										
											2018-09-04 17:02:32 +02:00
										 |  |  |         cancelBlur = setTimeout(() => { | 
					
						
							| 
									
										
										
										
											2018-03-20 12:36:02 +01:00
										 |  |  |           switchToLink(inputElem, paramIndex); | 
					
						
							|  |  |  |         }, 200); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-05 16:46:46 +02:00
										 |  |  |       function inputKeyPress(this: any, paramIndex: any, e: any) { | 
					
						
							| 
									
										
										
										
											2018-03-20 12:36:02 +01:00
										 |  |  |         if (e.which === 13) { | 
					
						
							|  |  |  |           $(this).blur(); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-30 10:49:18 +02:00
										 |  |  |       function inputKeyDown(this: any) { | 
					
						
							| 
									
										
										
										
											2018-03-20 12:36:02 +01:00
										 |  |  |         this.style.width = (3 + this.value.length) * 8 + 'px'; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-05 16:46:46 +02:00
										 |  |  |       function addTypeahead($input: any, paramIndex: any) { | 
					
						
							| 
									
										
										
										
											2018-03-20 12:36:02 +01:00
										 |  |  |         $input.attr('data-provide', 'typeahead'); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-30 09:03:11 +02:00
										 |  |  |         let options = paramDef(paramIndex).options; | 
					
						
							| 
									
										
										
										
											2018-03-20 12:36:02 +01:00
										 |  |  |         if (paramDef(paramIndex).type === 'int') { | 
					
						
							| 
									
										
										
										
											2018-09-04 17:02:32 +02:00
										 |  |  |           options = _.map(options, val => { | 
					
						
							| 
									
										
										
										
											2018-03-20 12:36:02 +01:00
										 |  |  |             return val.toString(); | 
					
						
							|  |  |  |           }); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $input.typeahead({ | 
					
						
							|  |  |  |           source: options, | 
					
						
							|  |  |  |           minLength: 0, | 
					
						
							|  |  |  |           items: 20, | 
					
						
							| 
									
										
										
										
											2019-07-05 16:46:46 +02:00
										 |  |  |           updater: (value: any) => { | 
					
						
							| 
									
										
										
										
											2018-03-20 12:36:02 +01:00
										 |  |  |             $input.val(value); | 
					
						
							|  |  |  |             switchToLink($input[0], paramIndex); | 
					
						
							|  |  |  |             return value; | 
					
						
							|  |  |  |           }, | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-29 14:26:50 +02:00
										 |  |  |         const typeahead = $input.data('typeahead'); | 
					
						
							| 
									
										
										
										
											2018-03-20 12:36:02 +01:00
										 |  |  |         typeahead.lookup = function() { | 
					
						
							|  |  |  |           this.query = this.$element.val() || ''; | 
					
						
							|  |  |  |           return this.process(this.source); | 
					
						
							|  |  |  |         }; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       function addElementsAndCompile() { | 
					
						
							|  |  |  |         $funcLink.appendTo(elem); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-15 12:11:52 +02:00
										 |  |  |         const defParams: any = _.clone(func.def.params); | 
					
						
							|  |  |  |         const lastParam: any = _.last(func.def.params); | 
					
						
							| 
									
										
										
										
											2018-03-20 12:36:02 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         while (func.params.length >= defParams.length && lastParam && lastParam.multiple) { | 
					
						
							|  |  |  |           defParams.push(_.assign({}, lastParam, { optional: true })); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-15 12:11:52 +02:00
										 |  |  |         _.each(defParams, (param: any, index: number) => { | 
					
						
							| 
									
										
										
										
											2018-03-20 12:36:02 +01:00
										 |  |  |           if (param.optional && func.params.length < index) { | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-30 09:03:11 +02:00
										 |  |  |           let paramValue = templateSrv.highlightVariablesAsHtml(func.params[index]); | 
					
						
							| 
									
										
										
										
											2020-01-30 08:06:23 +00:00
										 |  |  |           const hasValue = paramValue !== null && paramValue !== undefined && paramValue !== ''; | 
					
						
							| 
									
										
										
										
											2019-03-20 12:06:40 +01:00
										 |  |  |           const last = index >= func.params.length - 1 && param.optional && !hasValue; | 
					
						
							| 
									
										
										
										
											2020-01-30 08:06:23 +00:00
										 |  |  |           let linkClass = 'query-part__link'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           if (last) { | 
					
						
							|  |  |  |             linkClass += ' query-part__last'; | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-20 12:36:02 +01:00
										 |  |  |           if (last && param.multiple) { | 
					
						
							|  |  |  |             paramValue = '+'; | 
					
						
							| 
									
										
										
										
											2020-01-30 08:06:23 +00:00
										 |  |  |           } else if (!hasValue) { | 
					
						
							|  |  |  |             // for params with no value default to param name
 | 
					
						
							|  |  |  |             paramValue = param.name; | 
					
						
							|  |  |  |             linkClass += ' query-part__link--no-value'; | 
					
						
							| 
									
										
										
										
											2018-03-20 12:36:02 +01:00
										 |  |  |           } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           if (index > 0) { | 
					
						
							|  |  |  |             $('<span class="comma' + (last ? ' query-part__last' : '') + '">, </span>').appendTo(elem); | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-30 08:06:23 +00:00
										 |  |  |           const $paramLink = $(`<a ng-click="" class="${linkClass}">${paramValue}</a>`); | 
					
						
							| 
									
										
										
										
											2018-08-29 14:26:50 +02:00
										 |  |  |           const $input = $(paramTemplate); | 
					
						
							| 
									
										
										
										
											2018-03-20 12:36:02 +01:00
										 |  |  |           $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); | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           return true; | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $('<span>)</span>').appendTo(elem); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $compile(elem.contents())($scope); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       function ifJustAddedFocusFirstParam() { | 
					
						
							|  |  |  |         if ($scope.func.added) { | 
					
						
							|  |  |  |           $scope.func.added = false; | 
					
						
							| 
									
										
										
										
											2018-09-04 17:02:32 +02:00
										 |  |  |           setTimeout(() => { | 
					
						
							| 
									
										
										
										
											2018-03-20 12:36:02 +01:00
										 |  |  |             elem | 
					
						
							| 
									
										
										
										
											2020-01-30 08:06:23 +00:00
										 |  |  |               .find('.query-part__link') | 
					
						
							| 
									
										
										
										
											2018-03-20 12:36:02 +01:00
										 |  |  |               .first() | 
					
						
							|  |  |  |               .click(); | 
					
						
							|  |  |  |           }, 10); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       function relink() { | 
					
						
							|  |  |  |         elem.children().remove(); | 
					
						
							|  |  |  |         addElementsAndCompile(); | 
					
						
							|  |  |  |         ifJustAddedFocusFirstParam(); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       relink(); | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-14 15:39:34 +02:00
										 |  |  | coreModule.directive('graphiteFuncEditor', graphiteFuncEditor); |