diff --git a/js/dist/carousel.js b/js/dist/carousel.js
index 87b5cc086..d12b95331 100644
--- a/js/dist/carousel.js
+++ b/js/dist/carousel.js
@@ -35,6 +35,14 @@ var Carousel = (function ($) {
wrap: true
};
+ var DefaultType = {
+ interval: '(number|boolean)',
+ keyboard: 'boolean',
+ slide: '(boolean|string)',
+ pause: 'string',
+ wrap: 'boolean'
+ };
+
var Direction = {
NEXT: 'next',
PREVIOUS: 'prev'
@@ -86,7 +94,7 @@ var Carousel = (function ($) {
this._isPaused = false;
this._isSliding = false;
- this._config = config;
+ this._config = this._getConfig(config);
this._element = $(element)[0];
this._indicatorsElement = $(this._element).find(Selector.INDICATORS)[0];
@@ -187,10 +195,17 @@ var Carousel = (function ($) {
this._indicatorsElement = null;
}
}, {
- key: '_addEventListeners',
+ key: '_getConfig',
// private
+ value: function _getConfig(config) {
+ config = $.extend({}, Default, config);
+ Util.typeCheckConfig(NAME, config, DefaultType);
+ return config;
+ }
+ }, {
+ key: '_addEventListeners',
value: function _addEventListeners() {
if (this._config.keyboard) {
$(this._element).on(Event.KEYDOWN, $.proxy(this._keydown, this));
diff --git a/js/dist/carousel.js.map b/js/dist/carousel.js.map
index da3cb6c58..a1f88ef45 100644
Binary files a/js/dist/carousel.js.map and b/js/dist/carousel.js.map differ
diff --git a/js/dist/collapse.js b/js/dist/collapse.js
index 0d014da2e..596e8f9f7 100644
--- a/js/dist/collapse.js
+++ b/js/dist/collapse.js
@@ -32,6 +32,11 @@ var Collapse = (function ($) {
parent: null
};
+ var DefaultType = {
+ toggle: 'boolean',
+ parent: '(string|null)'
+ };
+
var Event = {
SHOW: 'show' + EVENT_KEY,
SHOWN: 'shown' + EVENT_KEY,
@@ -69,7 +74,7 @@ var Collapse = (function ($) {
this._isTransitioning = false;
this._element = element;
- this._config = $.extend({}, Default, config);
+ this._config = this._getConfig(config);
this._triggerArray = $.makeArray($('[data-toggle="collapse"][href="#' + element.id + '"],' + ('[data-toggle="collapse"][data-target="#' + element.id + '"]')));
this._parent = this._config.parent ? this._getParent() : null;
@@ -230,10 +235,18 @@ var Collapse = (function ($) {
this._isTransitioning = null;
}
}, {
- key: '_getDimension',
+ key: '_getConfig',
// private
+ value: function _getConfig(config) {
+ config = $.extend({}, Default, config);
+ config.toggle = !!config.toggle;
+ Util.typeCheckConfig(NAME, config, DefaultType);
+ return config;
+ }
+ }, {
+ key: '_getDimension',
value: function _getDimension() {
var hasWidth = $(this._element).hasClass(Dimension.WIDTH);
return hasWidth ? Dimension.WIDTH : Dimension.HEIGHT;
diff --git a/js/dist/collapse.js.map b/js/dist/collapse.js.map
index 8f50a03f3..af69b6d5c 100644
Binary files a/js/dist/collapse.js.map and b/js/dist/collapse.js.map differ
diff --git a/js/dist/modal.js b/js/dist/modal.js
index 80a493777..2a6387725 100644
--- a/js/dist/modal.js
+++ b/js/dist/modal.js
@@ -35,6 +35,13 @@ var Modal = (function ($) {
show: true
};
+ var DefaultType = {
+ backdrop: '(boolean|string)',
+ keyboard: 'boolean',
+ focus: 'boolean',
+ show: 'boolean'
+ };
+
var Event = {
HIDE: 'hide' + EVENT_KEY,
HIDDEN: 'hidden' + EVENT_KEY,
@@ -73,7 +80,7 @@ var Modal = (function ($) {
function Modal(element, config) {
_classCallCheck(this, Modal);
- this._config = config;
+ this._config = this._getConfig(config);
this._element = element;
this._dialog = $(element).find(Selector.DIALOG)[0];
this._backdrop = null;
@@ -184,10 +191,17 @@ var Modal = (function ($) {
this._scrollbarWidth = null;
}
}, {
- key: '_showElement',
+ key: '_getConfig',
// private
+ value: function _getConfig(config) {
+ config = $.extend({}, Default, config);
+ Util.typeCheckConfig(NAME, config, DefaultType);
+ return config;
+ }
+ }, {
+ key: '_showElement',
value: function _showElement(relatedTarget) {
var _this2 = this;
diff --git a/js/dist/modal.js.map b/js/dist/modal.js.map
index 247fbab7d..5d10cd6d3 100644
Binary files a/js/dist/modal.js.map and b/js/dist/modal.js.map differ
diff --git a/js/dist/popover.js b/js/dist/popover.js
index b9ef35586..8a30917c3 100644
--- a/js/dist/popover.js
+++ b/js/dist/popover.js
@@ -34,6 +34,10 @@ var Popover = (function ($) {
template: '
'
});
+ var DefaultType = $.extend({}, Tooltip.DefaultType, {
+ content: '(string|function)'
+ });
+
var ClassName = {
FADE: 'fade',
IN: 'in'
@@ -148,6 +152,11 @@ var Popover = (function ($) {
get: function () {
return EVENT_KEY;
}
+ }, {
+ key: 'DefaultType',
+ get: function () {
+ return DefaultType;
+ }
}, {
key: '_jQueryInterface',
diff --git a/js/dist/popover.js.map b/js/dist/popover.js.map
index 5f29d837d..edc130101 100644
Binary files a/js/dist/popover.js.map and b/js/dist/popover.js.map differ
diff --git a/js/dist/scrollspy.js b/js/dist/scrollspy.js
index 47774a95b..b214f86ee 100644
--- a/js/dist/scrollspy.js
+++ b/js/dist/scrollspy.js
@@ -32,6 +32,12 @@ var ScrollSpy = (function ($) {
target: ''
};
+ var DefaultType = {
+ offset: 'number',
+ method: 'string',
+ target: '(string|element)'
+ };
+
var Event = {
ACTIVATE: 'activate' + EVENT_KEY,
SCROLL: 'scroll' + EVENT_KEY,
@@ -155,6 +161,8 @@ var ScrollSpy = (function ($) {
config.target = '#' + id;
}
+ Util.typeCheckConfig(NAME, config, DefaultType);
+
return config;
}
}, {
diff --git a/js/dist/scrollspy.js.map b/js/dist/scrollspy.js.map
index 3f2a3c41f..bde96eda3 100644
Binary files a/js/dist/scrollspy.js.map and b/js/dist/scrollspy.js.map differ
diff --git a/js/dist/tab.js b/js/dist/tab.js
index d2935869d..95c561940 100644
--- a/js/dist/tab.js
+++ b/js/dist/tab.js
@@ -219,11 +219,6 @@ var Tab = (function ($) {
get: function () {
return VERSION;
}
- }, {
- key: 'Default',
- get: function () {
- return Default;
- }
}, {
key: '_jQueryInterface',
diff --git a/js/dist/tab.js.map b/js/dist/tab.js.map
index 9e7b58298..82a86ecb1 100644
Binary files a/js/dist/tab.js.map and b/js/dist/tab.js.map differ
diff --git a/js/dist/tooltip.js b/js/dist/tooltip.js
index b4db8deca..864e0bb76 100644
--- a/js/dist/tooltip.js
+++ b/js/dist/tooltip.js
@@ -37,7 +37,20 @@ var Tooltip = (function ($) {
selector: false,
placement: 'top',
offset: '0 0',
- constraints: null
+ constraints: []
+ };
+
+ var DefaultType = {
+ animation: 'boolean',
+ template: 'string',
+ title: '(string|function)',
+ trigger: 'string',
+ delay: '(number|object)',
+ html: 'boolean',
+ selector: '(string|boolean)',
+ placement: '(string|function)',
+ offset: 'string',
+ constraints: 'array'
};
var AttachmentMap = {
@@ -476,6 +489,8 @@ var Tooltip = (function ($) {
};
}
+ Util.typeCheckConfig(NAME, config, this.constructor.DefaultType);
+
return config;
}
}, {
@@ -527,6 +542,11 @@ var Tooltip = (function ($) {
get: function () {
return EVENT_KEY;
}
+ }, {
+ key: 'DefaultType',
+ get: function () {
+ return DefaultType;
+ }
}, {
key: '_jQueryInterface',
diff --git a/js/dist/tooltip.js.map b/js/dist/tooltip.js.map
index ce5ce2d80..e823424cb 100644
Binary files a/js/dist/tooltip.js.map and b/js/dist/tooltip.js.map differ
diff --git a/js/dist/util.js b/js/dist/util.js
index f3654eacf..a135c3f38 100644
--- a/js/dist/util.js
+++ b/js/dist/util.js
@@ -24,6 +24,15 @@ var Util = (function ($) {
transition: 'transitionend'
};
+ // shoutout AngusCroll (https://goo.gl/pxwQGp)
+ function toType(obj) {
+ return ({}).toString.call(obj).match(/\s([a-zA-Z]+)/)[1].toLowerCase();
+ }
+
+ function isElement(obj) {
+ return (obj[0] || obj).nodeType;
+ }
+
function getSpecialTransitionEndEvent() {
return {
bindType: transition.end,
@@ -116,6 +125,21 @@ var Util = (function ($) {
supportsTransitionEnd: function supportsTransitionEnd() {
return !!transition;
+ },
+
+ typeCheckConfig: function typeCheckConfig(componentName, config, configTypes) {
+
+ for (var property in configTypes) {
+ var expectedTypes = configTypes[property];
+ var value = config[property];
+ var valueType = undefined;
+
+ if (value && isElement(value)) valueType = 'element';else valueType = toType(value);
+
+ if (!new RegExp(expectedTypes).test(valueType)) {
+ throw new Error('' + componentName.toUpperCase() + ': ' + ('Option "' + property + '" provided type "' + valueType + '" ') + ('but expected type "' + expectedTypes + '".'));
+ }
+ }
}
};
diff --git a/js/dist/util.js.map b/js/dist/util.js.map
index 34275a5b7..7c73c492e 100644
Binary files a/js/dist/util.js.map and b/js/dist/util.js.map differ
diff --git a/js/src/carousel.js b/js/src/carousel.js
index a4b6da298..c11f0a599 100644
--- a/js/src/carousel.js
+++ b/js/src/carousel.js
@@ -33,6 +33,14 @@ const Carousel = (($) => {
wrap : true
}
+ const DefaultType = {
+ interval : '(number|boolean)',
+ keyboard : 'boolean',
+ slide : '(boolean|string)',
+ pause : '(string|boolean)',
+ wrap : 'boolean'
+ }
+
const Direction = {
NEXT : 'next',
PREVIOUS : 'prev'
@@ -84,7 +92,7 @@ const Carousel = (($) => {
this._isPaused = false
this._isSliding = false
- this._config = config
+ this._config = this._getConfig(config)
this._element = $(element)[0]
this._indicatorsElement = $(this._element).find(Selector.INDICATORS)[0]
@@ -193,6 +201,12 @@ const Carousel = (($) => {
// private
+ _getConfig(config) {
+ config = $.extend({}, Default, config)
+ Util.typeCheckConfig(NAME, config, DefaultType)
+ return config
+ }
+
_addEventListeners() {
if (this._config.keyboard) {
$(this._element)
diff --git a/js/src/collapse.js b/js/src/collapse.js
index ded623448..6a5fcd854 100644
--- a/js/src/collapse.js
+++ b/js/src/collapse.js
@@ -30,6 +30,11 @@ const Collapse = (($) => {
parent : null
}
+ const DefaultType = {
+ toggle : 'boolean',
+ parent : '(string|null)'
+ }
+
const Event = {
SHOW : `show${EVENT_KEY}`,
SHOWN : `shown${EVENT_KEY}`,
@@ -67,7 +72,7 @@ const Collapse = (($) => {
constructor(element, config) {
this._isTransitioning = false
this._element = element
- this._config = $.extend({}, Default, config)
+ this._config = this._getConfig(config)
this._triggerArray = $.makeArray($(
`[data-toggle="collapse"][href="#${element.id}"],` +
`[data-toggle="collapse"][data-target="#${element.id}"]`
@@ -259,6 +264,13 @@ const Collapse = (($) => {
// private
+ _getConfig(config) {
+ config = $.extend({}, Default, config)
+ config.toggle = !!config.toggle // coerce string values
+ Util.typeCheckConfig(NAME, config, DefaultType)
+ return config
+ }
+
_getDimension() {
let hasWidth = $(this._element).hasClass(Dimension.WIDTH)
return hasWidth ? Dimension.WIDTH : Dimension.HEIGHT
diff --git a/js/src/modal.js b/js/src/modal.js
index 084c4ec3a..2ca603b23 100644
--- a/js/src/modal.js
+++ b/js/src/modal.js
@@ -33,6 +33,13 @@ const Modal = (($) => {
show : true
}
+ const DefaultType = {
+ backdrop : '(boolean|string)',
+ keyboard : 'boolean',
+ focus : 'boolean',
+ show : 'boolean'
+ }
+
const Event = {
HIDE : `hide${EVENT_KEY}`,
HIDDEN : `hidden${EVENT_KEY}`,
@@ -71,7 +78,7 @@ const Modal = (($) => {
class Modal {
constructor(element, config) {
- this._config = config
+ this._config = this._getConfig(config)
this._element = element
this._dialog = $(element).find(Selector.DIALOG)[0]
this._backdrop = null
@@ -198,6 +205,12 @@ const Modal = (($) => {
// private
+ _getConfig(config) {
+ config = $.extend({}, Default, config)
+ Util.typeCheckConfig(NAME, config, DefaultType)
+ return config
+ }
+
_showElement(relatedTarget) {
let transition = Util.supportsTransitionEnd() &&
$(this._element).hasClass(ClassName.FADE)
diff --git a/js/src/popover.js b/js/src/popover.js
index eab4c7e63..31c7a3ae1 100644
--- a/js/src/popover.js
+++ b/js/src/popover.js
@@ -33,6 +33,10 @@ const Popover = (($) => {
+ ''
})
+ const DefaultType = $.extend({}, Tooltip.DefaultType, {
+ content : '(string|function)'
+ })
+
const ClassName = {
FADE : 'fade',
IN : 'in'
@@ -93,6 +97,10 @@ const Popover = (($) => {
return EVENT_KEY
}
+ static get DefaultType() {
+ return DefaultType
+ }
+
// overrides
diff --git a/js/src/scrollspy.js b/js/src/scrollspy.js
index bb639f91b..a407511f6 100644
--- a/js/src/scrollspy.js
+++ b/js/src/scrollspy.js
@@ -30,6 +30,12 @@ const ScrollSpy = (($) => {
target : ''
}
+ const DefaultType = {
+ offset : 'number',
+ method : 'string',
+ target : '(string|element)'
+ }
+
const Event = {
ACTIVATE : `activate${EVENT_KEY}`,
SCROLL : `scroll${EVENT_KEY}`,
@@ -164,6 +170,8 @@ const ScrollSpy = (($) => {
config.target = `#${id}`
}
+ Util.typeCheckConfig(NAME, config, DefaultType)
+
return config
}
diff --git a/js/src/tab.js b/js/src/tab.js
index 61c062d89..4d8d7dec8 100644
--- a/js/src/tab.js
+++ b/js/src/tab.js
@@ -72,10 +72,6 @@ const Tab = (($) => {
return VERSION
}
- static get Default() {
- return Default
- }
-
// public
diff --git a/js/src/tooltip.js b/js/src/tooltip.js
index 42639895e..5d62e154a 100644
--- a/js/src/tooltip.js
+++ b/js/src/tooltip.js
@@ -37,7 +37,20 @@ const Tooltip = (($) => {
selector : false,
placement : 'top',
offset : '0 0',
- constraints : null
+ constraints : []
+ }
+
+ const DefaultType = {
+ animation : 'boolean',
+ template : 'string',
+ title : '(string|function)',
+ trigger : 'string',
+ delay : '(number|object)',
+ html : 'boolean',
+ selector : '(string|boolean)',
+ placement : '(string|function)',
+ offset : 'string',
+ constraints : 'array'
}
const AttachmentMap = {
@@ -141,6 +154,10 @@ const Tooltip = (($) => {
return EVENT_KEY
}
+ static get DefaultType() {
+ return DefaultType
+ }
+
// public
@@ -544,6 +561,12 @@ const Tooltip = (($) => {
}
}
+ Util.typeCheckConfig(
+ NAME,
+ config,
+ this.constructor.DefaultType
+ )
+
return config
}
diff --git a/js/src/util.js b/js/src/util.js
index c9ffbe555..86bea6578 100644
--- a/js/src/util.js
+++ b/js/src/util.js
@@ -23,6 +23,15 @@ const Util = (($) => {
transition : 'transitionend'
}
+ // shoutout AngusCroll (https://goo.gl/pxwQGp)
+ function toType(obj) {
+ return ({}).toString.call(obj).match(/\s([a-zA-Z]+)/)[1].toLowerCase()
+ }
+
+ function isElement(obj) {
+ return (obj[0] || obj).nodeType;
+ }
+
function getSpecialTransitionEndEvent() {
return {
bindType: transition.end,
@@ -115,6 +124,25 @@ const Util = (($) => {
supportsTransitionEnd() {
return !!transition
+ },
+
+ typeCheckConfig(componentName, config, configTypes) {
+
+ for (let property in configTypes) {
+ let expectedTypes = configTypes[property]
+ let value = config[property]
+ let valueType
+
+ if (value && isElement(value)) valueType = 'element'
+ else valueType = toType(value)
+
+ if (!new RegExp(expectedTypes).test(valueType)) {
+ throw new Error(
+ `${componentName.toUpperCase()}: ` +
+ `Option "${property}" provided type "${valueType}" ` +
+ `but expected type "${expectedTypes}".`)
+ }
+ }
}
}
diff --git a/js/tests/unit/carousel.js b/js/tests/unit/carousel.js
index a8a36ad32..95345c39e 100644
--- a/js/tests/unit/carousel.js
+++ b/js/tests/unit/carousel.js
@@ -32,6 +32,36 @@ $(function () {
assert.strictEqual($carousel[0], $el[0], 'collection contains element')
})
+ QUnit.test('should type check config options', function (assert) {
+ var message
+ var expectedMessage = 'CAROUSEL: Option "interval" provided type "string" but expected type "(number|boolean)".'
+ var config = {
+ interval: 'fat sux'
+ }
+
+ try {
+ $('').bootstrapCarousel(config)
+ } catch (e) {
+ message = e.message
+ }
+
+ assert.ok(message === expectedMessage, 'correct error message')
+
+ config = {
+ keyboard: $('div')
+ }
+ expectedMessage = 'CAROUSEL: Option "keyboard" provided type "element" but expected type "boolean".'
+
+ try {
+ $('').bootstrapCarousel(config)
+ } catch (e) {
+ message = e.message
+ }
+
+ assert.ok(message === expectedMessage, 'correct error message')
+ })
+
+
QUnit.test('should not fire slid when slide is prevented', function (assert) {
assert.expect(1)
var done = assert.async()