angular-strap.js 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. /**
  2. * AngularStrap - Twitter Bootstrap directives for AngularJS
  3. * @version v0.7.5 - 2013-07-21
  4. * @link http://mgcrea.github.com/angular-strap
  5. * @author Olivier Louvignes <olivier@mg-crea.com>
  6. * @license MIT License, http://www.opensource.org/licenses/MIT
  7. */
  8. angular.module('$strap.config', []).value('$strapConfig', {});
  9. angular.module('$strap.filters', ['$strap.config']);
  10. angular.module('$strap.directives', ['$strap.config']);
  11. angular.module('$strap', [
  12. '$strap.filters',
  13. '$strap.directives',
  14. '$strap.config'
  15. ]);
  16. 'use strict';
  17. angular.module('$strap.directives').factory('$modal', [
  18. '$rootScope',
  19. '$compile',
  20. '$http',
  21. '$timeout',
  22. '$q',
  23. '$templateCache',
  24. '$strapConfig',
  25. function ($rootScope, $compile, $http, $timeout, $q, $templateCache, $strapConfig) {
  26. var ModalFactory = function ModalFactory(config) {
  27. function Modal(config) {
  28. var options = angular.extend({ show: true }, $strapConfig.modal, config), scope = options.scope ? options.scope : $rootScope.$new(), templateUrl = options.template;
  29. return $q.when($templateCache.get(templateUrl) || $http.get(templateUrl, { cache: true }).then(function (res) {
  30. return res.data;
  31. })).then(function onSuccess(template) {
  32. var id = templateUrl.replace('.html', '').replace(/[\/|\.|:]/g, '-') + '-' + scope.$id;
  33. // grafana change, removed fade
  34. var $modal = $('<div class="modal hide" tabindex="-1"></div>').attr('id', id).html(template);
  35. if (options.modalClass)
  36. $modal.addClass(options.modalClass);
  37. $('body').append($modal);
  38. $timeout(function () {
  39. $compile($modal)(scope);
  40. });
  41. scope.$modal = function (name) {
  42. $modal.modal(name);
  43. };
  44. angular.forEach([
  45. 'show',
  46. 'hide'
  47. ], function (name) {
  48. scope[name] = function () {
  49. $modal.modal(name);
  50. };
  51. });
  52. scope.dismiss = scope.hide;
  53. angular.forEach([
  54. 'show',
  55. 'shown',
  56. 'hide',
  57. 'hidden'
  58. ], function (name) {
  59. $modal.on(name, function (ev) {
  60. scope.$emit('modal-' + name, ev);
  61. });
  62. });
  63. $modal.on('shown', function (ev) {
  64. $('input[autofocus], textarea[autofocus]', $modal).first().trigger('focus');
  65. });
  66. $modal.on('hidden', function (ev) {
  67. if (!options.persist)
  68. scope.$destroy();
  69. });
  70. scope.$on('$destroy', function () {
  71. $modal.remove();
  72. });
  73. $modal.modal(options);
  74. return $modal;
  75. });
  76. }
  77. return new Modal(config);
  78. };
  79. return ModalFactory;
  80. }
  81. ])
  82. 'use strict';
  83. angular.module('$strap.directives').directive('bsTabs', [
  84. '$parse',
  85. '$compile',
  86. '$timeout',
  87. function ($parse, $compile, $timeout) {
  88. var template = '<div class="tabs">' + '<ul class="nav nav-tabs">' + '<li ng-repeat="pane in panes" ng-class="{active:pane.active}">' + '<a data-target="#{{pane.id}}" data-index="{{$index}}" data-toggle="tab">{{pane.title}}</a>' + '</li>' + '</ul>' + '<div class="tab-content" ng-transclude>' + '</div>';
  89. return {
  90. restrict: 'A',
  91. require: '?ngModel',
  92. priority: 0,
  93. scope: true,
  94. template: template,
  95. replace: true,
  96. transclude: true,
  97. compile: function compile(tElement, tAttrs, transclude) {
  98. return function postLink(scope, iElement, iAttrs, controller) {
  99. var getter = $parse(iAttrs.bsTabs), setter = getter.assign, value = getter(scope);
  100. scope.panes = [];
  101. var $tabs = iElement.find('ul.nav-tabs');
  102. var $panes = iElement.find('div.tab-content');
  103. var activeTab = 0, id, title, active;
  104. $timeout(function () {
  105. $panes.find('[data-title], [data-tab]').each(function (index) {
  106. var $this = angular.element(this);
  107. id = 'tab-' + scope.$id + '-' + index;
  108. title = $this.data('title') || $this.data('tab');
  109. active = !active && $this.hasClass('active');
  110. $this.attr('id', id).addClass('tab-pane');
  111. if (iAttrs.fade)
  112. $this.addClass('fade');
  113. scope.panes.push({
  114. id: id,
  115. title: title,
  116. content: this.innerHTML,
  117. active: active
  118. });
  119. });
  120. if (scope.panes.length && !active) {
  121. $panes.find('.tab-pane:first-child').addClass('active' + (iAttrs.fade ? ' in' : ''));
  122. scope.panes[0].active = true;
  123. }
  124. });
  125. if (controller) {
  126. iElement.on('show', function (ev) {
  127. var $target = $(ev.target);
  128. scope.$apply(function () {
  129. controller.$setViewValue($target.data('index'));
  130. });
  131. });
  132. scope.$watch(iAttrs.ngModel, function (newValue, oldValue) {
  133. if (angular.isUndefined(newValue))
  134. return;
  135. activeTab = newValue;
  136. setTimeout(function () {
  137. // Check if we're still on the same tab before making the switch
  138. if(activeTab === newValue) {
  139. var $next = $($tabs[0].querySelectorAll('li')[newValue * 1]);
  140. if (!$next.hasClass('active')) {
  141. $next.children('a').tab('show');
  142. }
  143. }
  144. });
  145. });
  146. }
  147. };
  148. }
  149. };
  150. }
  151. ]);
  152. 'use strict';
  153. angular.module('$strap.directives').directive('bsTooltip', [
  154. '$parse',
  155. '$compile',
  156. function ($parse, $compile) {
  157. return {
  158. restrict: 'A',
  159. scope: true,
  160. link: function postLink(scope, element, attrs, ctrl) {
  161. var getter = $parse(attrs.bsTooltip), setter = getter.assign, value = getter(scope);
  162. scope.$watch(attrs.bsTooltip, function (newValue, oldValue) {
  163. if (newValue !== oldValue) {
  164. value = newValue;
  165. }
  166. });
  167. // Grafana change, always hide other tooltips
  168. if (true) {
  169. element.on('show', function (ev) {
  170. $('.tooltip.in').each(function () {
  171. var $this = $(this), tooltip = $this.data('tooltip');
  172. if (tooltip && !tooltip.$element.is(element)) {
  173. $this.tooltip('hide');
  174. }
  175. });
  176. });
  177. }
  178. element.tooltip({
  179. title: function () {
  180. return angular.isFunction(value) ? value.apply(null, arguments) : value;
  181. },
  182. html: true,
  183. container: 'body', // Grafana change
  184. });
  185. var tooltip = element.data('tooltip');
  186. tooltip.show = function () {
  187. var r = $.fn.tooltip.Constructor.prototype.show.apply(this, arguments);
  188. this.tip().data('tooltip', this);
  189. return r;
  190. };
  191. scope._tooltip = function (event) {
  192. element.tooltip(event);
  193. };
  194. scope.hide = function () {
  195. element.tooltip('hide');
  196. };
  197. scope.show = function () {
  198. element.tooltip('show');
  199. };
  200. scope.dismiss = scope.hide;
  201. }
  202. };
  203. }
  204. ]);
  205. 'use strict';
  206. angular.module('$strap.directives').directive('bsTypeahead', [
  207. '$parse',
  208. function ($parse) {
  209. return {
  210. restrict: 'A',
  211. require: '?ngModel',
  212. link: function postLink(scope, element, attrs, controller) {
  213. var getter = $parse(attrs.bsTypeahead), setter = getter.assign, value = getter(scope);
  214. scope.$watch(attrs.bsTypeahead, function (newValue, oldValue) {
  215. if (newValue !== oldValue) {
  216. value = newValue;
  217. }
  218. });
  219. element.attr('data-provide', 'typeahead');
  220. element.typeahead({
  221. source: function (query) {
  222. return angular.isFunction(value) ? value.apply(null, arguments) : value;
  223. },
  224. minLength: attrs.minLength || 1,
  225. items: attrs.items,
  226. updater: function (value) {
  227. if (controller) {
  228. scope.$apply(function () {
  229. controller.$setViewValue(value);
  230. });
  231. }
  232. scope.$emit('typeahead-updated', value);
  233. return value;
  234. }
  235. });
  236. var typeahead = element.data('typeahead');
  237. typeahead.lookup = function (ev) {
  238. var items;
  239. this.query = this.$element.val() || '';
  240. if (this.query.length < this.options.minLength) {
  241. return this.shown ? this.hide() : this;
  242. }
  243. items = $.isFunction(this.source) ? this.source(this.query, $.proxy(this.process, this)) : this.source;
  244. return items ? this.process(items) : this;
  245. };
  246. if (!!attrs.matchAll) {
  247. typeahead.matcher = function (item) {
  248. return true;
  249. };
  250. }
  251. if (attrs.minLength === '0') {
  252. setTimeout(function () {
  253. element.on('focus', function () {
  254. element.val().length === 0 && setTimeout(element.typeahead.bind(element, 'lookup'), 200);
  255. });
  256. });
  257. }
  258. }
  259. };
  260. }
  261. ]);