diff --git a/public/app/app.js b/public/app/app.js
index 5d4e38bab91..7bd8a086b74 100644
--- a/public/app/app.js
+++ b/public/app/app.js
@@ -73,7 +73,6 @@ function (angular, $, _, appLevelRequire) {
'services/all',
'features/all',
'controllers/all',
- 'directives/all',
'components/partials',
'routes/all',
];
diff --git a/public/app/core/core.ts b/public/app/core/core.ts
index 4883464a428..609059fc8e9 100644
--- a/public/app/core/core.ts
+++ b/public/app/core/core.ts
@@ -1,6 +1,22 @@
+///
+///
+///
+///
+///
+///
+///
+///
+///
+///
+///
+///
+///
+///
+///
+///
export * from './directives/array_join'
-export * from './directives/giveFocus'
+export * from './directives/give_focus'
export * from './routes/module_loader'
export * from './filters/filters'
diff --git a/public/app/core/core_module.ts b/public/app/core/core_module.ts
index 5de6eb1221f..1f6a02c79d9 100644
--- a/public/app/core/core_module.ts
+++ b/public/app/core/core_module.ts
@@ -2,4 +2,4 @@
import angular = require('angular');
-export default angular.module('grafana.core', []);
+export = angular.module('grafana.core', []);
diff --git a/public/app/directives/annotationTooltip.js b/public/app/core/directives/annotation_tooltip.js
similarity index 88%
rename from public/app/directives/annotationTooltip.js
rename to public/app/core/directives/annotation_tooltip.js
index f75f05a6715..c79ca97189c 100644
--- a/public/app/directives/annotationTooltip.js
+++ b/public/app/core/directives/annotation_tooltip.js
@@ -1,14 +1,12 @@
define([
- 'angular',
'jquery',
- 'lodash'
+ 'lodash',
+ '../core_module',
],
-function (angular, $, _) {
+function ($, _, coreModule) {
'use strict';
- angular
- .module('grafana.directives')
- .directive('annotationTooltip', function($sanitize, dashboardSrv, $compile) {
+ coreModule.directive('annotationTooltip', function($sanitize, dashboardSrv, $compile) {
function sanitizeString(str) {
try {
diff --git a/public/app/core/directives/array_join.ts b/public/app/core/directives/array_join.ts
index ff27a0e9e25..b9fb15406f0 100644
--- a/public/app/core/directives/array_join.ts
+++ b/public/app/core/directives/array_join.ts
@@ -2,7 +2,7 @@
import angular = require('angular');
import _ = require('lodash');
-import coreModule from '../core_module';
+import coreModule = require('../core_module');
export function arrayJoin() {
'use strict';
diff --git a/public/app/core/directives/body_class.js b/public/app/core/directives/body_class.js
new file mode 100644
index 00000000000..f9e748ea6c2
--- /dev/null
+++ b/public/app/core/directives/body_class.js
@@ -0,0 +1,48 @@
+define([
+ 'lodash',
+ 'jquery',
+ '../core_module',
+],
+function (_, $, coreModule) {
+ 'use strict';
+
+ coreModule.directive('bodyClass', function() {
+ return {
+ link: function($scope, elem) {
+
+ var lastHideControlsVal;
+
+ // tooltip removal fix
+ $scope.$on("$routeChangeSuccess", function() {
+ $("#tooltip, .tooltip").remove();
+ });
+
+ $scope.$watch('submenuEnabled', function() {
+ if (!$scope.dashboard) {
+ return;
+ }
+
+ elem.toggleClass('submenu-controls-visible', $scope.submenuEnabled);
+ });
+
+ $scope.$watch('dashboard.hideControls', function() {
+ if (!$scope.dashboard) {
+ return;
+ }
+
+ var hideControls = $scope.dashboard.hideControls || $scope.playlist_active;
+
+ if (lastHideControlsVal !== hideControls) {
+ elem.toggleClass('hide-controls', hideControls);
+ lastHideControlsVal = hideControls;
+ }
+ });
+
+ $scope.$watch('playlistSrv', function(newValue) {
+ elem.toggleClass('playlist-active', _.isObject(newValue));
+ });
+ }
+ };
+ });
+
+});
diff --git a/public/app/core/directives/config_modal.js b/public/app/core/directives/config_modal.js
new file mode 100644
index 00000000000..36129b0cc96
--- /dev/null
+++ b/public/app/core/directives/config_modal.js
@@ -0,0 +1,46 @@
+define([
+ 'lodash',
+ 'jquery',
+ '../core_module',
+],
+function (_, $, coreModule) {
+ 'use strict';
+
+ coreModule.directive('configModal', function($modal, $q, $timeout) {
+ return {
+ restrict: 'A',
+ link: function(scope, elem, attrs) {
+ var partial = attrs.configModal;
+ var id = '#' + partial.replace('.html', '').replace(/[\/|\.|:]/g, '-') + '-' + scope.$id;
+
+ elem.bind('click',function() {
+ if ($(id).length) {
+ elem.attr('data-target', id).attr('data-toggle', 'modal');
+ scope.$apply(function() { scope.$broadcast('modal-opened'); });
+ return;
+ }
+
+ var panelModal = $modal({
+ template: partial,
+ persist: false,
+ show: false,
+ scope: scope.$new(),
+ keyboard: false
+ });
+
+ $q.when(panelModal).then(function(modalEl) {
+ elem.attr('data-target', id).attr('data-toggle', 'modal');
+
+ $timeout(function () {
+ if (!modalEl.data('modal').isShown) {
+ modalEl.modal('show');
+ }
+ }, 50);
+ });
+
+ scope.$apply();
+ });
+ }
+ };
+ });
+});
diff --git a/public/app/directives/confirmClick.js b/public/app/core/directives/confirm_click.js
similarity index 75%
rename from public/app/directives/confirmClick.js
rename to public/app/core/directives/confirm_click.js
index a3fe1af29b4..f73a847af45 100644
--- a/public/app/directives/confirmClick.js
+++ b/public/app/core/directives/confirm_click.js
@@ -1,13 +1,10 @@
define([
- 'angular',
- 'kbn'
+ '../core_module',
],
-function (angular) {
+function (coreModule) {
'use strict';
- var module = angular.module('grafana.directives');
-
- module.directive('confirmClick', function() {
+ coreModule.directive('confirmClick', function() {
return {
restrict: 'A',
link: function(scope, elem, attrs) {
@@ -23,4 +20,4 @@ function (angular) {
},
};
});
-});
\ No newline at end of file
+});
diff --git a/public/app/core/directives/dash_edit_link.js b/public/app/core/directives/dash_edit_link.js
new file mode 100644
index 00000000000..9eaa3ab6287
--- /dev/null
+++ b/public/app/core/directives/dash_edit_link.js
@@ -0,0 +1,103 @@
+define([
+ 'jquery',
+ '../core_module',
+],
+function ($, coreModule) {
+ 'use strict';
+
+ var editViewMap = {
+ 'settings': { src: 'app/features/dashboard/partials/settings.html', title: "Settings" },
+ 'annotations': { src: 'app/features/annotations/partials/editor.html', title: "Annotations" },
+ 'templating': { src: 'app/features/templating/partials/editor.html', title: "Templating" }
+ };
+
+ coreModule.directive('dashEditorLink', function($timeout) {
+ return {
+ restrict: 'A',
+ link: function(scope, elem, attrs) {
+ var partial = attrs.dashEditorLink;
+
+ elem.bind('click',function() {
+ $timeout(function() {
+ var editorScope = attrs.editorScope === 'isolated' ? null : scope;
+ scope.appEvent('show-dash-editor', { src: partial, scope: editorScope });
+ });
+ });
+ }
+ };
+ });
+
+ coreModule.directive('dashEditorView', function($compile, $location) {
+ return {
+ restrict: 'A',
+ link: function(scope, elem) {
+ var editorScope;
+ var lastEditor;
+
+ function hideEditorPane() {
+ if (editorScope) { editorScope.dismiss(); }
+ }
+
+ function showEditorPane(evt, payload, editview) {
+ if (editview) {
+ scope.contextSrv.editview = editViewMap[editview];
+ payload.src = scope.contextSrv.editview.src;
+ }
+
+ if (lastEditor === payload.src) {
+ hideEditorPane();
+ return;
+ }
+
+ hideEditorPane();
+
+ scope.exitFullscreen();
+
+ lastEditor = payload.src;
+ editorScope = payload.scope ? payload.scope.$new() : scope.$new();
+
+ editorScope.dismiss = function() {
+ editorScope.$destroy();
+ elem.empty();
+ lastEditor = null;
+ editorScope = null;
+
+ if (editview) {
+ var urlParams = $location.search();
+ if (editview === urlParams.editview) {
+ delete urlParams.editview;
+ $location.search(urlParams);
+ }
+ }
+ };
+
+ var src = "'" + payload.src + "'";
+ var view = $('
' +
+ '
' +
+ '
' +
+ '
';
+ elem.replaceWith($compile(angular.element(template))(scope));
+ }
+ };
+ });
+
+ coreModule.directive('editorCheckbox', function($compile, $interpolate) {
+ return {
+ restrict: 'E',
+ link: function(scope, elem, attrs) {
+ var text = $interpolate(attrs.text)(scope);
+ var model = $interpolate(attrs.model)(scope);
+ var ngchange = attrs.change ? (' ng-change="' + attrs.change + '"') : '';
+ var tip = attrs.tip ? ('
' + attrs.tip + '') : '';
+ var label = '
';
+
+ var template = '
' +
+ '
';
+
+ template = label + template;
+ elem.replaceWith($compile(angular.element(template))(scope));
+ }
+ };
+ });
+
+ coreModule.directive('gfDropdown', function ($parse, $compile, $timeout) {
+ function buildTemplate(items, placement) {
+ var upclass = placement === 'top' ? 'dropup' : '';
+ var ul = [
+ ''
+ ];
+
+ angular.forEach(items, function (item, index) {
+ if (item.divider) {
+ return ul.splice(index + 1, 0, '
');
+ }
+
+ var li = '';
+ ul.splice(index + 1, 0, li);
+ });
+ return ul;
+ }
+
+ return {
+ restrict: 'EA',
+ scope: true,
+ link: function postLink(scope, iElement, iAttrs) {
+ var getter = $parse(iAttrs.gfDropdown), items = getter(scope);
+ $timeout(function () {
+ var placement = iElement.data('placement');
+ var dropdown = angular.element(buildTemplate(items, placement).join(''));
+ dropdown.insertAfter(iElement);
+ $compile(iElement.next('ul.dropdown-menu'))(scope);
+ });
+
+ iElement.addClass('dropdown-toggle').attr('data-toggle', 'dropdown');
+ }
+ };
+ });
+
+});
diff --git a/public/app/core/directives/ng_model_on_blur.js b/public/app/core/directives/ng_model_on_blur.js
new file mode 100644
index 00000000000..3c0a6cd70de
--- /dev/null
+++ b/public/app/core/directives/ng_model_on_blur.js
@@ -0,0 +1,54 @@
+define([
+ 'kbn',
+ '../core_module',
+],
+function (kbn, coreModule) {
+ 'use strict';
+
+ coreModule.directive('ngModelOnblur', function() {
+ return {
+ restrict: 'A',
+ priority: 1,
+ require: 'ngModel',
+ link: function(scope, elm, attr, ngModelCtrl) {
+ if (attr.type === 'radio' || attr.type === 'checkbox') {
+ return;
+ }
+
+ elm.off('input keydown change');
+ elm.bind('blur', function() {
+ scope.$apply(function() {
+ ngModelCtrl.$setViewValue(elm.val());
+ });
+ });
+ }
+ };
+ });
+
+ coreModule.directive('emptyToNull', function () {
+ return {
+ restrict: 'A',
+ require: 'ngModel',
+ link: function (scope, elm, attrs, ctrl) {
+ ctrl.$parsers.push(function (viewValue) {
+ if(viewValue === "") { return null; }
+ return viewValue;
+ });
+ }
+ };
+ });
+
+ coreModule.directive('validTimeSpan', function() {
+ return {
+ require: 'ngModel',
+ link: function(scope, elm, attrs, ctrl) {
+ ctrl.$validators.integer = function(modelValue, viewValue) {
+ if (ctrl.$isEmpty(modelValue)) {
+ return true;
+ }
+ return kbn.isValidTimeSpan(viewValue);
+ };
+ }
+ };
+ });
+});
diff --git a/public/app/core/directives/password_strenght.js b/public/app/core/directives/password_strenght.js
new file mode 100644
index 00000000000..b53113f9365
--- /dev/null
+++ b/public/app/core/directives/password_strenght.js
@@ -0,0 +1,45 @@
+define([
+ '../core_module',
+],
+function (coreModule) {
+ 'use strict';
+
+ coreModule.directive('passwordStrength', function() {
+ var template = '
' +
+ '{{strengthText}}' +
+ '
';
+ return {
+ template: template,
+ scope: {
+ password: "=",
+ },
+ link: function($scope) {
+
+ $scope.strengthClass = '';
+
+ function passwordChanged(newValue) {
+ if (!newValue) {
+ $scope.strengthText = "";
+ $scope.strengthClass = "hidden";
+ return;
+ }
+ if (newValue.length < 4) {
+ $scope.strengthText = "strength: weak sauce.";
+ $scope.strengthClass = "password-strength-bad";
+ return;
+ }
+ if (newValue.length <= 8) {
+ $scope.strengthText = "strength: you can do better.";
+ $scope.strengthClass = "password-strength-ok";
+ return;
+ }
+
+ $scope.strengthText = "strength: strong like a bull.";
+ $scope.strengthClass = "password-strength-good";
+ }
+
+ $scope.$watch("password", passwordChanged);
+ }
+ };
+ });
+});
diff --git a/public/app/core/directives/spectrum_picker.js b/public/app/core/directives/spectrum_picker.js
new file mode 100644
index 00000000000..92fc3982d7a
--- /dev/null
+++ b/public/app/core/directives/spectrum_picker.js
@@ -0,0 +1,41 @@
+define([
+ 'angular',
+ '../core_module',
+ 'spectrum',
+],
+function (angular, coreModule) {
+ 'use strict';
+
+ coreModule.directive('spectrumPicker', function() {
+ return {
+ restrict: 'E',
+ require: 'ngModel',
+ scope: false,
+ replace: true,
+ template: "
",
+ link: function(scope, element, attrs, ngModel) {
+ var input = element.find('input');
+ var options = angular.extend({
+ showAlpha: true,
+ showButtons: false,
+ color: ngModel.$viewValue,
+ change: function(color) {
+ scope.$apply(function() {
+ ngModel.$setViewValue(color.toRgbString());
+ });
+ }
+ }, scope.$eval(attrs.options));
+
+ ngModel.$render = function() {
+ input.spectrum('set', ngModel.$viewValue || '');
+ };
+
+ input.spectrum(options);
+
+ scope.$on('$destroy', function() {
+ input.spectrum('destroy');
+ });
+ }
+ };
+ });
+});
diff --git a/public/app/directives/tags.js b/public/app/core/directives/tags.js
similarity index 94%
rename from public/app/directives/tags.js
rename to public/app/core/directives/tags.js
index f408a5e3864..01f4c4f3c37 100644
--- a/public/app/directives/tags.js
+++ b/public/app/core/directives/tags.js
@@ -1,9 +1,10 @@
define([
'angular',
'jquery',
- 'bootstrap-tagsinput'
+ '../core_module',
+ 'bootstrap-tagsinput',
],
-function (angular, $) {
+function (angular, $, coreModule) {
'use strict';
function djb2(str) {
@@ -38,9 +39,7 @@ function (angular, $) {
element.css("border-color", borderColor);
}
- angular
- .module('grafana.directives')
- .directive('tagColorFromName', function() {
+ coreModule.directive('tagColorFromName', function() {
return {
scope: { tagColorFromName: "=" },
link: function (scope, element) {
@@ -49,9 +48,7 @@ function (angular, $) {
};
});
- angular
- .module('grafana.directives')
- .directive('bootstrapTagsinput', function() {
+ coreModule.directive('bootstrapTagsinput', function() {
function getItemProperty(scope, property) {
if (!property) {
diff --git a/public/app/core/directives/topnav.js b/public/app/core/directives/topnav.js
new file mode 100644
index 00000000000..38aa9a9e6b3
--- /dev/null
+++ b/public/app/core/directives/topnav.js
@@ -0,0 +1,50 @@
+define([
+ '../core_module',
+],
+function (coreModule) {
+ 'use strict';
+
+ coreModule.directive('topnav', function($rootScope, contextSrv) {
+ return {
+ restrict: 'E',
+ transclude: true,
+ scope: {
+ title: "@",
+ section: "@",
+ titleAction: "&",
+ subnav: "=",
+ },
+ template:
+ '
' +
+ '
' +
+ '' +
+
+ '
' +
+ '' +
+ '' +
+
+ '
' +
+ '{{section}}' +
+ '' +
+ '' +
+
+ '
' +
+ '{{title}}' +
+ '' +
+ '
' +
+ '
',
+ link: function(scope, elem, attrs) {
+ scope.icon = attrs.icon;
+ scope.contextSrv = contextSrv;
+
+ scope.toggle = function() {
+ $rootScope.appEvent('toggle-sidemenu');
+ };
+ }
+ };
+ });
+
+});
diff --git a/public/app/core/directives/value_select_dropdown.js b/public/app/core/directives/value_select_dropdown.js
new file mode 100644
index 00000000000..4f5076abd2f
--- /dev/null
+++ b/public/app/core/directives/value_select_dropdown.js
@@ -0,0 +1,284 @@
+define([
+ 'angular',
+ 'lodash',
+ '../core_module',
+],
+function (angular, _, coreModule) {
+ 'use strict';
+
+ coreModule.controller('ValueSelectDropdownCtrl', function($q) {
+ var vm = this;
+
+ vm.show = function() {
+ vm.oldVariableText = vm.variable.current.text;
+ vm.highlightIndex = -1;
+
+ vm.options = vm.variable.options;
+ vm.selectedValues = _.filter(vm.options, {selected: true});
+
+ vm.tags = _.map(vm.variable.tags, function(value) {
+ var tag = { text: value, selected: false };
+ _.each(vm.variable.current.tags, function(tagObj) {
+ if (tagObj.text === value) {
+ tag = tagObj;
+ }
+ });
+ return tag;
+ });
+
+ vm.search = {
+ query: '',
+ options: vm.options.slice(0, Math.min(vm.options.length, 1000))
+ };
+
+ vm.dropdownVisible = true;
+ };
+
+ vm.updateLinkText = function() {
+ var current = vm.variable.current;
+
+ if (current.tags && current.tags.length) {
+ // filer out values that are in selected tags
+ var selectedAndNotInTag = _.filter(vm.variable.options, function(option) {
+ if (!option.selected) { return false; }
+ for (var i = 0; i < current.tags.length; i++) {
+ var tag = current.tags[i];
+ if (_.indexOf(tag.values, option.value) !== -1) {
+ return false;
+ }
+ }
+ return true;
+ });
+
+ // convert values to text
+ var currentTexts = _.pluck(selectedAndNotInTag, 'text');
+
+ // join texts
+ vm.linkText = currentTexts.join(' + ');
+ if (vm.linkText.length > 0) {
+ vm.linkText += ' + ';
+ }
+ } else {
+ vm.linkText = vm.variable.current.text;
+ }
+ };
+
+ vm.clearSelections = function() {
+ _.each(vm.options, function(option) {
+ option.selected = false;
+ });
+
+ vm.selectionsChanged(false);
+ };
+
+ vm.selectTag = function(tag) {
+ tag.selected = !tag.selected;
+ var tagValuesPromise;
+ if (!tag.values) {
+ tagValuesPromise = vm.getValuesForTag({tagKey: tag.text});
+ } else {
+ tagValuesPromise = $q.when(tag.values);
+ }
+
+ tagValuesPromise.then(function(values) {
+ tag.values = values;
+ tag.valuesText = values.join(' + ');
+ _.each(vm.options, function(option) {
+ if (_.indexOf(tag.values, option.value) !== -1) {
+ option.selected = tag.selected;
+ }
+ });
+
+ vm.selectionsChanged(false);
+ });
+ };
+
+ vm.keyDown = function (evt) {
+ if (evt.keyCode === 27) {
+ vm.hide();
+ }
+ if (evt.keyCode === 40) {
+ vm.moveHighlight(1);
+ }
+ if (evt.keyCode === 38) {
+ vm.moveHighlight(-1);
+ }
+ if (evt.keyCode === 13) {
+ if (vm.search.options.length === 0) {
+ vm.commitChanges();
+ } else {
+ vm.selectValue(vm.search.options[vm.highlightIndex], {}, true, false);
+ }
+ }
+ if (evt.keyCode === 32) {
+ vm.selectValue(vm.search.options[vm.highlightIndex], {}, false, false);
+ }
+ };
+
+ vm.moveHighlight = function(direction) {
+ vm.highlightIndex = (vm.highlightIndex + direction) % vm.search.options.length;
+ };
+
+ vm.selectValue = function(option, event, commitChange, excludeOthers) {
+ if (!option) { return; }
+
+ option.selected = !option.selected;
+
+ commitChange = commitChange || false;
+ excludeOthers = excludeOthers || false;
+
+ var setAllExceptCurrentTo = function(newValue) {
+ _.each(vm.options, function(other) {
+ if (option !== other) { other.selected = newValue; }
+ });
+ };
+
+ // commit action (enter key), should not deselect it
+ if (commitChange) {
+ option.selected = true;
+ }
+
+ if (option.text === 'All' || excludeOthers) {
+ setAllExceptCurrentTo(false);
+ commitChange = true;
+ }
+ else if (!vm.variable.multi) {
+ setAllExceptCurrentTo(false);
+ commitChange = true;
+ } else if (event.ctrlKey || event.metaKey || event.shiftKey) {
+ commitChange = true;
+ setAllExceptCurrentTo(false);
+ }
+
+ vm.selectionsChanged(commitChange);
+ };
+
+ vm.selectionsChanged = function(commitChange) {
+ vm.selectedValues = _.filter(vm.options, {selected: true});
+
+ if (vm.selectedValues.length > 1 && vm.selectedValues.length !== vm.options.length) {
+ if (vm.selectedValues[0].text === 'All') {
+ vm.selectedValues[0].selected = false;
+ vm.selectedValues = vm.selectedValues.slice(1, vm.selectedValues.length);
+ }
+ }
+
+ // validate selected tags
+ _.each(vm.tags, function(tag) {
+ if (tag.selected) {
+ _.each(tag.values, function(value) {
+ if (!_.findWhere(vm.selectedValues, {value: value})) {
+ tag.selected = false;
+ }
+ });
+ }
+ });
+
+ vm.selectedTags = _.filter(vm.tags, {selected: true});
+ vm.variable.current.value = _.pluck(vm.selectedValues, 'value');
+ vm.variable.current.text = _.pluck(vm.selectedValues, 'text').join(' + ');
+ vm.variable.current.tags = vm.selectedTags;
+
+ // only single value
+ if (vm.selectedValues.length === 1) {
+ vm.variable.current.value = vm.selectedValues[0].value;
+ }
+
+ if (commitChange) {
+ vm.commitChanges();
+ }
+ };
+
+ vm.commitChanges = function() {
+ // if we have a search query and no options use that
+ if (vm.search.options.length === 0 && vm.search.query.length > 0) {
+ vm.variable.current = {text: vm.search.query, value: vm.search.query};
+ }
+ else if (vm.selectedValues.length === 0) {
+ // make sure one option is selected
+ vm.options[0].selected = true;
+ vm.selectionsChanged(false);
+ }
+
+ vm.dropdownVisible = false;
+ vm.updateLinkText();
+
+ if (vm.variable.current.text !== vm.oldVariableText) {
+ vm.onUpdated();
+ }
+ };
+
+ vm.queryChanged = function() {
+ vm.highlightIndex = -1;
+ vm.search.options = _.filter(vm.options, function(option) {
+ return option.text.toLowerCase().indexOf(vm.search.query.toLowerCase()) !== -1;
+ });
+
+ vm.search.options = vm.search.options.slice(0, Math.min(vm.search.options.length, 1000));
+ };
+
+ vm.init = function() {
+ vm.selectedTags = vm.variable.current.tags || [];
+ vm.updateLinkText();
+ };
+
+ });
+
+ coreModule.directive('valueSelectDropdown', function($compile, $window, $timeout, $rootScope) {
+ return {
+ scope: { variable: "=", onUpdated: "&", getValuesForTag: "&" },
+ templateUrl: 'app/partials/valueSelectDropdown.html',
+ controller: 'ValueSelectDropdownCtrl',
+ controllerAs: 'vm',
+ bindToController: true,
+ link: function(scope, elem) {
+ var bodyEl = angular.element($window.document.body);
+ var linkEl = elem.find('.variable-value-link');
+ var inputEl = elem.find('input');
+
+ function openDropdown() {
+ inputEl.css('width', Math.max(linkEl.width(), 30) + 'px');
+
+ inputEl.show();
+ linkEl.hide();
+
+ inputEl.focus();
+ $timeout(function() { bodyEl.on('click', bodyOnClick); }, 0, false);
+ }
+
+ function switchToLink() {
+ inputEl.hide();
+ linkEl.show();
+ bodyEl.off('click', bodyOnClick);
+ }
+
+ function bodyOnClick (e) {
+ if (elem.has(e.target).length === 0) {
+ scope.$apply(function() {
+ scope.vm.commitChanges();
+ });
+ }
+ }
+
+ scope.$watch('vm.dropdownVisible', function(newValue) {
+ if (newValue) {
+ openDropdown();
+ } else {
+ switchToLink();
+ }
+ });
+
+ var cleanUp = $rootScope.$on('template-variable-value-updated', function() {
+ scope.vm.updateLinkText();
+ });
+
+ scope.$on("$destroy", function() {
+ cleanUp();
+ });
+
+ scope.vm.init();
+ },
+ };
+ });
+
+});
diff --git a/public/app/core/filters/filters.ts b/public/app/core/filters/filters.ts
index 333ecd545f6..7ebba903686 100644
--- a/public/app/core/filters/filters.ts
+++ b/public/app/core/filters/filters.ts
@@ -4,7 +4,7 @@ import angular = require('angular');
import jquery = require('jquery');
import moment = require('moment');
import _ = require('lodash');
-import coreModule from '../core_module';
+import coreModule = require('../core_module');
coreModule.filter('stringSort', function() {
return function(input) {
diff --git a/public/app/directives/all.js b/public/app/directives/all.js
deleted file mode 100644
index b724c6d2bc4..00000000000
--- a/public/app/directives/all.js
+++ /dev/null
@@ -1,18 +0,0 @@
-define([
- './dashUpload',
- './dashEditLink',
- './ngModelOnBlur',
- './misc',
- './confirmClick',
- './configModal',
- './spectrumPicker',
- './tags',
- './bodyClass',
- './valueSelectDropdown',
- './metric.segment',
- './grafanaVersionCheck',
- './dropdown.typeahead',
- './topnav',
- './annotationTooltip',
- './passwordStrenght',
-], function () {});
diff --git a/public/app/directives/bodyClass.js b/public/app/directives/bodyClass.js
deleted file mode 100644
index 495390a59e8..00000000000
--- a/public/app/directives/bodyClass.js
+++ /dev/null
@@ -1,50 +0,0 @@
-define([
- 'angular',
- 'lodash',
- 'jquery'
-],
-function (angular, _, $) {
- 'use strict';
-
- angular
- .module('grafana.directives')
- .directive('bodyClass', function() {
- return {
- link: function($scope, elem) {
-
- var lastHideControlsVal;
-
- // tooltip removal fix
- $scope.$on("$routeChangeSuccess", function() {
- $("#tooltip, .tooltip").remove();
- });
-
- $scope.$watch('submenuEnabled', function() {
- if (!$scope.dashboard) {
- return;
- }
-
- elem.toggleClass('submenu-controls-visible', $scope.submenuEnabled);
- });
-
- $scope.$watch('dashboard.hideControls', function() {
- if (!$scope.dashboard) {
- return;
- }
-
- var hideControls = $scope.dashboard.hideControls || $scope.playlist_active;
-
- if (lastHideControlsVal !== hideControls) {
- elem.toggleClass('hide-controls', hideControls);
- lastHideControlsVal = hideControls;
- }
- });
-
- $scope.$watch('playlistSrv', function(newValue) {
- elem.toggleClass('playlist-active', _.isObject(newValue));
- });
- }
- };
- });
-
-});
diff --git a/public/app/directives/configModal.js b/public/app/directives/configModal.js
deleted file mode 100644
index 35a70173798..00000000000
--- a/public/app/directives/configModal.js
+++ /dev/null
@@ -1,48 +0,0 @@
-define([
- 'angular',
- 'lodash',
- 'jquery'
-],
-function (angular, _, $) {
- 'use strict';
-
- angular
- .module('grafana.directives')
- .directive('configModal', function($modal, $q, $timeout) {
- return {
- restrict: 'A',
- link: function(scope, elem, attrs) {
- var partial = attrs.configModal;
- var id = '#' + partial.replace('.html', '').replace(/[\/|\.|:]/g, '-') + '-' + scope.$id;
-
- elem.bind('click',function() {
- if ($(id).length) {
- elem.attr('data-target', id).attr('data-toggle', 'modal');
- scope.$apply(function() { scope.$broadcast('modal-opened'); });
- return;
- }
-
- var panelModal = $modal({
- template: partial,
- persist: false,
- show: false,
- scope: scope.$new(),
- keyboard: false
- });
-
- $q.when(panelModal).then(function(modalEl) {
- elem.attr('data-target', id).attr('data-toggle', 'modal');
-
- $timeout(function () {
- if (!modalEl.data('modal').isShown) {
- modalEl.modal('show');
- }
- }, 50);
- });
-
- scope.$apply();
- });
- }
- };
- });
-});
diff --git a/public/app/directives/dashEditLink.js b/public/app/directives/dashEditLink.js
deleted file mode 100644
index f184af76f20..00000000000
--- a/public/app/directives/dashEditLink.js
+++ /dev/null
@@ -1,107 +0,0 @@
-define([
- 'angular',
- 'jquery'
-],
-function (angular, $) {
- 'use strict';
-
- var editViewMap = {
- 'settings': { src: 'app/features/dashboard/partials/settings.html', title: "Settings" },
- 'annotations': { src: 'app/features/annotations/partials/editor.html', title: "Annotations" },
- 'templating': { src: 'app/features/templating/partials/editor.html', title: "Templating" }
- };
-
- angular
- .module('grafana.directives')
- .directive('dashEditorLink', function($timeout) {
- return {
- restrict: 'A',
- link: function(scope, elem, attrs) {
- var partial = attrs.dashEditorLink;
-
- elem.bind('click',function() {
- $timeout(function() {
- var editorScope = attrs.editorScope === 'isolated' ? null : scope;
- scope.appEvent('show-dash-editor', { src: partial, scope: editorScope });
- });
- });
- }
- };
- });
-
- angular
- .module('grafana.directives')
- .directive('dashEditorView', function($compile, $location) {
- return {
- restrict: 'A',
- link: function(scope, elem) {
- var editorScope;
- var lastEditor;
-
- function hideEditorPane() {
- if (editorScope) { editorScope.dismiss(); }
- }
-
- function showEditorPane(evt, payload, editview) {
- if (editview) {
- scope.contextSrv.editview = editViewMap[editview];
- payload.src = scope.contextSrv.editview.src;
- }
-
- if (lastEditor === payload.src) {
- hideEditorPane();
- return;
- }
-
- hideEditorPane();
-
- scope.exitFullscreen();
-
- lastEditor = payload.src;
- editorScope = payload.scope ? payload.scope.$new() : scope.$new();
-
- editorScope.dismiss = function() {
- editorScope.$destroy();
- elem.empty();
- lastEditor = null;
- editorScope = null;
-
- if (editview) {
- var urlParams = $location.search();
- if (editview === urlParams.editview) {
- delete urlParams.editview;
- $location.search(urlParams);
- }
- }
- };
-
- var src = "'" + payload.src + "'";
- var view = $('
');
-
- if (payload.cssClass) {
- view.addClass(payload.cssClass);
- }
-
- elem.append(view);
- $compile(elem.contents())(editorScope);
- }
-
- scope.$watch("dashboardViewState.state.editview", function(newValue, oldValue) {
- if (newValue) {
- showEditorPane(null, {}, newValue);
- } else if (oldValue) {
- scope.contextSrv.editview = null;
- if (lastEditor === editViewMap[oldValue]) {
- hideEditorPane();
- }
- }
- });
-
- scope.contextSrv.editview = null;
- scope.$on("$destroy", hideEditorPane);
- scope.onAppEvent('hide-dash-editor', hideEditorPane);
- scope.onAppEvent('show-dash-editor', showEditorPane);
- }
- };
- });
-});
diff --git a/public/app/directives/dropdown.typeahead.js b/public/app/directives/dropdown.typeahead.js
deleted file mode 100644
index 59b59ccea3a..00000000000
--- a/public/app/directives/dropdown.typeahead.js
+++ /dev/null
@@ -1,115 +0,0 @@
-define([
- 'angular',
- 'lodash',
- 'jquery',
-],
-function (angular, _, $) {
- 'use strict';
-
- angular
- .module('grafana.directives')
- .directive('dropdownTypeahead', function($compile) {
-
- var inputTemplate = '
';
-
- var buttonTemplate = '
';
-
- return {
- scope: {
- menuItems: "=dropdownTypeahead",
- dropdownTypeaheadOnSelect: "&dropdownTypeaheadOnSelect",
- model: '=ngModel'
- },
- link: function($scope, elem, attrs) {
- var $input = $(inputTemplate);
- var $button = $(buttonTemplate);
- $input.appendTo(elem);
- $button.appendTo(elem);
-
- if (attrs.linkText) {
- $button.html(attrs.linkText);
- }
-
- if (attrs.ngModel) {
- $scope.$watch('model', function(newValue) {
- _.each($scope.menuItems, function(item) {
- _.each(item.submenu, function(subItem) {
- if (subItem.value === newValue) {
- $button.html(subItem.text);
- }
- });
- });
- });
- }
-
- var typeaheadValues = _.reduce($scope.menuItems, function(memo, value, index) {
- _.each(value.submenu, function(item, subIndex) {
- item.click = 'menuItemSelected(' + index + ',' + subIndex + ')';
- memo.push(value.text + ' ' + item.text);
- });
- return memo;
- }, []);
-
- $scope.menuItemSelected = function(index, subIndex) {
- var item = $scope.menuItems[index];
- $scope.dropdownTypeaheadOnSelect({$item: item, $subItem: item.submenu[subIndex]});
- };
-
- $input.attr('data-provide', 'typeahead');
- $input.typeahead({
- source: typeaheadValues,
- minLength: 1,
- items: 10,
- updater: function (value) {
- var result = {};
- _.each($scope.menuItems, function(menuItem) {
- _.each(menuItem.submenu, function(submenuItem) {
- if (value === (menuItem.text + ' ' + submenuItem.text)) {
- result.$item = menuItem;
- result.$subItem = submenuItem;
- }
- });
- });
-
- if (result.$item) {
- $scope.$apply(function() {
- $scope.dropdownTypeaheadOnSelect(result);
- });
- }
-
- $input.trigger('blur');
- return '';
- }
- });
-
- $button.click(function() {
- $button.hide();
- $input.show();
- $input.focus();
- });
-
- $input.keyup(function() {
- elem.toggleClass('open', $input.val() === '');
- });
-
- $input.blur(function() {
- $input.hide();
- $input.val('');
- $button.show();
- $button.focus();
- // clicking the function dropdown menu wont
- // work if you remove class at once
- setTimeout(function() {
- elem.removeClass('open');
- }, 200);
- });
-
- $compile(elem.contents())($scope);
- }
- };
- });
-});
diff --git a/public/app/directives/grafanaVersionCheck.js b/public/app/directives/grafanaVersionCheck.js
deleted file mode 100644
index c98ce269381..00000000000
--- a/public/app/directives/grafanaVersionCheck.js
+++ /dev/null
@@ -1,33 +0,0 @@
-define([
- 'angular'
-],
-function (angular) {
- 'use strict';
-
- angular
- .module('grafana.directives')
- .directive('grafanaVersionCheck', function($http, contextSrv) {
- return {
- restrict: 'A',
- link: function(scope, elem) {
- if (contextSrv.version === 'master') {
- return;
- }
-
- $http({ method: 'GET', url: 'https://grafanarel.s3.amazonaws.com/latest.json' })
- .then(function(response) {
- if (!response.data || !response.data.version) {
- return;
- }
-
- if (contextSrv.version !== response.data.version) {
- elem.append('
' +
- '
' +
- 'New version available: ' + response.data.version +
- '');
- }
- });
- }
- };
- });
-});
diff --git a/public/app/directives/metric.segment.js b/public/app/directives/metric.segment.js
deleted file mode 100644
index b9f80b919f7..00000000000
--- a/public/app/directives/metric.segment.js
+++ /dev/null
@@ -1,216 +0,0 @@
-define([
- 'angular',
- 'lodash',
- 'jquery',
-],
-function (angular, _, $) {
- 'use strict';
-
- angular
- .module('grafana.directives')
- .directive('metricSegment', function($compile, $sce) {
- var inputTemplate = '
';
-
- var buttonTemplate = '
';
-
- return {
- scope: {
- segment: "=",
- getOptions: "&",
- onChange: "&",
- },
-
- link: function($scope, elem) {
- var $input = $(inputTemplate);
- var $button = $(buttonTemplate);
- var segment = $scope.segment;
- var options = null;
- var cancelBlur = null;
-
- $input.appendTo(elem);
- $button.appendTo(elem);
-
- $scope.updateVariableValue = function(value) {
- if (value === '' || segment.value === value) {
- return;
- }
-
- $scope.$apply(function() {
- var selected = _.findWhere($scope.altSegments, { value: value });
- if (selected) {
- segment.value = selected.value;
- segment.html = selected.html;
- segment.fake = false;
- segment.expandable = selected.expandable;
- }
- else if (segment.custom !== 'false') {
- segment.value = value;
- segment.html = $sce.trustAsHtml(value);
- segment.expandable = true;
- segment.fake = false;
- }
-
- $scope.onChange();
- });
- };
-
- $scope.switchToLink = function(now) {
- if (now === true || cancelBlur) {
- clearTimeout(cancelBlur);
- cancelBlur = null;
- $input.hide();
- $button.show();
- $scope.updateVariableValue($input.val());
- }
- else {
- // need to have long delay because the blur
- // happens long before the click event on the typeahead options
- cancelBlur = setTimeout($scope.switchToLink, 100);
- }
- };
-
- $scope.source = function(query, callback) {
- if (options) { return options; }
-
- $scope.$apply(function() {
- $scope.getOptions().then(function(altSegments) {
- $scope.altSegments = altSegments;
- options = _.map($scope.altSegments, function(alt) { return alt.value; });
-
- // add custom values
- if (segment.custom !== 'false') {
- if (!segment.fake && _.indexOf(options, segment.value) === -1) {
- options.unshift(segment.value);
- }
- }
-
- callback(options);
- });
- });
- };
-
- $scope.updater = function(value) {
- if (value === segment.value) {
- clearTimeout(cancelBlur);
- $input.focus();
- return value;
- }
-
- $input.val(value);
- $scope.switchToLink(true);
-
- return value;
- };
-
- $scope.matcher = function(item) {
- var str = this.query;
- if (str[0] === '/') { str = str.substring(1); }
- if (str[str.length - 1] === '/') { str = str.substring(0, str.length-1); }
- try {
- return item.toLowerCase().match(str);
- } catch(e) {
- return false;
- }
- };
-
- $input.attr('data-provide', 'typeahead');
- $input.typeahead({ source: $scope.source, minLength: 0, items: 10000, updater: $scope.updater, matcher: $scope.matcher });
-
- var typeahead = $input.data('typeahead');
- typeahead.lookup = function () {
- this.query = this.$element.val() || '';
- var items = this.source(this.query, $.proxy(this.process, this));
- return items ? this.process(items) : items;
- };
-
- $button.keydown(function(evt) {
- // trigger typeahead on down arrow or enter key
- if (evt.keyCode === 40 || evt.keyCode === 13) {
- $button.click();
- }
- });
-
- $button.click(function() {
- options = null;
- $input.css('width', ($button.width() + 16) + 'px');
-
- $button.hide();
- $input.show();
- $input.focus();
-
- var typeahead = $input.data('typeahead');
- if (typeahead) {
- $input.val('');
- typeahead.lookup();
- }
- });
-
- $input.blur($scope.switchToLink);
-
- $compile(elem.contents())($scope);
- }
- };
- });
-
- angular
- .module('grafana.directives')
- .directive('metricSegmentModel', function(uiSegmentSrv, $q) {
- return {
- template: '
',
- restrict: 'E',
- scope: {
- property: "=",
- options: "=",
- getOptions: "&",
- onChange: "&",
- },
- link: {
- pre: function postLink($scope, elem, attrs) {
-
- $scope.valueToSegment = function(value) {
- var option = _.findWhere($scope.options, {value: value});
- var segment = {
- cssClass: attrs.cssClass,
- custom: attrs.custom,
- value: option ? option.text : value,
- };
- return uiSegmentSrv.newSegment(segment);
- };
-
- $scope.getOptionsInternal = function() {
- if ($scope.options) {
- var optionSegments = _.map($scope.options, function(option) {
- return uiSegmentSrv.newSegment({value: option.text});
- });
- return $q.when(optionSegments);
- } else {
- return $scope.getOptions();
- }
- };
-
- $scope.onSegmentChange = function() {
- if ($scope.options) {
- var option = _.findWhere($scope.options, {text: $scope.segment.value});
- if (option && option.value !== $scope.property) {
- $scope.property = option.value;
- }
- } else {
- $scope.property = $scope.segment.value;
- }
-
- // needs to call this after digest so
- // property is synced with outerscope
- $scope.$$postDigest(function() {
- $scope.onChange();
- });
- };
-
- $scope.segment = $scope.valueToSegment($scope.property);
- }
- }
- };
- });
-});
diff --git a/public/app/directives/misc.js b/public/app/directives/misc.js
deleted file mode 100644
index 6e4b83bf726..00000000000
--- a/public/app/directives/misc.js
+++ /dev/null
@@ -1,132 +0,0 @@
-define([
- 'angular',
- 'kbn'
-],
-function (angular, kbn) {
- 'use strict';
-
- angular
- .module('grafana.directives')
- .directive('tip', function($compile) {
- return {
- restrict: 'E',
- link: function(scope, elem, attrs) {
- var _t = '
';
- _t = _t.replace(/{/g, '\\{').replace(/}/g, '\\}');
- elem.replaceWith($compile(angular.element(_t))(scope));
- }
- };
- });
-
- angular
- .module('grafana.directives')
- .directive('watchChange', function() {
- return {
- scope: { onchange: '&watchChange' },
- link: function(scope, element) {
- element.on('input', function() {
- scope.$apply(function () {
- scope.onchange({ inputValue: element.val() });
- });
- });
- }
- };
- });
-
- angular
- .module('grafana.directives')
- .directive('editorOptBool', function($compile) {
- return {
- restrict: 'E',
- link: function(scope, elem, attrs) {
- var ngchange = attrs.change ? (' ng-change="' + attrs.change + '"') : '';
- var tip = attrs.tip ? ('
' + attrs.tip + '') : '';
- var showIf = attrs.showIf ? (' ng-show="' + attrs.showIf + '" ') : '';
-
- var template = '
' +
- '
' +
- '
' +
- '
';
- elem.replaceWith($compile(angular.element(template))(scope));
- }
- };
- });
-
- angular
- .module('grafana.directives')
- .directive('editorCheckbox', function($compile, $interpolate) {
- return {
- restrict: 'E',
- link: function(scope, elem, attrs) {
- var text = $interpolate(attrs.text)(scope);
- var model = $interpolate(attrs.model)(scope);
- var ngchange = attrs.change ? (' ng-change="' + attrs.change + '"') : '';
- var tip = attrs.tip ? ('
' + attrs.tip + '') : '';
- var label = '
';
-
- var template = '
' +
- '
';
-
- template = label + template;
- elem.replaceWith($compile(angular.element(template))(scope));
- }
- };
- });
-
- angular
- .module('grafana.directives')
- .directive('gfDropdown', function ($parse, $compile, $timeout) {
-
- function buildTemplate(items, placement) {
- var upclass = placement === 'top' ? 'dropup' : '';
- var ul = [
- ''
- ];
-
- angular.forEach(items, function (item, index) {
- if (item.divider) {
- return ul.splice(index + 1, 0, '
');
- }
-
- var li = '';
- ul.splice(index + 1, 0, li);
- });
- return ul;
- }
-
- return {
- restrict: 'EA',
- scope: true,
- link: function postLink(scope, iElement, iAttrs) {
- var getter = $parse(iAttrs.gfDropdown), items = getter(scope);
- $timeout(function () {
- var placement = iElement.data('placement');
- var dropdown = angular.element(buildTemplate(items, placement).join(''));
- dropdown.insertAfter(iElement);
- $compile(iElement.next('ul.dropdown-menu'))(scope);
- });
-
- iElement.addClass('dropdown-toggle').attr('data-toggle', 'dropdown');
- }
- };
- });
-
-});
diff --git a/public/app/directives/ngModelOnBlur.js b/public/app/directives/ngModelOnBlur.js
deleted file mode 100644
index 8057c071bdb..00000000000
--- a/public/app/directives/ngModelOnBlur.js
+++ /dev/null
@@ -1,54 +0,0 @@
-define([
- 'angular',
- 'kbn'
-],
-function (angular, kbn) {
- 'use strict';
-
- angular
- .module('grafana.directives')
- .directive('ngModelOnblur', function() {
- return {
- restrict: 'A',
- priority: 1,
- require: 'ngModel',
- link: function(scope, elm, attr, ngModelCtrl) {
- if (attr.type === 'radio' || attr.type === 'checkbox') {
- return;
- }
-
- elm.off('input keydown change');
- elm.bind('blur', function() {
- scope.$apply(function() {
- ngModelCtrl.$setViewValue(elm.val());
- });
- });
- }
- };
- })
- .directive('emptyToNull', function () {
- return {
- restrict: 'A',
- require: 'ngModel',
- link: function (scope, elm, attrs, ctrl) {
- ctrl.$parsers.push(function (viewValue) {
- if(viewValue === "") { return null; }
- return viewValue;
- });
- }
- };
- })
- .directive('validTimeSpan', function() {
- return {
- require: 'ngModel',
- link: function(scope, elm, attrs, ctrl) {
- ctrl.$validators.integer = function(modelValue, viewValue) {
- if (ctrl.$isEmpty(modelValue)) {
- return true;
- }
- return kbn.isValidTimeSpan(viewValue);
- };
- }
- };
- });
-});
diff --git a/public/app/directives/passwordStrenght.js b/public/app/directives/passwordStrenght.js
deleted file mode 100644
index f75a8fe8854..00000000000
--- a/public/app/directives/passwordStrenght.js
+++ /dev/null
@@ -1,47 +0,0 @@
-define([
- 'angular',
-],
-function (angular) {
- 'use strict';
-
- angular
- .module('grafana.directives')
- .directive('passwordStrength', function() {
- var template = '
' +
- '{{strengthText}}' +
- '
';
- return {
- template: template,
- scope: {
- password: "=",
- },
- link: function($scope) {
-
- $scope.strengthClass = '';
-
- function passwordChanged(newValue) {
- if (!newValue) {
- $scope.strengthText = "";
- $scope.strengthClass = "hidden";
- return;
- }
- if (newValue.length < 4) {
- $scope.strengthText = "strength: weak sauce.";
- $scope.strengthClass = "password-strength-bad";
- return;
- }
- if (newValue.length <= 8) {
- $scope.strengthText = "strength: you can do better.";
- $scope.strengthClass = "password-strength-ok";
- return;
- }
-
- $scope.strengthText = "strength: strong like a bull.";
- $scope.strengthClass = "password-strength-good";
- }
-
- $scope.$watch("password", passwordChanged);
- }
- };
- });
-});
diff --git a/public/app/directives/spectrumPicker.js b/public/app/directives/spectrumPicker.js
deleted file mode 100644
index c6d3de6f884..00000000000
--- a/public/app/directives/spectrumPicker.js
+++ /dev/null
@@ -1,42 +0,0 @@
-define([
- 'angular',
- 'spectrum'
-],
-function (angular) {
- 'use strict';
-
- angular
- .module('grafana.directives')
- .directive('spectrumPicker', function() {
- return {
- restrict: 'E',
- require: 'ngModel',
- scope: false,
- replace: true,
- template: "
",
- link: function(scope, element, attrs, ngModel) {
- var input = element.find('input');
- var options = angular.extend({
- showAlpha: true,
- showButtons: false,
- color: ngModel.$viewValue,
- change: function(color) {
- scope.$apply(function() {
- ngModel.$setViewValue(color.toRgbString());
- });
- }
- }, scope.$eval(attrs.options));
-
- ngModel.$render = function() {
- input.spectrum('set', ngModel.$viewValue || '');
- };
-
- input.spectrum(options);
-
- scope.$on('$destroy', function() {
- input.spectrum('destroy');
- });
- }
- };
- });
-});
diff --git a/public/app/directives/topnav.js b/public/app/directives/topnav.js
deleted file mode 100644
index 88b98d55984..00000000000
--- a/public/app/directives/topnav.js
+++ /dev/null
@@ -1,53 +0,0 @@
-define([
- 'angular',
- 'kbn'
-],
-function (angular) {
- 'use strict';
-
- angular
- .module('grafana.directives')
- .directive('topnav', function($rootScope, contextSrv) {
- return {
- restrict: 'E',
- transclude: true,
- scope: {
- title: "@",
- section: "@",
- titleAction: "&",
- subnav: "=",
- },
- template:
- '
' +
- '
' +
- '' +
-
- '
' +
- '' +
- '' +
-
- '
' +
- '{{section}}' +
- '' +
- '' +
-
- '
' +
- '{{title}}' +
- '' +
- '
' +
- '
',
- link: function(scope, elem, attrs) {
- scope.icon = attrs.icon;
- scope.contextSrv = contextSrv;
-
- scope.toggle = function() {
- $rootScope.appEvent('toggle-sidemenu');
- };
- }
- };
- });
-
-});
diff --git a/public/app/directives/valueSelectDropdown.js b/public/app/directives/valueSelectDropdown.js
deleted file mode 100644
index f54381b442b..00000000000
--- a/public/app/directives/valueSelectDropdown.js
+++ /dev/null
@@ -1,289 +0,0 @@
-define([
- 'angular',
- 'lodash',
- 'jquery',
-],
-function (angular, _) {
- 'use strict';
-
- angular
- .module('grafana.controllers')
- .controller('ValueSelectDropdownCtrl', function($q) {
- var vm = this;
-
- vm.show = function() {
- vm.oldVariableText = vm.variable.current.text;
- vm.highlightIndex = -1;
-
- vm.options = vm.variable.options;
- vm.selectedValues = _.filter(vm.options, {selected: true});
-
- vm.tags = _.map(vm.variable.tags, function(value) {
- var tag = { text: value, selected: false };
- _.each(vm.variable.current.tags, function(tagObj) {
- if (tagObj.text === value) {
- tag = tagObj;
- }
- });
- return tag;
- });
-
- vm.search = {
- query: '',
- options: vm.options.slice(0, Math.min(vm.options.length, 1000))
- };
-
- vm.dropdownVisible = true;
- };
-
- vm.updateLinkText = function() {
- var current = vm.variable.current;
-
- if (current.tags && current.tags.length) {
- // filer out values that are in selected tags
- var selectedAndNotInTag = _.filter(vm.variable.options, function(option) {
- if (!option.selected) { return false; }
- for (var i = 0; i < current.tags.length; i++) {
- var tag = current.tags[i];
- if (_.indexOf(tag.values, option.value) !== -1) {
- return false;
- }
- }
- return true;
- });
-
- // convert values to text
- var currentTexts = _.pluck(selectedAndNotInTag, 'text');
-
- // join texts
- vm.linkText = currentTexts.join(' + ');
- if (vm.linkText.length > 0) {
- vm.linkText += ' + ';
- }
- } else {
- vm.linkText = vm.variable.current.text;
- }
- };
-
- vm.clearSelections = function() {
- _.each(vm.options, function(option) {
- option.selected = false;
- });
-
- vm.selectionsChanged(false);
- };
-
- vm.selectTag = function(tag) {
- tag.selected = !tag.selected;
- var tagValuesPromise;
- if (!tag.values) {
- tagValuesPromise = vm.getValuesForTag({tagKey: tag.text});
- } else {
- tagValuesPromise = $q.when(tag.values);
- }
-
- tagValuesPromise.then(function(values) {
- tag.values = values;
- tag.valuesText = values.join(' + ');
- _.each(vm.options, function(option) {
- if (_.indexOf(tag.values, option.value) !== -1) {
- option.selected = tag.selected;
- }
- });
-
- vm.selectionsChanged(false);
- });
- };
-
- vm.keyDown = function (evt) {
- if (evt.keyCode === 27) {
- vm.hide();
- }
- if (evt.keyCode === 40) {
- vm.moveHighlight(1);
- }
- if (evt.keyCode === 38) {
- vm.moveHighlight(-1);
- }
- if (evt.keyCode === 13) {
- if (vm.search.options.length === 0) {
- vm.commitChanges();
- } else {
- vm.selectValue(vm.search.options[vm.highlightIndex], {}, true, false);
- }
- }
- if (evt.keyCode === 32) {
- vm.selectValue(vm.search.options[vm.highlightIndex], {}, false, false);
- }
- };
-
- vm.moveHighlight = function(direction) {
- vm.highlightIndex = (vm.highlightIndex + direction) % vm.search.options.length;
- };
-
- vm.selectValue = function(option, event, commitChange, excludeOthers) {
- if (!option) { return; }
-
- option.selected = !option.selected;
-
- commitChange = commitChange || false;
- excludeOthers = excludeOthers || false;
-
- var setAllExceptCurrentTo = function(newValue) {
- _.each(vm.options, function(other) {
- if (option !== other) { other.selected = newValue; }
- });
- };
-
- // commit action (enter key), should not deselect it
- if (commitChange) {
- option.selected = true;
- }
-
- if (option.text === 'All' || excludeOthers) {
- setAllExceptCurrentTo(false);
- commitChange = true;
- }
- else if (!vm.variable.multi) {
- setAllExceptCurrentTo(false);
- commitChange = true;
- } else if (event.ctrlKey || event.metaKey || event.shiftKey) {
- commitChange = true;
- setAllExceptCurrentTo(false);
- }
-
- vm.selectionsChanged(commitChange);
- };
-
- vm.selectionsChanged = function(commitChange) {
- vm.selectedValues = _.filter(vm.options, {selected: true});
-
- if (vm.selectedValues.length > 1 && vm.selectedValues.length !== vm.options.length) {
- if (vm.selectedValues[0].text === 'All') {
- vm.selectedValues[0].selected = false;
- vm.selectedValues = vm.selectedValues.slice(1, vm.selectedValues.length);
- }
- }
-
- // validate selected tags
- _.each(vm.tags, function(tag) {
- if (tag.selected) {
- _.each(tag.values, function(value) {
- if (!_.findWhere(vm.selectedValues, {value: value})) {
- tag.selected = false;
- }
- });
- }
- });
-
- vm.selectedTags = _.filter(vm.tags, {selected: true});
- vm.variable.current.value = _.pluck(vm.selectedValues, 'value');
- vm.variable.current.text = _.pluck(vm.selectedValues, 'text').join(' + ');
- vm.variable.current.tags = vm.selectedTags;
-
- // only single value
- if (vm.selectedValues.length === 1) {
- vm.variable.current.value = vm.selectedValues[0].value;
- }
-
- if (commitChange) {
- vm.commitChanges();
- }
- };
-
- vm.commitChanges = function() {
- // if we have a search query and no options use that
- if (vm.search.options.length === 0 && vm.search.query.length > 0) {
- vm.variable.current = {text: vm.search.query, value: vm.search.query};
- }
- else if (vm.selectedValues.length === 0) {
- // make sure one option is selected
- vm.options[0].selected = true;
- vm.selectionsChanged(false);
- }
-
- vm.dropdownVisible = false;
- vm.updateLinkText();
-
- if (vm.variable.current.text !== vm.oldVariableText) {
- vm.onUpdated();
- }
- };
-
- vm.queryChanged = function() {
- vm.highlightIndex = -1;
- vm.search.options = _.filter(vm.options, function(option) {
- return option.text.toLowerCase().indexOf(vm.search.query.toLowerCase()) !== -1;
- });
-
- vm.search.options = vm.search.options.slice(0, Math.min(vm.search.options.length, 1000));
- };
-
- vm.init = function() {
- vm.selectedTags = vm.variable.current.tags || [];
- vm.updateLinkText();
- };
-
- });
-
- angular
- .module('grafana.directives')
- .directive('valueSelectDropdown', function($compile, $window, $timeout, $rootScope) {
-
- return {
- scope: { variable: "=", onUpdated: "&", getValuesForTag: "&" },
- templateUrl: 'app/partials/valueSelectDropdown.html',
- controller: 'ValueSelectDropdownCtrl',
- controllerAs: 'vm',
- bindToController: true,
- link: function(scope, elem) {
- var bodyEl = angular.element($window.document.body);
- var linkEl = elem.find('.variable-value-link');
- var inputEl = elem.find('input');
-
- function openDropdown() {
- inputEl.css('width', Math.max(linkEl.width(), 30) + 'px');
-
- inputEl.show();
- linkEl.hide();
-
- inputEl.focus();
- $timeout(function() { bodyEl.on('click', bodyOnClick); }, 0, false);
- }
-
- function switchToLink() {
- inputEl.hide();
- linkEl.show();
- bodyEl.off('click', bodyOnClick);
- }
-
- function bodyOnClick (e) {
- if (elem.has(e.target).length === 0) {
- scope.$apply(function() {
- scope.vm.commitChanges();
- });
- }
- }
-
- scope.$watch('vm.dropdownVisible', function(newValue) {
- if (newValue) {
- openDropdown();
- } else {
- switchToLink();
- }
- });
-
- var cleanUp = $rootScope.$on('template-variable-value-updated', function() {
- scope.vm.updateLinkText();
- });
-
- scope.$on("$destroy", function() {
- cleanUp();
- });
-
- scope.vm.init();
- },
- };
- });
-
-});
diff --git a/public/app/partials/login.html b/public/app/partials/login.html
index 2d9ed8d3b47..630f295ecd3 100644
--- a/public/app/partials/login.html
+++ b/public/app/partials/login.html
@@ -17,7 +17,7 @@