diff --git a/js/src/alert.js b/js/src/alert.js index 9332aa891d..9d8a865d1c 100644 --- a/js/src/alert.js +++ b/js/src/alert.js @@ -137,6 +137,10 @@ class Alert { } }) } + + static _getInstance(element) { + return Data.getData(element, DATA_KEY) + } } /** diff --git a/js/src/button.js b/js/src/button.js index 0e3b8a9df5..ce69253e44 100644 --- a/js/src/button.js +++ b/js/src/button.js @@ -112,6 +112,10 @@ class Button { if (triggerChangeEvent) { this._element.classList.toggle(ClassName.ACTIVE) } + + static _getInstance(element) { + return Data.getData(element, DATA_KEY) + } } dispose() { diff --git a/js/src/carousel.js b/js/src/carousel.js index fd6d5bf802..15a56bd76a 100644 --- a/js/src/carousel.js +++ b/js/src/carousel.js @@ -579,6 +579,10 @@ class Carousel { event.preventDefault() } + + static _getInstance(element) { + return Data.getData(element, DATA_KEY) + } } /** @@ -597,7 +601,6 @@ EventHandler.on(window, Event.LOAD_DATA_API, () => { } }) - /** * ------------------------------------------------------------------------ * jQuery diff --git a/js/src/collapse.js b/js/src/collapse.js index 7d6aa30ed8..d04743d039 100644 --- a/js/src/collapse.js +++ b/js/src/collapse.js @@ -373,6 +373,10 @@ class Collapse { Collapse._collapseInterface(this, config) }) } + + static _getInstance(element) { + return Data.getData(element, DATA_KEY) + } } /** diff --git a/js/src/dom/polyfill.js b/js/src/dom/polyfill.js new file mode 100644 index 0000000000..644e6025b1 --- /dev/null +++ b/js/src/dom/polyfill.js @@ -0,0 +1,94 @@ +import EventHandler from './eventHandler' + +const Polyfill = (() => { + // defaultPrevented is broken in IE. + // https://connect.microsoft.com/IE/feedback/details/790389/event-defaultprevented-returns-false-after-preventdefault-was-called + const workingDefaultPrevented = (() => { + const e = document.createEvent('CustomEvent') + e.initEvent('Bootstrap', true, true) + e.preventDefault() + return e.defaultPrevented + })() + + let defaultPreventedPreservedOnDispatch = true + + // CustomEvent polyfill for IE (see: https://mzl.la/2v76Zvn) + if (typeof window.CustomEvent !== 'function') { + window.CustomEvent = (event, params) => { + params = params || { + bubbles: false, + cancelable: false, + detail: null + } + const evt = document.createEvent('CustomEvent') + evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail) + if (!workingDefaultPrevented) { + const origPreventDefault = Event.prototype.preventDefault + evt.preventDefault = () => { + if (!evt.cancelable) { + return + } + + origPreventDefault.call(evt) + Object.defineProperty(evt, 'defaultPrevented', { + get() { + return true + }, + configurable: true + }) + } + } + return evt + } + + window.CustomEvent.prototype = window.Event.prototype + } else { + // MSEdge resets defaultPrevented flag upon dispatchEvent call if at least one listener is attached + defaultPreventedPreservedOnDispatch = (() => { + const e = new CustomEvent('Bootstrap', { + cancelable: true + }) + + const element = document.createElement('div') + element.addEventListener('Bootstrap', () => null) + + e.preventDefault() + element.dispatchEvent(e) + return e.defaultPrevented + })() + } + + // Event constructor shim + if (!window.Event || typeof window.Event !== 'function') { + const origEvent = window.Event + window.Event = (inType, params) => { + params = params || {} + const e = document.createEvent('Event') + e.initEvent(inType, Boolean(params.bubbles), Boolean(params.cancelable)) + return e + } + window.Event.prototype = origEvent.prototype + } + + // focusin and focusout polyfill + if (typeof window.onfocusin === 'undefined') { + (() => { + function listenerFocus(event) { + EventHandler.trigger(event.target, 'focusin') + } + function listenerBlur(event) { + EventHandler.trigger(event.target, 'focusout') + } + EventHandler.on(document, 'focus', 'input', listenerFocus) + EventHandler.on(document, 'blur', 'input', listenerBlur) + })() + } + + return { + get defaultPreventedPreservedOnDispatch() { + return defaultPreventedPreservedOnDispatch + } + } +})() + +export default Polyfill diff --git a/js/src/dropdown.js b/js/src/dropdown.js index ddc783c0dd..282e7645ff 100644 --- a/js/src/dropdown.js +++ b/js/src/dropdown.js @@ -505,6 +505,10 @@ class Dropdown { items[index].focus() } + + static _getInstance(element) { + return Data.getData(element, DATA_KEY) + } } /** diff --git a/js/src/index.js b/js/src/index.js index 327de242b2..aa35ed07b3 100644 --- a/js/src/index.js +++ b/js/src/index.js @@ -5,7 +5,7 @@ import Collapse from './collapse' import Dropdown from './dropdown' import Modal from './modal' import Popover from './popover' -import Scrollspy from './scrollspy' +import ScrollSpy from './scrollspy' import Tab from './tab' import Toast from './toast' import Tooltip from './tooltip' @@ -27,7 +27,7 @@ export { Dropdown, Modal, Popover, - Scrollspy, + ScrollSpy, Tab, Toast, Tooltip diff --git a/js/src/modal.js b/js/src/modal.js index 6da3d25aa8..4f23fff741 100644 --- a/js/src/modal.js +++ b/js/src/modal.js @@ -498,6 +498,10 @@ class Modal { } else { document.body.style.paddingRight = '' } + + static _getInstance(element) { + return Data.getData(element, DATA_KEY) + } } _getScrollbarWidth() { // thx d.walsh diff --git a/js/src/popover.js b/js/src/popover.js index e7c00a4cd2..b462326190 100644 --- a/js/src/popover.js +++ b/js/src/popover.js @@ -165,6 +165,10 @@ class Popover extends Tooltip { } }) } + + static _getInstance(element) { + return Data.getData(element, DATA_KEY) + } } /** diff --git a/js/src/scrollspy.js b/js/src/scrollspy.js index ea6d528157..458f5170e9 100644 --- a/js/src/scrollspy.js +++ b/js/src/scrollspy.js @@ -308,6 +308,10 @@ class ScrollSpy { } }) } + + static _getInstance(element) { + return Data.getData(element, DATA_KEY) + } } /** diff --git a/js/src/tab.js b/js/src/tab.js index 9343413480..121669aad5 100644 --- a/js/src/tab.js +++ b/js/src/tab.js @@ -219,6 +219,10 @@ class Tab { } }) } + + static _getInstance(element) { + return Data.getData(element, DATA_KEY) + } } /** diff --git a/js/src/tooltip.js b/js/src/tooltip.js index 164f6e990f..9b8b8263a9 100644 --- a/js/src/tooltip.js +++ b/js/src/tooltip.js @@ -741,6 +741,10 @@ class Tooltip { .map((token) => token.trim()) .forEach((tClass) => tip.classList.remove(tClass)) } + + static _getInstance(element) { + return Data.getData(element, DATA_KEY) + } } _handlePopperPlacementChange(popperData) { diff --git a/js/tests/unit/.eslintrc.json b/js/tests/unit/.eslintrc.json index cb40cd0ab8..726fc25c91 100644 --- a/js/tests/unit/.eslintrc.json +++ b/js/tests/unit/.eslintrc.json @@ -14,7 +14,6 @@ "Simulator": false, "Toast": false, "EventHandler": false, - "Data": false, "Manipulator": false }, "parserOptions": { diff --git a/js/tests/unit/alert.js b/js/tests/unit/alert.js index 3fe796e28b..d5437a7376 100644 --- a/js/tests/unit/alert.js +++ b/js/tests/unit/alert.js @@ -1,6 +1,8 @@ $(function () { 'use strict' + var Alert = typeof window.bootstrap !== 'undefined' ? window.bootstrap.Alert : window.Alert + QUnit.module('alert plugin') QUnit.test('should be defined on jquery object', function (assert) { @@ -91,7 +93,7 @@ $(function () { var done = assert.async() var $el = $('
') var $alert = $el.bootstrapAlert() - var alertInstance = Data.getData($alert[0], 'bs.alert') + var alertInstance = Alert._getInstance($alert[0]) $alert.one('closed.bs.alert', function () { assert.ok('alert closed') @@ -107,11 +109,11 @@ $(function () { var $el = $('
') var $alert = $el.bootstrapAlert() - assert.ok(typeof Data.getData($alert[0], 'bs.alert') !== 'undefined') + assert.ok(typeof Alert._getInstance($alert[0]) !== 'undefined') - Data.getData($alert[0], 'bs.alert').dispose() + Alert._getInstance($alert[0]).dispose() - assert.ok(Data.getData($alert[0], 'bs.alert') === null) + assert.ok(Alert._getInstance($alert[0]) === null) }) QUnit.test('should return alert version', function (assert) { diff --git a/js/tests/unit/button.js b/js/tests/unit/button.js index 0fa19d1548..50b531f6f0 100644 --- a/js/tests/unit/button.js +++ b/js/tests/unit/button.js @@ -1,6 +1,8 @@ $(function () { 'use strict' + var Button = typeof window.bootstrap !== 'undefined' ? window.bootstrap.Button : window.Button + QUnit.module('button plugin') QUnit.test('should be defined on jquery object', function (assert) { @@ -208,11 +210,11 @@ $(function () { var $el = $('
') var $button = $el.bootstrapButton() - assert.ok(typeof Data.getData($button[0], 'bs.button') !== 'undefined') + assert.ok(typeof Button._getInstance($button[0]) !== 'undefined') - Data.getData($button[0], 'bs.button').dispose() + Button._getInstance($button[0]).dispose() - assert.ok(Data.getData($button[0], 'bs.button') === null) + assert.ok(Button._getInstance($button[0]) === null) }) QUnit.test('should return button version', function (assert) { diff --git a/js/tests/unit/carousel.js b/js/tests/unit/carousel.js index fd9cf85097..f89dfcf969 100644 --- a/js/tests/unit/carousel.js +++ b/js/tests/unit/carousel.js @@ -475,26 +475,26 @@ $(function () { $carousel.appendTo('body') EventHandler.trigger($('[data-slide]').first()[0], 'click') - assert.strictEqual(Data.getData($carousel[0], 'bs.carousel')._config.interval, 1814) + assert.strictEqual(Carousel._getInstance($carousel[0])._config.interval, 1814) $carousel.remove() $carousel.appendTo('body').attr('data-modal', 'foobar') EventHandler.trigger($('[data-slide]').first()[0], 'click') - assert.strictEqual(Data.getData($carousel[0], 'bs.carousel')._config.interval, 1814, 'even if there is an data-modal attribute set') + assert.strictEqual(Carousel._getInstance($carousel[0])._config.interval, 1814, 'even if there is an data-modal attribute set') $carousel.remove() $carousel.appendTo('body') EventHandler.trigger($('[data-slide]').first()[0], 'click') $carousel.attr('data-interval', 1860) EventHandler.trigger($('[data-slide]').first()[0], 'click') - assert.strictEqual(Data.getData($carousel[0], 'bs.carousel')._config.interval, 1814, 'attributes should be read only on initialization') + assert.strictEqual(Carousel._getInstance($carousel[0])._config.interval, 1814, 'attributes should be read only on initialization') $carousel.bootstrapCarousel('dispose') $carousel.remove() $carousel.attr('data-interval', false) $carousel.appendTo('body') $carousel.bootstrapCarousel(1) - assert.strictEqual(Data.getData($carousel[0], 'bs.carousel')._config.interval, false, 'data attribute has higher priority than default options') + assert.strictEqual(Carousel._getInstance($carousel[0])._config.interval, false, 'data attribute has higher priority than default options') $carousel.remove() }) diff --git a/js/tests/unit/collapse.js b/js/tests/unit/collapse.js index 8dccf5c18e..66cf6af961 100644 --- a/js/tests/unit/collapse.js +++ b/js/tests/unit/collapse.js @@ -1,6 +1,8 @@ $(function () { 'use strict' + var Collapse = typeof window.bootstrap !== 'undefined' ? window.bootstrap.Collapse : window.Collapse + QUnit.module('collapse plugin') QUnit.test('should be defined on jquery object', function (assert) { @@ -473,7 +475,7 @@ $(function () { EventHandler.trigger($target2[0], 'click') $body2.toggleClass('show collapsing') - Data.getData($body2[0], 'bs.collapse')._isTransitioning = true + Collapse._getInstance($body2[0])._isTransitioning = true EventHandler.trigger($target1[0], 'click') diff --git a/js/tests/unit/dropdown.js b/js/tests/unit/dropdown.js index c94b8a4410..c89d5abb29 100644 --- a/js/tests/unit/dropdown.js +++ b/js/tests/unit/dropdown.js @@ -1,6 +1,8 @@ $(function () { 'use strict' + var Dropdown = typeof window.bootstrap !== 'undefined' ? window.bootstrap.Dropdown : window.Dropdown + QUnit.module('dropdowns plugin') QUnit.test('should be defined on jquery object', function (assert) { @@ -1025,7 +1027,7 @@ $(function () { .find('[data-toggle="dropdown"]') .bootstrapDropdown() - var dropdown = Data.getData($dropdown[0], 'bs.dropdown') + var dropdown = Dropdown._getInstance($dropdown[0]) dropdown.toggle() assert.ok(dropdown._popper) @@ -1053,7 +1055,7 @@ $(function () { .find('[data-toggle="dropdown"]') .bootstrapDropdown() - var dropdown = Data.getData($dropdown[0], 'bs.dropdown') + var dropdown = Dropdown._getInstance($dropdown[0]) var spyDetectNavbar = sinon.spy(dropdown, '_detectNavbar') dropdown.update() @@ -1078,7 +1080,7 @@ $(function () { .find('[data-toggle="dropdown"]') .bootstrapDropdown() - var dropdown = Data.getData($dropdown[0], 'bs.dropdown') + var dropdown = Dropdown._getInstance($dropdown[0]) dropdown.toggle() assert.ok(dropdown._popper) @@ -1109,7 +1111,7 @@ $(function () { .find('[data-toggle="dropdown"]') .bootstrapDropdown() - var dropdown = Data.getData($dropdown[0], 'bs.dropdown') + var dropdown = Dropdown._getInstance($dropdown[0]) assert.notOk(dropdown._popper) assert.ok(dropdown._menu !== null) diff --git a/js/tests/unit/popover.js b/js/tests/unit/popover.js index 0dd9c98e39..023db67530 100644 --- a/js/tests/unit/popover.js +++ b/js/tests/unit/popover.js @@ -1,6 +1,8 @@ $(function () { 'use strict' + var Popover = typeof window.bootstrap !== 'undefined' ? window.bootstrap.Popover : window.Popover + QUnit.module('popover plugin') QUnit.test('should be defined on jquery object', function (assert) { @@ -65,7 +67,7 @@ $(function () { assert.expect(1) var $popover = $('@mdo').bootstrapPopover() - assert.ok(Data.getData($popover[0], 'bs.popover'), 'popover instance exists') + assert.ok(Popover._getInstance($popover[0]), 'popover instance exists') }) QUnit.test('should store popover trigger in popover instance data object', function (assert) { @@ -76,7 +78,7 @@ $(function () { $popover.bootstrapPopover('show') - assert.ok(Data.getData($('.popover')[0], 'bs.popover'), 'popover trigger stored in instance data') + assert.ok(Popover._getInstance($('.popover')[0]), 'popover trigger stored in instance data') }) QUnit.test('should get title and content from options', function (assert) { @@ -259,13 +261,13 @@ $(function () { }) .on('click.foo', $.noop) - assert.ok(Data.getData($popover[0], 'bs.popover'), 'popover has data') + assert.ok(Popover._getInstance($popover[0]), 'popover has data') $popover.bootstrapPopover('show') $popover.bootstrapPopover('dispose') assert.ok(!$popover.hasClass('show'), 'popover is hidden') - assert.ok(!$popover.data('popover'), 'popover does not have data') + assert.ok(!Popover._getInstance($popover[0]), 'popover does not have data') }) QUnit.test('should render popover element using delegated selector', function (assert) { @@ -338,7 +340,7 @@ $(function () { assert.ok(false, 'should not fire any popover events') }) .bootstrapPopover('hide') - assert.ok(Data.getData($popover[0], 'bs.popover') === null, 'should not initialize the popover') + assert.ok(Popover._getInstance($popover[0]) === null, 'should not initialize the popover') }) QUnit.test('should fire inserted event', function (assert) { diff --git a/js/tests/unit/scrollspy.js b/js/tests/unit/scrollspy.js index 7470a94c2d..27f899576a 100644 --- a/js/tests/unit/scrollspy.js +++ b/js/tests/unit/scrollspy.js @@ -1,6 +1,8 @@ $(function () { 'use strict' + var ScrollSpy = typeof window.bootstrap !== 'undefined' ? window.bootstrap.ScrollSpy : window.ScrollSpy + QUnit.module('scrollspy plugin') QUnit.test('should be defined on jquery object', function (assert) { @@ -667,7 +669,7 @@ $(function () { } var $target = $('#div-' + type + 'm-2') - var scrollspy = Data.getData($content[0], 'bs.scrollspy') + var scrollspy = ScrollSpy._getInstance($content[0]) assert.ok(scrollspy._offsets[1] === $target.offset().top, 'offset method with ' + type + ' option') assert.ok(scrollspy._offsets[1] !== $target.position().top, 'position method with ' + type + ' option') @@ -714,7 +716,7 @@ $(function () { } var $target = $('#div-' + type + 'm-2') - var scrollspy = Data.getData($content[0], 'bs.scrollspy') + var scrollspy = ScrollSpy._getInstance($content[0]) assert.ok(scrollspy._offsets[1] !== $target.offset().top, 'offset method with ' + type + ' option') assert.ok(scrollspy._offsets[1] === $target.position().top, 'position method with ' + type + ' option') diff --git a/js/tests/unit/tooltip.js b/js/tests/unit/tooltip.js index 081cbb1099..8c6d604b76 100644 --- a/js/tests/unit/tooltip.js +++ b/js/tests/unit/tooltip.js @@ -1,6 +1,8 @@ $(function () { 'use strict' + var Tooltip = typeof window.bootstrap !== 'undefined' ? window.bootstrap.Tooltip : window.Tooltip + QUnit.module('tooltip plugin') QUnit.test('should be defined on jquery object', function (assert) { @@ -122,7 +124,7 @@ $(function () { $tooltip.bootstrapTooltip('hide') }) .one('hidden.bs.tooltip', function () { - assert.strictEqual(Data.getData($tooltip[0], 'bs.tooltip').tip.parentNode, null, 'tooltip removed') + assert.strictEqual(Tooltip._getInstance($tooltip[0]).tip.parentNode, null, 'tooltip removed') done() }) .bootstrapTooltip('show') @@ -143,7 +145,7 @@ $(function () { $tooltip.bootstrapTooltip('hide') }) .one('hidden.bs.tooltip', function () { - assert.strictEqual(Data.getData($tooltip[0], 'bs.tooltip').tip.parentNode, null, 'tooltip removed') + assert.strictEqual(Tooltip._getInstance($tooltip[0]).tip.parentNode, null, 'tooltip removed') done() }) .bootstrapTooltip('show') @@ -205,7 +207,7 @@ $(function () { $tooltip.bootstrapTooltip('hide') }) .one('hidden.bs.tooltip', function () { - assert.strictEqual(Data.getData($tooltip[0], 'bs.tooltip').tip.parentNode, null, 'tooltip removed') + assert.strictEqual(Tooltip._getInstance($tooltip[0]).tip.parentNode, null, 'tooltip removed') done() }) .bootstrapTooltip('show') @@ -336,13 +338,13 @@ $(function () { .bootstrapTooltip() .on('click.foo', function () {}) // eslint-disable-line no-empty-function - assert.ok(Data.getData($tooltip[0], 'bs.tooltip'), 'tooltip has data') + assert.ok(Tooltip._getInstance($tooltip[0]), 'tooltip has data') $tooltip.bootstrapTooltip('show') $tooltip.bootstrapTooltip('dispose') assert.ok(!$tooltip.hasClass('show'), 'tooltip is hidden') - assert.ok(!Data.getData($tooltip[0], 'bs.tooltip'), 'tooltip does not have data') + assert.ok(!Tooltip._getInstance($tooltip[0]), 'tooltip does not have data') }) // QUnit.test('should show tooltip with delegate selector on click', function (assert) { @@ -471,7 +473,7 @@ $(function () { trigger: 'manual' }) .on('inserted.bs.tooltip', function () { - var $tooltip = $(Data.getData(this, 'bs.tooltip').tip) + var $tooltip = $(Tooltip._getInstance(this).tip) assert.ok($tooltip.hasClass('bs-tooltip-right')) assert.ok(typeof $tooltip.attr('style') === 'undefined') $styles.remove() @@ -701,16 +703,16 @@ $(function () { }) setTimeout(function () { - assert.ok($(Data.getData($tooltip[0], 'bs.tooltip').tip).is('.fade.show'), '1ms: tooltip faded active') + assert.ok($(Tooltip._getInstance($tooltip[0]).tip).is('.fade.show'), '1ms: tooltip faded active') EventHandler.trigger($tooltip[0], 'mouseout') setTimeout(function () { - assert.ok($(Data.getData($tooltip[0], 'bs.tooltip').tip).is('.fade.show'), '100ms: tooltip still faded active') + assert.ok($(Tooltip._getInstance($tooltip[0]).tip).is('.fade.show'), '100ms: tooltip still faded active') }, 100) setTimeout(function () { - assert.ok(!$(Data.getData($tooltip[0], 'bs.tooltip').tip).is('.show'), '200ms: tooltip removed') + assert.ok(!$(Tooltip._getInstance($tooltip[0]).tip).is('.show'), '200ms: tooltip removed') done() }, 200) }, 0) @@ -770,7 +772,7 @@ $(function () { title: titleHtml }) - var obj = Data.getData($tooltip[0], 'bs.tooltip') + var obj = Tooltip._getInstance($tooltip[0]) EventHandler.trigger($('#tt-outer')[0], 'mouseover') @@ -796,7 +798,7 @@ $(function () { assert.ok(false, 'should not fire any tooltip events') }) .bootstrapTooltip('hide') - assert.ok(Data.getData($tooltip[0], 'bs.tooltip') === null, 'should not initialize the tooltip') + assert.ok(Tooltip._getInstance($tooltip[0]) === null, 'should not initialize the tooltip') }) QUnit.test('should not remove tooltip if multiple triggers are set and one is still active', function (assert) { @@ -808,7 +810,7 @@ $(function () { animation: false }) - var tooltip = Data.getData($el[0], 'bs.tooltip') + var tooltip = Tooltip._getInstance($el[0]) var $tooltip = $(tooltip.getTipElement()) function showingTooltip() { @@ -852,7 +854,7 @@ $(function () { animation: false }) - var tooltip = Data.getData($el[0], 'bs.tooltip') + var tooltip = Tooltip._getInstance($el[0]) var $tooltip = $(tooltip.getTipElement()) function showingTooltip() { @@ -947,7 +949,7 @@ $(function () { .appendTo('#qunit-fixture') .bootstrapTooltip('show') .on('hidden.bs.tooltip', function () { - var tooltip = Data.getData($el[0], 'bs.tooltip') + var tooltip = Tooltip._getInstance($el[0]) var $tooltip = $(tooltip.getTipElement()) assert.ok($tooltip.hasClass('tooltip')) assert.ok($tooltip.hasClass('fade')) @@ -963,7 +965,7 @@ $(function () { var $el = $('') .appendTo('#qunit-fixture') .on('shown.bs.tooltip', function () { - var tooltip = Data.getData($el[0], 'bs.tooltip') + var tooltip = Tooltip._getInstance($el[0]) var $tooltip = $(tooltip.getTipElement()) assert.strictEqual($tooltip.children().text(), '7') done() @@ -1000,7 +1002,7 @@ $(function () { .appendTo('#qunit-fixture') .bootstrapTooltip() - var tooltip = Data.getData($tooltip[0], 'bs.tooltip') + var tooltip = Tooltip._getInstance($tooltip[0]) tooltip.show() assert.ok(tooltip._popper) @@ -1016,7 +1018,7 @@ $(function () { .appendTo('#qunit-fixture') .bootstrapTooltip() - var tooltip = Data.getData($tooltip[0], 'bs.tooltip') + var tooltip = Tooltip._getInstance($tooltip[0]) tooltip.update() assert.ok(tooltip._popper === null)