tooltip.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616
  1. /**
  2. * --------------------------------------------------------------------------
  3. * Bootstrap (v4.0.0-alpha.6): tooltip.js
  4. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
  5. * --------------------------------------------------------------------------
  6. */
  7. var Tooltip = function ($) {
  8. /**
  9. * Check for Tether dependency
  10. * Tether - http://tether.io/
  11. */
  12. if (typeof Tether === 'undefined') {
  13. throw new Error('Bootstrap tooltips require Tether (http://tether.io/)');
  14. }
  15. /**
  16. * ------------------------------------------------------------------------
  17. * Constants
  18. * ------------------------------------------------------------------------
  19. */
  20. var NAME = 'tooltip';
  21. var VERSION = '4.0.0-alpha.6';
  22. var DATA_KEY = 'bs.tooltip';
  23. var EVENT_KEY = '.' + DATA_KEY;
  24. var JQUERY_NO_CONFLICT = $.fn[NAME];
  25. var TRANSITION_DURATION = 150;
  26. var CLASS_PREFIX = 'bs-tether';
  27. var Default = {
  28. animation: true,
  29. template: '<div class="tooltip" role="tooltip">' + '<div class="tooltip-inner"></div></div>',
  30. trigger: 'hover focus',
  31. title: '',
  32. delay: 0,
  33. html: false,
  34. selector: false,
  35. placement: 'top',
  36. offset: '0 0',
  37. constraints: [],
  38. container: false
  39. };
  40. var DefaultType = {
  41. animation: 'boolean',
  42. template: 'string',
  43. title: '(string|element|function)',
  44. trigger: 'string',
  45. delay: '(number|object)',
  46. html: 'boolean',
  47. selector: '(string|boolean)',
  48. placement: '(string|function)',
  49. offset: 'string',
  50. constraints: 'array',
  51. container: '(string|element|boolean)'
  52. };
  53. var AttachmentMap = {
  54. TOP: 'bottom center',
  55. RIGHT: 'middle left',
  56. BOTTOM: 'top center',
  57. LEFT: 'middle right'
  58. };
  59. var HoverState = {
  60. SHOW: 'show',
  61. OUT: 'out'
  62. };
  63. var Event = {
  64. HIDE: 'hide' + EVENT_KEY,
  65. HIDDEN: 'hidden' + EVENT_KEY,
  66. SHOW: 'show' + EVENT_KEY,
  67. SHOWN: 'shown' + EVENT_KEY,
  68. INSERTED: 'inserted' + EVENT_KEY,
  69. CLICK: 'click' + EVENT_KEY,
  70. FOCUSIN: 'focusin' + EVENT_KEY,
  71. FOCUSOUT: 'focusout' + EVENT_KEY,
  72. MOUSEENTER: 'mouseenter' + EVENT_KEY,
  73. MOUSELEAVE: 'mouseleave' + EVENT_KEY
  74. };
  75. var ClassName = {
  76. FADE: 'fade',
  77. SHOW: 'show'
  78. };
  79. var Selector = {
  80. TOOLTIP: '.tooltip',
  81. TOOLTIP_INNER: '.tooltip-inner'
  82. };
  83. var TetherClass = {
  84. element: false,
  85. enabled: false
  86. };
  87. var Trigger = {
  88. HOVER: 'hover',
  89. FOCUS: 'focus',
  90. CLICK: 'click',
  91. MANUAL: 'manual'
  92. };
  93. /**
  94. * ------------------------------------------------------------------------
  95. * Class Definition
  96. * ------------------------------------------------------------------------
  97. */
  98. var Tooltip = function () {
  99. function Tooltip(element, config) {
  100. _classCallCheck(this, Tooltip);
  101. // private
  102. this._isEnabled = true;
  103. this._timeout = 0;
  104. this._hoverState = '';
  105. this._activeTrigger = {};
  106. this._isTransitioning = false;
  107. this._tether = null;
  108. // protected
  109. this.element = element;
  110. this.config = this._getConfig(config);
  111. this.tip = null;
  112. this._setListeners();
  113. }
  114. // getters
  115. // public
  116. Tooltip.prototype.enable = function enable() {
  117. this._isEnabled = true;
  118. };
  119. Tooltip.prototype.disable = function disable() {
  120. this._isEnabled = false;
  121. };
  122. Tooltip.prototype.toggleEnabled = function toggleEnabled() {
  123. this._isEnabled = !this._isEnabled;
  124. };
  125. Tooltip.prototype.toggle = function toggle(event) {
  126. if (event) {
  127. var dataKey = this.constructor.DATA_KEY;
  128. var context = $(event.currentTarget).data(dataKey);
  129. if (!context) {
  130. context = new this.constructor(event.currentTarget, this._getDelegateConfig());
  131. $(event.currentTarget).data(dataKey, context);
  132. }
  133. context._activeTrigger.click = !context._activeTrigger.click;
  134. if (context._isWithActiveTrigger()) {
  135. context._enter(null, context);
  136. } else {
  137. context._leave(null, context);
  138. }
  139. } else {
  140. if ($(this.getTipElement()).hasClass(ClassName.SHOW)) {
  141. this._leave(null, this);
  142. return;
  143. }
  144. this._enter(null, this);
  145. }
  146. };
  147. Tooltip.prototype.dispose = function dispose() {
  148. clearTimeout(this._timeout);
  149. this.cleanupTether();
  150. $.removeData(this.element, this.constructor.DATA_KEY);
  151. $(this.element).off(this.constructor.EVENT_KEY);
  152. $(this.element).closest('.modal').off('hide.bs.modal');
  153. if (this.tip) {
  154. $(this.tip).remove();
  155. }
  156. this._isEnabled = null;
  157. this._timeout = null;
  158. this._hoverState = null;
  159. this._activeTrigger = null;
  160. this._tether = null;
  161. this.element = null;
  162. this.config = null;
  163. this.tip = null;
  164. };
  165. Tooltip.prototype.show = function show() {
  166. var _this22 = this;
  167. if ($(this.element).css('display') === 'none') {
  168. throw new Error('Please use show on visible elements');
  169. }
  170. var showEvent = $.Event(this.constructor.Event.SHOW);
  171. if (this.isWithContent() && this._isEnabled) {
  172. if (this._isTransitioning) {
  173. throw new Error('Tooltip is transitioning');
  174. }
  175. $(this.element).trigger(showEvent);
  176. var isInTheDom = $.contains(this.element.ownerDocument.documentElement, this.element);
  177. if (showEvent.isDefaultPrevented() || !isInTheDom) {
  178. return;
  179. }
  180. var tip = this.getTipElement();
  181. var tipId = Util.getUID(this.constructor.NAME);
  182. tip.setAttribute('id', tipId);
  183. this.element.setAttribute('aria-describedby', tipId);
  184. this.setContent();
  185. if (this.config.animation) {
  186. $(tip).addClass(ClassName.FADE);
  187. }
  188. var placement = typeof this.config.placement === 'function' ? this.config.placement.call(this, tip, this.element) : this.config.placement;
  189. var attachment = this._getAttachment(placement);
  190. var container = this.config.container === false ? document.body : $(this.config.container);
  191. $(tip).data(this.constructor.DATA_KEY, this).appendTo(container);
  192. $(this.element).trigger(this.constructor.Event.INSERTED);
  193. this._tether = new Tether({
  194. attachment: attachment,
  195. element: tip,
  196. target: this.element,
  197. classes: TetherClass,
  198. classPrefix: CLASS_PREFIX,
  199. offset: this.config.offset,
  200. constraints: this.config.constraints,
  201. addTargetClasses: false
  202. });
  203. Util.reflow(tip);
  204. this._tether.position();
  205. $(tip).addClass(ClassName.SHOW);
  206. var complete = function complete() {
  207. var prevHoverState = _this22._hoverState;
  208. _this22._hoverState = null;
  209. _this22._isTransitioning = false;
  210. $(_this22.element).trigger(_this22.constructor.Event.SHOWN);
  211. if (prevHoverState === HoverState.OUT) {
  212. _this22._leave(null, _this22);
  213. }
  214. };
  215. if (Util.supportsTransitionEnd() && $(this.tip).hasClass(ClassName.FADE)) {
  216. this._isTransitioning = true;
  217. $(this.tip).one(Util.TRANSITION_END, complete).emulateTransitionEnd(Tooltip._TRANSITION_DURATION);
  218. return;
  219. }
  220. complete();
  221. }
  222. };
  223. Tooltip.prototype.hide = function hide(callback) {
  224. var _this23 = this;
  225. var tip = this.getTipElement();
  226. var hideEvent = $.Event(this.constructor.Event.HIDE);
  227. if (this._isTransitioning) {
  228. throw new Error('Tooltip is transitioning');
  229. }
  230. var complete = function complete() {
  231. if (_this23._hoverState !== HoverState.SHOW && tip.parentNode) {
  232. tip.parentNode.removeChild(tip);
  233. }
  234. _this23.element.removeAttribute('aria-describedby');
  235. $(_this23.element).trigger(_this23.constructor.Event.HIDDEN);
  236. _this23._isTransitioning = false;
  237. _this23.cleanupTether();
  238. if (callback) {
  239. callback();
  240. }
  241. };
  242. $(this.element).trigger(hideEvent);
  243. if (hideEvent.isDefaultPrevented()) {
  244. return;
  245. }
  246. $(tip).removeClass(ClassName.SHOW);
  247. this._activeTrigger[Trigger.CLICK] = false;
  248. this._activeTrigger[Trigger.FOCUS] = false;
  249. this._activeTrigger[Trigger.HOVER] = false;
  250. if (Util.supportsTransitionEnd() && $(this.tip).hasClass(ClassName.FADE)) {
  251. this._isTransitioning = true;
  252. $(tip).one(Util.TRANSITION_END, complete).emulateTransitionEnd(TRANSITION_DURATION);
  253. } else {
  254. complete();
  255. }
  256. this._hoverState = '';
  257. };
  258. // protected
  259. Tooltip.prototype.isWithContent = function isWithContent() {
  260. return Boolean(this.getTitle());
  261. };
  262. Tooltip.prototype.getTipElement = function getTipElement() {
  263. return this.tip = this.tip || $(this.config.template)[0];
  264. };
  265. Tooltip.prototype.setContent = function setContent() {
  266. var $tip = $(this.getTipElement());
  267. this.setElementContent($tip.find(Selector.TOOLTIP_INNER), this.getTitle());
  268. $tip.removeClass(ClassName.FADE + ' ' + ClassName.SHOW);
  269. this.cleanupTether();
  270. };
  271. Tooltip.prototype.setElementContent = function setElementContent($element, content) {
  272. var html = this.config.html;
  273. if ((typeof content === 'undefined' ? 'undefined' : _typeof(content)) === 'object' && (content.nodeType || content.jquery)) {
  274. // content is a DOM node or a jQuery
  275. if (html) {
  276. if (!$(content).parent().is($element)) {
  277. $element.empty().append(content);
  278. }
  279. } else {
  280. $element.text($(content).text());
  281. }
  282. } else {
  283. $element[html ? 'html' : 'text'](content);
  284. }
  285. };
  286. Tooltip.prototype.getTitle = function getTitle() {
  287. var title = this.element.getAttribute('data-original-title');
  288. if (!title) {
  289. title = typeof this.config.title === 'function' ? this.config.title.call(this.element) : this.config.title;
  290. }
  291. return title;
  292. };
  293. Tooltip.prototype.cleanupTether = function cleanupTether() {
  294. if (this._tether) {
  295. this._tether.destroy();
  296. }
  297. };
  298. // private
  299. Tooltip.prototype._getAttachment = function _getAttachment(placement) {
  300. return AttachmentMap[placement.toUpperCase()];
  301. };
  302. Tooltip.prototype._setListeners = function _setListeners() {
  303. var _this24 = this;
  304. var triggers = this.config.trigger.split(' ');
  305. triggers.forEach(function (trigger) {
  306. if (trigger === 'click') {
  307. $(_this24.element).on(_this24.constructor.Event.CLICK, _this24.config.selector, function (event) {
  308. return _this24.toggle(event);
  309. });
  310. } else if (trigger !== Trigger.MANUAL) {
  311. var eventIn = trigger === Trigger.HOVER ? _this24.constructor.Event.MOUSEENTER : _this24.constructor.Event.FOCUSIN;
  312. var eventOut = trigger === Trigger.HOVER ? _this24.constructor.Event.MOUSELEAVE : _this24.constructor.Event.FOCUSOUT;
  313. $(_this24.element).on(eventIn, _this24.config.selector, function (event) {
  314. return _this24._enter(event);
  315. }).on(eventOut, _this24.config.selector, function (event) {
  316. return _this24._leave(event);
  317. });
  318. }
  319. $(_this24.element).closest('.modal').on('hide.bs.modal', function () {
  320. return _this24.hide();
  321. });
  322. });
  323. if (this.config.selector) {
  324. this.config = $.extend({}, this.config, {
  325. trigger: 'manual',
  326. selector: ''
  327. });
  328. } else {
  329. this._fixTitle();
  330. }
  331. };
  332. Tooltip.prototype._fixTitle = function _fixTitle() {
  333. var titleType = _typeof(this.element.getAttribute('data-original-title'));
  334. if (this.element.getAttribute('title') || titleType !== 'string') {
  335. this.element.setAttribute('data-original-title', this.element.getAttribute('title') || '');
  336. this.element.setAttribute('title', '');
  337. }
  338. };
  339. Tooltip.prototype._enter = function _enter(event, context) {
  340. var dataKey = this.constructor.DATA_KEY;
  341. context = context || $(event.currentTarget).data(dataKey);
  342. if (!context) {
  343. context = new this.constructor(event.currentTarget, this._getDelegateConfig());
  344. $(event.currentTarget).data(dataKey, context);
  345. }
  346. if (event) {
  347. context._activeTrigger[event.type === 'focusin' ? Trigger.FOCUS : Trigger.HOVER] = true;
  348. }
  349. if ($(context.getTipElement()).hasClass(ClassName.SHOW) || context._hoverState === HoverState.SHOW) {
  350. context._hoverState = HoverState.SHOW;
  351. return;
  352. }
  353. clearTimeout(context._timeout);
  354. context._hoverState = HoverState.SHOW;
  355. if (!context.config.delay || !context.config.delay.show) {
  356. context.show();
  357. return;
  358. }
  359. context._timeout = setTimeout(function () {
  360. if (context._hoverState === HoverState.SHOW) {
  361. context.show();
  362. }
  363. }, context.config.delay.show);
  364. };
  365. Tooltip.prototype._leave = function _leave(event, context) {
  366. var dataKey = this.constructor.DATA_KEY;
  367. context = context || $(event.currentTarget).data(dataKey);
  368. if (!context) {
  369. context = new this.constructor(event.currentTarget, this._getDelegateConfig());
  370. $(event.currentTarget).data(dataKey, context);
  371. }
  372. if (event) {
  373. context._activeTrigger[event.type === 'focusout' ? Trigger.FOCUS : Trigger.HOVER] = false;
  374. }
  375. if (context._isWithActiveTrigger()) {
  376. return;
  377. }
  378. clearTimeout(context._timeout);
  379. context._hoverState = HoverState.OUT;
  380. if (!context.config.delay || !context.config.delay.hide) {
  381. context.hide();
  382. return;
  383. }
  384. context._timeout = setTimeout(function () {
  385. if (context._hoverState === HoverState.OUT) {
  386. context.hide();
  387. }
  388. }, context.config.delay.hide);
  389. };
  390. Tooltip.prototype._isWithActiveTrigger = function _isWithActiveTrigger() {
  391. for (var trigger in this._activeTrigger) {
  392. if (this._activeTrigger[trigger]) {
  393. return true;
  394. }
  395. }
  396. return false;
  397. };
  398. Tooltip.prototype._getConfig = function _getConfig(config) {
  399. config = $.extend({}, this.constructor.Default, $(this.element).data(), config);
  400. if (config.delay && typeof config.delay === 'number') {
  401. config.delay = {
  402. show: config.delay,
  403. hide: config.delay
  404. };
  405. }
  406. Util.typeCheckConfig(NAME, config, this.constructor.DefaultType);
  407. return config;
  408. };
  409. Tooltip.prototype._getDelegateConfig = function _getDelegateConfig() {
  410. var config = {};
  411. if (this.config) {
  412. for (var key in this.config) {
  413. if (this.constructor.Default[key] !== this.config[key]) {
  414. config[key] = this.config[key];
  415. }
  416. }
  417. }
  418. return config;
  419. };
  420. // static
  421. Tooltip._jQueryInterface = function _jQueryInterface(config) {
  422. return this.each(function () {
  423. var data = $(this).data(DATA_KEY);
  424. var _config = (typeof config === 'undefined' ? 'undefined' : _typeof(config)) === 'object' && config;
  425. if (!data && /dispose|hide/.test(config)) {
  426. return;
  427. }
  428. if (!data) {
  429. data = new Tooltip(this, _config);
  430. $(this).data(DATA_KEY, data);
  431. }
  432. if (typeof config === 'string') {
  433. if (data[config] === undefined) {
  434. throw new Error('No method named "' + config + '"');
  435. }
  436. data[config]();
  437. }
  438. });
  439. };
  440. _createClass(Tooltip, null, [{
  441. key: 'VERSION',
  442. get: function get() {
  443. return VERSION;
  444. }
  445. }, {
  446. key: 'Default',
  447. get: function get() {
  448. return Default;
  449. }
  450. }, {
  451. key: 'NAME',
  452. get: function get() {
  453. return NAME;
  454. }
  455. }, {
  456. key: 'DATA_KEY',
  457. get: function get() {
  458. return DATA_KEY;
  459. }
  460. }, {
  461. key: 'Event',
  462. get: function get() {
  463. return Event;
  464. }
  465. }, {
  466. key: 'EVENT_KEY',
  467. get: function get() {
  468. return EVENT_KEY;
  469. }
  470. }, {
  471. key: 'DefaultType',
  472. get: function get() {
  473. return DefaultType;
  474. }
  475. }]);
  476. return Tooltip;
  477. }();
  478. /**
  479. * ------------------------------------------------------------------------
  480. * jQuery
  481. * ------------------------------------------------------------------------
  482. */
  483. $.fn[NAME] = Tooltip._jQueryInterface;
  484. $.fn[NAME].Constructor = Tooltip;
  485. $.fn[NAME].noConflict = function () {
  486. $.fn[NAME] = JQUERY_NO_CONFLICT;
  487. return Tooltip._jQueryInterface;
  488. };
  489. return Tooltip;
  490. }(jQuery);