One Hat Cyber Team
  • Dir : ~/proc/self/root/usr/share/grafana/public/build/
  • View File Name : 1133.cd705902767ed2e9bf55.js.map
    \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
    DirectiveHowSourceRendered
    ng-bind-htmlAutomatically uses $sanitize
    <div ng-bind-html=\"snippet\">
    </div>
    ng-bind-htmlBypass $sanitize by explicitly trusting the dangerous value\n
    <div ng-bind-html=\"deliberatelyTrustDangerousSnippet()\">\n</div>
    \n
    ng-bindAutomatically escapes
    <div ng-bind=\"snippet\">
    </div>
    \n
    \n \n \n it('should sanitize the html snippet by default', function() {\n expect(element(by.css('#bind-html-with-sanitize div')).getAttribute('innerHTML')).\n toBe('

    an html\\nclick here\\nsnippet

    ');\n });\n\n it('should inline raw snippet if bound to a trusted value', function() {\n expect(element(by.css('#bind-html-with-trust div')).getAttribute('innerHTML')).\n toBe(\"

    an html\\n\" +\n \"click here\\n\" +\n \"snippet

    \");\n });\n\n it('should escape snippet without any filter', function() {\n expect(element(by.css('#bind-default div')).getAttribute('innerHTML')).\n toBe(\"<p style=\\\"color:blue\\\">an html\\n\" +\n \"<em onmouseover=\\\"this.textContent='PWN3D!'\\\">click here</em>\\n\" +\n \"snippet</p>\");\n });\n\n it('should update', function() {\n element(by.model('snippet')).clear();\n element(by.model('snippet')).sendKeys('new text');\n expect(element(by.css('#bind-html-with-sanitize div')).getAttribute('innerHTML')).\n toBe('new text');\n expect(element(by.css('#bind-html-with-trust div')).getAttribute('innerHTML')).toBe(\n 'new text');\n expect(element(by.css('#bind-default div')).getAttribute('innerHTML')).toBe(\n \"new <b onclick=\\\"alert(1)\\\">text</b>\");\n });\n
    \n \n */\n\n\n/**\n * @ngdoc provider\n * @name $sanitizeProvider\n * @this\n *\n * @description\n * Creates and configures {@link $sanitize} instance.\n */\nfunction $SanitizeProvider() {\n var hasBeenInstantiated = false;\n var svgEnabled = false;\n\n this.$get = ['$$sanitizeUri', function($$sanitizeUri) {\n hasBeenInstantiated = true;\n if (svgEnabled) {\n extend(validElements, svgElements);\n }\n return function(html) {\n var buf = [];\n htmlParser(html, htmlSanitizeWriter(buf, function(uri, isImage) {\n return !/^unsafe:/.test($$sanitizeUri(uri, isImage));\n }));\n return buf.join('');\n };\n }];\n\n\n /**\n * @ngdoc method\n * @name $sanitizeProvider#enableSvg\n * @kind function\n *\n * @description\n * Enables a subset of svg to be supported by the sanitizer.\n *\n *
    \n *

    By enabling this setting without taking other precautions, you might expose your\n * application to click-hijacking attacks. In these attacks, sanitized svg elements could be positioned\n * outside of the containing element and be rendered over other elements on the page (e.g. a login\n * link). Such behavior can then result in phishing incidents.

    \n *\n *

    To protect against these, explicitly setup `overflow: hidden` css rule for all potential svg\n * tags within the sanitized content:

    \n *\n *
    \n *\n *
    \n   *   .rootOfTheIncludedContent svg {\n   *     overflow: hidden !important;\n   *   }\n   *   
    \n *
    \n *\n * @param {boolean=} flag Enable or disable SVG support in the sanitizer.\n * @returns {boolean|$sanitizeProvider} Returns the currently configured value if called\n * without an argument or self for chaining otherwise.\n */\n this.enableSvg = function(enableSvg) {\n if (isDefined(enableSvg)) {\n svgEnabled = enableSvg;\n return this;\n } else {\n return svgEnabled;\n }\n };\n\n\n /**\n * @ngdoc method\n * @name $sanitizeProvider#addValidElements\n * @kind function\n *\n * @description\n * Extends the built-in lists of valid HTML/SVG elements, i.e. elements that are considered safe\n * and are not stripped off during sanitization. You can extend the following lists of elements:\n *\n * - `htmlElements`: A list of elements (tag names) to extend the current list of safe HTML\n * elements. HTML elements considered safe will not be removed during sanitization. All other\n * elements will be stripped off.\n *\n * - `htmlVoidElements`: This is similar to `htmlElements`, but marks the elements as\n * \"void elements\" (similar to HTML\n * [void elements](https://rawgit.com/w3c/html/html5.1-2/single-page.html#void-elements)). These\n * elements have no end tag and cannot have content.\n *\n * - `svgElements`: This is similar to `htmlElements`, but for SVG elements. This list is only\n * taken into account if SVG is {@link ngSanitize.$sanitizeProvider#enableSvg enabled} for\n * `$sanitize`.\n *\n *
    \n * This method must be called during the {@link angular.Module#config config} phase. Once the\n * `$sanitize` service has been instantiated, this method has no effect.\n *
    \n *\n *
    \n * Keep in mind that extending the built-in lists of elements may expose your app to XSS or\n * other vulnerabilities. Be very mindful of the elements you add.\n *
    \n *\n * @param {Array|Object} elements - A list of valid HTML elements or an object with one or\n * more of the following properties:\n * - **htmlElements** - `{Array}` - A list of elements to extend the current list of\n * HTML elements.\n * - **htmlVoidElements** - `{Array}` - A list of elements to extend the current list of\n * void HTML elements; i.e. elements that do not have an end tag.\n * - **svgElements** - `{Array}` - A list of elements to extend the current list of SVG\n * elements. The list of SVG elements is only taken into account if SVG is\n * {@link ngSanitize.$sanitizeProvider#enableSvg enabled} for `$sanitize`.\n *\n * Passing an array (`[...]`) is equivalent to passing `{htmlElements: [...]}`.\n *\n * @return {$sanitizeProvider} Returns self for chaining.\n */\n this.addValidElements = function(elements) {\n if (!hasBeenInstantiated) {\n if (isArray(elements)) {\n elements = {htmlElements: elements};\n }\n\n addElementsTo(svgElements, elements.svgElements);\n addElementsTo(voidElements, elements.htmlVoidElements);\n addElementsTo(validElements, elements.htmlVoidElements);\n addElementsTo(validElements, elements.htmlElements);\n }\n\n return this;\n };\n\n\n /**\n * @ngdoc method\n * @name $sanitizeProvider#addValidAttrs\n * @kind function\n *\n * @description\n * Extends the built-in list of valid attributes, i.e. attributes that are considered safe and are\n * not stripped off during sanitization.\n *\n * **Note**:\n * The new attributes will not be treated as URI attributes, which means their values will not be\n * sanitized as URIs using `$compileProvider`'s\n * {@link ng.$compileProvider#aHrefSanitizationTrustedUrlList aHrefSanitizationTrustedUrlList} and\n * {@link ng.$compileProvider#imgSrcSanitizationTrustedUrlList imgSrcSanitizationTrustedUrlList}.\n *\n *
    \n * This method must be called during the {@link angular.Module#config config} phase. Once the\n * `$sanitize` service has been instantiated, this method has no effect.\n *
    \n *\n *
    \n * Keep in mind that extending the built-in list of attributes may expose your app to XSS or\n * other vulnerabilities. Be very mindful of the attributes you add.\n *
    \n *\n * @param {Array} attrs - A list of valid attributes.\n *\n * @returns {$sanitizeProvider} Returns self for chaining.\n */\n this.addValidAttrs = function(attrs) {\n if (!hasBeenInstantiated) {\n extend(validAttrs, arrayToMap(attrs, true));\n }\n return this;\n };\n\n //////////////////////////////////////////////////////////////////////////////////////////////////\n // Private stuff\n //////////////////////////////////////////////////////////////////////////////////////////////////\n\n bind = angular.bind;\n extend = angular.extend;\n forEach = angular.forEach;\n isArray = angular.isArray;\n isDefined = angular.isDefined;\n lowercase = angular.$$lowercase;\n noop = angular.noop;\n\n htmlParser = htmlParserImpl;\n htmlSanitizeWriter = htmlSanitizeWriterImpl;\n\n nodeContains = window.Node.prototype.contains || /** @this */ function(arg) {\n // eslint-disable-next-line no-bitwise\n return !!(this.compareDocumentPosition(arg) & 16);\n };\n\n // Regular Expressions for parsing tags and attributes\n var SURROGATE_PAIR_REGEXP = /[\\uD800-\\uDBFF][\\uDC00-\\uDFFF]/g,\n // Match everything outside of normal chars and \" (quote character)\n NON_ALPHANUMERIC_REGEXP = /([^#-~ |!])/g;\n\n\n // Good source of info about elements and attributes\n // http://dev.w3.org/html5/spec/Overview.html#semantics\n // http://simon.html5.org/html-elements\n\n // Safe Void Elements - HTML5\n // http://dev.w3.org/html5/spec/Overview.html#void-elements\n var voidElements = stringToMap('area,br,col,hr,img,wbr');\n\n // Elements that you can, intentionally, leave open (and which close themselves)\n // http://dev.w3.org/html5/spec/Overview.html#optional-tags\n var optionalEndTagBlockElements = stringToMap('colgroup,dd,dt,li,p,tbody,td,tfoot,th,thead,tr'),\n optionalEndTagInlineElements = stringToMap('rp,rt'),\n optionalEndTagElements = extend({},\n optionalEndTagInlineElements,\n optionalEndTagBlockElements);\n\n // Safe Block Elements - HTML5\n var blockElements = extend({}, optionalEndTagBlockElements, stringToMap('address,article,' +\n 'aside,blockquote,caption,center,del,dir,div,dl,figure,figcaption,footer,h1,h2,h3,h4,h5,' +\n 'h6,header,hgroup,hr,ins,map,menu,nav,ol,pre,section,table,ul'));\n\n // Inline Elements - HTML5\n var inlineElements = extend({}, optionalEndTagInlineElements, stringToMap('a,abbr,acronym,b,' +\n 'bdi,bdo,big,br,cite,code,del,dfn,em,font,i,img,ins,kbd,label,map,mark,q,ruby,rp,rt,s,' +\n 'samp,small,span,strike,strong,sub,sup,time,tt,u,var'));\n\n // SVG Elements\n // https://wiki.whatwg.org/wiki/Sanitization_rules#svg_Elements\n // Note: the elements animate,animateColor,animateMotion,animateTransform,set are intentionally omitted.\n // They can potentially allow for arbitrary javascript to be executed. See #11290\n var svgElements = stringToMap('circle,defs,desc,ellipse,font-face,font-face-name,font-face-src,g,glyph,' +\n 'hkern,image,linearGradient,line,marker,metadata,missing-glyph,mpath,path,polygon,polyline,' +\n 'radialGradient,rect,stop,svg,switch,text,title,tspan');\n\n // Blocked Elements (will be stripped)\n var blockedElements = stringToMap('script,style');\n\n var validElements = extend({},\n voidElements,\n blockElements,\n inlineElements,\n optionalEndTagElements);\n\n //Attributes that have href and hence need to be sanitized\n var uriAttrs = stringToMap('background,cite,href,longdesc,src,xlink:href,xml:base');\n\n var htmlAttrs = stringToMap('abbr,align,alt,axis,bgcolor,border,cellpadding,cellspacing,class,clear,' +\n 'color,cols,colspan,compact,coords,dir,face,headers,height,hreflang,hspace,' +\n 'ismap,lang,language,nohref,nowrap,rel,rev,rows,rowspan,rules,' +\n 'scope,scrolling,shape,size,span,start,summary,tabindex,target,title,type,' +\n 'valign,value,vspace,width');\n\n // SVG attributes (without \"id\" and \"name\" attributes)\n // https://wiki.whatwg.org/wiki/Sanitization_rules#svg_Attributes\n var svgAttrs = stringToMap('accent-height,accumulate,additive,alphabetic,arabic-form,ascent,' +\n 'baseProfile,bbox,begin,by,calcMode,cap-height,class,color,color-rendering,content,' +\n 'cx,cy,d,dx,dy,descent,display,dur,end,fill,fill-rule,font-family,font-size,font-stretch,' +\n 'font-style,font-variant,font-weight,from,fx,fy,g1,g2,glyph-name,gradientUnits,hanging,' +\n 'height,horiz-adv-x,horiz-origin-x,ideographic,k,keyPoints,keySplines,keyTimes,lang,' +\n 'marker-end,marker-mid,marker-start,markerHeight,markerUnits,markerWidth,mathematical,' +\n 'max,min,offset,opacity,orient,origin,overline-position,overline-thickness,panose-1,' +\n 'path,pathLength,points,preserveAspectRatio,r,refX,refY,repeatCount,repeatDur,' +\n 'requiredExtensions,requiredFeatures,restart,rotate,rx,ry,slope,stemh,stemv,stop-color,' +\n 'stop-opacity,strikethrough-position,strikethrough-thickness,stroke,stroke-dasharray,' +\n 'stroke-dashoffset,stroke-linecap,stroke-linejoin,stroke-miterlimit,stroke-opacity,' +\n 'stroke-width,systemLanguage,target,text-anchor,to,transform,type,u1,u2,underline-position,' +\n 'underline-thickness,unicode,unicode-range,units-per-em,values,version,viewBox,visibility,' +\n 'width,widths,x,x-height,x1,x2,xlink:actuate,xlink:arcrole,xlink:role,xlink:show,xlink:title,' +\n 'xlink:type,xml:base,xml:lang,xml:space,xmlns,xmlns:xlink,y,y1,y2,zoomAndPan', true);\n\n var validAttrs = extend({},\n uriAttrs,\n svgAttrs,\n htmlAttrs);\n\n function stringToMap(str, lowercaseKeys) {\n return arrayToMap(str.split(','), lowercaseKeys);\n }\n\n function arrayToMap(items, lowercaseKeys) {\n var obj = {}, i;\n for (i = 0; i < items.length; i++) {\n obj[lowercaseKeys ? lowercase(items[i]) : items[i]] = true;\n }\n return obj;\n }\n\n function addElementsTo(elementsMap, newElements) {\n if (newElements && newElements.length) {\n extend(elementsMap, arrayToMap(newElements));\n }\n }\n\n /**\n * Create an inert document that contains the dirty HTML that needs sanitizing.\n * We use the DOMParser API by default and fall back to createHTMLDocument if DOMParser is not\n * available.\n */\n var getInertBodyElement /* function(html: string): HTMLBodyElement */ = (function(window, document) {\n if (isDOMParserAvailable()) {\n return getInertBodyElement_DOMParser;\n }\n\n if (!document || !document.implementation) {\n throw $sanitizeMinErr('noinert', 'Can\\'t create an inert html document');\n }\n var inertDocument = document.implementation.createHTMLDocument('inert');\n var inertBodyElement = (inertDocument.documentElement || inertDocument.getDocumentElement()).querySelector('body');\n return getInertBodyElement_InertDocument;\n\n function isDOMParserAvailable() {\n try {\n return !!getInertBodyElement_DOMParser('');\n } catch (e) {\n return false;\n }\n }\n\n function getInertBodyElement_DOMParser(html) {\n // We add this dummy element to ensure that the rest of the content is parsed as expected\n // e.g. leading whitespace is maintained and tags like `` do not get hoisted to the `` tag.\n html = '' + html;\n try {\n var body = new window.DOMParser().parseFromString(html, 'text/html').body;\n body.firstChild.remove();\n return body;\n } catch (e) {\n return undefined;\n }\n }\n\n function getInertBodyElement_InertDocument(html) {\n inertBodyElement.innerHTML = html;\n\n // Support: IE 9-11 only\n // strip custom-namespaced attributes on IE<=11\n if (document.documentMode) {\n stripCustomNsAttrs(inertBodyElement);\n }\n\n return inertBodyElement;\n }\n })(window, window.document);\n\n /**\n * @example\n * htmlParser(htmlString, {\n * start: function(tag, attrs) {},\n * end: function(tag) {},\n * chars: function(text) {},\n * comment: function(text) {}\n * });\n *\n * @param {string} html string\n * @param {object} handler\n */\n function htmlParserImpl(html, handler) {\n if (html === null || html === undefined) {\n html = '';\n } else if (typeof html !== 'string') {\n html = '' + html;\n }\n\n var inertBodyElement = getInertBodyElement(html);\n if (!inertBodyElement) return '';\n\n //mXSS protection\n var mXSSAttempts = 5;\n do {\n if (mXSSAttempts === 0) {\n throw $sanitizeMinErr('uinput', 'Failed to sanitize html because the input is unstable');\n }\n mXSSAttempts--;\n\n // trigger mXSS if it is going to happen by reading and writing the innerHTML\n html = inertBodyElement.innerHTML;\n inertBodyElement = getInertBodyElement(html);\n } while (html !== inertBodyElement.innerHTML);\n\n var node = inertBodyElement.firstChild;\n while (node) {\n switch (node.nodeType) {\n case 1: // ELEMENT_NODE\n handler.start(node.nodeName.toLowerCase(), attrToMap(node.attributes));\n break;\n case 3: // TEXT NODE\n handler.chars(node.textContent);\n break;\n }\n\n var nextNode;\n if (!(nextNode = node.firstChild)) {\n if (node.nodeType === 1) {\n handler.end(node.nodeName.toLowerCase());\n }\n nextNode = getNonDescendant('nextSibling', node);\n if (!nextNode) {\n while (nextNode == null) {\n node = getNonDescendant('parentNode', node);\n if (node === inertBodyElement) break;\n nextNode = getNonDescendant('nextSibling', node);\n if (node.nodeType === 1) {\n handler.end(node.nodeName.toLowerCase());\n }\n }\n }\n }\n node = nextNode;\n }\n\n while ((node = inertBodyElement.firstChild)) {\n inertBodyElement.removeChild(node);\n }\n }\n\n function attrToMap(attrs) {\n var map = {};\n for (var i = 0, ii = attrs.length; i < ii; i++) {\n var attr = attrs[i];\n map[attr.name] = attr.value;\n }\n return map;\n }\n\n\n /**\n * Escapes all potentially dangerous characters, so that the\n * resulting string can be safely inserted into attribute or\n * element text.\n * @param value\n * @returns {string} escaped text\n */\n function encodeEntities(value) {\n return value.\n replace(/&/g, '&').\n replace(SURROGATE_PAIR_REGEXP, function(value) {\n var hi = value.charCodeAt(0);\n var low = value.charCodeAt(1);\n return '&#' + (((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000) + ';';\n }).\n replace(NON_ALPHANUMERIC_REGEXP, function(value) {\n return '&#' + value.charCodeAt(0) + ';';\n }).\n replace(//g, '>');\n }\n\n /**\n * create an HTML/XML writer which writes to buffer\n * @param {Array} buf use buf.join('') to get out sanitized html string\n * @returns {object} in the form of {\n * start: function(tag, attrs) {},\n * end: function(tag) {},\n * chars: function(text) {},\n * comment: function(text) {}\n * }\n */\n function htmlSanitizeWriterImpl(buf, uriValidator) {\n var ignoreCurrentElement = false;\n var out = bind(buf, buf.push);\n return {\n start: function(tag, attrs) {\n tag = lowercase(tag);\n if (!ignoreCurrentElement && blockedElements[tag]) {\n ignoreCurrentElement = tag;\n }\n if (!ignoreCurrentElement && validElements[tag] === true) {\n out('<');\n out(tag);\n forEach(attrs, function(value, key) {\n var lkey = lowercase(key);\n var isImage = (tag === 'img' && lkey === 'src') || (lkey === 'background');\n if (validAttrs[lkey] === true &&\n (uriAttrs[lkey] !== true || uriValidator(value, isImage))) {\n out(' ');\n out(key);\n out('=\"');\n out(encodeEntities(value));\n out('\"');\n }\n });\n out('>');\n }\n },\n end: function(tag) {\n tag = lowercase(tag);\n if (!ignoreCurrentElement && validElements[tag] === true && voidElements[tag] !== true) {\n out('');\n }\n // eslint-disable-next-line eqeqeq\n if (tag == ignoreCurrentElement) {\n ignoreCurrentElement = false;\n }\n },\n chars: function(chars) {\n if (!ignoreCurrentElement) {\n out(encodeEntities(chars));\n }\n }\n };\n }\n\n\n /**\n * When IE9-11 comes across an unknown namespaced attribute e.g. 'xlink:foo' it adds 'xmlns:ns1' attribute to declare\n * ns1 namespace and prefixes the attribute with 'ns1' (e.g. 'ns1:xlink:foo'). This is undesirable since we don't want\n * to allow any of these custom attributes. This method strips them all.\n *\n * @param node Root element to process\n */\n function stripCustomNsAttrs(node) {\n while (node) {\n if (node.nodeType === window.Node.ELEMENT_NODE) {\n var attrs = node.attributes;\n for (var i = 0, l = attrs.length; i < l; i++) {\n var attrNode = attrs[i];\n var attrName = attrNode.name.toLowerCase();\n if (attrName === 'xmlns:ns1' || attrName.lastIndexOf('ns1:', 0) === 0) {\n node.removeAttributeNode(attrNode);\n i--;\n l--;\n }\n }\n }\n\n var nextNode = node.firstChild;\n if (nextNode) {\n stripCustomNsAttrs(nextNode);\n }\n\n node = getNonDescendant('nextSibling', node);\n }\n }\n\n function getNonDescendant(propName, node) {\n // An element is clobbered if its `propName` property points to one of its descendants\n var nextNode = node[propName];\n if (nextNode && nodeContains.call(node, nextNode)) {\n throw $sanitizeMinErr('elclob', 'Failed to sanitize html because the element is clobbered: {0}', node.outerHTML || node.outerText);\n }\n return nextNode;\n }\n}\n\nfunction sanitizeText(chars) {\n var buf = [];\n var writer = htmlSanitizeWriter(buf, noop);\n writer.chars(chars);\n return buf.join('');\n}\n\n\n// define ngSanitize module and register $sanitize service\nangular.module('ngSanitize', [])\n .provider('$sanitize', $SanitizeProvider)\n .info({ angularVersion: '1.8.3' });\n\n/**\n * @ngdoc filter\n * @name linky\n * @kind function\n *\n * @description\n * Finds links in text input and turns them into html links. Supports `http/https/ftp/sftp/mailto` and\n * plain email address links.\n *\n * Requires the {@link ngSanitize `ngSanitize`} module to be installed.\n *\n * @param {string} text Input text.\n * @param {string} [target] Window (`_blank|_self|_parent|_top`) or named frame to open links in.\n * @param {object|function(url)} [attributes] Add custom attributes to the link element.\n *\n * Can be one of:\n *\n * - `object`: A map of attributes\n * - `function`: Takes the url as a parameter and returns a map of attributes\n *\n * If the map of attributes contains a value for `target`, it overrides the value of\n * the target parameter.\n *\n *\n * @returns {string} Html-linkified and {@link $sanitize sanitized} text.\n *\n * @usage\n \n *\n * @example\n \n \n
    \n Snippet: \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
    FilterSourceRendered
    linky filter\n
    <div ng-bind-html=\"snippet | linky\">
    </div>
    \n
    \n
    \n
    linky target\n
    <div ng-bind-html=\"snippetWithSingleURL | linky:'_blank'\">
    </div>
    \n
    \n
    \n
    linky custom attributes\n
    <div ng-bind-html=\"snippetWithSingleURL | linky:'_self':{rel: 'nofollow'}\">
    </div>
    \n
    \n
    \n
    no filter
    <div ng-bind=\"snippet\">
    </div>
    \n \n \n angular.module('linkyExample', ['ngSanitize'])\n .controller('ExampleController', ['$scope', function($scope) {\n $scope.snippet =\n 'Pretty text with some links:\\n' +\n 'http://angularjs.org/,\\n' +\n 'mailto:us@somewhere.org,\\n' +\n 'another@somewhere.org,\\n' +\n 'and one more: ftp://127.0.0.1/.';\n $scope.snippetWithSingleURL = 'http://angularjs.org/';\n }]);\n \n \n it('should linkify the snippet with urls', function() {\n expect(element(by.id('linky-filter')).element(by.binding('snippet | linky')).getText()).\n toBe('Pretty text with some links: http://angularjs.org/, us@somewhere.org, ' +\n 'another@somewhere.org, and one more: ftp://127.0.0.1/.');\n expect(element.all(by.css('#linky-filter a')).count()).toEqual(4);\n });\n\n it('should not linkify snippet without the linky filter', function() {\n expect(element(by.id('escaped-html')).element(by.binding('snippet')).getText()).\n toBe('Pretty text with some links: http://angularjs.org/, mailto:us@somewhere.org, ' +\n 'another@somewhere.org, and one more: ftp://127.0.0.1/.');\n expect(element.all(by.css('#escaped-html a')).count()).toEqual(0);\n });\n\n it('should update', function() {\n element(by.model('snippet')).clear();\n element(by.model('snippet')).sendKeys('new http://link.');\n expect(element(by.id('linky-filter')).element(by.binding('snippet | linky')).getText()).\n toBe('new http://link.');\n expect(element.all(by.css('#linky-filter a')).count()).toEqual(1);\n expect(element(by.id('escaped-html')).element(by.binding('snippet')).getText())\n .toBe('new http://link.');\n });\n\n it('should work with the target property', function() {\n expect(element(by.id('linky-target')).\n element(by.binding(\"snippetWithSingleURL | linky:'_blank'\")).getText()).\n toBe('http://angularjs.org/');\n expect(element(by.css('#linky-target a')).getAttribute('target')).toEqual('_blank');\n });\n\n it('should optionally add custom attributes', function() {\n expect(element(by.id('linky-custom-attributes')).\n element(by.binding(\"snippetWithSingleURL | linky:'_self':{rel: 'nofollow'}\")).getText()).\n toBe('http://angularjs.org/');\n expect(element(by.css('#linky-custom-attributes a')).getAttribute('rel')).toEqual('nofollow');\n });\n \n \n */\nangular.module('ngSanitize').filter('linky', ['$sanitize', function($sanitize) {\n var LINKY_URL_REGEXP =\n /((s?ftp|https?):\\/\\/|(www\\.)|(mailto:)?[A-Za-z0-9._%+-]+@)\\S*[^\\s.;,(){}<>\"\\u201d\\u2019]/i,\n MAILTO_REGEXP = /^mailto:/i;\n\n var linkyMinErr = angular.$$minErr('linky');\n var isDefined = angular.isDefined;\n var isFunction = angular.isFunction;\n var isObject = angular.isObject;\n var isString = angular.isString;\n\n return function(text, target, attributes) {\n if (text == null || text === '') return text;\n if (!isString(text)) throw linkyMinErr('notstring', 'Expected string but received: {0}', text);\n\n var attributesFn =\n isFunction(attributes) ? attributes :\n isObject(attributes) ? function getAttributesObject() {return attributes;} :\n function getEmptyAttributesObject() {return {};};\n\n var match;\n var raw = text;\n var html = [];\n var url;\n var i;\n while ((match = raw.match(LINKY_URL_REGEXP))) {\n // We can not end in these as they are sometimes found at the end of the sentence\n url = match[0];\n // if we did not match ftp/http/www/mailto then assume mailto\n if (!match[2] && !match[4]) {\n url = (match[3] ? 'http://' : 'mailto:') + url;\n }\n i = match.index;\n addText(raw.substr(0, i));\n addLink(url, match[0].replace(MAILTO_REGEXP, ''));\n raw = raw.substring(i + match[0].length);\n }\n addText(raw);\n return $sanitize(html.join(''));\n\n function addText(text) {\n if (!text) {\n return;\n }\n html.push(sanitizeText(text));\n }\n\n function addLink(url, text) {\n var key, linkAttributes = attributesFn(url);\n html.push('');\n addText(text);\n html.push('');\n }\n };\n}]);\n\n\n})(window, window.angular);\n","require('./angular-sanitize');\nmodule.exports = 'ngSanitize';\n","'use strict'\n\n/* Autoupdate plugin for baron 0.6+ */\n\nfunction autoUpdateOne(MutationObserver) {\n var self = this\n var watcher\n\n if (this._au) {\n return\n }\n\n function actualizeWatcher() {\n if (!self.root[self.origin.offset]) {\n startWatch()\n } else {\n stopWatch()\n }\n }\n\n // Set interval timeout for watching when root node will be visible\n function startWatch() {\n if (watcher) return\n\n watcher = setInterval(function() {\n if (self.root[self.origin.offset]) {\n stopWatch()\n self.update()\n }\n }, 300) // is it good enought for you?)\n }\n\n function stopWatch() {\n clearInterval(watcher)\n watcher = null\n }\n\n var debouncedUpdater = self._debounce(function() {\n self.update()\n }, 300)\n\n this._observer = new MutationObserver(function() {\n actualizeWatcher()\n self.update()\n debouncedUpdater()\n })\n\n this.on('init', function() {\n self._observer.observe(self.root, {\n childList: true,\n subtree: true,\n characterData: true\n // attributes: true\n // No reasons to set attributes to true\n // The case when root/child node with already properly inited baron toggled to hidden and then back to visible,\n // and the size of parent was changed during that hidden state, is very rare\n // Other cases are covered by watcher, and you still can do .update by yourself\n })\n\n actualizeWatcher()\n })\n\n this.on('dispose', function() {\n self._observer.disconnect()\n stopWatch()\n delete self._observer\n })\n\n this._au = true\n}\n\nmodule.exports = function autoUpdateCreator(win) {\n var MutationObserver = win.MutationObserver || win.WebKitMutationObserver || win.MozMutationObserver || null\n\n return function autoUpdate() {\n if (!MutationObserver) return this\n\n autoUpdateOne.call(this, MutationObserver)\n\n return this\n }\n}\n","'use strict'\n\n/* Controls plugin for baron */\n\nvar qs = require('./utils').qs\n\nmodule.exports = function controls(params) {\n var forward, backward, track, screen,\n self = this,\n event\n\n screen = params.screen || 0.9\n\n if (params.forward) {\n forward = qs(params.forward, this.clipper)\n\n event = {\n element: forward,\n\n handler: function() {\n var y = self.pos() + (params.delta || 30)\n\n self.pos(y)\n },\n\n type: 'click'\n }\n\n this._eventHandlers.push(event) // For auto-dispose\n this.event(event.element, event.type, event.handler, 'on')\n }\n\n if (params.backward) {\n backward = qs(params.backward, this.clipper)\n\n event = {\n element: backward,\n\n handler: function() {\n var y = self.pos() - (params.delta || 30)\n\n self.pos(y)\n },\n\n type: 'click'\n }\n\n this._eventHandlers.push(event) // For auto-dispose\n this.event(event.element, event.type, event.handler, 'on')\n }\n\n if (params.track) {\n if (params.track === true) {\n track = this.track\n } else {\n track = qs(params.track, this.clipper)\n }\n\n if (track) {\n event = {\n element: track,\n\n handler: function(e) {\n // https://github.com/Diokuz/baron/issues/121\n if (e.target != track) return\n\n var x = e['offset' + self.origin.x],\n xBar = self.bar[self.origin.offsetPos],\n sign = 0\n\n if (x < xBar) {\n sign = -1\n } else if (x > xBar + self.bar[self.origin.offset]) {\n sign = 1\n }\n\n var y = self.pos() + sign * screen * self.scroller[self.origin.client]\n\n self.pos(y)\n },\n\n type: 'mousedown'\n }\n\n this._eventHandlers.push(event) // For auto-dispose\n this.event(event.element, event.type, event.handler, 'on')\n }\n }\n}\n","'use strict'\n\nvar g = (function() {\n return this || (1, eval)('this')\n}())\nvar scopedWindow = g && g.window || g\n\nvar event = require('./utils').event\nvar css = require('./utils').css\nvar add = require('./utils').add\nvar has = require('./utils').has\nvar rm = require('./utils').rm\nvar clone = require('./utils').clone\nvar qs = require('./utils').qs\n\nvar _baron = baron // Stored baron value for noConflict usage\n// var Item = {}\nvar pos = ['left', 'top', 'right', 'bottom', 'width', 'height']\n// Global store for all baron instances (to be able to dispose them on html-nodes)\nvar instances = []\nvar origin = {\n v: { // Vertical\n x: 'Y', pos: pos[1], oppos: pos[3], crossPos: pos[0], crossOpPos: pos[2],\n size: pos[5],\n crossSize: pos[4], crossMinSize: 'min-' + pos[4], crossMaxSize: 'max-' + pos[4],\n client: 'clientHeight', crossClient: 'clientWidth',\n scrollEdge: 'scrollLeft',\n offset: 'offsetHeight', crossOffset: 'offsetWidth', offsetPos: 'offsetTop',\n scroll: 'scrollTop', scrollSize: 'scrollHeight'\n },\n h: { // Horizontal\n x: 'X', pos: pos[0], oppos: pos[2], crossPos: pos[1], crossOpPos: pos[3],\n size: pos[4],\n crossSize: pos[5], crossMinSize: 'min-' + pos[5], crossMaxSize: 'max-' + pos[5],\n client: 'clientWidth', crossClient: 'clientHeight',\n scrollEdge: 'scrollTop',\n offset: 'offsetWidth', crossOffset: 'offsetHeight', offsetPos: 'offsetLeft',\n scroll: 'scrollLeft', scrollSize: 'scrollWidth'\n }\n}\n\n// Some ugly vars\nvar opera12maxScrollbarSize = 17\n// I hate you https://github.com/Diokuz/baron/issues/110\nvar macmsxffScrollbarSize = 15\nvar macosxffRe = /[\\s\\S]*Macintosh[\\s\\S]*\\) Gecko[\\s\\S]*/\nvar isMacFF = macosxffRe.test(scopedWindow.navigator && scopedWindow.navigator.userAgent)\n\nvar log, liveBarons, shownErrors\n\nif (process.env.NODE_ENV !== 'production') {\n log = require('./log')\n liveBarons = 0\n shownErrors = {\n liveTooMany: false,\n allTooMany: false\n }\n}\n\n// window.baron and jQuery.fn.baron points to this function\nfunction baron(user) {\n var withParams = !!user\n var tryNode = (user && user[0]) || user\n var isNode = typeof user == 'string' || tryNode instanceof HTMLElement\n var params = isNode ? { root: user } : clone(user)\n var jQueryMode\n var rootNode\n var defaultParams = {\n direction: 'v',\n barOnCls: '_scrollbar',\n resizeDebounce: 0,\n event: event,\n cssGuru: false,\n impact: 'scroller',\n position: 'static'\n }\n\n params = params || {}\n\n // Extending default params by user-defined params\n for (var key in defaultParams) {\n if (params[key] == null) { // eslint-disable-line\n params[key] = defaultParams[key]\n }\n }\n\n if (process.env.NODE_ENV !== 'production') {\n if (params.position == 'absolute' && params.impact == 'clipper') {\n log('error', [\n 'Simultaneous use of `absolute` position and `clipper` impact values detected.',\n 'Those values cannot be used together.',\n 'See more https://github.com/Diokuz/baron/issues/138'\n ].join(' '), params)\n }\n }\n\n // `this` could be a jQuery instance\n jQueryMode = this && this instanceof scopedWindow.jQuery\n\n if (params._chain) {\n rootNode = params.root\n } else if (jQueryMode) {\n params.root = rootNode = this[0]\n } else {\n rootNode = qs(params.root || params.scroller)\n }\n\n if (process.env.NODE_ENV !== 'production') {\n if (!rootNode) {\n log('error', [\n 'Baron initialization failed: root node not found.'\n ].join(', '), params)\n\n return // or return baron-shell?\n }\n }\n\n var attr = manageAttr(rootNode, params.direction)\n var id = +attr // Could be NaN\n\n params.index = id\n\n // baron() can return existing instances,\n // @TODO update params on-the-fly\n // https://github.com/Diokuz/baron/issues/124\n if (id == id && attr !== null && instances[id]) {\n if (process.env.NODE_ENV !== 'production') {\n if (withParams) {\n log('error', [\n 'repeated initialization for html-node detected',\n 'https://github.com/Diokuz/baron/blob/master/docs/logs/repeated.md'\n ].join(', '), params.root)\n }\n }\n\n return instances[id]\n }\n\n // root and scroller can be different nodes\n if (params.root && params.scroller) {\n params.scroller = qs(params.scroller, rootNode)\n if (process.env.NODE_ENV !== 'production') {\n if (!params.scroller) {\n log('error', 'Scroller not found!', rootNode, params.scroller)\n }\n }\n } else {\n params.scroller = rootNode\n }\n\n params.root = rootNode\n\n var instance = init(params)\n\n if (instance.autoUpdate) {\n instance.autoUpdate()\n }\n\n return instance\n}\n\nfunction arrayEach(_obj, iterator) {\n var i = 0\n var obj = _obj\n\n if (obj.length === undefined || obj === scopedWindow) obj = [obj]\n\n while (obj[i]) {\n iterator.call(this, obj[i], i)\n i++\n }\n}\n\n// shortcut for getTime\nfunction getTime() {\n return new Date().getTime()\n}\n\nif (process.env.NODE_ENV !== 'production') {\n baron._instances = instances\n}\n\nfunction manageEvents(item, eventManager, mode) {\n // Creating new functions for one baron item only one time\n item._eventHandlers = item._eventHandlers || [\n {\n // onScroll:\n element: item.scroller,\n\n handler: function(e) {\n item.scroll(e)\n },\n\n type: 'scroll'\n }, {\n // css transitions & animations\n element: item.root,\n\n handler: function() {\n item.update()\n },\n\n type: 'transitionend animationend'\n }, {\n // onKeyup (textarea):\n element: item.scroller,\n\n handler: function() {\n item.update()\n },\n\n type: 'keyup'\n }, {\n // onMouseDown:\n element: item.bar,\n\n handler: function(e) {\n e.preventDefault() // Text selection disabling in Opera\n item.selection() // Disable text selection in ie8\n item.drag.now = 1 // Save private byte\n if (item.draggingCls) {\n add(item.root, item.draggingCls)\n }\n },\n\n type: 'touchstart mousedown'\n }, {\n // onMouseUp:\n element: document,\n\n handler: function() {\n item.selection(1) // Enable text selection\n item.drag.now = 0\n if (item.draggingCls) {\n rm(item.root, item.draggingCls)\n }\n },\n\n type: 'mouseup blur touchend'\n }, {\n // onCoordinateReset:\n element: document,\n\n handler: function(e) {\n if (e.button != 2) { // Not RM\n item._pos0(e)\n }\n },\n\n type: 'touchstart mousedown'\n }, {\n // onMouseMove:\n element: document,\n\n handler: function(e) {\n if (item.drag.now) {\n item.drag(e)\n }\n },\n\n type: 'mousemove touchmove'\n }, {\n // @TODO make one global listener\n // onResize:\n element: scopedWindow,\n\n handler: function() {\n item.update()\n },\n\n type: 'resize'\n }, {\n // @todo remove\n // sizeChange:\n element: item.root,\n\n handler: function() {\n item.update()\n },\n\n type: 'sizeChange'\n }, {\n // Clipper onScroll bug https://github.com/Diokuz/baron/issues/116\n element: item.clipper,\n\n handler: function() {\n item.clipperOnScroll()\n },\n\n type: 'scroll'\n }\n ]\n\n arrayEach(item._eventHandlers, function(evt) {\n if (evt.element) {\n // workaround for element-elements in `fix` plugin\n // @todo dispose `fix` in proper way and remove workaround\n if (evt.element.length && evt.element !== scopedWindow) {\n for (var i = 0; i < evt.element.length; i++) {\n eventManager(evt.element[i], evt.type, evt.handler, mode)\n }\n } else {\n eventManager(evt.element, evt.type, evt.handler, mode)\n }\n }\n })\n\n // if (item.scroller) {\n // event(item.scroller, 'scroll', item._eventHandlers.onScroll, mode)\n // }\n // if (item.bar) {\n // event(item.bar, 'touchstart mousedown', item._eventHandlers.onMouseDown, mode)\n // }\n // event(document, 'mouseup blur touchend', item._eventHandlers.onMouseUp, mode)\n // event(document, 'touchstart mousedown', item._eventHandlers.onCoordinateReset, mode)\n // event(document, 'mousemove touchmove', item._eventHandlers.onMouseMove, mode)\n // event(window, 'resize', item._eventHandlers.onResize, mode)\n // if (item.root) {\n // event(item.root, 'sizeChange', item._eventHandlers.onResize, mode)\n // // Custon event for alternate baron update mechanism\n // }\n}\n\n// set, remove or read baron-specific id-attribute\n// @returns {String|null} - id node value, or null, if there is no attr\nfunction manageAttr(node, direction, mode, id) {\n var attrName = 'data-baron-' + direction + '-id'\n\n if (mode == 'on') {\n node.setAttribute(attrName, id)\n } else if (mode == 'off') {\n node.removeAttribute(attrName)\n }\n\n return node.getAttribute(attrName)\n}\n\nfunction init(params) {\n var out = new baron.prototype.constructor(params)\n\n manageEvents(out, params.event, 'on')\n\n manageAttr(out.root, params.direction, 'on', instances.length)\n instances.push(out)\n\n if (process.env.NODE_ENV !== 'production') {\n liveBarons++\n if (liveBarons > 100 && !shownErrors.liveTooMany) {\n log('warn', [\n 'You have too many live baron instances on page (' + liveBarons + ')!',\n 'Are you forget to dispose some of them?',\n 'All baron instances can be found in baron._instances:'\n ].join(' '), instances)\n shownErrors.liveTooMany = true\n }\n if (instances.length > 1000 && !shownErrors.allTooMany) {\n log('warn', [\n 'You have too many inited baron instances on page (' + instances.length + ')!',\n 'Some of them are disposed, and thats good news.',\n 'but baron.init was call too many times, and thats is bad news.',\n 'All baron instances can be found in baron._instances:'\n ].join(' '), instances)\n shownErrors.allTooMany = true\n }\n }\n\n out.update()\n\n return out\n}\n\nfunction fire(eventName) {\n if (this.events && this.events[eventName]) {\n for (var i = 0; i < this.events[eventName].length; i++) {\n var args = Array.prototype.slice.call( arguments, 1 )\n\n this.events[eventName][i].apply(this, args)\n }\n }\n}\n\nbaron.prototype = {\n // underscore.js realization\n // used in autoUpdate plugin\n _debounce: function(func, wait) {\n var self = this,\n timeout,\n // args, // right now there is no need for arguments\n // context, // and for context\n timestamp\n // result // and for result\n\n var later = function() {\n if (self._disposed) {\n clearTimeout(timeout)\n timeout = self = null\n return\n }\n\n var last = getTime() - timestamp\n\n if (last < wait && last >= 0) {\n timeout = setTimeout(later, wait - last)\n } else {\n timeout = null\n // result = func.apply(context, args)\n func()\n // context = args = null\n }\n }\n\n return function() {\n // context = this\n // args = arguments\n timestamp = getTime()\n\n if (!timeout) {\n timeout = setTimeout(later, wait)\n }\n\n // return result\n }\n },\n\n constructor: function(params) {\n var barPos,\n scrollerPos0,\n track,\n resizePauseTimer,\n scrollingTimer,\n resizeLastFire,\n oldBarSize\n\n resizeLastFire = getTime()\n\n this.params = params\n this.event = params.event\n this.events = {}\n\n // DOM elements\n this.root = params.root // Always html node, not just selector\n this.scroller = qs(params.scroller)\n if (process.env.NODE_ENV !== 'production') {\n if (this.scroller.tagName == 'body') {\n log('error', [\n 'Please, do not use BODY as a scroller.',\n 'https://github.com/Diokuz/baron/blob/master/docs/logs/do-not-use-body.md'\n ].join(', '), params)\n }\n }\n this.bar = qs(params.bar, this.root)\n track = this.track = qs(params.track, this.root)\n if (!this.track && this.bar) {\n track = this.bar.parentNode\n }\n this.clipper = this.scroller.parentNode\n\n // Parameters\n this.direction = params.direction\n this.rtl = params.rtl\n this.origin = origin[this.direction]\n this.barOnCls = params.barOnCls\n this.scrollingCls = params.scrollingCls\n this.draggingCls = params.draggingCls\n this.impact = params.impact\n this.position = params.position\n this.rtl = params.rtl\n this.barTopLimit = 0\n this.resizeDebounce = params.resizeDebounce\n\n // Updating height or width of bar\n function setBarSize(_size) {\n var barMinSize = this.barMinSize || 20\n var size = _size\n\n if (size > 0 && size < barMinSize) {\n size = barMinSize\n }\n\n if (this.bar) {\n css(this.bar, this.origin.size, parseInt(size, 10) + 'px')\n }\n }\n\n // Updating top or left bar position\n function posBar(_pos) {\n if (this.bar) {\n var was = css(this.bar, this.origin.pos),\n will = +_pos + 'px'\n\n if (will && will != was) {\n css(this.bar, this.origin.pos, will)\n }\n }\n }\n\n // Free path for bar\n function k() {\n return track[this.origin.client] - this.barTopLimit - this.bar[this.origin.offset]\n }\n\n // Relative content top position to bar top position\n function relToPos(r) {\n return r * k.call(this) + this.barTopLimit\n }\n\n // Bar position to relative content position\n function posToRel(t) {\n return (t - this.barTopLimit) / k.call(this)\n }\n\n // Cursor position in main direction in px // Now with iOs support\n this.cursor = function(e) {\n return e['client' + this.origin.x] ||\n (((e.originalEvent || e).touches || {})[0] || {})['page' + this.origin.x]\n }\n\n // Text selection pos preventing\n function dontPosSelect() {\n return false\n }\n\n this.pos = function(x) { // Absolute scroller position in px\n var ie = 'page' + this.origin.x + 'Offset',\n key = (this.scroller[ie]) ? ie : this.origin.scroll\n\n if (x !== undefined) this.scroller[key] = x\n\n return this.scroller[key]\n }\n\n this.rpos = function(r) { // Relative scroller position (0..1)\n var free = this.scroller[this.origin.scrollSize] - this.scroller[this.origin.client],\n x\n\n if (r) {\n x = this.pos(r * free)\n } else {\n x = this.pos()\n }\n\n return x / (free || 1)\n }\n\n // Switch on the bar by adding user-defined CSS classname to scroller\n this.barOn = function(dispose) {\n if (this.barOnCls) {\n var noScroll = this.scroller[this.origin.client] >= this.scroller[this.origin.scrollSize]\n\n if (dispose || noScroll) {\n if (has(this.root, this.barOnCls)) {\n rm(this.root, this.barOnCls)\n }\n } else if (!has(this.root, this.barOnCls)) {\n add(this.root, this.barOnCls)\n }\n }\n }\n\n this._pos0 = function(e) {\n scrollerPos0 = this.cursor(e) - barPos\n }\n\n this.drag = function(e) {\n var rel = posToRel.call(this, this.cursor(e) - scrollerPos0)\n var sub = (this.scroller[this.origin.scrollSize] - this.scroller[this.origin.client])\n\n this.scroller[this.origin.scroll] = rel * sub\n }\n\n // Text selection preventing on drag\n this.selection = function(enable) {\n this.event(document, 'selectpos selectstart', dontPosSelect, enable ? 'off' : 'on')\n }\n\n // onResize & DOM modified handler\n // also fires on init\n // Note: max/min-size didnt sets if size did not really changed (for example, on init in Chrome)\n this.resize = function() {\n var self = this\n var minPeriod = (self.resizeDebounce === undefined) ? 300 : self.resizeDebounce\n var delay = 0\n\n if (getTime() - resizeLastFire < minPeriod) {\n clearTimeout(resizePauseTimer)\n delay = minPeriod\n }\n\n function upd() {\n var offset = self.scroller[self.origin.crossOffset]\n var client = self.scroller[self.origin.crossClient]\n var padding = 0\n var was, will\n\n // https://github.com/Diokuz/baron/issues/110\n if (isMacFF) {\n padding = macmsxffScrollbarSize\n\n // Opera 12 bug https://github.com/Diokuz/baron/issues/105\n } else if (client > 0 && offset === 0) {\n // Only Opera 12 in some rare nested flexbox cases goes here\n // Sorry guys for magic,\n // but I dont want to create temporary html-nodes set\n // just for measuring scrollbar size in Opera 12.\n // 17px for Windows XP-8.1, 15px for Mac (really rare).\n offset = client + opera12maxScrollbarSize\n }\n\n if (offset) { // if there is no size, css should not be set\n self.barOn()\n\n if (self.impact == 'scroller') { // scroller\n var delta = offset - client + padding\n\n // `static` position works only for `scroller` impact\n if (self.position == 'static') { // static\n was = css(self.scroller, self.origin.crossSize)\n will = self.clipper[self.origin.crossClient] + delta + 'px'\n\n if (was != will) {\n self._setCrossSizes(self.scroller, will)\n }\n } else { // absolute\n var styles = {}\n var key = self.rtl ? 'Left' : 'Right'\n\n if (self.direction == 'h') {\n key = 'Bottom'\n }\n\n styles['padding' + key] = delta + 'px'\n css(self.scroller, styles)\n }\n } else { // clipper\n was = css(self.clipper, self.origin.crossSize)\n will = client + 'px'\n\n if (was != will) {\n self._setCrossSizes(self.clipper, will)\n }\n }\n } else {\n // do nothing (display: none, or something)\n }\n\n Array.prototype.unshift.call(arguments, 'resize')\n fire.apply(self, arguments)\n\n resizeLastFire = getTime()\n }\n\n if (delay) {\n resizePauseTimer = setTimeout(upd, delay)\n } else {\n upd()\n }\n }\n\n this.updatePositions = function(force) {\n var newBarSize,\n self = this\n\n if (self.bar) {\n newBarSize = (track[self.origin.client] - self.barTopLimit) *\n self.scroller[self.origin.client] / self.scroller[self.origin.scrollSize]\n\n // Positioning bar\n if (force || parseInt(oldBarSize, 10) != parseInt(newBarSize, 10)) {\n setBarSize.call(self, newBarSize)\n oldBarSize = newBarSize\n }\n\n barPos = relToPos.call(self, self.rpos())\n\n posBar.call(self, barPos)\n }\n\n Array.prototype.unshift.call( arguments, 'scroll' )\n fire.apply(self, arguments)\n }\n\n // onScroll handler\n this.scroll = function() {\n var self = this\n\n self.updatePositions()\n\n if (self.scrollingCls) {\n if (!scrollingTimer) {\n add(self.root, self.scrollingCls)\n }\n clearTimeout(scrollingTimer)\n scrollingTimer = setTimeout(function() {\n rm(self.root, self.scrollingCls)\n scrollingTimer = undefined\n }, 300)\n }\n }\n\n // https://github.com/Diokuz/baron/issues/116\n this.clipperOnScroll = function() {\n // WTF is this line? https://github.com/Diokuz/baron/issues/134\n // if (this.direction == 'h') return\n\n // assign `initial scroll position` to `clipper.scrollLeft` (0 for ltr, ~20 for rtl)\n if (!this.rtl) {\n this.clipper[this.origin.scrollEdge] = 0\n } else {\n this.clipper[this.origin.scrollEdge] = this.clipper[this.origin.scrollSize]\n }\n }\n\n // Flexbox `align-items: stretch` (default) requires to set min-width for vertical\n // and max-height for horizontal scroll. Just set them all.\n // http://www.w3.org/TR/css-flexbox-1/#valdef-align-items-stretch\n this._setCrossSizes = function(node, size) {\n var styles = {}\n\n styles[this.origin.crossSize] = size\n styles[this.origin.crossMinSize] = size\n styles[this.origin.crossMaxSize] = size\n\n css(node, styles)\n }\n\n // Set common css rules\n this._dumbCss = function(on) {\n if (params.cssGuru) return\n\n var overflow = on ? 'hidden' : null\n var msOverflowStyle = on ? 'none' : null\n\n css(this.clipper, {\n overflow: overflow,\n msOverflowStyle: msOverflowStyle,\n position: this.position == 'static' ? '' : 'relative'\n })\n\n var scroll = on ? 'scroll' : null\n var axis = this.direction == 'v' ? 'y' : 'x'\n var scrollerCss = {}\n\n scrollerCss['overflow-' + axis] = scroll\n scrollerCss['box-sizing'] = 'border-box'\n scrollerCss.margin = '0'\n scrollerCss.border = '0'\n\n if (this.position == 'absolute') {\n scrollerCss.position = 'absolute'\n scrollerCss.top = '0'\n\n if (this.direction == 'h') {\n scrollerCss.left = scrollerCss.right = '0'\n } else {\n scrollerCss.bottom = '0'\n scrollerCss.right = this.rtl ? '0' : ''\n scrollerCss.left = this.rtl ? '' : '0'\n }\n }\n\n css(this.scroller, scrollerCss)\n }\n\n // onInit actions\n this._dumbCss(true)\n\n if (isMacFF) {\n var padding = 'paddingRight'\n var styles = {}\n // getComputedStyle is ie9+, but we here only in f ff\n var paddingWas = scopedWindow.getComputedStyle(this.scroller)[[padding]]\n\n if (params.direction == 'h') {\n padding = 'paddingBottom'\n } else if (params.rtl) {\n padding = 'paddingLeft'\n }\n\n var numWas = parseInt(paddingWas, 10)\n\n if (numWas != numWas) numWas = 0\n styles[padding] = (macmsxffScrollbarSize + numWas) + 'px'\n css(this.scroller, styles)\n }\n\n return this\n },\n\n // fires on any update and on init\n update: function(params) {\n if (process.env.NODE_ENV !== 'production') {\n if (this._disposed) {\n log('error', [\n 'Update on disposed baron instance detected.',\n 'You should clear your stored baron value for this instance:',\n this\n ].join(' '), params)\n }\n }\n\n fire.call(this, 'upd', params) // Update all plugins' params\n\n this.resize(1)\n this.updatePositions(1)\n\n return this\n },\n\n // One instance\n dispose: function() {\n if (process.env.NODE_ENV !== 'production') {\n if (this._disposed) {\n log('error', 'Already disposed:', this)\n }\n\n liveBarons--\n }\n\n manageEvents(this, this.event, 'off')\n manageAttr(this.root, this.params.direction, 'off')\n if (this.params.direction == 'v') {\n this._setCrossSizes(this.scroller, '')\n } else {\n this._setCrossSizes(this.clipper, '')\n }\n this._dumbCss(false)\n this.barOn(true)\n fire.call(this, 'dispose')\n instances[this.params.index] = null\n this.params = null\n this._disposed = true\n },\n\n on: function(eventName, func, arg) {\n var names = eventName.split(' ')\n\n for (var i = 0; i < names.length; i++) {\n if (names[i] == 'init') {\n func.call(this, arg)\n } else {\n this.events[names[i]] = this.events[names[i]] || []\n\n this.events[names[i]].push(function(userArg) {\n func.call(this, userArg || arg)\n })\n }\n }\n },\n\n baron: function(params) {\n params.root = this.params.root\n params.scroller = this.params.scroller\n params.direction = (this.params.direction == 'v') ? 'h' : 'v'\n params._chain = true\n\n return baron(params)\n }\n}\n\n// baron.fn.constructor.prototype = baron.fn\nbaron.prototype.constructor.prototype = baron.prototype\n\n// Use when you need \"baron\" global var for another purposes\nbaron.noConflict = function() {\n scopedWindow.baron = _baron // Restoring original value of \"baron\" global var\n\n return baron\n}\n\nbaron.version = '3.0.1'\n\nbaron.prototype.autoUpdate = require('./autoUpdate')(scopedWindow)\nbaron.prototype.fix = require('./fix')\nbaron.prototype.controls = require('./controls')\n\nmodule.exports = baron\n","'use strict'\n\n/* Fixable elements plugin for baron */\n\nvar log = require('./log')\nvar css = require('./utils').css\nvar add = require('./utils').add\nvar rm = require('./utils').rm\n\nmodule.exports = function fix(userParams) {\n var elements,\n viewPortSize,\n params = { // Default params\n outside: '',\n inside: '',\n before: '',\n after: '',\n past: '',\n future: '',\n radius: 0,\n minView: 0\n },\n topFixHeights = [], // inline style for element\n topRealHeights = [], // ? something related to negative margins for fixable elements\n headerTops = [], // offset positions when not fixed\n scroller = this.scroller,\n eventManager = this.event,\n self = this\n\n if (process.env.NODE_ENV !== 'production') {\n if (this.position != 'static') {\n log('error', [\n 'Fix plugin cannot work properly in non-static baron position.',\n 'See more https://github.com/Diokuz/baron/issues/135'\n ].join(' '), this.params)\n }\n }\n\n // i - number of fixing element, pos - fix-position in px, flag - 1: top, 2: bottom\n // Invocation only in case when fix-state changed\n function fixElement(i, _pos, flag) {\n var pos = _pos\n var ori = flag == 1 ? 'pos' : 'oppos'\n\n if (viewPortSize < (params.minView || 0)) { // No headers fixing when no enought space for viewport\n pos = undefined\n }\n\n // Removing all fixing stuff - we can do this because fixElement triggers only when fixState really changed\n css(elements[i], this.origin.pos, '')\n css(elements[i], this.origin.oppos, '')\n rm(elements[i], params.outside)\n\n // Fixing if needed\n if (pos !== undefined) {\n pos += 'px'\n css(elements[i], this.origin[ori], pos)\n add(elements[i], params.outside)\n }\n }\n\n function bubbleWheel(e) {\n try {\n var i = document.createEvent('WheelEvent') // i - for extra byte\n\n // evt.initWebKitWheelEvent(deltaX, deltaY, window, screenX, screenY, clientX, clientY, ctrlKey, altKey, shiftKey, metaKey)\n i.initWebKitWheelEvent(e.originalEvent.wheelDeltaX, e.originalEvent.wheelDeltaY)\n scroller.dispatchEvent(i)\n e.preventDefault()\n } catch (ex) {\n //\n }\n }\n\n function init(_params) {\n var pos\n\n for (var key in _params) {\n params[key] = _params[key]\n }\n\n if (params.elements instanceof HTMLElement) {\n elements = [params.elements]\n } else if (typeof params.elements == 'string') {\n elements = this.scroller.querySelectorAll(params.elements)\n } else if (params.elements && params.elements[0] instanceof HTMLElement) {\n elements = params.elements\n }\n\n if (elements) {\n viewPortSize = this.scroller[this.origin.client]\n for (var i = 0; i < elements.length; i++) {\n // Variable header heights\n pos = {}\n pos[this.origin.size] = elements[i][this.origin.offset] + 'px'\n if (elements[i].parentNode !== this.scroller) {\n css(elements[i].parentNode, pos)\n }\n pos = {}\n pos[this.origin.crossSize] = elements[i].parentNode[this.origin.crossClient] + 'px'\n css(elements[i], pos)\n\n // Between fixed headers\n viewPortSize -= elements[i][this.origin.offset]\n\n headerTops[i] = elements[i].parentNode[this.origin.offsetPos] // No paddings for parentNode\n\n // Summary elements height above current\n topFixHeights[i] = (topFixHeights[i - 1] || 0) // Not zero because of negative margins\n topRealHeights[i] = (topRealHeights[i - 1] || Math.min(headerTops[i], 0))\n\n if (elements[i - 1]) {\n topFixHeights[i] += elements[i - 1][this.origin.offset]\n topRealHeights[i] += elements[i - 1][this.origin.offset]\n }\n\n if ( !(i == 0 && headerTops[i] == 0)/* && force */) {\n this.event(elements[i], 'mousewheel', bubbleWheel, 'off')\n this.event(elements[i], 'mousewheel', bubbleWheel)\n }\n }\n\n if (params.limiter && elements[0]) { // Bottom edge of first header as top limit for track\n if (this.track && this.track != this.scroller) {\n pos = {}\n pos[this.origin.pos] = elements[0].parentNode[this.origin.offset] + 'px'\n css(this.track, pos)\n } else {\n this.barTopLimit = elements[0].parentNode[this.origin.offset]\n }\n // this.barTopLimit = elements[0].parentNode[this.origin.offset]\n this.scroll()\n }\n\n if (params.limiter === false) { // undefined (in second fix instance) should have no influence on bar limit\n this.barTopLimit = 0\n }\n }\n\n var event = {\n element: elements,\n\n handler: function() {\n var parent = this.parentNode,\n top = parent.offsetTop,\n num\n\n // finding num -> elements[num] === this\n for (var j = 0; j < elements.length; j++ ) {\n if (elements[j] === this) num = j\n }\n\n var locPos = top - topFixHeights[num]\n\n if (params.scroll) { // User defined callback\n params.scroll({\n x1: self.scroller.scrollTop,\n x2: locPos\n })\n } else {\n self.scroller.scrollTop = locPos\n }\n },\n\n type: 'click'\n }\n\n if (params.clickable) {\n this._eventHandlers.push(event) // For auto-dispose\n // eventManager(event.element, event.type, event.handler, 'off')\n for (var j = 0; j < event.element.length; j++) {\n eventManager(event.element[j], event.type, event.handler, 'on')\n }\n }\n }\n\n this.on('init', init, userParams)\n\n var fixFlag = [], // 1 - past, 2 - future, 3 - current (not fixed)\n gradFlag = []\n\n this.on('init scroll', function() {\n var fixState, hTop, gradState\n var i\n\n if (elements) {\n var change\n\n // fixFlag update\n for (i = 0; i < elements.length; i++) {\n fixState = 0\n if (headerTops[i] - this.pos() < topRealHeights[i] + params.radius) {\n // Header trying to go up\n fixState = 1\n hTop = topFixHeights[i]\n } else if (headerTops[i] - this.pos() > topRealHeights[i] + viewPortSize - params.radius) {\n // Header trying to go down\n fixState = 2\n // console.log('topFixHeights[i] + viewPortSize + topRealHeights[i]', topFixHeights[i], this.scroller[this.origin.client], topRealHeights[i])\n hTop = this.scroller[this.origin.client] - elements[i][this.origin.offset] - topFixHeights[i] - viewPortSize\n // console.log('hTop', hTop, viewPortSize, elements[this.origin.offset], topFixHeights[i])\n // (topFixHeights[i] + viewPortSize + elements[this.origin.offset]) - this.scroller[this.origin.client]\n } else {\n // Header in viewport\n fixState = 3\n hTop = undefined\n }\n\n gradState = false\n if (headerTops[i] - this.pos() < topRealHeights[i] || headerTops[i] - this.pos() > topRealHeights[i] + viewPortSize) {\n gradState = true\n }\n\n if (fixState != fixFlag[i] || gradState != gradFlag[i]) {\n fixElement.call(this, i, hTop, fixState)\n fixFlag[i] = fixState\n gradFlag[i] = gradState\n change = true\n }\n }\n\n // Adding positioning classes (on last top and first bottom header)\n if (change) { // At leats one change in elements flag structure occured\n for (i = 0; i < elements.length; i++) {\n if (fixFlag[i] == 1 && params.past) {\n add(elements[i], params.past)\n rm(elements[i], params.future)\n }\n\n if (fixFlag[i] == 2 && params.future) {\n add(elements[i], params.future)\n rm(elements[i], params.past)\n }\n\n if (fixFlag[i] == 3) {\n rm(elements[i], params.past)\n rm(elements[i], params.future)\n add(elements[i], params.inside)\n }\n\n if (fixFlag[i] != fixFlag[i + 1] && fixFlag[i] == 1) {\n add(elements[i], params.before)\n rm(elements[i], params.after) // Last top fixed header\n } else if (fixFlag[i] != fixFlag[i - 1] && fixFlag[i] == 2) {\n add(elements[i], params.after)\n rm(elements[i], params.before) // First bottom fixed header\n } else {\n rm(elements[i], params.before)\n rm(elements[i], params.after)\n }\n\n if (params.grad) {\n if (gradFlag[i]) {\n add(elements[i], params.grad)\n } else {\n rm(elements[i], params.grad)\n }\n }\n }\n }\n }\n })\n\n this.on('resize upd', function(updParams) {\n init.call(this, updParams && updParams.fix)\n })\n\n return this\n}\n","module.exports = function log(level, msg, more) {\n var func = console[level] || console.log\n var args = [\n 'Baron: ' + msg,\n more\n ]\n\n Function.prototype.apply.call(func, console, args)\n}\n","'use strict'\n\n// Test via a getter in the options object to see if the passive property is accessed\n// https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md#feature-detection\nvar supportsPassive = false\n\ntry {\n var opts = Object.defineProperty({}, 'passive', {\n get: function() {\n supportsPassive = true\n }\n })\n\n window.addEventListener('test', null, opts)\n} catch (e) {\n // pass\n}\n\nmodule.exports.event = function event(elem, _eventNames, handler, mode) {\n var eventNames = _eventNames.split(' ')\n var prefix = mode == 'on' ? 'add' : 'remove'\n\n eventNames.forEach(function(eventName) {\n var options = false\n\n if (['scroll', 'touchstart', 'touchmove'].indexOf(eventName) != -1 && supportsPassive) {\n options = { passive: true }\n }\n\n elem[prefix + 'EventListener'](eventName, handler, options)\n })\n}\n\nfunction each(obj, handler) {\n for (var key in obj) {\n if (obj.hasOwnProperty(key)) {\n handler(key, obj[key])\n }\n }\n}\n\nmodule.exports.css = function css(node, key, value) {\n var styles\n\n if (value === undefined) {\n // Getter mode\n if (typeof key == 'string') {\n return node.style[key]\n }\n\n styles = key\n } else {\n styles = {}\n styles[key] = value\n }\n\n each(styles, function(k, val) {\n node.style[k] = val\n })\n}\n\nmodule.exports.add = function add(node, cls) {\n if (!cls) {\n return\n }\n\n node.classList.add(cls)\n}\n\nmodule.exports.rm = function add(node, cls) {\n if (!cls) {\n return\n }\n\n node.classList.remove(cls)\n}\n\nmodule.exports.has = function has(node, cls) {\n if (!cls) {\n return false\n }\n\n return node.classList.contains(cls)\n}\n\nmodule.exports.clone = function clone(_input) {\n var output = {}\n var input = _input || {}\n\n each(input, function(key, value) {\n output[key] = value\n })\n\n return output\n}\n\nmodule.exports.qs = function qs(selector, _ctx) {\n if (selector instanceof HTMLElement) {\n return selector\n }\n\n var ctx = _ctx || document\n\n return ctx.querySelector(selector)\n}\n\nmodule.exports.each = each\n","import { deprecationWarning, urlUtil } from '@grafana/data';\nimport { locationSearchToObject, locationService, navigationLogger } from '@grafana/runtime';\n\n// Ref: https://github.com/angular/angular.js/blob/ae8e903edf88a83fedd116ae02c0628bf72b150c/src/ng/location.js#L5\nconst DEFAULT_PORTS: Record = { http: 80, https: 443, ftp: 21 };\n\nexport class AngularLocationWrapper {\n constructor() {\n this.absUrl = this.wrapInDeprecationWarning(this.absUrl);\n this.hash = this.wrapInDeprecationWarning(this.hash);\n this.host = this.wrapInDeprecationWarning(this.host);\n this.path = this.wrapInDeprecationWarning(this.path);\n this.port = this.wrapInDeprecationWarning(this.port, 'window.location');\n this.protocol = this.wrapInDeprecationWarning(this.protocol, 'window.location');\n this.replace = this.wrapInDeprecationWarning(this.replace);\n this.search = this.wrapInDeprecationWarning(this.search);\n this.state = this.wrapInDeprecationWarning(this.state);\n this.url = this.wrapInDeprecationWarning(this.url);\n }\n\n wrapInDeprecationWarning(fn: Function, replacement?: string) {\n let self = this;\n\n return function wrapper() {\n deprecationWarning('$location', fn.name, replacement || 'locationService');\n return fn.apply(self, arguments);\n };\n }\n\n absUrl(): string {\n return `${window.location.origin}${this.url()}`;\n }\n\n hash(newHash?: string | null) {\n navigationLogger('AngularLocationWrapper', false, 'Angular compat layer: hash');\n\n if (!newHash) {\n return locationService.getLocation().hash.slice(1);\n } else {\n throw new Error('AngularLocationWrapper method not implemented.');\n }\n }\n\n host(): string {\n return new URL(window.location.href).hostname;\n }\n\n path(pathname?: any) {\n navigationLogger('AngularLocationWrapper', false, 'Angular compat layer: path');\n\n const location = locationService.getLocation();\n\n if (pathname !== undefined && pathname !== null) {\n let parsedPath = String(pathname);\n parsedPath = parsedPath.startsWith('/') ? parsedPath : `/${parsedPath}`;\n const url = new URL(`${window.location.origin}${parsedPath}`);\n\n locationService.push({\n pathname: url.pathname,\n search: url.search.length > 0 ? url.search : location.search,\n hash: url.hash.length > 0 ? url.hash : location.hash,\n });\n return this;\n }\n\n if (pathname === null) {\n locationService.push('/');\n return this;\n }\n\n return location.pathname;\n }\n\n port(): number | null {\n const url = new URL(window.location.href);\n return parseInt(url.port, 10) || DEFAULT_PORTS[url.protocol] || null;\n }\n\n protocol(): string {\n return new URL(window.location.href).protocol.slice(0, -1);\n }\n\n replace() {\n throw new Error('AngularLocationWrapper method not implemented.');\n }\n\n search(search?: any, paramValue?: any) {\n navigationLogger('AngularLocationWrapper', false, 'Angular compat layer: search');\n if (!search) {\n return locationService.getSearchObject();\n }\n\n if (search && arguments.length > 1) {\n locationService.partial({\n [search]: paramValue,\n });\n\n return this;\n }\n\n if (search) {\n let newQuery;\n\n if (typeof search === 'object') {\n newQuery = { ...search };\n } else {\n newQuery = locationSearchToObject(search);\n }\n\n for (const key of Object.keys(newQuery)) {\n // removing params with null | undefined\n if (newQuery[key] === null || newQuery[key] === undefined) {\n delete newQuery[key];\n }\n }\n\n const updatedUrl = urlUtil.renderUrl(locationService.getLocation().pathname, newQuery);\n locationService.push(updatedUrl);\n }\n\n return this;\n }\n\n state(state?: any) {\n navigationLogger('AngularLocationWrapper', false, 'Angular compat layer: state');\n throw new Error('AngularLocationWrapper method not implemented.');\n }\n\n url(newUrl?: any) {\n navigationLogger('AngularLocationWrapper', false, 'Angular compat layer: url');\n\n if (newUrl !== undefined) {\n if (newUrl.startsWith('#')) {\n locationService.push({ ...locationService.getLocation(), hash: newUrl });\n } else if (newUrl.startsWith('?')) {\n locationService.push({ ...locationService.getLocation(), search: newUrl });\n } else if (newUrl.trim().length === 0) {\n locationService.push('/');\n } else {\n locationService.push(newUrl);\n }\n\n return locationService;\n }\n\n const location = locationService.getLocation();\n return `${location.pathname}${location.search}${location.hash}`;\n }\n}\n","// @ts-ignore\nimport baron from 'baron';\nimport { Subscription } from 'rxjs';\n\nimport { PanelEvents } from '@grafana/data';\nimport { RefreshEvent } from '@grafana/runtime';\nimport { coreModule } from 'app/angular/core_module';\nimport { PanelDirectiveReadyEvent, RenderEvent } from 'app/types/events';\n\nimport { PanelModel } from '../../features/dashboard/state';\n\nimport { PanelCtrl } from './panel_ctrl';\n\nconst panelTemplate = `\n \n`;\n\ncoreModule.directive('grafanaPanel', [\n '$timeout',\n ($timeout) => {\n return {\n restrict: 'E',\n template: panelTemplate,\n transclude: true,\n scope: { ctrl: '=' },\n link: (scope: any, elem) => {\n const ctrl: PanelCtrl = scope.ctrl;\n const panel: PanelModel = scope.ctrl.panel;\n const subs = new Subscription();\n\n let panelScrollbar: any;\n\n function resizeScrollableContent() {\n if (panelScrollbar) {\n panelScrollbar.update();\n }\n }\n\n ctrl.events.on(PanelEvents.componentDidMount, () => {\n if ((ctrl as any).__proto__.constructor.scrollable) {\n const scrollRootClass = 'baron baron__root baron__clipper panel-content--scrollable';\n const scrollerClass = 'baron__scroller';\n const scrollBarHTML = `\n
    \n
    \n
    \n `;\n\n const scrollRoot = elem;\n const scroller = elem.find(':first').find(':first');\n\n scrollRoot.addClass(scrollRootClass);\n $(scrollBarHTML).appendTo(scrollRoot);\n scroller.addClass(scrollerClass);\n\n panelScrollbar = baron({\n root: scrollRoot[0],\n scroller: scroller[0],\n bar: '.baron__bar',\n barOnCls: '_scrollbar',\n scrollingCls: '_scrolling',\n });\n\n panelScrollbar.scroll();\n }\n });\n\n function updateDimensionsFromParentScope() {\n ctrl.height = scope.$parent.$parent.size.height;\n ctrl.width = scope.$parent.$parent.size.width;\n }\n\n updateDimensionsFromParentScope();\n\n // Pass PanelModel events down to angular controller event emitter\n subs.add(\n panel.events.subscribe(RefreshEvent, () => {\n updateDimensionsFromParentScope();\n ctrl.events.emit('refresh');\n })\n );\n\n subs.add(\n panel.events.subscribe(RenderEvent, (event) => {\n // this event originated from angular so no need to bubble it back\n if (event.payload?.fromAngular) {\n return;\n }\n\n updateDimensionsFromParentScope();\n\n $timeout(() => {\n resizeScrollableContent();\n ctrl.events.emit('render');\n });\n })\n );\n\n subs.add(\n ctrl.events.subscribe(RenderEvent, (event) => {\n // this event originated from angular so bubble it to react so the PanelChromeAngular can update the panel header alert state\n if (event.payload) {\n event.payload.fromAngular = true;\n panel.events.publish(event);\n }\n })\n );\n\n scope.$on('$destroy', () => {\n elem.off();\n\n // Remove PanelModel.event subs\n subs.unsubscribe();\n // Remove Angular controller event subs\n ctrl.events.emit(PanelEvents.panelTeardown);\n ctrl.events.removeAllListeners();\n\n if (panelScrollbar) {\n panelScrollbar.dispose();\n }\n });\n\n panel.events.publish(PanelDirectiveReadyEvent);\n },\n };\n },\n]);\n","import angular from 'angular';\n\nconst directiveModule = angular.module('grafana.directives');\nconst directiveCache: any = {};\n\ndirectiveModule.directive('panelEditorTab', ['dynamicDirectiveSrv', panelEditorTab]);\n\nfunction panelEditorTab(dynamicDirectiveSrv: any) {\n return dynamicDirectiveSrv.create({\n scope: {\n ctrl: '=',\n editorTab: '=',\n },\n directive: (scope: any) => {\n const pluginId = scope.ctrl.pluginId;\n const tabName = scope.editorTab.title\n .toLowerCase()\n .replace(' ', '-')\n .replace('&', '')\n .replace(' ', '')\n .replace(' ', '-');\n\n if (directiveCache[pluginId]) {\n if (directiveCache[pluginId][tabName]) {\n return directiveCache[pluginId][tabName];\n }\n } else {\n directiveCache[pluginId] = [];\n }\n\n const result = {\n fn: () => scope.editorTab.directiveFn(),\n name: `panel-editor-tab-${pluginId}${tabName}`,\n };\n\n directiveCache[pluginId][tabName] = result;\n\n return result;\n },\n });\n}\n","import { coreModule } from 'app/angular/core_module';\n\nexport class QueryRowCtrl {\n target: any;\n queryCtrl: any;\n panelCtrl: any;\n panel: any;\n hasTextEditMode = false;\n\n $onInit() {\n this.panelCtrl = this.queryCtrl.panelCtrl;\n this.target = this.queryCtrl.target;\n this.panel = this.panelCtrl.panel;\n\n if (this.hasTextEditMode && this.queryCtrl.toggleEditorMode) {\n // expose this function to react parent component\n this.panelCtrl.toggleEditorMode = this.queryCtrl.toggleEditorMode.bind(this.queryCtrl);\n }\n\n if (this.queryCtrl.getCollapsedText) {\n // expose this function to react parent component\n this.panelCtrl.getCollapsedText = this.queryCtrl.getCollapsedText.bind(this.queryCtrl);\n }\n }\n}\n\ncoreModule.directive('queryEditorRow', queryEditorRowDirective);\n\nfunction queryEditorRowDirective() {\n return {\n restrict: 'E',\n controller: QueryRowCtrl,\n bindToController: true,\n controllerAs: 'ctrl',\n templateUrl: 'public/app/angular/panel/partials/query_editor_row.html',\n transclude: true,\n scope: {\n queryCtrl: '=',\n canCollapse: '=',\n hasTextEditMode: '=',\n },\n };\n}\n","import angular from 'angular';\nimport { isArray, isNull, isObject, isUndefined } from 'lodash';\n\nimport { dateTime } from '@grafana/data';\nimport { getTemplateSrv, TemplateSrv } from 'app/features/templating/template_srv';\n\nimport coreModule from '../core_module';\n\ncoreModule.filter('stringSort', () => {\n return (input: any) => {\n return input.sort();\n };\n});\n\ncoreModule.filter('slice', () => {\n return (arr: any[], start: any, end: any) => {\n if (!isUndefined(arr)) {\n return arr.slice(start, end);\n }\n return arr;\n };\n});\n\ncoreModule.filter('stringify', () => {\n return (arr: any[]) => {\n if (isObject(arr) && !isArray(arr)) {\n return angular.toJson(arr);\n } else {\n return isNull(arr) ? null : arr.toString();\n }\n };\n});\n\ncoreModule.filter('moment', () => {\n return (date: string, mode: string) => {\n switch (mode) {\n case 'ago':\n return dateTime(date).fromNow();\n }\n return dateTime(date).fromNow();\n };\n});\n\nfunction interpolateTemplateVars(templateSrv: TemplateSrv = getTemplateSrv()) {\n const filterFunc: any = (text: string, scope: any) => {\n let scopedVars;\n if (scope.ctrl) {\n scopedVars = (scope.ctrl.panel || scope.ctrl.row).scopedVars;\n } else {\n scopedVars = scope.row.scopedVars;\n }\n\n return templateSrv.replaceWithText(text, scopedVars);\n };\n\n filterFunc.$stateful = true;\n return filterFunc;\n}\n\ncoreModule.filter('interpolateTemplateVars', interpolateTemplateVars);\nexport default {};\n","import coreModule from 'app/angular/core_module';\n\nexport class AlertSrv {\n constructor() {}\n\n set() {\n console.warn('old deprecated alert srv being used');\n }\n}\n\n// this is just added to not break old plugins that might be using it\ncoreModule.service('alertSrv', AlertSrv);\n","import angular from 'angular';\n\nimport coreModule from '../core_module';\n\nclass DynamicDirectiveSrv {\n static $inject = ['$compile'];\n\n constructor(private $compile: angular.ICompileService) {}\n\n addDirective(element: any, name: string, scope: any) {\n const child = angular.element(document.createElement(name));\n this.$compile(child)(scope);\n\n element.empty();\n element.append(child);\n }\n\n link(scope: any, elem: JQLite, attrs: any, options: any) {\n const directiveInfo = options.directive(scope);\n if (!directiveInfo || !directiveInfo.fn) {\n elem.empty();\n return;\n }\n\n if (!directiveInfo.fn.registered) {\n coreModule.directive(attrs.$normalize(directiveInfo.name), directiveInfo.fn);\n directiveInfo.fn.registered = true;\n }\n\n this.addDirective(elem, directiveInfo.name, scope);\n }\n\n create(options: any) {\n const directiveDef = {\n restrict: 'E',\n scope: options.scope,\n link: (scope: any, elem: JQLite, attrs: any) => {\n if (options.watchPath) {\n let childScope: any = null;\n scope.$watch(options.watchPath, () => {\n if (childScope) {\n childScope.$destroy();\n }\n childScope = scope.$new();\n this.link(childScope, elem, attrs, options);\n });\n } else {\n this.link(scope, elem, attrs, options);\n }\n },\n };\n\n return directiveDef;\n }\n}\n\ncoreModule.service('dynamicDirectiveSrv', DynamicDirectiveSrv);\n","//\n// This is using ng-react with this PR applied https://github.com/ngReact/ngReact/pull/199\n//\n\n// # ngReact\n// ### Use React Components inside of your Angular applications\n//\n// Composed of\n// - reactComponent (generic directive for delegating off to React Components)\n// - reactDirective (factory for creating specific directives that correspond to reactComponent directives)\n\nimport angular, { auto } from 'angular';\nimport { kebabCase } from 'lodash';\nimport React, { ComponentType } from 'react';\nimport { createRoot, Root } from 'react-dom/client';\n\n// get a react component from name (components can be an angular injectable e.g. value, factory or\n// available on window\nfunction getReactComponent(\n name: string | Function,\n $injector: auto.IInjectorService\n): ComponentType> {\n // if name is a function assume it is component and return it\n if (angular.isFunction(name)) {\n return name as unknown as ComponentType>;\n }\n\n // a React component name must be specified\n if (!name) {\n throw new Error('ReactComponent name attribute must be specified');\n }\n\n // ensure the specified React component is accessible, and fail fast if it's not\n let reactComponent;\n try {\n reactComponent = $injector.get(name);\n } catch (e) {}\n\n if (!reactComponent) {\n try {\n reactComponent = name.split('.').reduce((current, namePart) => {\n // @ts-ignore\n return current[namePart];\n }, window);\n } catch (e) {}\n }\n\n if (!reactComponent) {\n throw Error('Cannot find react component ' + name);\n }\n\n return reactComponent as unknown as ComponentType>;\n}\n\n// wraps a function with scope.$apply, if already applied just return\nfunction applied(fn: any, scope: any) {\n if (fn.wrappedInApply) {\n return fn;\n }\n // this had the equivalent of `eslint-disable-next-line prefer-arrow/prefer-arrow-functions`\n const wrapped: any = function () {\n const args = arguments;\n const phase = scope.$root.$$phase;\n if (phase === '$apply' || phase === '$digest') {\n return fn.apply(null, args);\n } else {\n return scope.$apply(() => {\n return fn.apply(null, args);\n });\n }\n };\n wrapped.wrappedInApply = true;\n return wrapped;\n}\n\n/**\n * wraps functions on obj in scope.$apply\n *\n * keeps backwards compatibility, as if propsConfig is not passed, it will\n * work as before, wrapping all functions and won't wrap only when specified.\n *\n * @version 0.4.1\n * @param obj react component props\n * @param scope current scope\n * @param propsConfig configuration object for all properties\n * @returns {Object} props with the functions wrapped in scope.$apply\n */\nfunction applyFunctions(obj: any, scope: any, propsConfig?: any): object {\n return Object.keys(obj || {}).reduce((prev, key) => {\n const value = obj[key];\n const config = (propsConfig || {})[key] || {};\n /**\n * wrap functions in a function that ensures they are scope.$applied\n * ensures that when function is called from a React component\n * the Angular digest cycle is run\n */\n // @ts-ignore\n prev[key] = angular.isFunction(value) && config.wrapApply !== false ? applied(value, scope) : value;\n\n return prev;\n }, {});\n}\n\n/**\n *\n * @param watchDepth (value of HTML watch-depth attribute)\n * @param scope (angular scope)\n *\n * Uses the watchDepth attribute to determine how to watch props on scope.\n * If watchDepth attribute is NOT reference or collection, watchDepth defaults to deep watching by value\n */\nfunction watchProps(watchDepth: string, scope: any, watchExpressions: any[], listener: any) {\n const supportsWatchCollection = angular.isFunction(scope.$watchCollection);\n const supportsWatchGroup = angular.isFunction(scope.$watchGroup);\n\n const watchGroupExpressions = [];\n\n for (const expr of watchExpressions) {\n const actualExpr = getPropExpression(expr);\n const exprWatchDepth = getPropWatchDepth(watchDepth, expr);\n\n // ignore empty expressions & expressions with functions\n if (!actualExpr || actualExpr.match(/\\(.*\\)/) || exprWatchDepth === 'one-time') {\n continue;\n }\n\n if (exprWatchDepth === 'collection' && supportsWatchCollection) {\n scope.$watchCollection(actualExpr, listener);\n } else if (exprWatchDepth === 'reference' && supportsWatchGroup) {\n watchGroupExpressions.push(actualExpr);\n } else {\n scope.$watch(actualExpr, listener, exprWatchDepth !== 'reference');\n }\n }\n\n if (watchDepth === 'one-time') {\n listener();\n }\n\n if (watchGroupExpressions.length) {\n scope.$watchGroup(watchGroupExpressions, listener);\n }\n}\n\n// render React component, with scope[attrs.props] being passed in as the component props\nfunction renderComponent(component: any, props: object, scope: any, root: Root) {\n scope.$evalAsync(() => {\n root.render(React.createElement(component, props));\n });\n}\n\n// get prop name from prop (string or array)\nfunction getPropName(prop: any) {\n return Array.isArray(prop) ? prop[0] : prop;\n}\n\n// get prop name from prop (string or array)\nfunction getPropConfig(prop: any) {\n return Array.isArray(prop) ? prop[1] : {};\n}\n\n// get prop expression from prop (string or array)\nfunction getPropExpression(prop: any) {\n return Array.isArray(prop) ? prop[0] : prop;\n}\n\n/**\n * Finds the normalized attribute knowing that React props accept any type of capitalization and it also handles\n * kabab case attributes which can be used in case the attribute would also be a standard html attribute and would be\n * evaluated by the browser as such.\n * @param attrs All attributes of the component.\n * @param propName Name of the prop that react component expects.\n */\nfunction findAttribute(attrs: object, propName: string): string {\n const index = Object.keys(attrs).find((attr: any) => {\n return attr.toLowerCase() === propName.toLowerCase() || kebabCase(attr) === kebabCase(propName);\n });\n // @ts-ignore\n return attrs[index];\n}\n\n// get watch depth of prop (string or array)\nfunction getPropWatchDepth(defaultWatch: string, prop: string | any[]) {\n const customWatchDepth = Array.isArray(prop) && angular.isObject(prop[1]) && prop[1].watchDepth;\n return customWatchDepth || defaultWatch;\n}\n\n// # reactComponent\n// Directive that allows React components to be used in Angular templates.\n//\n// Usage:\n// \n//\n// This requires that there exists an injectable or globally available 'Hello' React component.\n// The 'props' attribute is optional and is passed to the component.\n//\n// The following would would create and register the component:\n//\n// var module = angular.module('ace.react.components');\n// module.value('Hello', React.createClass({\n// render: function() {\n// return
    Hello {this.props.name}
    ;\n// }\n// }));\n//\nconst reactComponent = ($injector: any): any => {\n return {\n restrict: 'E',\n replace: true,\n link: function (scope: any, elem: Element[], attrs: any) {\n const reactComponent = getReactComponent(attrs.name, $injector);\n\n const root = createRoot(elem[0]);\n const renderMyComponent = () => {\n const scopeProps = scope.$eval(attrs.props);\n const props = applyFunctions(scopeProps, scope);\n\n renderComponent(reactComponent, props, scope, root);\n };\n\n // If there are props, re-render when they change\n attrs.props ? watchProps(attrs.watchDepth, scope, [attrs.props], renderMyComponent) : renderMyComponent();\n\n // cleanup when scope is destroyed\n scope.$on('$destroy', () => {\n if (!attrs.onScopeDestroy) {\n root.unmount();\n } else {\n scope.$eval(attrs.onScopeDestroy, {\n unmountComponent: root.unmount.bind(this),\n });\n }\n });\n },\n };\n};\n\n// # reactDirective\n// Factory function to create directives for React components.\n//\n// With a component like this:\n//\n// var module = angular.module('ace.react.components');\n// module.value('Hello', React.createClass({\n// render: function() {\n// return
    Hello {this.props.name}
    ;\n// }\n// }));\n//\n// A directive can be created and registered with:\n//\n// module.directive('hello', function(reactDirective) {\n// return reactDirective('Hello', ['name']);\n// });\n//\n// Where the first argument is the injectable or globally accessible name of the React component\n// and the second argument is an array of property names to be watched and passed to the React component\n// as props.\n//\n// This directive can then be used like this:\n//\n// \n//\nconst reactDirective = ($injector: auto.IInjectorService) => {\n return (reactComponentName: string, props: string[], conf: any, injectableProps: any) => {\n const directive = {\n restrict: 'E',\n replace: true,\n link: function (scope: any, elem: Element[], attrs: any) {\n const reactComponent = getReactComponent(reactComponentName, $injector);\n const root = createRoot(elem[0]);\n\n // if props is not defined, fall back to use the React component's propTypes if present\n props = props || Object.keys(reactComponent.propTypes || {});\n\n // for each of the properties, get their scope value and set it to scope.props\n const renderMyComponent = () => {\n let scopeProps: any = {};\n const config: any = {};\n\n props.forEach((prop) => {\n const propName = getPropName(prop);\n scopeProps[propName] = scope.$eval(findAttribute(attrs, propName));\n config[propName] = getPropConfig(prop);\n });\n\n scopeProps = applyFunctions(scopeProps, scope, config);\n scopeProps = angular.extend({}, scopeProps, injectableProps);\n renderComponent(reactComponent, scopeProps, scope, root);\n };\n\n // watch each property name and trigger an update whenever something changes,\n // to update scope.props with new values\n const propExpressions = props.map((prop) => {\n return Array.isArray(prop)\n ? [findAttribute(attrs, prop[0]), getPropConfig(prop)]\n : findAttribute(attrs, prop);\n });\n\n // If we don't have any props, then our watch statement won't fire.\n props.length ? watchProps(attrs.watchDepth, scope, propExpressions, renderMyComponent) : renderMyComponent();\n\n // cleanup when scope is destroyed\n scope.$on('$destroy', () => {\n if (!attrs.onScopeDestroy) {\n root.unmount();\n } else {\n scope.$eval(attrs.onScopeDestroy, {\n unmountComponent: root.unmount.bind(this),\n });\n }\n });\n },\n };\n return angular.extend(directive, conf);\n };\n};\n\nconst ngModule = angular.module('react', []);\nngModule.directive('reactComponent', ['$injector', reactComponent]);\nngModule.factory('reactDirective', ['$injector', reactDirective]);\n","import { each, isString, map } from 'lodash';\n\nimport coreModule from '../core_module';\n\ncoreModule.service('uiSegmentSrv', ['$sce', 'templateSrv', uiSegmentSrv]);\n\nexport function uiSegmentSrv(this: any, $sce: any, templateSrv: any) {\n const self = this;\n\n class MetricSegment {\n value: string;\n html: any;\n type: any;\n expandable?: boolean;\n text?: string;\n cssClass?: string;\n fake?: boolean;\n custom?: boolean;\n selectMode?: any;\n\n constructor(options: any) {\n if (options === '*' || options.value === '*') {\n this.value = '*';\n this.html = $sce.trustAsHtml('');\n this.type = options.type;\n this.expandable = true;\n return;\n }\n\n if (isString(options)) {\n this.value = options;\n this.html = $sce.trustAsHtml(templateSrv.highlightVariablesAsHtml(this.value));\n return;\n }\n\n // temp hack to work around legacy inconsistency in segment model\n this.text = options.value;\n\n this.cssClass = options.cssClass;\n this.custom = options.custom;\n this.type = options.type;\n this.fake = options.fake;\n this.value = options.value;\n this.selectMode = options.selectMode;\n this.expandable = options.expandable;\n this.html = options.html || $sce.trustAsHtml(templateSrv.highlightVariablesAsHtml(this.value));\n }\n }\n\n this.getSegmentForValue = function (value: string, fallbackText: string) {\n if (value) {\n return this.newSegment(value);\n } else {\n return this.newSegment({ value: fallbackText, fake: true });\n }\n };\n\n this.newSelectMeasurement = () => {\n return new MetricSegment({ value: 'select measurement', fake: true });\n };\n\n this.newFake = (text: string, type: string, cssClass: string) => {\n return new MetricSegment({ value: text, fake: true, type: type, cssClass: cssClass });\n };\n\n this.newSegment = (options: any) => {\n return new MetricSegment(options);\n };\n\n this.newKey = (key: string) => {\n return new MetricSegment({ value: key, type: 'key', cssClass: 'query-segment-key' });\n };\n\n this.newKeyValue = (value: string) => {\n return new MetricSegment({ value: value, type: 'value', cssClass: 'query-segment-value' });\n };\n\n this.newCondition = (condition: string) => {\n return new MetricSegment({ value: condition, type: 'condition', cssClass: 'query-keyword' });\n };\n\n this.newOperator = (op: string) => {\n return new MetricSegment({ value: op, type: 'operator', cssClass: 'query-segment-operator' });\n };\n\n this.newOperators = (ops: string[]) => {\n return map(ops, (op) => {\n return new MetricSegment({ value: op, type: 'operator', cssClass: 'query-segment-operator' });\n });\n };\n\n this.transformToSegments = (addTemplateVars: boolean, variableTypeFilter: string) => {\n return (results: any[]) => {\n const segments = map(results, (segment) => {\n return self.newSegment({ value: segment.text, expandable: segment.expandable });\n });\n\n if (addTemplateVars) {\n each(templateSrv.getVariables(), (variable) => {\n if (variableTypeFilter === void 0 || variableTypeFilter === variable.type) {\n segments.unshift(self.newSegment({ type: 'value', value: '$' + variable.name, expandable: true }));\n }\n });\n }\n\n return segments;\n };\n };\n\n this.newSelectMetric = () => {\n return new MetricSegment({ value: 'select metric', fake: true });\n };\n\n this.newPlusButton = () => {\n return new MetricSegment({\n fake: true,\n html: '',\n type: 'plus-button',\n cssClass: 'query-part',\n });\n };\n}\n","import { extend } from 'lodash';\n// @ts-ignore\nimport Drop from 'tether-drop';\n\nimport { GrafanaRootScope } from 'app/angular/GrafanaCtrl';\nimport coreModule from 'app/angular/core_module';\n\ncoreModule.service('popoverSrv', ['$compile', '$rootScope', '$timeout', popoverSrv]);\n\nfunction popoverSrv(this: any, $compile: any, $rootScope: GrafanaRootScope, $timeout: any) {\n let openDrop: any = null;\n\n this.close = () => {\n if (openDrop) {\n openDrop.close();\n }\n };\n\n this.show = (options: any) => {\n if (openDrop) {\n openDrop.close();\n openDrop = null;\n }\n\n const scope = extend($rootScope.$new(true), options.model);\n let drop: any;\n\n const cleanUp = () => {\n setTimeout(() => {\n scope.$destroy();\n\n if (drop.tether) {\n drop.destroy();\n }\n\n if (options.onClose) {\n options.onClose();\n }\n });\n\n openDrop = null;\n };\n\n scope.dismiss = () => {\n drop.close();\n };\n\n const contentElement = document.createElement('div');\n contentElement.innerHTML = options.template;\n\n $compile(contentElement)(scope);\n\n $timeout(() => {\n drop = new Drop({\n target: options.element,\n content: contentElement,\n position: options.position,\n classes: options.classNames || 'drop-popover',\n openOn: options.openOn,\n hoverCloseDelay: 200,\n tetherOptions: {\n constraints: [{ to: 'scrollParent', attachment: 'together' }],\n },\n });\n\n drop.on('close', () => {\n cleanUp();\n });\n\n openDrop = drop;\n openDrop.open();\n }, 100);\n\n // return close function\n return () => {\n if (drop) {\n drop.close();\n }\n };\n };\n}\n","import { ITimeoutService } from 'angular';\nimport { without, each } from 'lodash';\n\nimport coreModule from 'app/angular/core_module';\n\n// This service really just tracks a list of $timeout promises to give us a\n// method for canceling them all when we need to\nexport class Timer {\n timers: Array> = [];\n\n static $inject = ['$timeout'];\n\n constructor(private $timeout: ITimeoutService) {}\n\n register(promise: angular.IPromise) {\n this.timers.push(promise);\n return promise;\n }\n\n cancel(promise: angular.IPromise) {\n this.timers = without(this.timers, promise);\n this.$timeout.cancel(promise);\n }\n\n cancelAll() {\n each(this.timers, (t) => {\n this.$timeout.cancel(t);\n });\n this.timers = [];\n }\n}\n\ncoreModule.service('timer', Timer);\n","import angular from 'angular';\nimport { assign } from 'lodash';\n\nimport { AngularComponent, AngularLoader as AngularLoaderInterface } from '@grafana/runtime';\nimport { GrafanaRootScope } from 'app/angular/GrafanaCtrl';\nimport coreModule from 'app/angular/core_module';\n\nexport class AngularLoader implements AngularLoaderInterface {\n static $inject = ['$compile', '$rootScope'];\n\n constructor(\n private $compile: angular.ICompileService,\n private $rootScope: GrafanaRootScope\n ) {}\n\n load(elem: HTMLElement, scopeProps: any, template: string): AngularComponent {\n const scope = this.$rootScope.$new();\n\n assign(scope, scopeProps);\n\n const compiledElem = this.$compile(template)(scope);\n const rootNode = angular.element(elem);\n rootNode.append(compiledElem);\n\n return {\n destroy: () => {\n scope.$destroy();\n compiledElem.remove();\n },\n digest: () => {\n if (!scope.$$phase) {\n scope.$digest();\n }\n },\n getScope: () => {\n return scope;\n },\n };\n }\n}\n\ncoreModule.service('angularLoader', AngularLoader);\n","import angular from 'angular';\nimport $ from 'jquery';\nimport { extend } from 'lodash';\n\nconst $win = $(window);\n\n$.fn.place_tt = (() => {\n const defaults = {\n offset: 5,\n };\n\n return function (this: any, x: number, y: number, opts: any) {\n opts = $.extend(true, {}, defaults, opts);\n\n return this.each(() => {\n const $tooltip = $(this);\n let width, height;\n\n $tooltip.addClass('grafana-tooltip');\n\n $('#tooltip').remove();\n $tooltip.appendTo(document.body);\n\n if (opts.compile) {\n angular\n .element(document)\n .injector()\n .invoke([\n '$compile',\n '$rootScope',\n ($compile, $rootScope) => {\n const tmpScope = $rootScope.$new(true);\n extend(tmpScope, opts.scopeData);\n\n $compile($tooltip)(tmpScope);\n tmpScope.$digest();\n tmpScope.$destroy();\n },\n ]);\n }\n\n width = $tooltip.outerWidth(true)!;\n height = $tooltip.outerHeight(true)!;\n\n const left = x + opts.offset + width > $win.width()! ? x - opts.offset - width : x + opts.offset;\n const top = y + opts.offset + height > $win.height()! ? y - opts.offset - height : y + opts.offset;\n\n $tooltip.css('left', left > 0 ? left : 0);\n $tooltip.css('top', top > 0 ? top : 0);\n });\n };\n})();\n","import $ from 'jquery';\nimport { each, reduce } from 'lodash';\n\nimport coreModule from './core_module';\n\nexport function dropdownTypeahead($compile: any) {\n const inputTemplate =\n '';\n\n const buttonTemplate =\n '';\n\n return {\n scope: {\n menuItems: '=dropdownTypeahead',\n dropdownTypeaheadOnSelect: '&dropdownTypeaheadOnSelect',\n model: '=ngModel',\n },\n link: ($scope: any, elem: any, attrs: any) => {\n const $input = $(inputTemplate);\n const $button = $(buttonTemplate);\n $input.appendTo(elem);\n $button.appendTo(elem);\n\n if (attrs.linkText) {\n $button.html(attrs.linkText);\n }\n\n if (attrs.ngModel) {\n $scope.$watch('model', (newValue: any) => {\n each($scope.menuItems, (item) => {\n each(item.submenu, (subItem) => {\n if (subItem.value === newValue) {\n $button.html(subItem.text);\n }\n });\n });\n });\n }\n\n const typeaheadValues = reduce(\n $scope.menuItems,\n (memo: any[], value, index) => {\n if (!value.submenu) {\n value.click = 'menuItemSelected(' + index + ')';\n memo.push(value.text);\n } else {\n each(value.submenu, (item, subIndex) => {\n item.click = 'menuItemSelected(' + index + ',' + subIndex + ')';\n memo.push(value.text + ' ' + item.text);\n });\n }\n return memo;\n },\n []\n );\n\n const closeDropdownMenu = () => {\n $input.hide();\n $input.val('');\n $button.show();\n $button.focus();\n elem.removeClass('open');\n };\n\n $scope.menuItemSelected = (index: number, subIndex: number) => {\n const menuItem = $scope.menuItems[index];\n const payload: any = { $item: menuItem };\n if (menuItem.submenu && subIndex !== void 0) {\n payload.$subItem = menuItem.submenu[subIndex];\n }\n $scope.dropdownTypeaheadOnSelect(payload);\n closeDropdownMenu();\n };\n\n $input.attr('data-provide', 'typeahead');\n $input.typeahead({\n source: typeaheadValues,\n minLength: 1,\n items: 10,\n updater: (value: string) => {\n const result: any = {};\n each($scope.menuItems, (menuItem) => {\n each(menuItem.submenu, (submenuItem) => {\n if (value === menuItem.text + ' ' + submenuItem.text) {\n result.$subItem = submenuItem;\n result.$item = menuItem;\n }\n });\n });\n\n if (result.$item) {\n $scope.$apply(() => {\n $scope.dropdownTypeaheadOnSelect(result);\n });\n }\n\n $input.trigger('blur');\n return '';\n },\n });\n\n $button.click(() => {\n $button.hide();\n $input.show();\n $input.focus();\n });\n\n $input.keyup(() => {\n elem.toggleClass('open', $input.val() === '');\n });\n\n elem.mousedown((evt: Event) => {\n evt.preventDefault();\n });\n\n $input.blur(() => {\n $input.hide();\n $input.val('');\n $button.show();\n $button.focus();\n // clicking the function dropdown menu won't\n // work if you remove class at once\n setTimeout(() => {\n elem.removeClass('open');\n }, 200);\n });\n\n $compile(elem.contents())($scope);\n },\n };\n}\n\nexport function dropdownTypeahead2($compile: any) {\n const inputTemplate =\n '';\n\n const buttonTemplate =\n '';\n\n return {\n scope: {\n menuItems: '=dropdownTypeahead2',\n dropdownTypeaheadOnSelect: '&dropdownTypeaheadOnSelect',\n model: '=ngModel',\n buttonTemplateClass: '@',\n },\n link: ($scope: any, elem: any, attrs: any) => {\n const $input = $(inputTemplate);\n\n if (!$scope.buttonTemplateClass) {\n $scope.buttonTemplateClass = 'gf-form-input';\n }\n\n const $button = $(buttonTemplate);\n const timeoutId = {\n blur: null as any,\n };\n $input.appendTo(elem);\n $button.appendTo(elem);\n\n if (attrs.linkText) {\n $button.html(attrs.linkText);\n }\n\n if (attrs.ngModel) {\n $scope.$watch('model', (newValue: any) => {\n each($scope.menuItems, (item) => {\n each(item.submenu, (subItem) => {\n if (subItem.value === newValue) {\n $button.html(subItem.text);\n }\n });\n });\n });\n }\n\n const typeaheadValues = reduce(\n $scope.menuItems,\n (memo: any[], value, index) => {\n if (!value.submenu) {\n value.click = 'menuItemSelected(' + index + ')';\n memo.push(value.text);\n } else {\n each(value.submenu, (item, subIndex) => {\n item.click = 'menuItemSelected(' + index + ',' + subIndex + ')';\n memo.push(value.text + ' ' + item.text);\n });\n }\n return memo;\n },\n []\n );\n\n const closeDropdownMenu = () => {\n $input.hide();\n $input.val('');\n $button.show();\n $button.focus();\n elem.removeClass('open');\n };\n\n $scope.menuItemSelected = (index: number, subIndex: number) => {\n const menuItem = $scope.menuItems[index];\n const payload: any = { $item: menuItem };\n if (menuItem.submenu && subIndex !== void 0) {\n payload.$subItem = menuItem.submenu[subIndex];\n }\n $scope.dropdownTypeaheadOnSelect(payload);\n closeDropdownMenu();\n };\n\n $input.attr('data-provide', 'typeahead');\n $input.typeahead({\n source: typeaheadValues,\n minLength: 1,\n items: 10,\n updater: (value: string) => {\n const result: any = {};\n each($scope.menuItems, (menuItem) => {\n each(menuItem.submenu, (submenuItem) => {\n if (value === menuItem.text + ' ' + submenuItem.text) {\n result.$subItem = submenuItem;\n result.$item = menuItem;\n }\n });\n });\n\n if (result.$item) {\n $scope.$apply(() => {\n $scope.dropdownTypeaheadOnSelect(result);\n });\n }\n\n $input.trigger('blur');\n return '';\n },\n });\n\n $button.click(() => {\n $button.hide();\n $input.show();\n $input.focus();\n });\n\n $input.keyup(() => {\n elem.toggleClass('open', $input.val() === '');\n });\n\n elem.mousedown((evt: Event) => {\n evt.preventDefault();\n timeoutId.blur = null;\n });\n\n $input.blur(() => {\n timeoutId.blur = setTimeout(() => {\n closeDropdownMenu();\n }, 1);\n });\n\n $compile(elem.contents())($scope);\n },\n };\n}\n\ncoreModule.directive('dropdownTypeahead', ['$compile', dropdownTypeahead]);\ncoreModule.directive('dropdownTypeahead2', ['$compile', dropdownTypeahead2]);\n","import coreModule from './core_module';\n\nexport function autofillEventFix($compile: any) {\n return {\n link: ($scope: any, elem: any) => {\n const input = elem[0];\n const dispatchChangeEvent = () => {\n const event = new Event('change');\n return input.dispatchEvent(event);\n };\n const onAnimationStart = ({ animationName }: AnimationEvent) => {\n switch (animationName) {\n case 'onAutoFillStart':\n return dispatchChangeEvent();\n case 'onAutoFillCancel':\n return dispatchChangeEvent();\n }\n return null;\n };\n\n // const onChange = (evt: Event) => console.log(evt);\n\n input.addEventListener('animationstart', onAnimationStart);\n // input.addEventListener('change', onChange);\n\n $scope.$on('$destroy', () => {\n input.removeEventListener('animationstart', onAnimationStart);\n // input.removeEventListener('change', onChange);\n });\n },\n };\n}\n\ncoreModule.directive('autofillEventFix', ['$compile', autofillEventFix]);\n","import $ from 'jquery';\nimport { debounce, find, indexOf, map, escape, unescape } from 'lodash';\n\nimport { TemplateSrv } from 'app/features/templating/template_srv';\n\nimport coreModule from './core_module';\n\nexport function metricSegment($compile: any, $sce: any, templateSrv: TemplateSrv) {\n const inputTemplate =\n '';\n\n const linkTemplate =\n '';\n\n const selectTemplate =\n '';\n\n return {\n scope: {\n segment: '=',\n getOptions: '&',\n onChange: '&',\n debounce: '@',\n },\n link: ($scope: any, elem: any) => {\n const $input = $(inputTemplate);\n const segment = $scope.segment;\n const $button = $(segment.selectMode ? selectTemplate : linkTemplate);\n let options = null;\n let cancelBlur: any = null;\n let linkMode = true;\n const debounceLookup = $scope.debounce;\n\n $input.appendTo(elem);\n $button.appendTo(elem);\n\n $scope.updateVariableValue = (value: string) => {\n if (value === '' || segment.value === value) {\n return;\n }\n\n $scope.$apply(() => {\n const selected: any = find($scope.altSegments, { value: value });\n if (selected) {\n segment.value = selected.value;\n segment.html = selected.html || $sce.trustAsHtml(templateSrv.highlightVariablesAsHtml(selected.value));\n segment.fake = false;\n segment.expandable = selected.expandable;\n\n if (selected.type) {\n segment.type = selected.type;\n }\n } else if (segment.custom !== 'false') {\n segment.value = value;\n segment.html = $sce.trustAsHtml(templateSrv.highlightVariablesAsHtml(value));\n segment.expandable = true;\n segment.fake = false;\n }\n\n $scope.onChange();\n });\n };\n\n $scope.switchToLink = (fromClick: boolean) => {\n if (linkMode && !fromClick) {\n return;\n }\n\n clearTimeout(cancelBlur);\n cancelBlur = null;\n linkMode = true;\n $input.hide();\n $button.show();\n $scope.updateVariableValue($input.val());\n };\n\n $scope.inputBlur = () => {\n // happens long before the click event on the typeahead options\n // need to have long delay because the blur\n cancelBlur = setTimeout($scope.switchToLink, 200);\n };\n\n $scope.source = (query: string, callback: any) => {\n $scope.$apply(() => {\n $scope.getOptions({ $query: query }).then((altSegments: any) => {\n $scope.altSegments = altSegments;\n options = map($scope.altSegments, (alt) => {\n return escape(alt.value);\n });\n\n // add custom values\n if (segment.custom !== 'false') {\n if (!segment.fake && indexOf(options, segment.value) === -1) {\n options.unshift(escape(segment.value));\n }\n }\n\n callback(options);\n });\n });\n };\n\n $scope.updater = (value: string) => {\n value = unescape(value);\n if (value === segment.value) {\n clearTimeout(cancelBlur);\n $input.focus();\n return value;\n }\n\n $input.val(value);\n $scope.switchToLink(true);\n\n return value;\n };\n\n $scope.matcher = function (item: string) {\n if (linkMode) {\n return false;\n }\n let str = this.query;\n if (str[0] === '/') {\n str = str.substring(1);\n }\n if (str[str.length - 1] === '/') {\n str = str.substring(0, str.length - 1);\n }\n try {\n return item.toLowerCase().match(str.toLowerCase());\n } catch (e) {\n return false;\n }\n };\n\n $input.attr('data-provide', 'typeahead');\n $input.typeahead({\n source: $scope.source,\n minLength: 0,\n items: 10000,\n updater: $scope.updater,\n matcher: $scope.matcher,\n });\n\n const typeahead = $input.data('typeahead');\n typeahead.lookup = function () {\n this.query = this.$element.val() || '';\n const items = this.source(this.query, $.proxy(this.process, this));\n return items ? this.process(items) : items;\n };\n\n if (debounceLookup) {\n typeahead.lookup = debounce(typeahead.lookup, 500, { leading: true });\n }\n\n $button.keydown((evt) => {\n // trigger typeahead on down arrow or enter key\n if (evt.keyCode === 40 || evt.keyCode === 13) {\n $button.click();\n }\n });\n\n $button.click(() => {\n options = null;\n $input.css('width', Math.max($button.width()!, 80) + 16 + 'px');\n\n $button.hide();\n $input.show();\n $input.focus();\n\n linkMode = false;\n\n const typeahead = $input.data('typeahead');\n if (typeahead) {\n $input.val('');\n typeahead.lookup();\n }\n });\n\n $input.blur($scope.inputBlur);\n\n $compile(elem.contents())($scope);\n },\n };\n}\n\nexport function metricSegmentModel(uiSegmentSrv: any) {\n return {\n template:\n '',\n restrict: 'E',\n scope: {\n property: '=',\n options: '=',\n getOptions: '&',\n onChange: '&',\n },\n link: {\n pre: function postLink($scope: any, elem: any, attrs: any) {\n let cachedOptions: any;\n\n $scope.valueToSegment = (value: any) => {\n const option: any = find($scope.options, { value: value });\n const segment = {\n cssClass: attrs.cssClass,\n custom: attrs.custom,\n value: option ? option.text : value,\n selectMode: attrs.selectMode,\n };\n\n return uiSegmentSrv.newSegment(segment);\n };\n\n $scope.getOptionsInternal = () => {\n if ($scope.options) {\n cachedOptions = $scope.options;\n return Promise.resolve(\n map($scope.options, (option) => {\n return { value: option.text };\n })\n );\n } else {\n return $scope.getOptions().then((options: any) => {\n cachedOptions = options;\n return map(options, (option) => {\n if (option.html) {\n return option;\n }\n return { value: option.text };\n });\n });\n }\n };\n\n $scope.onSegmentChange = () => {\n if (cachedOptions) {\n const option: any = find(cachedOptions, { text: $scope.segment.value });\n if (option && option.value !== $scope.property) {\n $scope.property = option.value;\n } else if (attrs.custom !== 'false') {\n $scope.property = $scope.segment.value;\n }\n } else {\n $scope.property = $scope.segment.value;\n }\n\n // needs to call this after digest so\n // property is synced with outerscope\n $scope.$$postDigest(() => {\n $scope.$apply(() => {\n $scope.onChange();\n });\n });\n };\n\n $scope.segment = $scope.valueToSegment($scope.property);\n },\n },\n };\n}\n\ncoreModule.directive('metricSegment', ['$compile', '$sce', 'templateSrv', metricSegment]);\ncoreModule.directive('metricSegmentModel', ['uiSegmentSrv', metricSegmentModel]);\n","import angular from 'angular';\n\nimport coreModule from './core_module';\n\ncoreModule.directive('tip', ['$compile', tip]);\n\nfunction tip($compile: any) {\n return {\n restrict: 'E',\n link: (scope: any, elem: any, attrs: any) => {\n let _t =\n '&]/g, (m: string) => '&#' + m.charCodeAt(0) + ';') +\n '\\'\">';\n elem.replaceWith($compile(angular.element(_t))(scope));\n },\n };\n}\n\ncoreModule.directive('compile', ['$compile', compile]);\n\nfunction compile($compile: any) {\n return {\n restrict: 'A',\n link: (scope: any, element: any, attrs: any) => {\n scope.$watch(\n (scope: any) => {\n return scope.$eval(attrs.compile);\n },\n (value: any) => {\n element.html(value);\n $compile(element.contents())(scope);\n }\n );\n },\n };\n}\n\ncoreModule.directive('watchChange', watchChange);\n\nfunction watchChange() {\n return {\n scope: { onchange: '&watchChange' },\n link: (scope: any, element: any) => {\n element.on('input', () => {\n scope.$apply(() => {\n scope.onchange({ inputValue: element.val() });\n });\n });\n },\n };\n}\n\ncoreModule.directive('editorOptBool', ['$compile', editorOptBool]);\n\nfunction editorOptBool($compile: any) {\n return {\n restrict: 'E',\n link: (scope: any, elem: any, attrs: any) => {\n const ngchange = attrs.change ? ' ng-change=\"' + attrs.change + '\"' : '';\n const tip = attrs.tip ? ' ' + attrs.tip + '' : '';\n const showIf = attrs.showIf ? ' ng-show=\"' + attrs.showIf + '\" ' : '';\n\n const template =\n '
    ' +\n ' ' +\n '' +\n ' ';\n elem.replaceWith($compile(angular.element(template))(scope));\n },\n };\n}\n\ncoreModule.directive('editorCheckbox', ['$compile, $interpolate', editorCheckbox]);\n\nfunction editorCheckbox($compile: any, $interpolate: any) {\n return {\n restrict: 'E',\n link: (scope: any, elem: any, attrs: any) => {\n const text = $interpolate(attrs.text)(scope);\n const model = $interpolate(attrs.model)(scope);\n const ngchange = attrs.change ? ' ng-change=\"' + attrs.change + '\"' : '';\n const tip = attrs.tip ? ' ' + attrs.tip + '' : '';\n const label = '';\n\n let template =\n '' +\n ' ';\n\n template = template + label;\n elem.addClass('gf-form-checkbox');\n elem.html($compile(angular.element(template))(scope));\n },\n };\n}\n\ncoreModule.directive('gfDropdown', ['$parse', '$compile', '$timeout', gfDropdown]);\n\nfunction gfDropdown($parse: any, $compile: any, $timeout: any) {\n function buildTemplate(items: any, placement?: any) {\n const upclass = placement === 'top' ? 'dropup' : '';\n const ul = ['
      ', '
    '];\n\n for (let index = 0; index < items.length; index++) {\n const item = items[index];\n\n if (item.divider) {\n ul.splice(index + 1, 0, '
  • ');\n continue;\n }\n\n let li =\n '' +\n '' +\n (item.text || '') +\n '';\n\n if (item.submenu && item.submenu.length) {\n li += buildTemplate(item.submenu).join('\\n');\n }\n\n li += '';\n ul.splice(index + 1, 0, li);\n }\n\n return ul;\n }\n\n return {\n restrict: 'EA',\n scope: true,\n link: function postLink(scope: any, iElement: any, iAttrs: any) {\n const getter = $parse(iAttrs.gfDropdown),\n items = getter(scope);\n $timeout(() => {\n const placement = iElement.data('placement');\n const dropdown = angular.element(buildTemplate(items, placement).join(''));\n dropdown.insertAfter(iElement);\n $compile(iElement.next('ul.dropdown-menu'))(scope);\n });\n\n iElement.addClass('dropdown-toggle').attr('data-toggle', 'dropdown');\n },\n };\n}\n","import angular from 'angular';\nimport $ from 'jquery';\n\nimport coreModule from './core_module';\n\ncoreModule.directive('bsTooltip', [\n '$parse',\n '$compile',\n function ($parse: any, $compile: any) {\n return {\n restrict: 'A',\n scope: true,\n link: function postLink(scope: any, element: any, attrs: any) {\n let getter = $parse(attrs.bsTooltip),\n value = getter(scope);\n scope.$watch(attrs.bsTooltip, function (newValue: any, oldValue: any) {\n if (newValue !== oldValue) {\n value = newValue;\n }\n });\n // Grafana change, always hide other tooltips\n if (true) {\n element.on('show', function (ev: any) {\n $('.tooltip.in').each(function () {\n const $this = $(this),\n tooltip = $this.data('tooltip');\n if (tooltip && !tooltip.$element.is(element)) {\n $this.tooltip('hide');\n }\n });\n });\n }\n element.tooltip({\n title: function () {\n return angular.isFunction(value) ? value.apply(null, arguments) : value;\n },\n html: true,\n container: 'body', // Grafana change\n });\n const tooltip = element.data('tooltip');\n tooltip.show = function () {\n const r = $.fn.tooltip.Constructor.prototype.show.apply(this, arguments);\n this.tip().data('tooltip', this);\n return r;\n };\n scope._tooltip = function (event: any) {\n element.tooltip(event);\n };\n scope.hide = function () {\n element.tooltip('hide');\n };\n scope.show = function () {\n element.tooltip('show');\n };\n scope.dismiss = scope.hide;\n },\n };\n },\n]);\n","import angular from 'angular';\nimport $ from 'jquery';\nimport { isFunction } from 'lodash';\n\nimport coreModule from './core_module';\n\ncoreModule.directive('bsTypeahead', [\n '$parse',\n function ($parse: any) {\n return {\n restrict: 'A',\n require: '?ngModel',\n link: function postLink(scope: any, element: any, attrs: any, controller: any) {\n let getter = $parse(attrs.bsTypeahead),\n value = getter(scope);\n scope.$watch(attrs.bsTypeahead, function (newValue: any, oldValue: any) {\n if (newValue !== oldValue) {\n value = newValue;\n }\n });\n element.attr('data-provide', 'typeahead');\n element.typeahead({\n source: function () {\n return angular.isFunction(value) ? value.apply(null, arguments) : value;\n },\n minLength: attrs.minLength || 1,\n items: attrs.item,\n updater: function (value: any) {\n if (controller) {\n scope.$apply(function () {\n controller.$setViewValue(value);\n });\n }\n scope.$emit('typeahead-updated', value);\n return value;\n },\n });\n const typeahead = element.data('typeahead');\n typeahead.lookup = function () {\n let items;\n this.query = this.$element.val() || '';\n if (this.query.length < this.options.minLength) {\n return this.shown ? this.hide() : this;\n }\n items = isFunction(this.source) ? this.source(this.query, $.proxy(this.process, this)) : this.source;\n return items ? this.process(items) : this;\n };\n if (!!attrs.matchAll) {\n typeahead.matcher = function () {\n return true;\n };\n }\n if (attrs.minLength === '0') {\n setTimeout(function () {\n element.on('focus', function () {\n element.val().length === 0 && setTimeout(element.typeahead.bind(element, 'lookup'), 200);\n });\n });\n }\n },\n };\n },\n]);\n","import { rangeUtil } from '@grafana/data';\n\nimport coreModule from './core_module';\n\nfunction ngModelOnBlur() {\n return {\n restrict: 'A',\n priority: 1,\n require: 'ngModel',\n link: (scope: any, elm: any, attr: any, ngModelCtrl: any) => {\n if (attr.type === 'radio' || attr.type === 'checkbox') {\n return;\n }\n\n elm.off('input keydown change');\n elm.bind('blur', () => {\n scope.$apply(() => {\n ngModelCtrl.$setViewValue(elm.val());\n });\n });\n },\n };\n}\n\nfunction emptyToNull() {\n return {\n restrict: 'A',\n require: 'ngModel',\n link: (scope: any, elm: any, attrs: any, ctrl: any) => {\n ctrl.$parsers.push((viewValue: any) => {\n if (viewValue === '') {\n return null;\n }\n return viewValue;\n });\n },\n };\n}\n\nfunction validTimeSpan() {\n return {\n require: 'ngModel',\n link: (scope: any, elm: any, attrs: any, ctrl: any) => {\n ctrl.$validators.integer = (modelValue: any, viewValue: any) => {\n if (ctrl.$isEmpty(modelValue)) {\n return true;\n }\n if (viewValue.indexOf('$') === 0 || viewValue.indexOf('+$') === 0) {\n return true; // allow template variable\n }\n const info = rangeUtil.describeTextRange(viewValue);\n return info.invalid !== true;\n };\n },\n };\n}\n\ncoreModule.directive('ngModelOnblur', ngModelOnBlur);\ncoreModule.directive('emptyToNull', emptyToNull);\ncoreModule.directive('validTimeSpan', validTimeSpan);\n","import angular from 'angular';\nimport $ from 'jquery';\n\nimport { getTagColorsFromName } from '@grafana/ui';\n\nimport coreModule from './core_module';\nimport 'vendor/tagsinput/bootstrap-tagsinput.js';\n\nfunction setColor(name: string, element: JQuery) {\n const { color, borderColor } = getTagColorsFromName(name);\n element.css('background-color', color);\n element.css('border-color', borderColor);\n}\n\nfunction tagColorFromName() {\n return {\n scope: { tagColorFromName: '=' },\n link: (scope: any, element: any) => {\n setColor(scope.tagColorFromName, element);\n },\n };\n}\n\nfunction bootstrapTagsinput() {\n function getItemProperty(scope: any, property: any) {\n if (!property) {\n return undefined;\n }\n\n if (angular.isFunction(scope.$parent[property])) {\n return scope.$parent[property];\n }\n\n return (item: any) => {\n return item[property];\n };\n }\n\n return {\n restrict: 'EA',\n scope: {\n model: '=ngModel',\n onTagsUpdated: '&',\n },\n template: '',\n replace: false,\n link: function (scope: any, element: any, attrs: any) {\n if (!angular.isArray(scope.model)) {\n scope.model = [];\n }\n\n const select = $('select', element);\n\n if (attrs.placeholder) {\n select.attr('placeholder', attrs.placeholder);\n }\n\n select.tagsinput({\n typeahead: {\n source: angular.isFunction(scope.$parent[attrs.typeaheadSource])\n ? scope.$parent[attrs.typeaheadSource]\n : null,\n },\n widthClass: attrs.widthClass,\n itemValue: getItemProperty(scope, attrs.itemvalue),\n itemText: getItemProperty(scope, attrs.itemtext),\n tagClass: angular.isFunction(scope.$parent[attrs.tagclass])\n ? scope.$parent[attrs.tagclass]\n : () => {\n return attrs.tagclass;\n },\n });\n\n select.on('itemAdded', (event: any) => {\n if (scope.model.indexOf(event.item) === -1) {\n scope.model.push(event.item);\n if (scope.onTagsUpdated) {\n scope.onTagsUpdated();\n }\n }\n const tagElement = select\n .next()\n .children('span')\n .filter(() => {\n return $(this).text() === event.item;\n });\n setColor(event.item, tagElement);\n });\n\n select.on('itemRemoved', (event: any) => {\n const idx = scope.model.indexOf(event.item);\n if (idx !== -1) {\n scope.model.splice(idx, 1);\n if (scope.onTagsUpdated) {\n scope.onTagsUpdated();\n }\n }\n });\n\n scope.$watch(\n 'model',\n () => {\n if (!angular.isArray(scope.model)) {\n scope.model = [];\n }\n\n select.tagsinput('removeAll');\n\n for (let i = 0; i < scope.model.length; i++) {\n select.tagsinput('add', scope.model[i]);\n }\n },\n true\n );\n },\n };\n}\n\ncoreModule.directive('tagColorFromName', tagColorFromName);\ncoreModule.directive('bootstrapTagsinput', bootstrapTagsinput);\n","import $ from 'jquery';\n\nimport coreModule from './core_module';\n\nfunction getBlockNodes(nodes: any[]) {\n let node = nodes[0];\n const endNode = nodes[nodes.length - 1];\n let blockNodes: any[] | undefined;\n node = node.nextSibling;\n\n for (let i = 1; node !== endNode && node; i++) {\n if (blockNodes || nodes[i] !== node) {\n if (!blockNodes) {\n blockNodes = $([].slice.call(nodes, 0, i)) as any;\n }\n\n blockNodes!.push(node);\n }\n node = node.nextSibling;\n }\n\n return blockNodes || nodes;\n}\n\ncoreModule.directive('rebuildOnChange', ['$animate', rebuildOnChange]);\n\nfunction rebuildOnChange($animate: any) {\n return {\n multiElement: true,\n terminal: true,\n transclude: true,\n priority: 600,\n restrict: 'E',\n link: (scope: any, elem: any, attrs: any, ctrl: any, transclude: any) => {\n let block: any, childScope: any, previousElements: any;\n\n function cleanUp() {\n if (previousElements) {\n previousElements.remove();\n previousElements = null;\n }\n if (childScope) {\n childScope.$destroy();\n childScope = null;\n }\n if (block) {\n previousElements = getBlockNodes(block.clone);\n $animate.leave(previousElements).then(() => {\n previousElements = null;\n });\n block = null;\n }\n }\n\n scope.$watch(attrs.property, function rebuildOnChangeAction(value: any, oldValue: any) {\n if (childScope && value !== oldValue) {\n cleanUp();\n }\n\n if (!childScope && (value || attrs.showNull)) {\n transclude((clone: any, newScope: any) => {\n childScope = newScope;\n clone[clone.length++] = document.createComment(' end rebuild on change ');\n block = { clone: clone };\n $animate.enter(clone, elem.parent(), elem);\n });\n } else {\n cleanUp();\n }\n });\n },\n };\n}\n","import coreModule from './core_module';\n\ncoreModule.directive('giveFocus', () => {\n return (scope: any, element: any, attrs: any) => {\n element.click((e: any) => {\n e.stopPropagation();\n });\n\n scope.$watch(\n attrs.giveFocus,\n (newValue: any) => {\n if (!newValue) {\n return;\n }\n setTimeout(() => {\n element.focus();\n const domEl: any = element[0];\n if (domEl.setSelectionRange) {\n const pos = element.val().length * 2;\n domEl.setSelectionRange(pos, pos);\n }\n }, 200);\n },\n true\n );\n };\n});\n\nexport default {};\n","import angular from 'angular';\n\nimport { GrafanaRootScope } from 'app/angular/GrafanaCtrl';\n\nimport coreModule from './core_module';\n\nexport class DeltaCtrl {\n observer: any;\n\n constructor() {\n const waitForCompile = () => {};\n\n this.observer = new MutationObserver(waitForCompile);\n\n const observerConfig = {\n attributes: true,\n attributeFilter: ['class'],\n characterData: false,\n childList: true,\n subtree: false,\n };\n\n this.observer.observe(angular.element('.delta-html')[0], observerConfig);\n }\n\n $onDestroy() {\n this.observer.disconnect();\n }\n}\n\nexport function delta() {\n return {\n controller: DeltaCtrl,\n replace: false,\n restrict: 'A',\n };\n}\n\ncoreModule.directive('diffDelta', delta);\n\n// Link to JSON line number\nexport class LinkJSONCtrl {\n static $inject = ['$scope', '$rootScope', '$anchorScroll'];\n\n constructor(\n private $scope: any,\n private $rootScope: GrafanaRootScope,\n private $anchorScroll: any\n ) {}\n\n goToLine(line: number) {\n let unbind: () => void;\n\n const scroll = () => {\n this.$anchorScroll(`l${line}`);\n unbind();\n };\n\n this.$scope.switchView().then(() => {\n unbind = this.$rootScope.$on('json-diff-ready', scroll.bind(this));\n });\n }\n}\n\nexport function linkJson() {\n return {\n controller: LinkJSONCtrl,\n controllerAs: 'ctrl',\n replace: true,\n restrict: 'E',\n scope: {\n line: '@lineDisplay',\n link: '@lineLink',\n switchView: '&',\n },\n template: `Line {{ line }}`,\n };\n}\n\ncoreModule.directive('diffLinkJson', linkJson);\n","import { isArray } from 'lodash';\n\nimport coreModule from './core_module';\n\nexport function arrayJoin() {\n 'use strict';\n\n return {\n restrict: 'A',\n require: 'ngModel',\n link: (scope: any, element: any, attr: any, ngModel: any) => {\n function split_array(text: string) {\n return (text || '').split(',');\n }\n\n function join_array(text: string) {\n if (isArray(text)) {\n return ((text || '') as any).join(',');\n } else {\n return text;\n }\n }\n\n ngModel.$parsers.push(split_array);\n ngModel.$formatters.push(join_array);\n },\n };\n}\n\ncoreModule.directive('arrayJoin', arrayJoin);\n","import { config } from '@grafana/runtime';\nimport coreModule from 'app/angular/core_module';\nimport { provideTheme } from 'app/core/utils/ConfigProvider';\n\nexport function react2AngularDirective(name: string, component: any, options: any) {\n coreModule.directive(name, [\n 'reactDirective',\n (reactDirective) => {\n return reactDirective(provideTheme(component, config.theme2), options);\n },\n ]);\n}\n","import { css } from '@emotion/css';\nimport React, { useState } from 'react';\n\nimport { PanelMenuItem, GrafanaTheme2 } from '@grafana/data';\nimport { selectors } from '@grafana/e2e-selectors';\nimport { Icon, toIconName, useStyles2 } from '@grafana/ui';\n\ninterface Props {\n children?: React.ReactNode;\n}\n\nexport const PanelHeaderMenuItem = (props: Props & PanelMenuItem) => {\n const [ref, setRef] = useState(null);\n const isSubMenu = props.type === 'submenu';\n const styles = useStyles2(getStyles);\n const icon = props.iconClassName ? toIconName(props.iconClassName) : undefined;\n\n switch (props.type) {\n case 'divider':\n return
  • ;\n case 'group':\n return (\n
  • \n {props.text}\n
  • \n );\n default:\n return (\n \n \n {icon && }\n \n {props.text}\n {isSubMenu && }\n \n\n {props.shortcut && (\n \n {props.shortcut}\n \n )}\n \n {props.children}\n \n );\n }\n};\n\nfunction getDropdownLocationCssClass(element: HTMLElement | null) {\n if (!element) {\n return 'invisible';\n }\n\n const wrapperPos = element.parentElement!.getBoundingClientRect();\n const pos = element.getBoundingClientRect();\n\n if (pos.width === 0) {\n return 'invisible';\n }\n\n if (wrapperPos.right + pos.width + 10 > window.innerWidth) {\n return 'pull-left';\n } else {\n return 'pull-right';\n }\n}\n\nfunction getStyles(theme: GrafanaTheme2) {\n return {\n menuIconClassName: css({\n marginRight: theme.spacing(1),\n 'a::after': {\n display: 'none',\n },\n }),\n shortcutIconClassName: css({\n position: 'absolute',\n top: '7px',\n right: theme.spacing(0.5),\n color: theme.colors.text.secondary,\n }),\n groupLabel: css({\n color: theme.colors.text.secondary,\n fontSize: theme.typography.size.sm,\n padding: theme.spacing(0.5, 1),\n }),\n };\n}\n","import { css, cx } from '@emotion/css';\nimport React from 'react';\n\nimport { NavModelItem, GrafanaTheme2 } from '@grafana/data';\nimport { Tab, TabsBar, Icon, useStyles2, toIconName } from '@grafana/ui';\nimport { PanelHeaderMenuItem } from 'app/core/components/PageHeader/PanelHeaderMenuItem';\n\nimport { PageInfoItem } from '../Page/types';\nimport { PageInfo } from '../PageInfo/PageInfo';\nimport { ProBadge } from '../Upgrade/ProBadge';\n\nexport interface Props {\n navItem: NavModelItem;\n renderTitle?: (title: string) => React.ReactNode;\n actions?: React.ReactNode;\n info?: PageInfoItem[];\n subTitle?: React.ReactNode;\n}\n\nconst SelectNav = ({ children, customCss }: { children: NavModelItem[]; customCss: string }) => {\n if (!children || children.length === 0) {\n return null;\n }\n\n const defaultSelectedItem = children.find((navItem) => {\n return navItem.active === true;\n });\n\n return (\n
    \n
    \n \n {defaultSelectedItem?.text}\n \n
      \n {children.map((navItem: NavModelItem) => {\n if (navItem.hideFromTabs) {\n // TODO: Rename hideFromTabs => hideFromNav\n return null;\n }\n return (\n \n );\n })}\n
    \n
    \n
    \n );\n};\n\nconst Navigation = ({ children }: { children: NavModelItem[] }) => {\n if (!children || children.length === 0) {\n return null;\n }\n\n return (\n \n );\n};\n\nexport const PageHeader = ({ navItem: model, renderTitle, actions, info, subTitle }: Props) => {\n const styles = useStyles2(getStyles);\n\n if (!model) {\n return null;\n }\n\n const renderHeader = (main: NavModelItem) => {\n const marginTop = main.icon === 'grafana' ? 12 : 14;\n const icon = main.icon && toIconName(main.icon);\n const sub = subTitle ?? main.subTitle;\n\n return (\n
    \n \n {icon && }\n {main.img && \"\"}\n \n\n
    \n {renderTitle ? renderTitle(main.text) : renderHeaderTitle(main.text, main.highlightText)}\n {info && }\n {sub &&
    {sub}
    }\n {actions &&
    {actions}
    }\n
    \n
    \n );\n };\n\n return (\n
    \n
    \n
    \n {renderHeader(model)}\n {model.children && model.children.length > 0 && {model.children}}\n
    \n
    \n
    \n );\n};\n\nfunction renderHeaderTitle(title: string, highlightText: NavModelItem['highlightText']) {\n if (!title) {\n return null;\n }\n\n return (\n

    \n {title}\n {highlightText && (\n \n )}\n

    \n );\n}\n\nconst getStyles = (theme: GrafanaTheme2) => ({\n actions: css({\n display: 'flex',\n flexDirection: 'row',\n gap: theme.spacing(1),\n }),\n headerText: css({\n display: 'flex',\n flexDirection: 'column',\n gap: theme.spacing(1),\n }),\n headerCanvas: css`\n background: ${theme.colors.background.canvas};\n `,\n});\n","import { flatten } from 'lodash';\nimport React, { useCallback, useMemo } from 'react';\n\nimport { SelectableValue } from '@grafana/data';\nimport { Select } from '@grafana/ui';\nimport { Variable } from 'app/types/templates';\n\nexport interface Props {\n onChange: (value: string | undefined) => void;\n options: Array>;\n isSearchable: boolean;\n value: string;\n placeholder?: string;\n className?: string;\n variables?: Variable[];\n}\n\nexport const MetricSelect = (props: Props) => {\n const { value, placeholder, className, isSearchable, onChange } = props;\n const options = useSelectOptions(props);\n const selected = useSelectedOption(options, value);\n const onChangeValue = useCallback((selectable: SelectableValue) => onChange(selectable.value), [onChange]);\n\n return (\n \n );\n};\n\nconst useSelectOptions = ({ variables = [], options }: Props): Array> => {\n return useMemo(() => {\n if (!Array.isArray(variables) || variables.length === 0) {\n return options;\n }\n\n return [\n {\n label: 'Template Variables',\n options: variables.map(({ name }) => ({\n label: `$${name}`,\n value: `$${name}`,\n })),\n },\n ...options,\n ];\n }, [variables, options]);\n};\n\nconst useSelectedOption = (options: Array>, value: string): SelectableValue => {\n return useMemo(() => {\n const allOptions = options.every((o) => o.options) ? flatten(options.map((o) => o.options)) : options;\n return allOptions.find((option) => option.value === value);\n }, [options, value]);\n};\n","import {\n ClipboardButton,\n ColorPicker,\n DataLinksInlineEditor,\n DataSourceHttpSettings,\n GraphContextMenu,\n Icon,\n LegacyForms,\n SeriesColorPickerPopoverWithTheme,\n Spinner,\n UnitPicker,\n} from '@grafana/ui';\nimport { react2AngularDirective } from 'app/angular/react2angular';\nimport { OldFolderPicker } from 'app/core/components/Select/OldFolderPicker';\nimport { TimePickerSettings } from 'app/features/dashboard/components/DashboardSettings/TimePickerSettings';\nimport { QueryEditor as CloudMonitoringQueryEditor } from 'app/plugins/datasource/cloud-monitoring/components/QueryEditor';\n\nimport EmptyListCTA from '../core/components/EmptyListCTA/EmptyListCTA';\nimport { Footer } from '../core/components/Footer/Footer';\nimport { PageHeader } from '../core/components/PageHeader/PageHeader';\nimport { MetricSelect } from '../core/components/Select/MetricSelect';\nimport { TagFilter } from '../core/components/TagFilter/TagFilter';\nimport { HelpModal } from '../core/components/help/HelpModal';\n\nconst { SecretFormField } = LegacyForms;\n\nexport function registerAngularDirectives() {\n react2AngularDirective('footer', Footer, []);\n react2AngularDirective('icon', Icon, [\n 'name',\n 'size',\n 'type',\n ['onClick', { watchDepth: 'reference', wrapApply: true }],\n ]);\n react2AngularDirective('spinner', Spinner, ['inline']);\n react2AngularDirective('helpModal', HelpModal, []);\n react2AngularDirective('pageHeader', PageHeader, ['model', 'noTabs']);\n react2AngularDirective('emptyListCta', EmptyListCTA, [\n 'title',\n 'buttonIcon',\n 'buttonLink',\n 'buttonTitle',\n ['onClick', { watchDepth: 'reference', wrapApply: true }],\n 'proTip',\n 'proTipLink',\n 'proTipLinkTitle',\n 'proTipTarget',\n 'infoBox',\n 'infoBoxTitle',\n ]);\n react2AngularDirective('tagFilter', TagFilter, [\n 'tags',\n ['onChange', { watchDepth: 'reference' }],\n ['tagOptions', { watchDepth: 'reference' }],\n ]);\n react2AngularDirective('colorPicker', ColorPicker, [\n 'color',\n ['onChange', { watchDepth: 'reference', wrapApply: true }],\n ]);\n react2AngularDirective('seriesColorPickerPopover', SeriesColorPickerPopoverWithTheme, [\n 'color',\n 'series',\n 'onColorChange',\n 'onToggleAxis',\n ]);\n react2AngularDirective('unitPicker', UnitPicker, [\n 'value',\n 'width',\n ['onChange', { watchDepth: 'reference', wrapApply: true }],\n ]);\n react2AngularDirective('metricSelect', MetricSelect, [\n 'options',\n 'onChange',\n 'value',\n 'isSearchable',\n 'className',\n 'placeholder',\n ['variables', { watchDepth: 'reference' }],\n ]);\n react2AngularDirective('cloudMonitoringQueryEditor', CloudMonitoringQueryEditor, [\n 'target',\n 'onQueryChange',\n 'onExecuteQuery',\n ['events', { watchDepth: 'reference' }],\n ['datasource', { watchDepth: 'reference' }],\n ['templateSrv', { watchDepth: 'reference' }],\n ]);\n react2AngularDirective('secretFormField', SecretFormField, [\n 'value',\n 'isConfigured',\n 'inputWidth',\n 'labelWidth',\n 'aria-label',\n ['onReset', { watchDepth: 'reference', wrapApply: true }],\n ['onChange', { watchDepth: 'reference', wrapApply: true }],\n ]);\n react2AngularDirective('graphContextMenu', GraphContextMenu, [\n 'x',\n 'y',\n 'itemsGroup',\n ['onClose', { watchDepth: 'reference', wrapApply: true }],\n ['getContextMenuSource', { watchDepth: 'reference', wrapApply: true }],\n ['timeZone', { watchDepth: 'reference', wrapApply: true }],\n ]);\n\n // We keep the drilldown terminology here because of as using data-* directive\n // being in conflict with HTML data attributes\n react2AngularDirective('drilldownLinksEditor', DataLinksInlineEditor, [\n 'value',\n 'links',\n 'suggestions',\n ['onChange', { watchDepth: 'reference', wrapApply: true }],\n ]);\n\n react2AngularDirective('datasourceHttpSettingsNext', DataSourceHttpSettings, [\n 'defaultUrl',\n 'showAccessOptions',\n 'dataSourceConfig',\n 'showForwardOAuthIdentityOption',\n ['onChange', { watchDepth: 'reference', wrapApply: true }],\n ]);\n react2AngularDirective('folderPicker', OldFolderPicker, [\n 'labelClass',\n 'rootName',\n 'enableCreateNew',\n 'enableReset',\n 'initialTitle',\n 'initialFolderId',\n 'dashboardId',\n 'onCreateFolder',\n ['enterFolderCreation', { watchDepth: 'reference', wrapApply: true }],\n ['exitFolderCreation', { watchDepth: 'reference', wrapApply: true }],\n ['onLoad', { watchDepth: 'reference', wrapApply: true }],\n ['onChange', { watchDepth: 'reference', wrapApply: true }],\n ]);\n\n react2AngularDirective('timePickerSettings', TimePickerSettings, [\n 'renderCount',\n 'refreshIntervals',\n 'timePickerHidden',\n 'nowDelay',\n 'timezone',\n ['onTimeZoneChange', { watchDepth: 'reference', wrapApply: true }],\n ['onRefreshIntervalChange', { watchDepth: 'reference', wrapApply: true }],\n ['onNowDelayChange', { watchDepth: 'reference', wrapApply: true }],\n ['onHideTimePickerChange', { watchDepth: 'reference', wrapApply: true }],\n ]);\n\n react2AngularDirective('clipboardButton', ClipboardButton, [\n ['getText', { watchDepth: 'reference', wrapApply: true }],\n ]);\n}\n","import $ from 'jquery';\nimport { debounce, each, map, partial, escape, unescape } from 'lodash';\n\nimport coreModule from 'app/angular/core_module';\n\nimport { promiseToDigest } from '../promiseToDigest';\n\nconst template = `\n
    \n{{part.def.type}}\n()\n\n`;\n\ncoreModule.directive('queryPartEditor', ['templateSrv', queryPartEditorDirective]);\n\nexport function queryPartEditorDirective(templateSrv: any) {\n const paramTemplate = '';\n\n return {\n restrict: 'E',\n template: template,\n scope: {\n part: '=',\n handleEvent: '&',\n debounce: '@',\n },\n link: function postLink($scope: any, elem: any) {\n const part = $scope.part;\n const partDef = part.def;\n const $paramsContainer = elem.find('.query-part-parameters');\n const debounceLookup = $scope.debounce;\n\n $scope.partActions = [];\n\n function clickFuncParam(this: any, paramIndex: number) {\n const $link = $(this);\n const $input = $link.next();\n\n $input.val(part.params[paramIndex]);\n $input.css('width', $link.width()! + 16 + 'px');\n\n $link.hide();\n $input.show();\n $input.focus();\n $input.select();\n\n const typeahead = $input.data('typeahead');\n if (typeahead) {\n $input.val('');\n typeahead.lookup();\n }\n }\n\n function inputBlur(this: any, paramIndex: number) {\n const $input = $(this);\n const $link = $input.prev();\n const newValue = $input.val();\n\n if (newValue !== '' || part.def.params[paramIndex].optional) {\n $link.html(templateSrv.highlightVariablesAsHtml(newValue));\n\n part.updateParam($input.val(), paramIndex);\n $scope.$apply(() => {\n $scope.handleEvent({ $event: { name: 'part-param-changed' } });\n });\n }\n\n $input.hide();\n $link.show();\n }\n\n function inputKeyPress(this: any, paramIndex: number, e: any) {\n if (e.which === 13) {\n inputBlur.call(this, paramIndex);\n }\n }\n\n function inputKeyDown(this: any) {\n this.style.width = (3 + this.value.length) * 8 + 'px';\n }\n\n function addTypeahead($input: JQuery, param: any, paramIndex: number) {\n if (!param.options && !param.dynamicLookup) {\n return;\n }\n\n const typeaheadSource = (query: string, callback: any) => {\n if (param.options) {\n let options = param.options;\n if (param.type === 'int') {\n options = map(options, (val) => {\n return val.toString();\n });\n }\n return options;\n }\n\n $scope.$apply(() => {\n $scope.handleEvent({ $event: { name: 'get-param-options' } }).then((result: any) => {\n const dynamicOptions = map(result, (op) => {\n return escape(op.value);\n });\n callback(dynamicOptions);\n });\n });\n };\n\n $input.attr('data-provide', 'typeahead');\n\n $input.typeahead({\n source: typeaheadSource,\n minLength: 0,\n items: 1000,\n updater: (value: string) => {\n value = unescape(value);\n setTimeout(() => {\n inputBlur.call($input[0], paramIndex);\n }, 0);\n return value;\n },\n });\n\n const typeahead = $input.data('typeahead');\n typeahead.lookup = function () {\n this.query = this.$element.val() || '';\n const items = this.source(this.query, $.proxy(this.process, this));\n return items ? this.process(items) : items;\n };\n\n if (debounceLookup) {\n typeahead.lookup = debounce(typeahead.lookup, 500, { leading: true });\n }\n }\n\n $scope.showActionsMenu = () => {\n promiseToDigest($scope)(\n $scope.handleEvent({ $event: { name: 'get-part-actions' } }).then((res: any) => {\n $scope.partActions = res;\n })\n );\n };\n\n $scope.triggerPartAction = (action: string) => {\n $scope.handleEvent({ $event: { name: 'action', action: action } });\n };\n\n function addElementsAndCompile() {\n each(partDef.params, (param: any, index: number) => {\n if (param.optional && part.params.length <= index) {\n return;\n }\n\n if (index > 0) {\n $(', ').appendTo($paramsContainer);\n }\n\n const paramValue = templateSrv.highlightVariablesAsHtml(part.params[index]);\n const $paramLink = $('' + paramValue + '');\n const $input = $(paramTemplate);\n\n $paramLink.appendTo($paramsContainer);\n $input.appendTo($paramsContainer);\n\n $input.blur(partial(inputBlur, index));\n $input.keyup(inputKeyDown);\n $input.keypress(partial(inputKeyPress, index));\n $paramLink.click(partial(clickFuncParam, index));\n\n addTypeahead($input, param, index);\n });\n }\n\n function relink() {\n $paramsContainer.empty();\n addElementsAndCompile();\n }\n\n relink();\n },\n };\n}\n","import { ISCEService } from 'angular';\nimport { debounce, find, indexOf, map, isObject, escape, unescape } from 'lodash';\n\nimport coreModule from '../../core_module';\nimport { promiseToDigest } from '../../promiseToDigest';\n\nfunction typeaheadMatcher(this: any, item: string) {\n let str = this.query;\n if (str === '') {\n return true;\n }\n if (str[0] === '/') {\n str = str.substring(1);\n }\n if (str[str.length - 1] === '/') {\n str = str.substring(0, str.length - 1);\n }\n return item.toLowerCase().match(str.toLowerCase());\n}\n\nexport class FormDropdownCtrl {\n inputElement: JQLite;\n linkElement: JQLite;\n model: any;\n display: any;\n text: any;\n options: any;\n cssClass: any;\n cssClasses: any;\n allowCustom: any;\n labelMode: boolean;\n linkMode: boolean;\n cancelBlur: any;\n onChange: any;\n getOptions: any;\n optionCache: any;\n lookupText: boolean;\n placeholder: any;\n startOpen: any;\n debounce: boolean;\n\n static $inject = ['$scope', '$element', '$sce', 'templateSrv'];\n\n constructor(\n private $scope: any,\n $element: JQLite,\n private $sce: ISCEService,\n private templateSrv: any\n ) {\n this.inputElement = $element.find('input').first();\n this.linkElement = $element.find('a').first();\n this.linkMode = true;\n this.cancelBlur = null;\n this.labelMode = false;\n this.lookupText = false;\n this.debounce = false;\n\n // listen to model changes\n $scope.$watch('ctrl.model', this.modelChanged.bind(this));\n }\n\n $onInit() {\n if (this.labelMode) {\n this.cssClasses = 'gf-form-label ' + this.cssClass;\n } else {\n this.cssClasses = 'gf-form-input gf-form-input--dropdown ' + this.cssClass;\n }\n\n if (this.placeholder) {\n this.inputElement.attr('placeholder', this.placeholder);\n }\n\n this.inputElement.attr('data-provide', 'typeahead');\n this.inputElement.typeahead({\n source: this.typeaheadSource.bind(this),\n minLength: 0,\n items: 10000,\n updater: this.typeaheadUpdater.bind(this),\n matcher: typeaheadMatcher,\n });\n\n // modify typeahead lookup\n // this = typeahead\n const typeahead = this.inputElement.data('typeahead');\n typeahead.lookup = function () {\n this.query = this.$element.val() || '';\n this.source(this.query, this.process.bind(this));\n };\n\n if (this.debounce) {\n typeahead.lookup = debounce(typeahead.lookup, 500, { leading: true });\n }\n\n this.linkElement.keydown((evt) => {\n // trigger typeahead on down arrow or enter key\n if (evt.keyCode === 40 || evt.keyCode === 13) {\n this.linkElement.click();\n }\n });\n\n this.inputElement.keydown((evt) => {\n if (evt.keyCode === 13) {\n setTimeout(() => {\n this.inputElement.blur();\n }, 300);\n }\n });\n\n this.inputElement.blur(this.inputBlur.bind(this));\n\n if (this.startOpen) {\n setTimeout(this.open.bind(this), 0);\n }\n }\n\n getOptionsInternal(query: string) {\n return promiseToDigest(this.$scope)(Promise.resolve(this.getOptions({ $query: query })));\n }\n\n isPromiseLike(obj: any) {\n return obj && typeof obj.then === 'function';\n }\n\n modelChanged() {\n if (isObject(this.model)) {\n this.updateDisplay((this.model as any).text);\n } else {\n // if we have text use it\n if (this.lookupText) {\n this.getOptionsInternal('').then((options: any) => {\n const item: any = find(options, { value: this.model });\n this.updateDisplay(item ? item.text : this.model);\n });\n } else {\n this.updateDisplay(this.model);\n }\n }\n }\n\n typeaheadSource(query: string, callback: (res: any) => void) {\n this.getOptionsInternal(query).then((options: any) => {\n this.optionCache = options;\n\n // extract texts\n const optionTexts = map(options, (op: any) => {\n return escape(op.text);\n });\n\n // add custom values\n if (this.allowCustom && this.text !== '') {\n if (indexOf(optionTexts, this.text) === -1) {\n optionTexts.unshift(this.text);\n }\n }\n\n callback(optionTexts);\n });\n }\n\n typeaheadUpdater(text: string) {\n if (text === this.text) {\n clearTimeout(this.cancelBlur);\n this.inputElement.focus();\n return text;\n }\n\n this.inputElement.val(text);\n this.switchToLink(true);\n return text;\n }\n\n switchToLink(fromClick: boolean) {\n if (this.linkMode && !fromClick) {\n return;\n }\n\n clearTimeout(this.cancelBlur);\n this.cancelBlur = null;\n this.linkMode = true;\n this.inputElement.hide();\n this.linkElement.show();\n this.updateValue(this.inputElement.val() as string);\n }\n\n inputBlur() {\n // happens long before the click event on the typeahead options\n // need to have long delay because the blur\n this.cancelBlur = setTimeout(this.switchToLink.bind(this), 200);\n }\n\n updateValue(text: string) {\n text = unescape(text);\n\n if (text === '' || this.text === text) {\n return;\n }\n\n this.$scope.$apply(() => {\n const option: any = find(this.optionCache, { text: text });\n\n if (option) {\n if (isObject(this.model)) {\n this.model = option;\n } else {\n this.model = option.value;\n }\n this.text = option.text;\n } else if (this.allowCustom) {\n if (isObject(this.model)) {\n (this.model as any).text = (this.model as any).value = text;\n } else {\n this.model = text;\n }\n this.text = text;\n }\n\n // needs to call this after digest so\n // property is synced with outerscope\n this.$scope.$$postDigest(() => {\n this.$scope.$apply(() => {\n this.onChange({ $option: option });\n });\n });\n });\n }\n\n updateDisplay(text: string) {\n this.text = text;\n this.display = this.$sce.trustAsHtml(this.templateSrv.highlightVariablesAsHtml(text));\n }\n\n open() {\n this.inputElement.css('width', Math.max(this.linkElement.width()!, 80) + 16 + 'px');\n\n this.inputElement.show();\n this.inputElement.focus();\n\n this.linkElement.hide();\n this.linkMode = false;\n\n const typeahead = this.inputElement.data('typeahead');\n if (typeahead) {\n this.inputElement.val('');\n typeahead.lookup();\n }\n }\n}\n\nconst template = `\n\n\n\n\n`;\n\nexport function formDropdownDirective() {\n return {\n restrict: 'E',\n template: template,\n controller: FormDropdownCtrl,\n bindToController: true,\n controllerAs: 'ctrl',\n scope: {\n model: '=',\n getOptions: '&',\n onChange: '&',\n cssClass: '@',\n allowCustom: '@',\n labelMode: '@',\n lookupText: '@',\n placeholder: '@',\n startOpen: '@',\n debounce: '@',\n },\n };\n}\n\ncoreModule.directive('gfFormDropdown', formDropdownDirective);\n","// @ts-ignore\nimport baron from 'baron';\nimport $ from 'jquery';\n\nimport coreModule from 'app/angular/core_module';\n\nconst scrollBarHTML = `\n
    \n
    \n
    \n`;\n\nconst scrollRootClass = 'baron baron__root';\nconst scrollerClass = 'baron__scroller';\n\nexport function geminiScrollbar() {\n return {\n restrict: 'A',\n link: (scope: any, elem: any, attrs: any) => {\n let scrollRoot = elem.parent();\n const scroller = elem;\n\n if (attrs.grafanaScrollbar && attrs.grafanaScrollbar === 'scrollonroot') {\n scrollRoot = scroller;\n }\n\n scrollRoot.addClass(scrollRootClass);\n $(scrollBarHTML).appendTo(scrollRoot);\n elem.addClass(scrollerClass);\n\n const scrollParams = {\n root: scrollRoot[0],\n scroller: scroller[0],\n bar: '.baron__bar',\n barOnCls: '_scrollbar',\n scrollingCls: '_scrolling',\n track: '.baron__track',\n direction: 'v',\n };\n\n const scrollbar = baron(scrollParams);\n\n scope.$on('$destroy', () => {\n scrollbar.dispose();\n });\n },\n };\n}\n\ncoreModule.directive('grafanaScrollbar', geminiScrollbar);\n","import { JsonExplorer } from '@grafana/ui';\nimport coreModule from 'app/angular/core_module';\n\ncoreModule.directive('jsonTree', [\n function jsonTreeDirective() {\n return {\n restrict: 'E',\n scope: {\n object: '=',\n startExpanded: '@',\n rootName: '@',\n },\n link: (scope: any, elem) => {\n let expansionLevel = scope.startExpanded;\n if (scope.startExpanded === 'true') {\n expansionLevel = 2;\n } else if (scope.startExpanded === 'false') {\n expansionLevel = 1;\n }\n const jsonObject = { [scope.rootName]: scope.object };\n const jsonExp = new JsonExplorer(jsonObject, expansionLevel, {\n animateOpen: true,\n });\n const html = jsonExp.render(true);\n elem.append(html);\n },\n };\n },\n]);\n","import coreModule from 'app/angular/core_module';\n\nconst template = `\n\n`;\n\nconst checkboxTemplate = `\n\n`;\n\nexport class SwitchCtrl {\n onChange: any;\n checked: any;\n show: any;\n id: any;\n label?: string;\n\n static $inject = ['$scope', '$timeout'];\n\n constructor(\n $scope: any,\n private $timeout: any\n ) {\n this.show = true;\n this.id = $scope.$id;\n }\n\n internalOnChange() {\n return this.$timeout(() => {\n return this.onChange();\n });\n }\n}\n\nexport function switchDirective() {\n return {\n restrict: 'E',\n controller: SwitchCtrl,\n controllerAs: 'ctrl',\n bindToController: true,\n scope: {\n checked: '=',\n label: '@',\n labelClass: '@',\n tooltip: '@',\n switchClass: '@',\n onChange: '&',\n },\n template: template,\n };\n}\n\nexport function checkboxDirective() {\n return {\n restrict: 'E',\n controller: SwitchCtrl,\n controllerAs: 'ctrl',\n bindToController: true,\n scope: {\n checked: '=',\n label: '@',\n labelClass: '@',\n tooltip: '@',\n switchClass: '@',\n onChange: '&',\n },\n template: checkboxTemplate,\n };\n}\n\ncoreModule.directive('gfFormSwitch', switchDirective);\ncoreModule.directive('gfFormCheckbox', checkboxDirective);\n","import { each } from 'lodash';\n// @ts-ignore\nimport Drop from 'tether-drop';\n\nimport coreModule from 'app/angular/core_module';\n\nexport function infoPopover() {\n return {\n restrict: 'E',\n template: ``,\n transclude: true,\n link: (scope: any, elem: any, attrs: any, ctrl: any, transclude: any) => {\n const offset = attrs.offset || '0 -10px';\n const position = attrs.position || 'right middle';\n let classes = 'drop-help drop-hide-out-of-bounds';\n const openOn = 'hover';\n\n elem.addClass('gf-form-help-icon');\n\n if (attrs.wide) {\n classes += ' drop-wide';\n }\n\n if (attrs.mode) {\n elem.addClass('gf-form-help-icon--' + attrs.mode);\n }\n\n transclude((clone: any, newScope: any) => {\n const content = document.createElement('div');\n content.className = 'markdown-html';\n\n each(clone, (node) => {\n content.appendChild(node);\n });\n\n const dropOptions = {\n target: elem[0],\n content: content,\n position: position,\n classes: classes,\n openOn: openOn,\n hoverOpenDelay: 400,\n tetherOptions: {\n offset: offset,\n constraints: [\n {\n to: 'window',\n attachment: 'together',\n pin: true,\n },\n ],\n },\n };\n\n // Create drop in next digest after directive content is rendered.\n scope.$applyAsync(() => {\n const drop = new Drop(dropOptions);\n\n const unbind = scope.$on('$destroy', () => {\n drop.destroy();\n unbind();\n });\n });\n });\n },\n };\n}\n\ncoreModule.directive('infoPopover', infoPopover);\n","/**\n * Wrapper for the new ngReact directive for backward compatibility.\n * Allows remaining untouched in outdated plugins.\n * Technically, it's just a wrapper for react component with two-way data binding support.\n */\nimport coreModule from '../core_module';\n\ncoreModule.directive('spectrumPicker', spectrumPicker);\n\nexport function spectrumPicker() {\n return {\n restrict: 'E',\n require: 'ngModel',\n scope: true,\n replace: true,\n template: '',\n link: (scope: any, element: any, attrs: any, ngModel: any) => {\n scope.ngModel = ngModel;\n scope.onColorChange = (color: string) => {\n ngModel.$setViewValue(color);\n };\n },\n };\n}\n","/**\n * codeEditor directive based on Ace code editor\n * https://github.com/ajaxorg/ace\n *\n * Basic usage:\n * \n * \n *\n * Params:\n * content: Editor content.\n * onChange: Function called on content change (invoked on editor blur, ctrl+enter, not on every change).\n * getCompleter: Function returned external completer. Completer is an object implemented getCompletions() method,\n * see Prometheus Data Source implementation for details.\n *\n * Some Ace editor options available via data-* attributes:\n * data-mode - Language mode (text, sql, javascript, etc.). Default is 'text'.\n * data-theme - Editor theme (eg 'solarized_dark').\n * data-max-lines - Max editor height in lines. Editor grows automatically from 1 to maxLines.\n * data-show-gutter - Show gutter (contains line numbers and additional info).\n * data-tab-size - Tab size, default is 2.\n * data-behaviours-enabled - Specifies whether to use behaviors or not. \"Behaviors\" in this case is the auto-pairing of\n * special characters, like quotation marks, parenthesis, or brackets.\n * data-snippets-enabled - Specifies whether to use snippets or not. \"Snippets\" are small pieces of code that can be\n * inserted via the completion box.\n *\n * Keybindings:\n * Ctrl-Enter (Command-Enter): run onChange() function\n */\n\nimport coreModule from 'app/angular/core_module';\nimport config from 'app/core/config';\n\nconst DEFAULT_THEME_DARK = 'ace/theme/grafana-dark';\nconst DEFAULT_THEME_LIGHT = 'ace/theme/textmate';\nconst DEFAULT_MODE = 'text';\nconst DEFAULT_MAX_LINES = 10;\nconst DEFAULT_TAB_SIZE = 2;\nconst DEFAULT_BEHAVIORS = true;\nconst DEFAULT_SNIPPETS = true;\n\nconst editorTemplate = `
    `;\n\nasync function link(scope: any, elem: any, attrs: any) {\n // Options\n const langMode = attrs.mode || DEFAULT_MODE;\n const maxLines = attrs.maxLines || DEFAULT_MAX_LINES;\n const showGutter = attrs.showGutter !== undefined;\n const tabSize = attrs.tabSize || DEFAULT_TAB_SIZE;\n const behavioursEnabled = attrs.behavioursEnabled ? attrs.behavioursEnabled === 'true' : DEFAULT_BEHAVIORS;\n const snippetsEnabled = attrs.snippetsEnabled ? attrs.snippetsEnabled === 'true' : DEFAULT_SNIPPETS;\n\n // Initialize editor\n const aceElem = elem.get(0);\n const { default: ace } = await import(/* webpackChunkName: \"brace\" */ 'brace');\n await import('brace/ext/language_tools');\n await import('brace/theme/textmate');\n await import('brace/mode/text');\n await import('brace/snippets/text');\n await import('brace/mode/sql');\n await import('brace/snippets/sql');\n await import('brace/mode/sqlserver');\n await import('brace/snippets/sqlserver');\n await import('brace/mode/markdown');\n await import('brace/snippets/markdown');\n await import('brace/mode/json');\n await import('brace/snippets/json');\n\n // @ts-ignore\n await import('./theme-grafana-dark');\n\n const codeEditor = ace.edit(aceElem);\n const editorSession = codeEditor.getSession();\n\n const editorOptions = {\n maxLines: maxLines,\n showGutter: showGutter,\n tabSize: tabSize,\n behavioursEnabled: behavioursEnabled,\n highlightActiveLine: false,\n showPrintMargin: false,\n autoScrollEditorIntoView: true, // this is needed if editor is inside scrollable page\n };\n\n // Set options\n codeEditor.setOptions(editorOptions);\n // disable depreacation warning\n codeEditor.$blockScrolling = Infinity;\n // Padding hacks\n (codeEditor.renderer as any).setScrollMargin(10, 10);\n codeEditor.renderer.setPadding(10);\n\n setThemeMode();\n setLangMode(langMode);\n setEditorContent(scope.content);\n\n // Add classes\n elem.addClass('gf-code-editor');\n const textarea = elem.find('textarea');\n textarea.addClass('gf-form-input');\n\n // All aria-label to be set for accessibility\n textarea.attr('aria-label', attrs.textareaLabel);\n\n if (scope.codeEditorFocus) {\n setTimeout(() => {\n textarea.focus();\n const domEl = textarea[0];\n if (domEl.setSelectionRange) {\n const pos = textarea.val().length * 2;\n domEl.setSelectionRange(pos, pos);\n }\n }, 100);\n }\n\n // Event handlers\n editorSession.on('change', (e) => {\n scope.$apply(() => {\n const newValue = codeEditor.getValue();\n scope.content = newValue;\n });\n });\n\n // Sync with outer scope - update editor content if model has been changed from outside of directive.\n scope.$watch('content', (newValue: any, oldValue: any) => {\n const editorValue = codeEditor.getValue();\n if (newValue !== editorValue && newValue !== oldValue) {\n scope.$$postDigest(() => {\n setEditorContent(newValue);\n });\n }\n });\n\n codeEditor.on('blur', () => {\n scope.onChange();\n });\n\n scope.$on('$destroy', () => {\n codeEditor.destroy();\n });\n\n // Keybindings\n codeEditor.commands.addCommand({\n name: 'executeQuery',\n bindKey: { win: 'Ctrl-Enter', mac: 'Command-Enter' },\n exec: () => {\n scope.onChange();\n },\n });\n\n function setLangMode(lang: string) {\n ace.acequire('ace/ext/language_tools');\n codeEditor.setOptions({\n enableBasicAutocompletion: true,\n enableLiveAutocompletion: true,\n enableSnippets: snippetsEnabled,\n });\n\n if (scope.getCompleter()) {\n // make copy of array as ace seems to share completers array between instances\n const anyEditor = codeEditor as any;\n anyEditor.completers = anyEditor.completers.slice();\n anyEditor.completers.push(scope.getCompleter());\n }\n\n const aceModeName = `ace/mode/${lang}`;\n editorSession.setMode(aceModeName);\n }\n\n function setThemeMode() {\n let theme = DEFAULT_THEME_DARK;\n if (config.bootData.user.lightTheme) {\n theme = DEFAULT_THEME_LIGHT;\n }\n\n codeEditor.setTheme(theme);\n }\n\n function setEditorContent(value: string) {\n codeEditor.setValue(value);\n codeEditor.clearSelection();\n }\n}\n\nexport function codeEditorDirective() {\n return {\n restrict: 'E',\n template: editorTemplate,\n scope: {\n content: '=',\n datasource: '=',\n codeEditorFocus: '<',\n onChange: '&',\n getCompleter: '&',\n },\n link: link,\n };\n}\n\ncoreModule.directive('codeEditor', codeEditorDirective);\n","import $ from 'jquery';\nimport { debounce, each, indexOf, map, partial, escape, unescape } from 'lodash';\n\nimport coreModule from 'app/angular/core_module';\n\nconst template = `\n
    \n{{part.label}}\n{{part.def.wrapOpen}}{{part.def.wrapClose}}\n\n`;\n\ncoreModule.directive('sqlPartEditor', ['templateSrv', sqlPartEditorDirective]);\n\nexport function sqlPartEditorDirective(templateSrv: any) {\n const paramTemplate = '';\n\n return {\n restrict: 'E',\n template: template,\n scope: {\n part: '=',\n handleEvent: '&',\n debounce: '@',\n },\n link: function postLink($scope: any, elem: any) {\n const part = $scope.part;\n const partDef = part.def;\n const $paramsContainer = elem.find('.query-part-parameters');\n const debounceLookup = $scope.debounce;\n let cancelBlur: any = null;\n\n $scope.partActions = [];\n\n function clickFuncParam(this: any, paramIndex: number) {\n const $link = $(this);\n const $input = $link.next();\n\n $input.val(part.params[paramIndex]);\n $input.css('width', $link.width()! + 16 + 'px');\n\n $link.hide();\n $input.show();\n $input.focus();\n $input.select();\n\n const typeahead = $input.data('typeahead');\n if (typeahead) {\n $input.val('');\n typeahead.lookup();\n }\n }\n\n function inputBlur($input: JQuery, paramIndex: number) {\n cancelBlur = setTimeout(() => {\n switchToLink($input, paramIndex);\n }, 200);\n }\n\n function switchToLink($input: JQuery, paramIndex: number) {\n const $link = $input.prev();\n const newValue = $input.val();\n\n if (newValue !== '' || part.def.params[paramIndex].optional) {\n $link.html(templateSrv.highlightVariablesAsHtml(newValue));\n\n part.updateParam($input.val(), paramIndex);\n $scope.$apply(() => {\n $scope.handleEvent({ $event: { name: 'part-param-changed' } });\n });\n }\n\n $input.hide();\n $link.show();\n }\n\n function inputKeyPress(this: any, paramIndex: number, e: any) {\n if (e.which === 13) {\n switchToLink($(this), paramIndex);\n }\n }\n\n function inputKeyDown(this: any) {\n this.style.width = (3 + this.value.length) * 8 + 'px';\n }\n\n function addTypeahead($input: JQuery, param: any, paramIndex: number) {\n if (!param.options && !param.dynamicLookup) {\n return;\n }\n\n const typeaheadSource = (query: string, callback: any) => {\n if (param.options) {\n let options = param.options;\n if (param.type === 'int') {\n options = map(options, (val) => {\n return val.toString();\n });\n }\n return options;\n }\n\n $scope.$apply(() => {\n $scope.handleEvent({ $event: { name: 'get-param-options', param: param } }).then((result: any) => {\n const dynamicOptions = map(result, (op) => {\n return escape(op.value);\n });\n\n // add current value to dropdown if it's not in dynamicOptions\n if (indexOf(dynamicOptions, part.params[paramIndex]) === -1) {\n dynamicOptions.unshift(escape(part.params[paramIndex]));\n }\n\n callback(dynamicOptions);\n });\n });\n };\n\n $input.attr('data-provide', 'typeahead');\n\n $input.typeahead({\n source: typeaheadSource,\n minLength: 0,\n items: 1000,\n updater: (value: string) => {\n value = unescape(value);\n if (value === part.params[paramIndex]) {\n clearTimeout(cancelBlur);\n $input.focus();\n return value;\n }\n return value;\n },\n });\n\n const typeahead = $input.data('typeahead');\n typeahead.lookup = function () {\n this.query = this.$element.val() || '';\n const items = this.source(this.query, $.proxy(this.process, this));\n return items ? this.process(items) : items;\n };\n\n if (debounceLookup) {\n typeahead.lookup = debounce(typeahead.lookup, 500, { leading: true });\n }\n }\n\n $scope.showActionsMenu = () => {\n $scope.handleEvent({ $event: { name: 'get-part-actions' } }).then((res: any) => {\n $scope.partActions = res;\n });\n };\n\n $scope.triggerPartAction = (action: string) => {\n $scope.handleEvent({ $event: { name: 'action', action: action } });\n };\n\n function addElementsAndCompile() {\n each(partDef.params, (param: any, index: number) => {\n if (param.optional && part.params.length <= index) {\n return;\n }\n\n if (index > 0) {\n $('' + partDef.separator + '').appendTo($paramsContainer);\n }\n\n const paramValue = templateSrv.highlightVariablesAsHtml(part.params[index]);\n const $paramLink = $('' + paramValue + '');\n const $input = $(paramTemplate);\n\n $paramLink.appendTo($paramsContainer);\n $input.appendTo($paramsContainer);\n\n $input.blur(partial(inputBlur, $input, index));\n $input.keyup(inputKeyDown);\n $input.keypress(partial(inputKeyPress, index));\n $paramLink.click(partial(clickFuncParam, index));\n\n addTypeahead($input, param, index);\n });\n }\n\n function relink() {\n $paramsContainer.empty();\n addElementsAndCompile();\n }\n\n relink();\n },\n };\n}\n","import { coreModule } from 'app/angular/core_module';\n\ncoreModule.directive('datasourceHttpSettings', () => {\n return {\n scope: {\n current: '=',\n suggestUrl: '@',\n noDirectAccess: '@',\n showForwardOAuthIdentityOption: '@',\n },\n templateUrl: 'public/app/angular/partials/http_settings_next.html',\n link: {\n pre: ($scope: any) => {\n // do not show access option if direct access is disabled\n $scope.showAccessOption = $scope.noDirectAccess !== 'true';\n $scope.onChange = (datasourceSetting: any) => {\n $scope.current = datasourceSetting;\n };\n },\n },\n };\n});\n","import { coreModule } from 'app/angular/core_module';\n\ncoreModule.directive('datasourceTlsAuthSettings', () => {\n return {\n scope: {\n current: '=',\n },\n templateUrl: 'public/app/angular/partials/tls_auth_settings.html',\n };\n});\n","import angular, { ILocationService } from 'angular';\nimport { each } from 'lodash';\n\nimport { DataSourceApi, PanelEvents } from '@grafana/data';\nimport coreModule from 'app/angular/core_module';\nimport config from 'app/core/config';\n\nimport { importPanelPlugin } from '../../features/plugins/importPanelPlugin';\nimport { importDataSourcePlugin, importAppPlugin } from '../../features/plugins/plugin_loader';\n\ncoreModule.directive('pluginComponent', ['$compile', '$http', '$templateCache', '$location', pluginDirectiveLoader]);\n\nfunction pluginDirectiveLoader($compile: any, $http: any, $templateCache: any, $location: ILocationService) {\n function getTemplate(component: { template: any; templateUrl: any }) {\n if (component.template) {\n return Promise.resolve(component.template);\n }\n const cached = $templateCache.get(component.templateUrl);\n if (cached) {\n return Promise.resolve(cached);\n }\n return $http.get(component.templateUrl).then((res: any) => {\n return res.data;\n });\n }\n\n function relativeTemplateUrlToAbs(templateUrl: string, baseUrl: string) {\n if (!templateUrl) {\n return undefined;\n }\n if (templateUrl.indexOf('public') === 0) {\n return templateUrl;\n }\n\n return baseUrl + '/' + templateUrl;\n }\n\n function getPluginComponentDirective(options: any) {\n // handle relative template urls for plugin templates\n options.Component.templateUrl = relativeTemplateUrlToAbs(options.Component.templateUrl, options.baseUrl);\n\n return () => {\n return {\n templateUrl: options.Component.templateUrl,\n template: options.Component.template,\n restrict: 'E',\n controller: options.Component,\n controllerAs: 'ctrl',\n bindToController: true,\n scope: options.bindings,\n link: (scope: any, elem: any, attrs: any, ctrl: any) => {\n if (ctrl.link) {\n ctrl.link(scope, elem, attrs, ctrl);\n }\n if (ctrl.init) {\n ctrl.init();\n }\n },\n };\n };\n }\n\n function loadPanelComponentInfo(scope: any, attrs: any) {\n const componentInfo: any = {\n name: 'panel-plugin-' + scope.panel.type,\n bindings: { dashboard: '=', panel: '=', row: '=' },\n attrs: {\n dashboard: 'dashboard',\n panel: 'panel',\n class: 'panel-height-helper',\n },\n };\n\n const panelInfo = config.panels[scope.panel.type];\n return importPanelPlugin(panelInfo.id).then((panelPlugin) => {\n const PanelCtrl = panelPlugin.angularPanelCtrl;\n componentInfo.Component = PanelCtrl;\n\n if (!PanelCtrl || PanelCtrl.registered) {\n return componentInfo;\n }\n\n if (PanelCtrl.templatePromise) {\n return PanelCtrl.templatePromise.then((res: any) => {\n return componentInfo;\n });\n }\n\n if (panelInfo) {\n PanelCtrl.templateUrl = relativeTemplateUrlToAbs(PanelCtrl.templateUrl, panelInfo.baseUrl);\n }\n\n PanelCtrl.templatePromise = getTemplate(PanelCtrl).then((template: any) => {\n PanelCtrl.templateUrl = null;\n PanelCtrl.template = `${template}`;\n return { ...componentInfo, baseUrl: panelInfo.baseUrl };\n });\n\n return PanelCtrl.templatePromise;\n });\n }\n\n function getModule(scope: any, attrs: any): any {\n switch (attrs.type) {\n // QueryCtrl\n case 'query-ctrl': {\n const ds: DataSourceApi = scope.ctrl.datasource as DataSourceApi;\n\n return Promise.resolve({\n baseUrl: ds.meta.baseUrl,\n name: 'query-ctrl-' + ds.meta.id,\n bindings: { target: '=', panelCtrl: '=', datasource: '=' },\n attrs: {\n target: 'ctrl.target',\n 'panel-ctrl': 'ctrl',\n datasource: 'ctrl.datasource',\n },\n Component: ds.components!.QueryCtrl,\n });\n }\n // Annotations\n case 'annotations-query-ctrl': {\n const baseUrl = scope.ctrl.currentDatasource.meta.baseUrl;\n const pluginId = scope.ctrl.currentDatasource.meta.id;\n\n return importDataSourcePlugin(scope.ctrl.currentDatasource.meta).then((dsPlugin) => {\n return {\n baseUrl,\n name: 'annotations-query-ctrl-' + pluginId,\n bindings: { annotation: '=', datasource: '=' },\n attrs: {\n annotation: 'ctrl.currentAnnotation',\n datasource: 'ctrl.currentDatasource',\n },\n Component: dsPlugin.components.AnnotationsQueryCtrl,\n };\n });\n }\n // Datasource ConfigCtrl\n case 'datasource-config-ctrl': {\n const dsMeta = scope.ctrl.datasourceMeta;\n const angularUrl = $location.url();\n return importDataSourcePlugin(dsMeta).then((dsPlugin) => {\n scope.$watch(\n 'ctrl.current',\n () => {\n // This watcher can trigger when we navigate away due to late digests\n // This check is to stop onModelChanged from being called when navigating away\n // as it triggers a redux action which comes before the angular $routeChangeSucces and\n // This makes the bridgeSrv think location changed from redux before detecting it was actually\n // changed from angular.\n if (angularUrl === $location.url()) {\n scope.onModelChanged(scope.ctrl.current);\n }\n },\n true\n );\n\n return {\n baseUrl: dsMeta.baseUrl,\n name: 'ds-config-' + dsMeta.id,\n bindings: { meta: '=', current: '=' },\n attrs: { meta: 'ctrl.datasourceMeta', current: 'ctrl.current' },\n Component: dsPlugin.angularConfigCtrl,\n };\n });\n }\n // AppConfigCtrl\n case 'app-config-ctrl': {\n const model = scope.ctrl.model;\n return importAppPlugin(model).then((appPlugin) => {\n return {\n baseUrl: model.baseUrl,\n name: 'app-config-' + model.id,\n bindings: { appModel: '=', appEditCtrl: '=' },\n attrs: { 'app-model': 'ctrl.model', 'app-edit-ctrl': 'ctrl' },\n Component: appPlugin.angularConfigCtrl,\n };\n });\n }\n // Panel\n case 'panel': {\n return loadPanelComponentInfo(scope, attrs);\n }\n default: {\n return Promise.reject({\n message: 'Could not find component type: ' + attrs.type,\n });\n }\n }\n }\n\n function appendAndCompile(scope: any, elem: JQuery, componentInfo: any) {\n const child = angular.element(document.createElement(componentInfo.name));\n each(componentInfo.attrs, (value, key) => {\n child.attr(key, value);\n });\n\n $compile(child)(scope);\n elem.empty();\n\n // let a binding digest cycle complete before adding to dom\n setTimeout(() => {\n scope.$applyAsync(() => {\n elem.append(child);\n setTimeout(() => {\n scope.$applyAsync(() => {\n scope.$broadcast(PanelEvents.componentDidMount.name);\n });\n });\n });\n });\n }\n\n function registerPluginComponent(scope: any, elem: JQuery, attrs: any, componentInfo: any) {\n if (componentInfo.notFound) {\n elem.empty();\n return;\n }\n\n if (!componentInfo.Component) {\n throw {\n message: 'Failed to find exported plugin component for ' + componentInfo.name,\n };\n }\n\n if (!componentInfo.Component.registered) {\n const directiveName = attrs.$normalize(componentInfo.name);\n const directiveFn = getPluginComponentDirective(componentInfo);\n coreModule.directive(directiveName, directiveFn);\n componentInfo.Component.registered = true;\n }\n\n appendAndCompile(scope, elem, componentInfo);\n }\n\n return {\n restrict: 'E',\n link: (scope: any, elem: JQuery, attrs: any) => {\n getModule(scope, attrs)\n .then((componentInfo: any) => {\n registerPluginComponent(scope, elem, attrs, componentInfo);\n })\n .catch((err: any) => {\n console.error('Plugin component error', err);\n });\n },\n };\n}\n","import { IRootScopeService, IAngularEvent, auto } from 'angular';\nimport $ from 'jquery';\nimport _ from 'lodash'; // eslint-disable-line lodash/import-scope\n\nimport { AppEvent } from '@grafana/data';\nimport { setLegacyAngularInjector, setAngularLoader } from '@grafana/runtime';\nimport { colors } from '@grafana/ui';\nimport coreModule from 'app/angular/core_module';\nimport { AngularLoader } from 'app/angular/services/AngularLoader';\nimport appEvents from 'app/core/app_events';\nimport config from 'app/core/config';\nimport { ContextSrv } from 'app/core/services/context_srv';\nimport { AppEventEmitter, AppEventConsumer } from 'app/types';\n\nimport { UtilSrv } from './services/UtilSrv';\n\nexport type GrafanaRootScope = IRootScopeService & AppEventEmitter & AppEventConsumer & { colors: string[] };\n\nexport class GrafanaCtrl {\n static $inject = ['$scope', 'utilSrv', '$rootScope', 'contextSrv', 'angularLoader', '$injector'];\n\n constructor(\n $scope: any,\n utilSrv: UtilSrv,\n $rootScope: GrafanaRootScope,\n contextSrv: ContextSrv,\n angularLoader: AngularLoader,\n $injector: auto.IInjectorService\n ) {\n // make angular loader service available to react components\n setAngularLoader(angularLoader);\n setLegacyAngularInjector($injector);\n\n $scope.init = () => {\n $scope.contextSrv = contextSrv;\n $scope.appSubUrl = config.appSubUrl;\n $scope._ = _;\n utilSrv.init();\n };\n\n $rootScope.colors = colors;\n\n $rootScope.onAppEvent = function (\n event: AppEvent | string,\n callback: (event: IAngularEvent, ...args: any[]) => void,\n localScope?: any\n ) {\n let unbind;\n if (typeof event === 'string') {\n unbind = $rootScope.$on(event, callback);\n } else {\n unbind = $rootScope.$on(event.name, callback);\n }\n\n let callerScope = this;\n if (callerScope.$id === 1 && !localScope) {\n console.warn('warning rootScope onAppEvent called without localscope');\n }\n if (localScope) {\n callerScope = localScope;\n }\n callerScope.$on('$destroy', unbind);\n };\n\n $rootScope.appEvent = (event: AppEvent | string, payload?: T | any) => {\n if (typeof event === 'string') {\n $rootScope.$emit(event, payload);\n appEvents.emit(event, payload);\n } else {\n $rootScope.$emit(event.name, payload);\n appEvents.emit(event, payload);\n }\n };\n\n $scope.init();\n }\n}\n\nexport function grafanaAppDirective() {\n return {\n restrict: 'E',\n controller: GrafanaCtrl,\n link: (scope: IRootScopeService & AppEventEmitter, elem: JQuery) => {\n const body = $('body');\n // see https://github.com/zenorocha/clipboard.js/issues/155\n $.fn.modal.Constructor.prototype.enforceFocus = () => {};\n\n // handle in active view state class\n let lastActivity = new Date().getTime();\n let activeUser = true;\n const inActiveTimeLimit = 60 * 5000;\n\n function checkForInActiveUser() {\n if (!activeUser) {\n return;\n }\n // only go to activity low mode on dashboard page\n if (!body.hasClass('page-dashboard')) {\n return;\n }\n\n if (new Date().getTime() - lastActivity > inActiveTimeLimit) {\n activeUser = false;\n body.addClass('view-mode--inactive');\n }\n }\n\n function userActivityDetected() {\n lastActivity = new Date().getTime();\n if (!activeUser) {\n activeUser = true;\n body.removeClass('view-mode--inactive');\n }\n }\n\n // mouse and keyboard is user activity\n body.mousemove(userActivityDetected);\n body.keydown(userActivityDetected);\n // set useCapture = true to catch event here\n document.addEventListener('wheel', userActivityDetected, { capture: true, passive: true });\n // treat tab change as activity\n document.addEventListener('visibilitychange', userActivityDetected);\n\n // check every 2 seconds\n setInterval(checkForInActiveUser, 2000);\n\n // handle document clicks that should hide things\n body.click((evt) => {\n const target = $(evt.target);\n if (target.parents().length === 0) {\n return;\n }\n\n // ensure dropdown menu doesn't impact on z-index\n body.find('.dropdown-menu-open').removeClass('dropdown-menu-open');\n\n // for stuff that animates, slides out etc, clicking it needs to\n // hide it right away\n const clickAutoHide = target.closest('[data-click-hide]');\n if (clickAutoHide.length) {\n const clickAutoHideParent = clickAutoHide.parent();\n clickAutoHide.detach();\n setTimeout(() => {\n clickAutoHideParent.append(clickAutoHide);\n }, 100);\n }\n\n // hide popovers\n const popover = elem.find('.popover');\n if (popover.length > 0 && target.parents('.graph-legend').length === 0) {\n popover.hide();\n }\n });\n },\n };\n}\n\ncoreModule.directive('grafanaApp', grafanaAppDirective);\n","// This is empty for now, as I think it's not going to be necessary.\n// This replaces Angular RouteParamsProvider implementation with a dummy one to keep the ball rolling\n\nimport { navigationLogger } from '@grafana/runtime';\n\nexport class RouteParamsProvider {\n constructor() {\n navigationLogger('Patch angular', false, 'RouteParamsProvider');\n }\n $get = () => {\n // throw new Error('TODO: Refactor $routeParams');\n };\n}\n","// This is empty for now, as I think it's not going to be necessary.\n// This replaces Angular RouteProvider implementation with a dummy one to keep the ball rolling\n\nimport { navigationLogger } from '@grafana/runtime';\n\nexport class RouteProvider {\n constructor() {\n navigationLogger('Patch angular', false, 'RouteProvider');\n }\n\n $get() {\n return this;\n }\n}\n","import { ILocationService } from 'angular';\n\nimport { RouteParamsProvider } from '../core/navigation/patch/RouteParamsProvider';\nimport { RouteProvider } from '../core/navigation/patch/RouteProvider';\n\nimport { AngularLocationWrapper } from './AngularLocationWrapper';\nimport { coreModule } from './core_module';\n\n// Neutralizing Angular’s location tampering\n// https://stackoverflow.com/a/19825756\nconst tamperAngularLocation = () => {\n coreModule.config([\n '$provide',\n ($provide: any) => {\n $provide.decorator('$browser', [\n '$delegate',\n ($delegate: any) => {\n $delegate.onUrlChange = () => {};\n $delegate.url = () => '';\n\n return $delegate;\n },\n ]);\n },\n ]);\n};\n\n// Intercepting $location service with implementation based on history\nconst interceptAngularLocation = () => {\n coreModule.config([\n '$provide',\n ($provide: any) => {\n $provide.decorator('$location', [\n '$delegate',\n ($delegate: ILocationService) => {\n $delegate = new AngularLocationWrapper() as unknown as ILocationService;\n return $delegate;\n },\n ]);\n },\n ]);\n coreModule.provider('$route', RouteProvider);\n coreModule.provider('$routeParams', RouteParamsProvider);\n};\n\nexport function initAngularRoutingBridge() {\n tamperAngularLocation();\n interceptAngularLocation();\n}\n","export function monkeyPatchInjectorWithPreAssignedBindings(injector: any) {\n injector.oldInvoke = injector.invoke;\n injector.invoke = (fn: any, self: any, locals: any, serviceName: any) => {\n const parentScope = locals?.$scope?.$parent;\n\n if (parentScope) {\n // PanelCtrl\n if (parentScope.panel) {\n self.panel = parentScope.panel;\n }\n\n // Panels & dashboard SettingsCtrl\n if (parentScope.dashboard) {\n self.dashboard = parentScope.dashboard;\n }\n\n // Query editors\n if (parentScope.ctrl?.target) {\n self.panelCtrl = parentScope.ctrl;\n self.datasource = parentScope.ctrl.datasource;\n self.target = parentScope.ctrl.target;\n }\n\n // Data source ConfigCtrl\n if (parentScope.ctrl?.datasourceMeta) {\n self.meta = parentScope.ctrl.datasourceMeta;\n self.current = parentScope.ctrl.current;\n }\n\n // Data source AnnotationsQueryCtrl\n if (parentScope.ctrl?.currentAnnotation) {\n self.annotation = parentScope.ctrl.currentAnnotation;\n self.datasource = parentScope.ctrl.currentDatasource;\n }\n\n // App config ctrl\n if (parentScope.isAppConfigCtrl) {\n self.appEditCtrl = parentScope.ctrl;\n self.appModel = parentScope.ctrl.model;\n }\n\n // App page ctrl\n if (parentScope.$parent?.$parent?.ctrl?.appModel) {\n self.appModel = parentScope.$parent?.$parent?.ctrl?.appModel;\n }\n }\n\n return injector.oldInvoke(fn, self, locals, serviceName);\n };\n}\n","import React, { ComponentType, useEffect, useRef } from 'react';\nimport { Observable, ReplaySubject } from 'rxjs';\n\nimport { EventBusSrv, PanelData, PanelPlugin, PanelProps, FieldConfigSource } from '@grafana/data';\nimport { AngularComponent, getAngularLoader, RefreshEvent } from '@grafana/runtime';\nimport { getDashboardSrv } from 'app/features/dashboard/services/DashboardSrv';\nimport { DashboardModelCompatibilityWrapper } from 'app/features/dashboard-scene/utils/DashboardModelCompatibilityWrapper';\nimport { GetDataOptions } from 'app/features/query/state/PanelQueryRunner';\nimport { RenderEvent } from 'app/types/events';\n\ninterface AngularScopeProps {\n panel: PanelModelCompatibilityWrapper;\n dashboard: DashboardModelCompatibilityWrapper;\n queryRunner: FakeQueryRunner;\n size: {\n height: number;\n width: number;\n };\n}\n\nexport function getAngularPanelReactWrapper(plugin: PanelPlugin): ComponentType {\n return function AngularWrapper(props: PanelProps) {\n const divRef = useRef(null);\n const angularState = useRef();\n const angularComponent = useRef();\n\n useEffect(() => {\n if (!divRef.current) {\n return;\n }\n\n const loader = getAngularLoader();\n const template = '';\n const queryRunner = new FakeQueryRunner();\n const fakePanel = new PanelModelCompatibilityWrapper(plugin, props, queryRunner);\n\n angularState.current = {\n // @ts-ignore\n panel: fakePanel,\n // @ts-ignore\n dashboard: getDashboardSrv().getCurrent(),\n size: { width: props.width, height: props.height },\n queryRunner: queryRunner,\n };\n\n angularComponent.current = loader.load(divRef.current, angularState.current, template);\n\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n // Re-render angular panel when dimensions change\n useEffect(() => {\n if (!angularComponent.current) {\n return;\n }\n\n angularState.current!.size.height = props.height;\n angularState.current!.size.width = props.width;\n angularState.current!.panel.events.publish(new RenderEvent());\n }, [props.width, props.height]);\n\n // Pass new data to angular panel\n useEffect(() => {\n if (!angularState.current?.panel) {\n return;\n }\n\n angularState.current.queryRunner.forwardNewData(props.data);\n }, [props.data]);\n\n return
    ;\n };\n}\n\nclass PanelModelCompatibilityWrapper {\n id: number;\n type: string;\n title: string;\n plugin: PanelPlugin;\n events: EventBusSrv;\n queryRunner: FakeQueryRunner;\n fieldConfig: FieldConfigSource;\n options: Record;\n\n constructor(plugin: PanelPlugin, props: PanelProps, queryRunner: FakeQueryRunner) {\n // Assign legacy \"root\" level options\n if (props.options.angularOptions) {\n Object.assign(this, props.options.angularOptions);\n }\n\n this.id = props.id;\n this.type = plugin.meta.id;\n this.title = props.title;\n this.fieldConfig = props.fieldConfig;\n this.options = props.options;\n\n this.plugin = plugin;\n this.events = new EventBusSrv();\n this.queryRunner = queryRunner;\n }\n\n refresh() {\n this.events.publish(new RefreshEvent());\n }\n\n render() {\n this.events.publish(new RenderEvent());\n }\n\n getQueryRunner() {\n return this.queryRunner;\n }\n}\n\nclass FakeQueryRunner {\n private subject = new ReplaySubject(1);\n\n getData(options: GetDataOptions): Observable {\n return this.subject;\n }\n\n forwardNewData(data: PanelData) {\n this.subject.next(data);\n }\n\n run() {}\n}\n","import { deprecationWarning } from '@grafana/data';\nimport { appEvents } from 'app/core/app_events';\n\nimport { HideModalEvent, ShowModalEvent } from '../../types/events';\n\n/**\n * Old legacy utilSrv exposed to angular services and handles angular modals.\n * Not used by any core or known external plugin.\n */\nexport class UtilSrv {\n modalScope: any;\n\n constructor() {}\n\n init() {\n appEvents.subscribe(ShowModalEvent, (e) => this.showModal(e.payload));\n appEvents.subscribe(HideModalEvent, this.hideModal.bind(this));\n }\n\n /**\n * @deprecated use showModalReact instead that has this capability built in\n */\n hideModal() {\n deprecationWarning('UtilSrv', 'hideModal', '');\n if (this.modalScope && this.modalScope.dismiss) {\n this.modalScope.dismiss();\n }\n }\n\n /**\n * @deprecated\n */\n showModal(options: any) {\n deprecationWarning('UtilSrv', 'showModal', 'publish ShowModalReactEvent');\n }\n}\n","import { cloneDeep } from 'lodash';\n\nimport { AnnotationEvent, deprecationWarning } from '@grafana/data';\nimport { deleteAnnotation, saveAnnotation, updateAnnotation } from 'app/features/annotations/api';\nimport { AnnotationQueryOptions } from 'app/features/annotations/types';\n\n/**\n * @deprecated AnnotationsSrv is deprecated in favor of DashboardQueryRunner\n */\nexport class AnnotationsSrv {\n /**\n * @deprecated clearPromiseCaches is deprecated\n */\n clearPromiseCaches() {\n deprecationWarning('annotations_srv.ts', 'clearPromiseCaches', 'DashboardQueryRunner');\n }\n\n /**\n * @deprecated getAnnotations is deprecated in favor of DashboardQueryRunner.getResult\n */\n getAnnotations(options: AnnotationQueryOptions) {\n deprecationWarning('annotations_srv.ts', 'getAnnotations', 'DashboardQueryRunner.getResult');\n return Promise.resolve({ annotations: [], alertState: undefined });\n }\n\n /**\n * @deprecated getAlertStates is deprecated in favor of DashboardQueryRunner.getResult\n */\n getAlertStates(options: any) {\n deprecationWarning('annotations_srv.ts', 'getAlertStates', 'DashboardQueryRunner.getResult');\n return Promise.resolve(undefined);\n }\n\n /**\n * @deprecated getGlobalAnnotations is deprecated in favor of DashboardQueryRunner.getResult\n */\n getGlobalAnnotations(options: AnnotationQueryOptions) {\n deprecationWarning('annotations_srv.ts', 'getGlobalAnnotations', 'DashboardQueryRunner.getResult');\n return Promise.resolve([]);\n }\n\n /**\n * @deprecated saveAnnotationEvent is deprecated\n */\n saveAnnotationEvent(annotation: AnnotationEvent) {\n deprecationWarning('annotations_srv.ts', 'saveAnnotationEvent', 'api/saveAnnotation');\n return saveAnnotation(annotation);\n }\n\n /**\n * @deprecated updateAnnotationEvent is deprecated\n */\n updateAnnotationEvent(annotation: AnnotationEvent) {\n deprecationWarning('annotations_srv.ts', 'updateAnnotationEvent', 'api/updateAnnotation');\n return updateAnnotation(annotation);\n }\n\n /**\n * @deprecated deleteAnnotationEvent is deprecated\n */\n deleteAnnotationEvent(annotation: AnnotationEvent) {\n deprecationWarning('annotations_srv.ts', 'deleteAnnotationEvent', 'api/deleteAnnotation');\n return deleteAnnotation(annotation);\n }\n\n /**\n * @deprecated translateQueryResult is deprecated in favor of DashboardQueryRunner/utils/translateQueryResult\n */\n translateQueryResult(annotation: any, results: any) {\n deprecationWarning('annotations_srv.ts', 'translateQueryResult', 'DashboardQueryRunner/utils/translateQueryResult');\n // if annotation has snapshotData\n // make clone and remove it\n if (annotation.snapshotData) {\n annotation = cloneDeep(annotation);\n delete annotation.snapshotData;\n }\n\n for (const item of results) {\n item.source = annotation;\n item.color = annotation.iconColor;\n item.type = annotation.name;\n item.isRegion = item.timeEnd && item.time !== item.timeEnd;\n }\n\n return results;\n }\n}\n","import { getBackendSrv, getDataSourceSrv } from '@grafana/runtime';\nimport { contextSrv } from 'app/core/core';\nimport { getDashboardSrv } from 'app/features/dashboard/services/DashboardSrv';\nimport { validationSrv } from 'app/features/manage-dashboards/services/ValidationSrv';\nimport { getLinkSrv } from 'app/features/panel/panellinks/link_srv';\n\nimport coreModule from './core_module';\nimport { UtilSrv } from './services/UtilSrv';\nimport { AnnotationsSrv } from './services/annotations_srv';\n\nexport function registerComponents() {\n coreModule.factory('backendSrv', () => getBackendSrv());\n coreModule.factory('contextSrv', () => contextSrv);\n coreModule.factory('dashboardSrv', () => getDashboardSrv());\n coreModule.factory('datasourceSrv', () => getDataSourceSrv());\n coreModule.factory('linkSrv', () => getLinkSrv());\n coreModule.factory('validationSrv', () => validationSrv);\n coreModule.service('annotationsSrv', AnnotationsSrv);\n coreModule.service('utilSrv', UtilSrv);\n}\n","import 'angular';\nimport 'angular-route';\nimport 'angular-sanitize';\nimport 'angular-bindonce';\n\nimport angular from 'angular'; // eslint-disable-line no-duplicate-imports\nimport { extend } from 'lodash';\n\nimport { getTemplateSrv, SystemJS } from '@grafana/runtime';\nimport { coreModule, angularModules } from 'app/angular/core_module';\nimport appEvents from 'app/core/app_events';\nimport { config } from 'app/core/config';\nimport { contextSrv } from 'app/core/services/context_srv';\nimport { DashboardLoaderSrv } from 'app/features/dashboard/services/DashboardLoaderSrv';\nimport { getTimeSrv } from 'app/features/dashboard/services/TimeSrv';\nimport { setAngularPanelReactWrapper } from 'app/features/plugins/importPanelPlugin';\nimport { buildImportMap } from 'app/features/plugins/loader/utils';\nimport * as sdk from 'app/plugins/sdk';\n\nimport { registerAngularDirectives } from './angular_wrappers';\nimport { initAngularRoutingBridge } from './bridgeReactAngularRouting';\nimport { monkeyPatchInjectorWithPreAssignedBindings } from './injectorMonkeyPatch';\nimport { getAngularPanelReactWrapper } from './panel/AngularPanelReactWrapper';\nimport { promiseToDigest } from './promiseToDigest';\nimport { registerComponents } from './registerComponents';\n\n// Angular plugin dependencies map\nconst importMap = {\n angular: {\n ...angular,\n default: angular,\n },\n 'app/core/core_module': {\n default: coreModule,\n __useDefault: true,\n },\n 'app/core/core': {\n appEvents: appEvents,\n contextSrv: contextSrv,\n coreModule: coreModule,\n },\n 'app/plugins/sdk': sdk,\n 'app/core/utils/promiseToDigest': { promiseToDigest },\n} as Record;\n\nexport class AngularApp {\n ngModuleDependencies: any[];\n preBootModules: any[];\n registerFunctions: any;\n\n constructor() {\n this.preBootModules = [];\n this.ngModuleDependencies = [];\n this.registerFunctions = {};\n }\n\n init() {\n const app = angular.module('grafana', []);\n\n setAngularPanelReactWrapper(getAngularPanelReactWrapper);\n\n app.config([\n '$controllerProvider',\n '$compileProvider',\n '$filterProvider',\n '$httpProvider',\n '$provide',\n '$sceDelegateProvider',\n (\n $controllerProvider: angular.IControllerProvider,\n $compileProvider: angular.ICompileProvider,\n $filterProvider: angular.IFilterProvider,\n $httpProvider: angular.IHttpProvider,\n $provide: angular.auto.IProvideService,\n $sceDelegateProvider: angular.ISCEDelegateProvider\n ) => {\n if (config.buildInfo.env !== 'development') {\n $compileProvider.debugInfoEnabled(false);\n }\n\n $httpProvider.useApplyAsync(true);\n\n if (Boolean(config.pluginsCDNBaseURL)) {\n $sceDelegateProvider.trustedResourceUrlList(['self', `${config.pluginsCDNBaseURL}/**`]);\n }\n\n this.registerFunctions.controller = $controllerProvider.register;\n this.registerFunctions.directive = $compileProvider.directive;\n this.registerFunctions.factory = $provide.factory;\n this.registerFunctions.service = $provide.service;\n this.registerFunctions.filter = $filterProvider.register;\n\n $provide.decorator('$http', [\n '$delegate',\n '$templateCache',\n ($delegate: any, $templateCache: any) => {\n const get = $delegate.get;\n $delegate.get = (url: string, config: any) => {\n if (url.match(/\\.html$/)) {\n // some template's already exist in the cache\n if (!$templateCache.get(url)) {\n url += '?v=' + new Date().getTime();\n }\n }\n return get(url, config);\n };\n return $delegate;\n },\n ]);\n },\n ]);\n\n this.ngModuleDependencies = ['grafana.core', 'ngSanitize', 'grafana', 'pasvaz.bindonce', 'react'];\n\n // makes it possible to add dynamic stuff\n angularModules.forEach((m: angular.IModule) => {\n this.useModule(m);\n });\n\n // register react angular wrappers\n angular.module('grafana.services').service('dashboardLoaderSrv', DashboardLoaderSrv);\n\n coreModule.factory('timeSrv', () => getTimeSrv());\n coreModule.factory('templateSrv', () => getTemplateSrv());\n\n registerAngularDirectives();\n registerComponents();\n initAngularRoutingBridge();\n\n const imports = buildImportMap(importMap);\n // pass the map of module names so systemjs can resolve them\n SystemJS.addImportMap({ imports });\n\n // disable tool tip animation\n $.fn.tooltip.defaults.animation = false;\n }\n\n useModule(module: angular.IModule) {\n if (this.preBootModules) {\n this.preBootModules.push(module);\n } else {\n extend(module, this.registerFunctions);\n }\n this.ngModuleDependencies.push(module.name);\n return module;\n }\n\n bootstrap() {\n const injector = angular.bootstrap(document.getElementById('ngRoot')!, this.ngModuleDependencies);\n\n monkeyPatchInjectorWithPreAssignedBindings(injector);\n\n injector.invoke(() => {\n this.preBootModules.forEach((module) => {\n extend(module, this.registerFunctions);\n });\n\n // I don't know\n return () => {};\n });\n\n return injector;\n }\n}\n","import { isArray } from 'lodash';\nimport { Unsubscribable } from 'rxjs';\n\nimport {\n DataFrame,\n DataQueryResponse,\n DataSourceApi,\n LegacyResponseData,\n LoadingState,\n PanelData,\n PanelEvents,\n TimeRange,\n toDataFrameDTO,\n toLegacyResponseData,\n} from '@grafana/data';\nimport { PanelCtrl } from 'app/angular/panel/panel_ctrl';\nimport { ContextSrv } from 'app/core/services/context_srv';\nimport { PanelModel } from 'app/features/dashboard/state';\nimport { applyPanelTimeOverrides } from 'app/features/dashboard/utils/panel';\n\nimport { PanelQueryRunner } from '../../features/query/state/PanelQueryRunner';\n\nclass MetricsPanelCtrl extends PanelCtrl {\n declare datasource: DataSourceApi;\n declare range: TimeRange;\n\n contextSrv: ContextSrv;\n datasourceSrv: any;\n timeSrv: any;\n templateSrv: any;\n interval: any;\n intervalMs: any;\n resolution: any;\n timeInfo?: string;\n skipDataOnInit = false;\n dataList: LegacyResponseData[] = [];\n querySubscription?: Unsubscribable | null;\n useDataFrames = false;\n panelData?: PanelData;\n\n constructor($scope: any, $injector: any) {\n super($scope, $injector);\n\n this.contextSrv = $injector.get('contextSrv');\n this.datasourceSrv = $injector.get('datasourceSrv');\n this.timeSrv = $injector.get('timeSrv');\n this.templateSrv = $injector.get('templateSrv');\n this.panel.datasource = this.panel.datasource || null;\n\n this.events.on(PanelEvents.refresh, this.onMetricsPanelRefresh.bind(this));\n this.events.on(PanelEvents.panelTeardown, this.onPanelTearDown.bind(this));\n this.events.on(PanelEvents.componentDidMount, this.onMetricsPanelMounted.bind(this));\n }\n\n private onMetricsPanelMounted() {\n const queryRunner = this.panel.getQueryRunner() as PanelQueryRunner;\n this.querySubscription = queryRunner\n .getData({ withTransforms: true, withFieldConfig: true })\n .subscribe(this.panelDataObserver);\n }\n\n private onPanelTearDown() {\n if (this.querySubscription) {\n this.querySubscription.unsubscribe();\n this.querySubscription = null;\n }\n }\n\n private onMetricsPanelRefresh() {\n // ignore fetching data if another panel is in fullscreen\n if (this.otherPanelInFullscreenMode()) {\n return;\n }\n\n // if we have snapshot data use that\n if (this.panel.snapshotData) {\n this.updateTimeRange();\n let data = this.panel.snapshotData;\n // backward compatibility\n if (!isArray(data)) {\n data = data.data;\n }\n\n this.panelData = {\n state: LoadingState.Done,\n series: data,\n timeRange: this.range,\n };\n\n // Defer panel rendering till the next digest cycle.\n // For some reason snapshot panels don't init at this time, so this helps to avoid rendering issues.\n return this.$timeout(() => {\n this.events.emit(PanelEvents.dataSnapshotLoad, data);\n });\n }\n\n // clear loading/error state\n delete this.error;\n this.loading = true;\n\n // load datasource service\n return this.datasourceSrv\n .get(this.panel.datasource, this.panel.scopedVars)\n .then(this.issueQueries.bind(this))\n .catch((err: any) => {\n this.processDataError(err);\n });\n }\n\n processDataError(err: any) {\n // if canceled keep loading set to true\n if (err.cancelled) {\n console.log('Panel request cancelled', err);\n return;\n }\n\n this.error = err.message || 'Request Error';\n\n if (err.data) {\n if (err.data.message) {\n this.error = err.data.message;\n } else if (err.data.error) {\n this.error = err.data.error;\n }\n }\n\n this.angularDirtyCheck();\n }\n\n angularDirtyCheck() {\n if (!this.$scope.$root.$$phase) {\n this.$scope.$digest();\n }\n }\n\n // Updates the response with information from the stream\n panelDataObserver = {\n next: (data: PanelData) => {\n this.panelData = data;\n\n if (data.state === LoadingState.Error) {\n this.loading = false;\n this.processDataError(data.error);\n }\n\n // Ignore data in loading state\n if (data.state === LoadingState.Loading) {\n this.loading = true;\n this.angularDirtyCheck();\n return;\n }\n\n if (data.request) {\n const { timeInfo } = data.request;\n if (timeInfo) {\n this.timeInfo = timeInfo;\n }\n }\n\n if (data.timeRange) {\n this.range = data.timeRange;\n }\n\n if (this.useDataFrames) {\n this.handleDataFrames(data.series);\n } else {\n // Make the results look as if they came directly from a <6.2 datasource request\n const legacy = data.series.map((v) => toLegacyResponseData(v));\n this.handleQueryResult({ data: legacy });\n }\n\n this.angularDirtyCheck();\n },\n };\n\n updateTimeRange(datasource?: DataSourceApi) {\n this.datasource = datasource || this.datasource;\n this.range = this.timeSrv.timeRange();\n\n const newTimeData = applyPanelTimeOverrides(this.panel, this.range);\n this.timeInfo = newTimeData.timeInfo;\n this.range = newTimeData.timeRange;\n }\n\n issueQueries(datasource: DataSourceApi) {\n this.updateTimeRange(datasource);\n\n this.datasource = datasource;\n\n const panel = this.panel as PanelModel;\n const queryRunner = panel.getQueryRunner();\n\n return queryRunner.run({\n datasource: panel.datasource,\n queries: panel.targets,\n panelId: panel.id,\n dashboardUID: this.dashboard.uid,\n timezone: this.dashboard.getTimezone(),\n timeInfo: this.timeInfo,\n timeRange: this.range,\n maxDataPoints: panel.maxDataPoints || this.width,\n minInterval: panel.interval,\n scopedVars: panel.scopedVars,\n cacheTimeout: panel.cacheTimeout,\n queryCachingTTL: panel.queryCachingTTL,\n transformations: panel.transformations,\n });\n }\n\n handleDataFrames(data: DataFrame[]) {\n this.loading = false;\n\n if (this.dashboard && this.dashboard.snapshot) {\n this.panel.snapshotData = data.map((frame) => toDataFrameDTO(frame));\n }\n\n try {\n this.events.emit(PanelEvents.dataFramesReceived, data);\n } catch (err) {\n this.processDataError(err);\n }\n }\n\n handleQueryResult(result: DataQueryResponse) {\n this.loading = false;\n\n if (this.dashboard.snapshot) {\n this.panel.snapshotData = result.data;\n }\n\n if (!result || !result.data) {\n console.log('Data source query result invalid, missing data field:', result);\n result = { data: [] };\n }\n\n try {\n this.events.emit(PanelEvents.dataReceived, result.data);\n } catch (err) {\n this.processDataError(err);\n }\n }\n}\n\nexport { MetricsPanelCtrl };\n","import { auto } from 'angular';\nimport { isString } from 'lodash';\n\nimport {\n AppEvent,\n PanelEvents,\n PanelPluginMeta,\n AngularPanelMenuItem,\n EventBusExtended,\n EventBusSrv,\n} from '@grafana/data';\nimport { AngularLocationWrapper } from 'app/angular/AngularLocationWrapper';\nimport config from 'app/core/config';\nimport { profiler } from 'app/core/core';\n\nimport { DashboardModel } from '../../features/dashboard/state';\n\nexport class PanelCtrl {\n panel: any;\n error: any;\n declare dashboard: DashboardModel;\n pluginName = '';\n pluginId = '';\n editorTabs: any;\n $scope: any;\n $injector: auto.IInjectorService;\n $timeout: any;\n editModeInitiated = false;\n declare height: number;\n declare width: number;\n containerHeight: any;\n events: EventBusExtended;\n loading = false;\n timing: any;\n $location: AngularLocationWrapper;\n\n constructor($scope: any, $injector: auto.IInjectorService) {\n this.panel = this.panel ?? $scope.$parent.panel;\n this.dashboard = this.dashboard ?? $scope.$parent.dashboard;\n this.$injector = $injector;\n this.$scope = $scope;\n this.$timeout = $injector.get('$timeout');\n this.editorTabs = [];\n this.$location = new AngularLocationWrapper();\n this.events = new EventBusSrv();\n this.timing = {}; // not used but here to not break plugins\n\n const plugin = config.panels[this.panel.type];\n if (plugin) {\n this.pluginId = plugin.id;\n this.pluginName = plugin.name;\n }\n\n $scope.$on(PanelEvents.componentDidMount.name, () => this.panelDidMount());\n }\n\n panelDidMount() {\n this.events.emit(PanelEvents.componentDidMount);\n this.events.emit(PanelEvents.initialized);\n this.dashboard.panelInitialized(this.panel);\n }\n\n renderingCompleted() {\n profiler.renderingCompleted();\n }\n\n refresh() {\n this.panel.refresh();\n }\n\n publishAppEvent(event: AppEvent, payload?: T) {\n this.$scope.$root.appEvent(event, payload);\n }\n\n initEditMode() {\n if (!this.editModeInitiated) {\n this.editModeInitiated = true;\n this.events.emit(PanelEvents.editModeInitialized);\n }\n }\n\n addEditorTab(title: string, directiveFn: any, index?: number, icon?: any) {\n const editorTab = { title, directiveFn, icon };\n\n if (isString(directiveFn)) {\n editorTab.directiveFn = () => {\n return { templateUrl: directiveFn };\n };\n }\n\n if (index) {\n this.editorTabs.splice(index, 0, editorTab);\n } else {\n this.editorTabs.push(editorTab);\n }\n }\n\n getExtendedMenu() {\n const menu: AngularPanelMenuItem[] = [];\n this.events.emit(PanelEvents.initPanelActions, menu);\n return menu;\n }\n\n // Override in sub-class to add items before extended menu\n async getAdditionalMenuItems(): Promise {\n return [];\n }\n\n otherPanelInFullscreenMode() {\n return this.dashboard.otherPanelInFullscreen(this.panel);\n }\n\n render(payload?: any) {\n this.events.emit(PanelEvents.render, payload);\n }\n\n // overriden from react\n onPluginTypeChange = (plugin: PanelPluginMeta) => {};\n}\n","import { auto } from 'angular';\nimport { indexOf } from 'lodash';\n\nexport class QueryCtrl {\n target!: T;\n datasource!: any;\n panelCtrl!: any;\n panel: any;\n hasRawMode!: boolean;\n error?: string | null;\n isLastQuery: boolean;\n\n constructor(\n public $scope: any,\n public $injector: auto.IInjectorService\n ) {\n this.panelCtrl = this.panelCtrl ?? $scope.ctrl.panelCtrl;\n this.target = this.target ?? $scope.ctrl.target;\n this.datasource = this.datasource ?? $scope.ctrl.datasource;\n this.panel = this.panelCtrl?.panel ?? $scope.ctrl.panelCtrl.panel;\n this.isLastQuery = indexOf(this.panel.targets, this.target) === this.panel.targets.length - 1;\n }\n\n refresh() {\n this.panelCtrl.refresh();\n }\n}\n","let templates = (require as any).context('../', true, /\\.html$/);\ntemplates.keys().forEach((key: string) => {\n templates(key);\n});\n","import { IScope } from 'angular';\n\nexport const promiseToDigest = ($scope: IScope) => (promise: Promise) => promise.finally($scope.$evalAsync);\n","import { makeClassES5Compatible } from '@grafana/data';\nimport { loadPluginCss } from '@grafana/runtime';\nimport { MetricsPanelCtrl as MetricsPanelCtrlES6 } from 'app/angular/panel/metrics_panel_ctrl';\nimport { PanelCtrl as PanelCtrlES6 } from 'app/angular/panel/panel_ctrl';\nimport { QueryCtrl as QueryCtrlES6 } from 'app/angular/panel/query_ctrl';\n\nconst PanelCtrl = makeClassES5Compatible(PanelCtrlES6);\nconst MetricsPanelCtrl = makeClassES5Compatible(MetricsPanelCtrlES6);\nconst QueryCtrl = makeClassES5Compatible(QueryCtrlES6);\n\nexport { PanelCtrl, MetricsPanelCtrl, QueryCtrl, loadPluginCss };\n","(function ($) {\n \"use strict\";\n\n var defaultOptions = {\n tagClass: function(item) {\n return 'label label-info';\n },\n itemValue: function(item) {\n return item ? item.toString() : item;\n },\n itemText: function(item) {\n return this.itemValue(item);\n },\n freeInput: true,\n maxTags: undefined,\n confirmKeys: [13],\n onTagExists: function(item, $tag) {\n $tag.hide().fadeIn();\n }\n };\n\n /**\n * Constructor function\n */\n function TagsInput(element, options) {\n this.itemsArray = [];\n\n this.$element = $(element);\n this.$element.hide();\n\n this.widthClass = options.widthClass || 'width-9';\n this.isSelect = (element.tagName === 'SELECT');\n this.multiple = (this.isSelect && element.hasAttribute('multiple'));\n this.objectItems = options && options.itemValue;\n this.placeholderText = element.hasAttribute('placeholder') ? this.$element.attr('placeholder') : '';\n\n this.$container = $('
    ');\n this.$input = $('').appendTo(this.$container);\n\n this.$element.after(this.$container);\n\n this.build(options);\n }\n\n TagsInput.prototype = {\n constructor: TagsInput,\n\n /**\n * Adds the given item as a new tag. Pass true to dontPushVal to prevent\n * updating the elements val()\n */\n add: function(item, dontPushVal) {\n var self = this;\n\n if (self.options.maxTags && self.itemsArray.length >= self.options.maxTags)\n return;\n\n // Ignore falsey values, except false\n if (item !== false && !item)\n return;\n\n // Throw an error when trying to add an object while the itemValue option was not set\n if (typeof item === \"object\" && !self.objectItems)\n throw(\"Can't add objects when itemValue option is not set\");\n\n // Ignore strings only containg whitespace\n if (item.toString().match(/^\\s*$/))\n return;\n\n // If SELECT but not multiple, remove current tag\n if (self.isSelect && !self.multiple && self.itemsArray.length > 0)\n self.remove(self.itemsArray[0]);\n\n if (typeof item === \"string\" && this.$element[0].tagName === 'INPUT') {\n var items = item.split(',');\n if (items.length > 1) {\n for (var i = 0; i < items.length; i++) {\n this.add(items[i], true);\n }\n\n if (!dontPushVal)\n self.pushVal();\n return;\n }\n }\n\n var itemValue = self.options.itemValue(item),\n itemText = self.options.itemText(item),\n tagClass = self.options.tagClass(item);\n\n // Ignore items already added\n var existing = $.grep(self.itemsArray, function(item) { return self.options.itemValue(item) === itemValue; } )[0];\n if (existing) {\n // Invoke onTagExists\n if (self.options.onTagExists) {\n var $existingTag = $(\".tag\", self.$container).filter(function() { return $(this).data(\"item\") === existing; });\n self.options.onTagExists(item, $existingTag);\n }\n return;\n }\n\n // register item in internal array and map\n self.itemsArray.push(item);\n\n // add a tag element\n var $tag = $('' + htmlEncode(itemText) + '');\n $tag.data('item', item);\n self.findInputWrapper().before($tag);\n $tag.after(' ');\n\n // add