From eaab1def7af7d7e1ab32ff69d043b46e2815ca22 Mon Sep 17 00:00:00 2001 From: fat Date: Wed, 13 May 2015 14:46:50 -0700 Subject: [PATCH] add simple type checker implementation --- js/dist/carousel.js | 19 +++++++++++++++++-- js/dist/carousel.js.map | Bin 22606 -> 23310 bytes js/dist/collapse.js | 17 +++++++++++++++-- js/dist/collapse.js.map | Bin 17628 -> 18196 bytes js/dist/modal.js | 18 ++++++++++++++++-- js/dist/modal.js.map | Bin 26637 -> 27283 bytes js/dist/popover.js | 9 +++++++++ js/dist/popover.js.map | Bin 7912 -> 8231 bytes js/dist/scrollspy.js | 8 ++++++++ js/dist/scrollspy.js.map | Bin 15763 -> 16092 bytes js/dist/tab.js | 5 ----- js/dist/tab.js.map | Bin 13774 -> 13676 bytes js/dist/tooltip.js | 22 +++++++++++++++++++++- js/dist/tooltip.js.map | Bin 28676 -> 29587 bytes js/dist/util.js | 24 ++++++++++++++++++++++++ js/dist/util.js.map | Bin 5361 -> 6988 bytes js/src/carousel.js | 16 +++++++++++++++- js/src/collapse.js | 14 +++++++++++++- js/src/modal.js | 15 ++++++++++++++- js/src/popover.js | 8 ++++++++ js/src/scrollspy.js | 8 ++++++++ js/src/tab.js | 4 ---- js/src/tooltip.js | 25 ++++++++++++++++++++++++- js/src/util.js | 28 ++++++++++++++++++++++++++++ js/tests/unit/carousel.js | 30 ++++++++++++++++++++++++++++++ 25 files changed, 250 insertions(+), 20 deletions(-) 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 da3cb6c586e45bef1654f73d912154d7669cb8d4..a1f88ef4534f6016af4457050e565b09ca0f395f 100644 GIT binary patch delta 695 zcmZWl%}N_l6ef{a?ZTf$Xu*P4Het=xPjP^;LrfN?nrI*sdfU_DNJe|1L(RfwKeH#=oRFi0UV=j z_Q%xRwj?Xt z;Bt{9Qy_{sUGE2M^~Lc(46foD1WNBhtm`KczRQunE64y9QIzlghP z(O&6$?p1JP`K80Y2)OI?tbU=Re#LNMV>McS_jhLL8O6TF(=ry`)Ry-XLy9#6%Ps}9 mDnAmPK@z%))iSVH32bZf9>vt=iD2Y8i6jQUMe~~6>RLIkJi7gt_YkIyweg!+@isS=i=x8kgBNR~SrWWlPS_OUq2(tY9Y1x%q~@ GnKS@PAR?Ur 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 8f50a03f36c4abe0af9d571ad3275df928ed045f..af69b6d5c9a553281114b984fe26f2757a30ed29 100644 GIT binary patch delta 579 zcmZWkO-lkn7{*l!qC?q-^vM{3ZVYi~6J04Qhx#LD3sKnd%7$5nvp zKac`Y_g#s3qtKx;R|F_x!i7Rs^C!0wnN!llYey!dt5xje#I+Qd>ca0dhg*Dihbu8x zi>R}V_`CEGOmQ*CD(((KmCoVA?=;au^8E(9uKtsO{V(~gzQqJ{$)4o+;2w|@P1Bm7 zd*_SyVZ|!DA4~g+S@y{CR(O7=Qot2}3a7{iaN*spJx}cBX}dYF*uiC&qAa><*oZ&o z7=}er1|^Oi$D&O;f#spw>`|MEL$c3$rfpo?1Itnqp1S!Oo?#ucpE@!qBaYoRjik4b zoQ|#sOUuX9aRfpVa6c2tTREnJ*er_2{~$6Op&B!-6yr@vhqlg$3@JlN$AK9}zrLf6 YWj)FUJv*RC@TOtQLA1?#8=04>FSc5(IsgCw delta 114 zcmbQz$9Sical=K%&C*PfOp^=cMJDSjuui@wrpQ#}vUv^*yU^wd;!Vs<#=euSrBx zKB;TWX^l4BIS53NRqZ@kE=k4d`u~ZarV{JQ7Ih1`tLwUrtpP-7KpT_Ojq}aYM~Pau zbeu?SS>6hdOgK<4qNv$H9KU`Aro7y!$uDa+l0A4Pv(N{vgI3}sWY@4 z&kch)+hO&_FbHPC_CG~&IQ7R`9Wst5PKJRUy2QyTMQao|i->%#x1vdKOr4@tQO@<` z?e?F_W;~fj&39uF^S~cX#|nn&a1oWUjHkjsZi)}F@ZHApGq8Lw$lF>L tnz4R7oi$^%EuRR-%SRnzr&Tbor1T_(>@EvMya@f=P0p=*iT>i}!@ocz(R%;@ delta 126 zcmbPym9h5%;|6J_%@dgxG4cC4I{NB3J32aAJ32b&IB$+--Nv)|n9x3Pro6DpYqeA+ zuhP%NK8H}Au>5jpKJ31T_MiRZsxaG a*pl<}(lXO03&e_U_OZFby?L+4HAMhI8Y$QS 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 5f29d837d8be04494f7b14112639cf9e08cd5745..edc1301017b50ae24bb15614ab7f5423bd11f576 100644 GIT binary patch delta 230 zcmaE1yWC-e91}~pqht7Fe?~nu5JSh+(b3h~(b2iUMF+_D2l3r?a-AFY3jEx4f*l=$tzp2? WF@5tLsWi6Dn-$A=Hz(;=@dE&|NEovK 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 9e7b58298a2a96db2292503311606243ba44b966..82a86ecb168f0dd46c95923ce9dbcb5fced65440 100644 GIT binary patch delta 60 zcmX??{U&R}e`dz?O)Q55m~y-~zZTieC}HX2uH)|L=xz-Ij*gYSAv*4p7qW?NzAR?I Kwb@Q_f(QVsQWSOo delta 119 zcmaEpbuN3ue`dz~O)Q557)vG}7ExsmcXZu+L}WLkM2@e!j=Q6yyEP0rI-2>0=(tZ_ z$R@gZjhF$Kz5);wmn4>CCM%?;mMFNSrX`l7 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 ce5ce2d804ec36055102594d4c9a0a9cbc9adb7b..e823424cb71ebd8ad1152c437a1cf8e9176a2066 100644 GIT binary patch delta 866 zcmZut!D@s z1>L)NU*HD_g<1LmF5UP6?z+?uaN)Z%N!qB{=~&inyjIIZfs`tMIEF(ruYO@Ie#;{_OOvkhmNxIXS*G! zyo@`l&g;^e7WROz07@1xK}+$^hJH7)9%a*8J^6)t54!I+j8UyKja~5j+ z6?4KpO%7rUjV_YRSxXUpio<;_WD;aSI}fZr6}PrmkBoZ?ZCA(%8rxxL&vMcC;t zPqXSHWY*&LfL`CY*yic}X7rifH4AO&dJm*7m<_lyVu9$neKTnn4{AWYGA|oHwVlS* R7kB$A$}V_2(br*l{SW490m%RW delta 118 zcmbRIoU!Er(5(eYv4i^PKYQm8EZYBIFtrv`QG0(F=Iww8dD< z=t3*B0eW+K4d}fHPG;XoFE&Qp0GO@f@f4j+t~D(r#3txpoSM z3^9>RmrsA)z8`U84rUT`K3k?8PJ7_>zA1{4c^lYa8V%LZ6Z>7>y7p z(dUo^DcFYr&Q=p^tBgP6`2)uH@-W)o^^fJxx1wB*gncrJ z<$6+Pdv4A@A61HertT4PGW0sikseX<-GwhK**?vXvCa?@2sI2mVvQ%6tddm~I9@N? zX}K;|Wd|+Q>07yaJ+x}A?Mt?O+-z)Ra+%uG29&>+BN6acC@kce)V(=6C$wEL5T5!c zBdDl7X^^WcqV;4w*%ew{{`$q;7NEJ75-cwclrap|2nJ*y)JzVra}O=}=1>0mUu zYk&}EGtK6!8P;i$Glv$BUcNVr_)}={j-li;jv!7t`eB&Ei@D(n#P<&sobI z3IhSxq~*WW8a)|NcRDA;GB6t(#0jIsIgDj81n;^D;5UV_bu(2V+)&Kb#7Aw~7l|Hw zVnp7Go_+3HX5M&Q*Z~nL5E8*anhluaMKR;4ejrCgx7$G=11nXa^Ud|~-0{GQy!XL% zvm%pS(j;389S1&cW_@jaoR~YQuaP5oIdi(Ai|0>tdgg%6{|(fJ&moy5ZAv0|n9!JQ NPD}S5O}{+&_y<^k*lPd) delta 26 icmX?O_EB?#IOApsrX}o~cXPKfZ`Ky@=a~FjDiHvPo(Y`* 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()