0
0
mirror of https://github.com/twbs/bootstrap.git synced 2025-02-07 04:54:24 +01:00

add simple type checker implementation

This commit is contained in:
fat 2015-05-13 14:46:50 -07:00
parent c2ced2292a
commit eaab1def7a
25 changed files with 258 additions and 28 deletions

19
js/dist/carousel.js vendored
View File

@ -35,6 +35,14 @@ var Carousel = (function ($) {
wrap: true wrap: true
}; };
var DefaultType = {
interval: '(number|boolean)',
keyboard: 'boolean',
slide: '(boolean|string)',
pause: 'string',
wrap: 'boolean'
};
var Direction = { var Direction = {
NEXT: 'next', NEXT: 'next',
PREVIOUS: 'prev' PREVIOUS: 'prev'
@ -86,7 +94,7 @@ var Carousel = (function ($) {
this._isPaused = false; this._isPaused = false;
this._isSliding = false; this._isSliding = false;
this._config = config; this._config = this._getConfig(config);
this._element = $(element)[0]; this._element = $(element)[0];
this._indicatorsElement = $(this._element).find(Selector.INDICATORS)[0]; this._indicatorsElement = $(this._element).find(Selector.INDICATORS)[0];
@ -187,10 +195,17 @@ var Carousel = (function ($) {
this._indicatorsElement = null; this._indicatorsElement = null;
} }
}, { }, {
key: '_addEventListeners', key: '_getConfig',
// private // private
value: function _getConfig(config) {
config = $.extend({}, Default, config);
Util.typeCheckConfig(NAME, config, DefaultType);
return config;
}
}, {
key: '_addEventListeners',
value: function _addEventListeners() { value: function _addEventListeners() {
if (this._config.keyboard) { if (this._config.keyboard) {
$(this._element).on(Event.KEYDOWN, $.proxy(this._keydown, this)); $(this._element).on(Event.KEYDOWN, $.proxy(this._keydown, this));

File diff suppressed because one or more lines are too long

17
js/dist/collapse.js vendored
View File

@ -32,6 +32,11 @@ var Collapse = (function ($) {
parent: null parent: null
}; };
var DefaultType = {
toggle: 'boolean',
parent: '(string|null)'
};
var Event = { var Event = {
SHOW: 'show' + EVENT_KEY, SHOW: 'show' + EVENT_KEY,
SHOWN: 'shown' + EVENT_KEY, SHOWN: 'shown' + EVENT_KEY,
@ -69,7 +74,7 @@ var Collapse = (function ($) {
this._isTransitioning = false; this._isTransitioning = false;
this._element = element; 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._triggerArray = $.makeArray($('[data-toggle="collapse"][href="#' + element.id + '"],' + ('[data-toggle="collapse"][data-target="#' + element.id + '"]')));
this._parent = this._config.parent ? this._getParent() : null; this._parent = this._config.parent ? this._getParent() : null;
@ -230,10 +235,18 @@ var Collapse = (function ($) {
this._isTransitioning = null; this._isTransitioning = null;
} }
}, { }, {
key: '_getDimension', key: '_getConfig',
// private // 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() { value: function _getDimension() {
var hasWidth = $(this._element).hasClass(Dimension.WIDTH); var hasWidth = $(this._element).hasClass(Dimension.WIDTH);
return hasWidth ? Dimension.WIDTH : Dimension.HEIGHT; return hasWidth ? Dimension.WIDTH : Dimension.HEIGHT;

File diff suppressed because one or more lines are too long

18
js/dist/modal.js vendored
View File

@ -35,6 +35,13 @@ var Modal = (function ($) {
show: true show: true
}; };
var DefaultType = {
backdrop: '(boolean|string)',
keyboard: 'boolean',
focus: 'boolean',
show: 'boolean'
};
var Event = { var Event = {
HIDE: 'hide' + EVENT_KEY, HIDE: 'hide' + EVENT_KEY,
HIDDEN: 'hidden' + EVENT_KEY, HIDDEN: 'hidden' + EVENT_KEY,
@ -73,7 +80,7 @@ var Modal = (function ($) {
function Modal(element, config) { function Modal(element, config) {
_classCallCheck(this, Modal); _classCallCheck(this, Modal);
this._config = config; this._config = this._getConfig(config);
this._element = element; this._element = element;
this._dialog = $(element).find(Selector.DIALOG)[0]; this._dialog = $(element).find(Selector.DIALOG)[0];
this._backdrop = null; this._backdrop = null;
@ -184,10 +191,17 @@ var Modal = (function ($) {
this._scrollbarWidth = null; this._scrollbarWidth = null;
} }
}, { }, {
key: '_showElement', key: '_getConfig',
// private // private
value: function _getConfig(config) {
config = $.extend({}, Default, config);
Util.typeCheckConfig(NAME, config, DefaultType);
return config;
}
}, {
key: '_showElement',
value: function _showElement(relatedTarget) { value: function _showElement(relatedTarget) {
var _this2 = this; var _this2 = this;

File diff suppressed because one or more lines are too long

9
js/dist/popover.js vendored
View File

@ -34,6 +34,10 @@ var Popover = (function ($) {
template: '<div class="popover" role="tooltip">' + '<div class="popover-arrow"></div>' + '<h3 class="popover-title"></h3>' + '<div class="popover-content"></div></div>' template: '<div class="popover" role="tooltip">' + '<div class="popover-arrow"></div>' + '<h3 class="popover-title"></h3>' + '<div class="popover-content"></div></div>'
}); });
var DefaultType = $.extend({}, Tooltip.DefaultType, {
content: '(string|function)'
});
var ClassName = { var ClassName = {
FADE: 'fade', FADE: 'fade',
IN: 'in' IN: 'in'
@ -148,6 +152,11 @@ var Popover = (function ($) {
get: function () { get: function () {
return EVENT_KEY; return EVENT_KEY;
} }
}, {
key: 'DefaultType',
get: function () {
return DefaultType;
}
}, { }, {
key: '_jQueryInterface', key: '_jQueryInterface',

File diff suppressed because one or more lines are too long

View File

@ -32,6 +32,12 @@ var ScrollSpy = (function ($) {
target: '' target: ''
}; };
var DefaultType = {
offset: 'number',
method: 'string',
target: '(string|element)'
};
var Event = { var Event = {
ACTIVATE: 'activate' + EVENT_KEY, ACTIVATE: 'activate' + EVENT_KEY,
SCROLL: 'scroll' + EVENT_KEY, SCROLL: 'scroll' + EVENT_KEY,
@ -155,6 +161,8 @@ var ScrollSpy = (function ($) {
config.target = '#' + id; config.target = '#' + id;
} }
Util.typeCheckConfig(NAME, config, DefaultType);
return config; return config;
} }
}, { }, {

File diff suppressed because one or more lines are too long

5
js/dist/tab.js vendored
View File

@ -219,11 +219,6 @@ var Tab = (function ($) {
get: function () { get: function () {
return VERSION; return VERSION;
} }
}, {
key: 'Default',
get: function () {
return Default;
}
}, { }, {
key: '_jQueryInterface', key: '_jQueryInterface',

2
js/dist/tab.js.map vendored

File diff suppressed because one or more lines are too long

22
js/dist/tooltip.js vendored
View File

@ -37,7 +37,20 @@ var Tooltip = (function ($) {
selector: false, selector: false,
placement: 'top', placement: 'top',
offset: '0 0', 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 = { var AttachmentMap = {
@ -476,6 +489,8 @@ var Tooltip = (function ($) {
}; };
} }
Util.typeCheckConfig(NAME, config, this.constructor.DefaultType);
return config; return config;
} }
}, { }, {
@ -527,6 +542,11 @@ var Tooltip = (function ($) {
get: function () { get: function () {
return EVENT_KEY; return EVENT_KEY;
} }
}, {
key: 'DefaultType',
get: function () {
return DefaultType;
}
}, { }, {
key: '_jQueryInterface', key: '_jQueryInterface',

File diff suppressed because one or more lines are too long

24
js/dist/util.js vendored
View File

@ -24,6 +24,15 @@ var Util = (function ($) {
transition: 'transitionend' 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() { function getSpecialTransitionEndEvent() {
return { return {
bindType: transition.end, bindType: transition.end,
@ -116,6 +125,21 @@ var Util = (function ($) {
supportsTransitionEnd: function supportsTransitionEnd() { supportsTransitionEnd: function supportsTransitionEnd() {
return !!transition; 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 + '".'));
}
}
} }
}; };

2
js/dist/util.js.map vendored

File diff suppressed because one or more lines are too long

View File

@ -33,6 +33,14 @@ const Carousel = (($) => {
wrap : true wrap : true
} }
const DefaultType = {
interval : '(number|boolean)',
keyboard : 'boolean',
slide : '(boolean|string)',
pause : '(string|boolean)',
wrap : 'boolean'
}
const Direction = { const Direction = {
NEXT : 'next', NEXT : 'next',
PREVIOUS : 'prev' PREVIOUS : 'prev'
@ -84,7 +92,7 @@ const Carousel = (($) => {
this._isPaused = false this._isPaused = false
this._isSliding = false this._isSliding = false
this._config = config this._config = this._getConfig(config)
this._element = $(element)[0] this._element = $(element)[0]
this._indicatorsElement = $(this._element).find(Selector.INDICATORS)[0] this._indicatorsElement = $(this._element).find(Selector.INDICATORS)[0]
@ -193,6 +201,12 @@ const Carousel = (($) => {
// private // private
_getConfig(config) {
config = $.extend({}, Default, config)
Util.typeCheckConfig(NAME, config, DefaultType)
return config
}
_addEventListeners() { _addEventListeners() {
if (this._config.keyboard) { if (this._config.keyboard) {
$(this._element) $(this._element)

View File

@ -30,6 +30,11 @@ const Collapse = (($) => {
parent : null parent : null
} }
const DefaultType = {
toggle : 'boolean',
parent : '(string|null)'
}
const Event = { const Event = {
SHOW : `show${EVENT_KEY}`, SHOW : `show${EVENT_KEY}`,
SHOWN : `shown${EVENT_KEY}`, SHOWN : `shown${EVENT_KEY}`,
@ -67,7 +72,7 @@ const Collapse = (($) => {
constructor(element, config) { constructor(element, config) {
this._isTransitioning = false this._isTransitioning = false
this._element = element this._element = element
this._config = $.extend({}, Default, config) this._config = this._getConfig(config)
this._triggerArray = $.makeArray($( this._triggerArray = $.makeArray($(
`[data-toggle="collapse"][href="#${element.id}"],` + `[data-toggle="collapse"][href="#${element.id}"],` +
`[data-toggle="collapse"][data-target="#${element.id}"]` `[data-toggle="collapse"][data-target="#${element.id}"]`
@ -259,6 +264,13 @@ const Collapse = (($) => {
// private // private
_getConfig(config) {
config = $.extend({}, Default, config)
config.toggle = !!config.toggle // coerce string values
Util.typeCheckConfig(NAME, config, DefaultType)
return config
}
_getDimension() { _getDimension() {
let hasWidth = $(this._element).hasClass(Dimension.WIDTH) let hasWidth = $(this._element).hasClass(Dimension.WIDTH)
return hasWidth ? Dimension.WIDTH : Dimension.HEIGHT return hasWidth ? Dimension.WIDTH : Dimension.HEIGHT

View File

@ -33,6 +33,13 @@ const Modal = (($) => {
show : true show : true
} }
const DefaultType = {
backdrop : '(boolean|string)',
keyboard : 'boolean',
focus : 'boolean',
show : 'boolean'
}
const Event = { const Event = {
HIDE   : `hide${EVENT_KEY}`, HIDE   : `hide${EVENT_KEY}`,
HIDDEN   : `hidden${EVENT_KEY}`, HIDDEN   : `hidden${EVENT_KEY}`,
@ -71,7 +78,7 @@ const Modal = (($) => {
class Modal { class Modal {
constructor(element, config) { constructor(element, config) {
this._config = config this._config = this._getConfig(config)
this._element = element this._element = element
this._dialog = $(element).find(Selector.DIALOG)[0] this._dialog = $(element).find(Selector.DIALOG)[0]
this._backdrop = null this._backdrop = null
@ -198,6 +205,12 @@ const Modal = (($) => {
// private // private
_getConfig(config) {
config = $.extend({}, Default, config)
Util.typeCheckConfig(NAME, config, DefaultType)
return config
}
_showElement(relatedTarget) { _showElement(relatedTarget) {
let transition = Util.supportsTransitionEnd() && let transition = Util.supportsTransitionEnd() &&
$(this._element).hasClass(ClassName.FADE) $(this._element).hasClass(ClassName.FADE)

View File

@ -33,6 +33,10 @@ const Popover = (($) => {
+ '<div class="popover-content"></div></div>' + '<div class="popover-content"></div></div>'
}) })
const DefaultType = $.extend({}, Tooltip.DefaultType, {
content : '(string|function)'
})
const ClassName = { const ClassName = {
FADE : 'fade', FADE : 'fade',
IN : 'in' IN : 'in'
@ -93,6 +97,10 @@ const Popover = (($) => {
return EVENT_KEY return EVENT_KEY
} }
static get DefaultType() {
return DefaultType
}
// overrides // overrides

View File

@ -30,6 +30,12 @@ const ScrollSpy = (($) => {
target : '' target : ''
} }
const DefaultType = {
offset : 'number',
method : 'string',
target : '(string|element)'
}
const Event = { const Event = {
ACTIVATE : `activate${EVENT_KEY}`, ACTIVATE : `activate${EVENT_KEY}`,
SCROLL : `scroll${EVENT_KEY}`, SCROLL : `scroll${EVENT_KEY}`,
@ -164,6 +170,8 @@ const ScrollSpy = (($) => {
config.target = `#${id}` config.target = `#${id}`
} }
Util.typeCheckConfig(NAME, config, DefaultType)
return config return config
} }

View File

@ -72,10 +72,6 @@ const Tab = (($) => {
return VERSION return VERSION
} }
static get Default() {
return Default
}
// public // public

View File

@ -37,7 +37,20 @@ const Tooltip = (($) => {
selector : false, selector : false,
placement : 'top', placement : 'top',
offset : '0 0', 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 = { const AttachmentMap = {
@ -141,6 +154,10 @@ const Tooltip = (($) => {
return EVENT_KEY return EVENT_KEY
} }
static get DefaultType() {
return DefaultType
}
// public // public
@ -544,6 +561,12 @@ const Tooltip = (($) => {
} }
} }
Util.typeCheckConfig(
NAME,
config,
this.constructor.DefaultType
)
return config return config
} }

View File

@ -23,6 +23,15 @@ const Util = (($) => {
transition : 'transitionend' 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() { function getSpecialTransitionEndEvent() {
return { return {
bindType: transition.end, bindType: transition.end,
@ -115,6 +124,25 @@ const Util = (($) => {
supportsTransitionEnd() { supportsTransitionEnd() {
return !!transition 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}".`)
}
}
} }
} }

View File

@ -32,6 +32,36 @@ $(function () {
assert.strictEqual($carousel[0], $el[0], 'collection contains element') 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 {
$('<div/>').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 {
$('<div/>').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) { QUnit.test('should not fire slid when slide is prevented', function (assert) {
assert.expect(1) assert.expect(1)
var done = assert.async() var done = assert.async()