| 
									
										
										
										
											2018-04-05 23:10:32 +08:00
										 |  |  | import $ from 'jquery'; | 
					
						
							| 
									
										
										
										
											2022-04-22 21:33:13 +08:00
										 |  |  | import { debounce, find, indexOf, map, escape, unescape } from 'lodash'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-28 15:58:12 +08:00
										 |  |  | import { TemplateSrv } from 'app/features/templating/template_srv'; | 
					
						
							| 
									
										
										
										
											2018-04-05 23:10:32 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-22 21:33:13 +08:00
										 |  |  | import coreModule from './core_module'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-10 15:24:54 +08:00
										 |  |  | /** @ngInject */ | 
					
						
							| 
									
										
										
										
											2019-04-28 15:58:12 +08:00
										 |  |  | export function metricSegment($compile: any, $sce: any, templateSrv: TemplateSrv) { | 
					
						
							| 
									
										
										
										
											2018-08-26 23:14:40 +08:00
										 |  |  |   const inputTemplate = | 
					
						
							| 
									
										
										
										
											2018-04-05 23:10:32 +08:00
										 |  |  |     '<input type="text" data-provide="typeahead" ' + | 
					
						
							|  |  |  |     ' class="gf-form-input input-medium"' + | 
					
						
							|  |  |  |     ' spellcheck="false" style="display:none"></input>'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-26 23:14:40 +08:00
										 |  |  |   const linkTemplate = | 
					
						
							| 
									
										
										
										
											2018-04-05 23:10:32 +08:00
										 |  |  |     '<a class="gf-form-label" ng-class="segment.cssClass" ' + | 
					
						
							|  |  |  |     'tabindex="1" give-focus="segment.focus" ng-bind-html="segment.html"></a>'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-26 23:14:40 +08:00
										 |  |  |   const selectTemplate = | 
					
						
							| 
									
										
										
										
											2018-04-05 23:10:32 +08:00
										 |  |  |     '<a class="gf-form-input gf-form-input--dropdown" ng-class="segment.cssClass" ' + | 
					
						
							|  |  |  |     'tabindex="1" give-focus="segment.focus" ng-bind-html="segment.html"></a>'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return { | 
					
						
							|  |  |  |     scope: { | 
					
						
							|  |  |  |       segment: '=', | 
					
						
							|  |  |  |       getOptions: '&', | 
					
						
							|  |  |  |       onChange: '&', | 
					
						
							|  |  |  |       debounce: '@', | 
					
						
							|  |  |  |     }, | 
					
						
							| 
									
										
										
										
											2019-04-28 15:58:12 +08:00
										 |  |  |     link: ($scope: any, elem: any) => { | 
					
						
							| 
									
										
										
										
											2018-08-26 23:14:40 +08:00
										 |  |  |       const $input = $(inputTemplate); | 
					
						
							|  |  |  |       const segment = $scope.segment; | 
					
						
							|  |  |  |       const $button = $(segment.selectMode ? selectTemplate : linkTemplate); | 
					
						
							| 
									
										
										
										
											2018-04-05 23:10:32 +08:00
										 |  |  |       let options = null; | 
					
						
							| 
									
										
										
										
											2019-04-28 15:58:12 +08:00
										 |  |  |       let cancelBlur: any = null; | 
					
						
							| 
									
										
										
										
											2018-04-05 23:10:32 +08:00
										 |  |  |       let linkMode = true; | 
					
						
							| 
									
										
										
										
											2018-08-26 23:14:40 +08:00
										 |  |  |       const debounceLookup = $scope.debounce; | 
					
						
							| 
									
										
										
										
											2018-04-05 23:10:32 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |       $input.appendTo(elem); | 
					
						
							|  |  |  |       $button.appendTo(elem); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-28 15:58:12 +08:00
										 |  |  |       $scope.updateVariableValue = (value: string) => { | 
					
						
							| 
									
										
										
										
											2018-04-05 23:10:32 +08:00
										 |  |  |         if (value === '' || segment.value === value) { | 
					
						
							|  |  |  |           return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-04 23:02:32 +08:00
										 |  |  |         $scope.$apply(() => { | 
					
						
							| 
									
										
										
										
											2021-04-21 15:38:00 +08:00
										 |  |  |           const selected: any = find($scope.altSegments, { value: value }); | 
					
						
							| 
									
										
										
										
											2018-04-05 23:10:32 +08:00
										 |  |  |           if (selected) { | 
					
						
							|  |  |  |             segment.value = selected.value; | 
					
						
							| 
									
										
										
										
											2018-10-18 21:04:54 +08:00
										 |  |  |             segment.html = selected.html || $sce.trustAsHtml(templateSrv.highlightVariablesAsHtml(selected.value)); | 
					
						
							| 
									
										
										
										
											2018-04-05 23:10:32 +08:00
										 |  |  |             segment.fake = false; | 
					
						
							|  |  |  |             segment.expandable = selected.expandable; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (selected.type) { | 
					
						
							|  |  |  |               segment.type = selected.type; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |           } else if (segment.custom !== 'false') { | 
					
						
							|  |  |  |             segment.value = value; | 
					
						
							| 
									
										
										
										
											2018-10-18 21:04:54 +08:00
										 |  |  |             segment.html = $sce.trustAsHtml(templateSrv.highlightVariablesAsHtml(value)); | 
					
						
							| 
									
										
										
										
											2018-04-05 23:10:32 +08:00
										 |  |  |             segment.expandable = true; | 
					
						
							|  |  |  |             segment.fake = false; | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           $scope.onChange(); | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-28 15:58:12 +08:00
										 |  |  |       $scope.switchToLink = (fromClick: boolean) => { | 
					
						
							| 
									
										
										
										
											2018-04-05 23:10:32 +08:00
										 |  |  |         if (linkMode && !fromClick) { | 
					
						
							|  |  |  |           return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         clearTimeout(cancelBlur); | 
					
						
							|  |  |  |         cancelBlur = null; | 
					
						
							|  |  |  |         linkMode = true; | 
					
						
							|  |  |  |         $input.hide(); | 
					
						
							|  |  |  |         $button.show(); | 
					
						
							|  |  |  |         $scope.updateVariableValue($input.val()); | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-04 23:02:32 +08:00
										 |  |  |       $scope.inputBlur = () => { | 
					
						
							| 
									
										
										
										
											2018-04-05 23:10:32 +08:00
										 |  |  |         // happens long before the click event on the typeahead options
 | 
					
						
							|  |  |  |         // need to have long delay because the blur
 | 
					
						
							|  |  |  |         cancelBlur = setTimeout($scope.switchToLink, 200); | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-28 15:58:12 +08:00
										 |  |  |       $scope.source = (query: string, callback: any) => { | 
					
						
							| 
									
										
										
										
											2018-09-04 23:02:32 +08:00
										 |  |  |         $scope.$apply(() => { | 
					
						
							| 
									
										
										
										
											2019-04-28 15:58:12 +08:00
										 |  |  |           $scope.getOptions({ $query: query }).then((altSegments: any) => { | 
					
						
							| 
									
										
										
										
											2018-04-05 23:10:32 +08:00
										 |  |  |             $scope.altSegments = altSegments; | 
					
						
							| 
									
										
										
										
											2021-04-21 15:38:00 +08:00
										 |  |  |             options = map($scope.altSegments, (alt) => { | 
					
						
							|  |  |  |               return escape(alt.value); | 
					
						
							| 
									
										
										
										
											2018-04-05 23:10:32 +08:00
										 |  |  |             }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             // add custom values
 | 
					
						
							|  |  |  |             if (segment.custom !== 'false') { | 
					
						
							| 
									
										
										
										
											2021-04-21 15:38:00 +08:00
										 |  |  |               if (!segment.fake && indexOf(options, segment.value) === -1) { | 
					
						
							|  |  |  |                 options.unshift(escape(segment.value)); | 
					
						
							| 
									
										
										
										
											2018-04-05 23:10:32 +08:00
										 |  |  |               } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             callback(options); | 
					
						
							|  |  |  |           }); | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-28 15:58:12 +08:00
										 |  |  |       $scope.updater = (value: string) => { | 
					
						
							| 
									
										
										
										
											2021-04-21 15:38:00 +08:00
										 |  |  |         value = unescape(value); | 
					
						
							| 
									
										
										
										
											2018-04-05 23:10:32 +08:00
										 |  |  |         if (value === segment.value) { | 
					
						
							|  |  |  |           clearTimeout(cancelBlur); | 
					
						
							|  |  |  |           $input.focus(); | 
					
						
							|  |  |  |           return value; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $input.val(value); | 
					
						
							|  |  |  |         $scope.switchToLink(true); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return value; | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-20 14:59:48 +08:00
										 |  |  |       $scope.matcher = function (item: string) { | 
					
						
							| 
									
										
										
										
											2018-09-22 05:17:29 +08:00
										 |  |  |         if (linkMode) { | 
					
						
							|  |  |  |           return false; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2018-04-05 23:10:32 +08:00
										 |  |  |         let str = this.query; | 
					
						
							|  |  |  |         if (str[0] === '/') { | 
					
						
							|  |  |  |           str = str.substring(1); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if (str[str.length - 1] === '/') { | 
					
						
							|  |  |  |           str = str.substring(0, str.length - 1); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         try { | 
					
						
							|  |  |  |           return item.toLowerCase().match(str.toLowerCase()); | 
					
						
							|  |  |  |         } catch (e) { | 
					
						
							|  |  |  |           return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       $input.attr('data-provide', 'typeahead'); | 
					
						
							|  |  |  |       $input.typeahead({ | 
					
						
							|  |  |  |         source: $scope.source, | 
					
						
							|  |  |  |         minLength: 0, | 
					
						
							|  |  |  |         items: 10000, | 
					
						
							|  |  |  |         updater: $scope.updater, | 
					
						
							|  |  |  |         matcher: $scope.matcher, | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-26 23:14:40 +08:00
										 |  |  |       const typeahead = $input.data('typeahead'); | 
					
						
							| 
									
										
										
										
											2021-01-20 14:59:48 +08:00
										 |  |  |       typeahead.lookup = function () { | 
					
						
							| 
									
										
										
										
											2018-04-05 23:10:32 +08:00
										 |  |  |         this.query = this.$element.val() || ''; | 
					
						
							| 
									
										
										
										
											2018-08-26 23:14:40 +08:00
										 |  |  |         const items = this.source(this.query, $.proxy(this.process, this)); | 
					
						
							| 
									
										
										
										
											2018-04-05 23:10:32 +08:00
										 |  |  |         return items ? this.process(items) : items; | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (debounceLookup) { | 
					
						
							| 
									
										
										
										
											2021-04-21 15:38:00 +08:00
										 |  |  |         typeahead.lookup = debounce(typeahead.lookup, 500, { leading: true }); | 
					
						
							| 
									
										
										
										
											2018-04-05 23:10:32 +08:00
										 |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-20 14:59:48 +08:00
										 |  |  |       $button.keydown((evt) => { | 
					
						
							| 
									
										
										
										
											2018-04-05 23:10:32 +08:00
										 |  |  |         // trigger typeahead on down arrow or enter key
 | 
					
						
							|  |  |  |         if (evt.keyCode === 40 || evt.keyCode === 13) { | 
					
						
							|  |  |  |           $button.click(); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-04 23:02:32 +08:00
										 |  |  |       $button.click(() => { | 
					
						
							| 
									
										
										
										
											2018-04-05 23:10:32 +08:00
										 |  |  |         options = null; | 
					
						
							| 
									
										
										
											
												Chore: Fix all Typescript strict null errors  (#26204)
* Chore: Fix typescript strict null errors
* Added new limit
* Fixed ts issue
* fixed tests
* trying to fix type inference
* Fixing more ts errors
* Revert tsconfig option
* Fix
* Fixed code
* More fixes
* fix tests
* Updated snapshot
* Chore: More ts strict null fixes
* More fixes in some really messed up azure config components
* More fixes, current count: 441
* 419
* More fixes
* Fixed invalid initial state in explore
* Fixing tests
* Fixed tests
* Explore fix
* More fixes
* Progress
* Sub 300
* Now at 218
* Progress
* Update
* Progress
* Updated tests
* at 159
* fixed tests
* Progress
* YAy blow 100! at 94
* 10,9,8,7,6,5,4,3,2,1... lift off
* Fixed tests
* Fixed more type errors
Co-authored-by: Ryan McKinley <ryantxu@gmail.com>
											
										 
											2020-07-10 18:46:59 +08:00
										 |  |  |         $input.css('width', Math.max($button.width()!, 80) + 16 + 'px'); | 
					
						
							| 
									
										
										
										
											2018-04-05 23:10:32 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |         $button.hide(); | 
					
						
							|  |  |  |         $input.show(); | 
					
						
							|  |  |  |         $input.focus(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         linkMode = false; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-26 23:14:40 +08:00
										 |  |  |         const typeahead = $input.data('typeahead'); | 
					
						
							| 
									
										
										
										
											2018-04-05 23:10:32 +08:00
										 |  |  |         if (typeahead) { | 
					
						
							|  |  |  |           $input.val(''); | 
					
						
							|  |  |  |           typeahead.lookup(); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       $input.blur($scope.inputBlur); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       $compile(elem.contents())($scope); | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-12 16:22:25 +08:00
										 |  |  | /** @ngInject */ | 
					
						
							| 
									
										
										
										
											2019-12-05 17:04:03 +08:00
										 |  |  | export function metricSegmentModel(uiSegmentSrv: any) { | 
					
						
							| 
									
										
										
										
											2018-04-05 23:10:32 +08:00
										 |  |  |   return { | 
					
						
							|  |  |  |     template: | 
					
						
							|  |  |  |       '<metric-segment segment="segment" get-options="getOptionsInternal()" on-change="onSegmentChange()"></metric-segment>', | 
					
						
							|  |  |  |     restrict: 'E', | 
					
						
							|  |  |  |     scope: { | 
					
						
							|  |  |  |       property: '=', | 
					
						
							|  |  |  |       options: '=', | 
					
						
							|  |  |  |       getOptions: '&', | 
					
						
							|  |  |  |       onChange: '&', | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  |     link: { | 
					
						
							| 
									
										
										
										
											2019-04-28 15:58:12 +08:00
										 |  |  |       pre: function postLink($scope: any, elem: any, attrs: any) { | 
					
						
							|  |  |  |         let cachedOptions: any; | 
					
						
							| 
									
										
										
										
											2018-04-05 23:10:32 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-28 15:58:12 +08:00
										 |  |  |         $scope.valueToSegment = (value: any) => { | 
					
						
							| 
									
										
										
										
											2021-04-21 15:38:00 +08:00
										 |  |  |           const option: any = find($scope.options, { value: value }); | 
					
						
							| 
									
										
										
										
											2018-08-26 23:14:40 +08:00
										 |  |  |           const segment = { | 
					
						
							| 
									
										
										
										
											2018-04-05 23:10:32 +08:00
										 |  |  |             cssClass: attrs.cssClass, | 
					
						
							|  |  |  |             custom: attrs.custom, | 
					
						
							|  |  |  |             value: option ? option.text : value, | 
					
						
							|  |  |  |             selectMode: attrs.selectMode, | 
					
						
							|  |  |  |           }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           return uiSegmentSrv.newSegment(segment); | 
					
						
							|  |  |  |         }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-04 23:02:32 +08:00
										 |  |  |         $scope.getOptionsInternal = () => { | 
					
						
							| 
									
										
										
										
											2018-04-05 23:10:32 +08:00
										 |  |  |           if ($scope.options) { | 
					
						
							|  |  |  |             cachedOptions = $scope.options; | 
					
						
							| 
									
										
										
										
											2019-12-05 17:04:03 +08:00
										 |  |  |             return Promise.resolve( | 
					
						
							| 
									
										
										
										
											2021-04-21 15:38:00 +08:00
										 |  |  |               map($scope.options, (option) => { | 
					
						
							| 
									
										
										
										
											2018-10-18 21:04:54 +08:00
										 |  |  |                 return { value: option.text }; | 
					
						
							| 
									
										
										
										
											2018-04-05 23:10:32 +08:00
										 |  |  |               }) | 
					
						
							|  |  |  |             ); | 
					
						
							|  |  |  |           } else { | 
					
						
							| 
									
										
										
										
											2019-04-28 15:58:12 +08:00
										 |  |  |             return $scope.getOptions().then((options: any) => { | 
					
						
							| 
									
										
										
										
											2018-04-05 23:10:32 +08:00
										 |  |  |               cachedOptions = options; | 
					
						
							| 
									
										
										
										
											2021-04-21 15:38:00 +08:00
										 |  |  |               return map(options, (option) => { | 
					
						
							| 
									
										
										
										
											2018-04-05 23:10:32 +08:00
										 |  |  |                 if (option.html) { | 
					
						
							|  |  |  |                   return option; | 
					
						
							|  |  |  |                 } | 
					
						
							| 
									
										
										
										
											2018-10-18 21:04:54 +08:00
										 |  |  |                 return { value: option.text }; | 
					
						
							| 
									
										
										
										
											2018-04-05 23:10:32 +08:00
										 |  |  |               }); | 
					
						
							|  |  |  |             }); | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-04 23:02:32 +08:00
										 |  |  |         $scope.onSegmentChange = () => { | 
					
						
							| 
									
										
										
										
											2018-04-05 23:10:32 +08:00
										 |  |  |           if (cachedOptions) { | 
					
						
							| 
									
										
										
										
											2021-04-21 15:38:00 +08:00
										 |  |  |             const option: any = find(cachedOptions, { text: $scope.segment.value }); | 
					
						
							| 
									
										
										
										
											2018-04-05 23:10:32 +08:00
										 |  |  |             if (option && option.value !== $scope.property) { | 
					
						
							|  |  |  |               $scope.property = option.value; | 
					
						
							|  |  |  |             } else if (attrs.custom !== 'false') { | 
					
						
							|  |  |  |               $scope.property = $scope.segment.value; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |           } else { | 
					
						
							|  |  |  |             $scope.property = $scope.segment.value; | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           // needs to call this after digest so
 | 
					
						
							|  |  |  |           // property is synced with outerscope
 | 
					
						
							| 
									
										
										
										
											2018-09-04 23:02:32 +08:00
										 |  |  |           $scope.$$postDigest(() => { | 
					
						
							|  |  |  |             $scope.$apply(() => { | 
					
						
							| 
									
										
										
										
											2018-04-05 23:10:32 +08:00
										 |  |  |               $scope.onChange(); | 
					
						
							|  |  |  |             }); | 
					
						
							|  |  |  |           }); | 
					
						
							|  |  |  |         }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $scope.segment = $scope.valueToSegment($scope.property); | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | coreModule.directive('metricSegment', metricSegment); | 
					
						
							|  |  |  | coreModule.directive('metricSegmentModel', metricSegmentModel); |