| Total Complexity | 89 |
| Complexity/F | 2.78 |
| Lines of Code | 422 |
| Function Count | 32 |
| Duplicated Lines | 18 |
| Ratio | 4.27 % |
| Changes | 1 | ||
| Bugs | 0 | Features | 0 |
Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
Complex classes like resources/bootstrap/js/collapse.js often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
| 1 | /*! |
||
| 6 | (function (global, factory) { |
||
| 7 | typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('jquery'), require('./util.js')) : |
||
| 8 | typeof define === 'function' && define.amd ? define(['jquery', './util.js'], factory) : |
||
|
|
|||
| 9 | (global = global || self, global.Collapse = factory(global.jQuery, global.Util)); |
||
| 10 | }(this, function ($, Util) { 'use strict'; |
||
| 11 | |||
| 12 | $ = $ && $.hasOwnProperty('default') ? $['default'] : $; |
||
| 13 | Util = Util && Util.hasOwnProperty('default') ? Util['default'] : Util; |
||
| 14 | |||
| 15 | function _defineProperties(target, props) { |
||
| 16 | for (var i = 0; i < props.length; i++) { |
||
| 17 | var descriptor = props[i]; |
||
| 18 | descriptor.enumerable = descriptor.enumerable || false; |
||
| 19 | descriptor.configurable = true; |
||
| 20 | if ("value" in descriptor) descriptor.writable = true; |
||
| 21 | Object.defineProperty(target, descriptor.key, descriptor); |
||
| 22 | } |
||
| 23 | } |
||
| 24 | |||
| 25 | function _createClass(Constructor, protoProps, staticProps) { |
||
| 26 | if (protoProps) _defineProperties(Constructor.prototype, protoProps); |
||
| 27 | if (staticProps) _defineProperties(Constructor, staticProps); |
||
| 28 | return Constructor; |
||
| 29 | } |
||
| 30 | |||
| 31 | function _defineProperty(obj, key, value) { |
||
| 32 | if (key in obj) { |
||
| 33 | Object.defineProperty(obj, key, { |
||
| 34 | value: value, |
||
| 35 | enumerable: true, |
||
| 36 | configurable: true, |
||
| 37 | writable: true |
||
| 38 | }); |
||
| 39 | } else { |
||
| 40 | obj[key] = value; |
||
| 41 | } |
||
| 42 | |||
| 43 | return obj; |
||
| 44 | } |
||
| 45 | |||
| 46 | View Code Duplication | function _objectSpread(target) { |
|
| 47 | for (var i = 1; i < arguments.length; i++) { |
||
| 48 | var source = arguments[i] != null ? arguments[i] : {}; |
||
| 49 | var ownKeys = Object.keys(source); |
||
| 50 | |||
| 51 | if (typeof Object.getOwnPropertySymbols === 'function') { |
||
| 52 | ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { |
||
| 53 | return Object.getOwnPropertyDescriptor(source, sym).enumerable; |
||
| 54 | })); |
||
| 55 | } |
||
| 56 | |||
| 57 | ownKeys.forEach(function (key) { |
||
| 58 | _defineProperty(target, key, source[key]); |
||
| 59 | }); |
||
| 60 | } |
||
| 61 | |||
| 62 | return target; |
||
| 63 | } |
||
| 64 | |||
| 65 | /** |
||
| 66 | * ------------------------------------------------------------------------ |
||
| 67 | * Constants |
||
| 68 | * ------------------------------------------------------------------------ |
||
| 69 | */ |
||
| 70 | |||
| 71 | var NAME = 'collapse'; |
||
| 72 | var VERSION = '4.3.1'; |
||
| 73 | var DATA_KEY = 'bs.collapse'; |
||
| 74 | var EVENT_KEY = "." + DATA_KEY; |
||
| 75 | var DATA_API_KEY = '.data-api'; |
||
| 76 | var JQUERY_NO_CONFLICT = $.fn[NAME]; |
||
| 77 | var Default = { |
||
| 78 | toggle: true, |
||
| 79 | parent: '' |
||
| 80 | }; |
||
| 81 | var DefaultType = { |
||
| 82 | toggle: 'boolean', |
||
| 83 | parent: '(string|element)' |
||
| 84 | }; |
||
| 85 | var Event = { |
||
| 86 | SHOW: "show" + EVENT_KEY, |
||
| 87 | SHOWN: "shown" + EVENT_KEY, |
||
| 88 | HIDE: "hide" + EVENT_KEY, |
||
| 89 | HIDDEN: "hidden" + EVENT_KEY, |
||
| 90 | CLICK_DATA_API: "click" + EVENT_KEY + DATA_API_KEY |
||
| 91 | }; |
||
| 92 | var ClassName = { |
||
| 93 | SHOW: 'show', |
||
| 94 | COLLAPSE: 'collapse', |
||
| 95 | COLLAPSING: 'collapsing', |
||
| 96 | COLLAPSED: 'collapsed' |
||
| 97 | }; |
||
| 98 | var Dimension = { |
||
| 99 | WIDTH: 'width', |
||
| 100 | HEIGHT: 'height' |
||
| 101 | }; |
||
| 102 | var Selector = { |
||
| 103 | ACTIVES: '.show, .collapsing', |
||
| 104 | DATA_TOGGLE: '[data-toggle="collapse"]' |
||
| 105 | /** |
||
| 106 | * ------------------------------------------------------------------------ |
||
| 107 | * Class Definition |
||
| 108 | * ------------------------------------------------------------------------ |
||
| 109 | */ |
||
| 110 | |||
| 111 | }; |
||
| 112 | |||
| 113 | var Collapse = |
||
| 114 | /*#__PURE__*/ |
||
| 115 | function () { |
||
| 116 | function Collapse(element, config) { |
||
| 117 | this._isTransitioning = false; |
||
| 118 | this._element = element; |
||
| 119 | this._config = this._getConfig(config); |
||
| 120 | this._triggerArray = [].slice.call(document.querySelectorAll("[data-toggle=\"collapse\"][href=\"#" + element.id + "\"]," + ("[data-toggle=\"collapse\"][data-target=\"#" + element.id + "\"]"))); |
||
| 121 | var toggleList = [].slice.call(document.querySelectorAll(Selector.DATA_TOGGLE)); |
||
| 122 | |||
| 123 | for (var i = 0, len = toggleList.length; i < len; i++) { |
||
| 124 | var elem = toggleList[i]; |
||
| 125 | var selector = Util.getSelectorFromElement(elem); |
||
| 126 | var filterElement = [].slice.call(document.querySelectorAll(selector)).filter(function (foundElem) { |
||
| 127 | return foundElem === element; |
||
| 128 | }); |
||
| 129 | |||
| 130 | if (selector !== null && filterElement.length > 0) { |
||
| 131 | this._selector = selector; |
||
| 132 | |||
| 133 | this._triggerArray.push(elem); |
||
| 134 | } |
||
| 135 | } |
||
| 136 | |||
| 137 | this._parent = this._config.parent ? this._getParent() : null; |
||
| 138 | |||
| 139 | if (!this._config.parent) { |
||
| 140 | this._addAriaAndCollapsedClass(this._element, this._triggerArray); |
||
| 141 | } |
||
| 142 | |||
| 143 | if (this._config.toggle) { |
||
| 144 | this.toggle(); |
||
| 145 | } |
||
| 146 | } // Getters |
||
| 147 | |||
| 148 | |||
| 149 | var _proto = Collapse.prototype; |
||
| 150 | |||
| 151 | // Public |
||
| 152 | _proto.toggle = function toggle() { |
||
| 153 | if ($(this._element).hasClass(ClassName.SHOW)) { |
||
| 154 | this.hide(); |
||
| 155 | } else { |
||
| 156 | this.show(); |
||
| 157 | } |
||
| 158 | }; |
||
| 159 | |||
| 160 | _proto.show = function show() { |
||
| 161 | var _this = this; |
||
| 162 | |||
| 163 | if (this._isTransitioning || $(this._element).hasClass(ClassName.SHOW)) { |
||
| 164 | return; |
||
| 165 | } |
||
| 166 | |||
| 167 | var actives; |
||
| 168 | var activesData; |
||
| 169 | |||
| 170 | if (this._parent) { |
||
| 171 | actives = [].slice.call(this._parent.querySelectorAll(Selector.ACTIVES)).filter(function (elem) { |
||
| 172 | if (typeof _this._config.parent === 'string') { |
||
| 173 | return elem.getAttribute('data-parent') === _this._config.parent; |
||
| 174 | } |
||
| 175 | |||
| 176 | return elem.classList.contains(ClassName.COLLAPSE); |
||
| 177 | }); |
||
| 178 | |||
| 179 | if (actives.length === 0) { |
||
| 180 | actives = null; |
||
| 181 | } |
||
| 182 | } |
||
| 183 | |||
| 184 | if (actives) { |
||
| 185 | activesData = $(actives).not(this._selector).data(DATA_KEY); |
||
| 186 | |||
| 187 | if (activesData && activesData._isTransitioning) { |
||
| 188 | return; |
||
| 189 | } |
||
| 190 | } |
||
| 191 | |||
| 192 | var startEvent = $.Event(Event.SHOW); |
||
| 193 | $(this._element).trigger(startEvent); |
||
| 194 | |||
| 195 | if (startEvent.isDefaultPrevented()) { |
||
| 196 | return; |
||
| 197 | } |
||
| 198 | |||
| 199 | if (actives) { |
||
| 200 | Collapse._jQueryInterface.call($(actives).not(this._selector), 'hide'); |
||
| 201 | |||
| 202 | if (!activesData) { |
||
| 203 | $(actives).data(DATA_KEY, null); |
||
| 204 | } |
||
| 205 | } |
||
| 206 | |||
| 207 | var dimension = this._getDimension(); |
||
| 208 | |||
| 209 | $(this._element).removeClass(ClassName.COLLAPSE).addClass(ClassName.COLLAPSING); |
||
| 210 | this._element.style[dimension] = 0; |
||
| 211 | |||
| 212 | if (this._triggerArray.length) { |
||
| 213 | $(this._triggerArray).removeClass(ClassName.COLLAPSED).attr('aria-expanded', true); |
||
| 214 | } |
||
| 215 | |||
| 216 | this.setTransitioning(true); |
||
| 217 | |||
| 218 | var complete = function complete() { |
||
| 219 | $(_this._element).removeClass(ClassName.COLLAPSING).addClass(ClassName.COLLAPSE).addClass(ClassName.SHOW); |
||
| 220 | _this._element.style[dimension] = ''; |
||
| 221 | |||
| 222 | _this.setTransitioning(false); |
||
| 223 | |||
| 224 | $(_this._element).trigger(Event.SHOWN); |
||
| 225 | }; |
||
| 226 | |||
| 227 | var capitalizedDimension = dimension[0].toUpperCase() + dimension.slice(1); |
||
| 228 | var scrollSize = "scroll" + capitalizedDimension; |
||
| 229 | var transitionDuration = Util.getTransitionDurationFromElement(this._element); |
||
| 230 | $(this._element).one(Util.TRANSITION_END, complete).emulateTransitionEnd(transitionDuration); |
||
| 231 | this._element.style[dimension] = this._element[scrollSize] + "px"; |
||
| 232 | }; |
||
| 233 | |||
| 234 | _proto.hide = function hide() { |
||
| 235 | var _this2 = this; |
||
| 236 | |||
| 237 | if (this._isTransitioning || !$(this._element).hasClass(ClassName.SHOW)) { |
||
| 238 | return; |
||
| 239 | } |
||
| 240 | |||
| 241 | var startEvent = $.Event(Event.HIDE); |
||
| 242 | $(this._element).trigger(startEvent); |
||
| 243 | |||
| 244 | if (startEvent.isDefaultPrevented()) { |
||
| 245 | return; |
||
| 246 | } |
||
| 247 | |||
| 248 | var dimension = this._getDimension(); |
||
| 249 | |||
| 250 | this._element.style[dimension] = this._element.getBoundingClientRect()[dimension] + "px"; |
||
| 251 | Util.reflow(this._element); |
||
| 252 | $(this._element).addClass(ClassName.COLLAPSING).removeClass(ClassName.COLLAPSE).removeClass(ClassName.SHOW); |
||
| 253 | var triggerArrayLength = this._triggerArray.length; |
||
| 254 | |||
| 255 | if (triggerArrayLength > 0) { |
||
| 256 | for (var i = 0; i < triggerArrayLength; i++) { |
||
| 257 | var trigger = this._triggerArray[i]; |
||
| 258 | var selector = Util.getSelectorFromElement(trigger); |
||
| 259 | |||
| 260 | if (selector !== null) { |
||
| 261 | var $elem = $([].slice.call(document.querySelectorAll(selector))); |
||
| 262 | |||
| 263 | if (!$elem.hasClass(ClassName.SHOW)) { |
||
| 264 | $(trigger).addClass(ClassName.COLLAPSED).attr('aria-expanded', false); |
||
| 265 | } |
||
| 266 | } |
||
| 267 | } |
||
| 268 | } |
||
| 269 | |||
| 270 | this.setTransitioning(true); |
||
| 271 | |||
| 272 | var complete = function complete() { |
||
| 273 | _this2.setTransitioning(false); |
||
| 274 | |||
| 275 | $(_this2._element).removeClass(ClassName.COLLAPSING).addClass(ClassName.COLLAPSE).trigger(Event.HIDDEN); |
||
| 276 | }; |
||
| 277 | |||
| 278 | this._element.style[dimension] = ''; |
||
| 279 | var transitionDuration = Util.getTransitionDurationFromElement(this._element); |
||
| 280 | $(this._element).one(Util.TRANSITION_END, complete).emulateTransitionEnd(transitionDuration); |
||
| 281 | }; |
||
| 282 | |||
| 283 | _proto.setTransitioning = function setTransitioning(isTransitioning) { |
||
| 284 | this._isTransitioning = isTransitioning; |
||
| 285 | }; |
||
| 286 | |||
| 287 | _proto.dispose = function dispose() { |
||
| 288 | $.removeData(this._element, DATA_KEY); |
||
| 289 | this._config = null; |
||
| 290 | this._parent = null; |
||
| 291 | this._element = null; |
||
| 292 | this._triggerArray = null; |
||
| 293 | this._isTransitioning = null; |
||
| 294 | } // Private |
||
| 295 | ; |
||
| 296 | |||
| 297 | _proto._getConfig = function _getConfig(config) { |
||
| 298 | config = _objectSpread({}, Default, config); |
||
| 299 | config.toggle = Boolean(config.toggle); // Coerce string values |
||
| 300 | |||
| 301 | Util.typeCheckConfig(NAME, config, DefaultType); |
||
| 302 | return config; |
||
| 303 | }; |
||
| 304 | |||
| 305 | _proto._getDimension = function _getDimension() { |
||
| 306 | var hasWidth = $(this._element).hasClass(Dimension.WIDTH); |
||
| 307 | return hasWidth ? Dimension.WIDTH : Dimension.HEIGHT; |
||
| 308 | }; |
||
| 309 | |||
| 310 | _proto._getParent = function _getParent() { |
||
| 311 | var _this3 = this; |
||
| 312 | |||
| 313 | var parent; |
||
| 314 | |||
| 315 | if (Util.isElement(this._config.parent)) { |
||
| 316 | parent = this._config.parent; // It's a jQuery object |
||
| 317 | |||
| 318 | if (typeof this._config.parent.jquery !== 'undefined') { |
||
| 319 | parent = this._config.parent[0]; |
||
| 320 | } |
||
| 321 | } else { |
||
| 322 | parent = document.querySelector(this._config.parent); |
||
| 323 | } |
||
| 324 | |||
| 325 | var selector = "[data-toggle=\"collapse\"][data-parent=\"" + this._config.parent + "\"]"; |
||
| 326 | var children = [].slice.call(parent.querySelectorAll(selector)); |
||
| 327 | $(children).each(function (i, element) { |
||
| 328 | _this3._addAriaAndCollapsedClass(Collapse._getTargetFromElement(element), [element]); |
||
| 329 | }); |
||
| 330 | return parent; |
||
| 331 | }; |
||
| 332 | |||
| 333 | _proto._addAriaAndCollapsedClass = function _addAriaAndCollapsedClass(element, triggerArray) { |
||
| 334 | var isOpen = $(element).hasClass(ClassName.SHOW); |
||
| 335 | |||
| 336 | if (triggerArray.length) { |
||
| 337 | $(triggerArray).toggleClass(ClassName.COLLAPSED, !isOpen).attr('aria-expanded', isOpen); |
||
| 338 | } |
||
| 339 | } // Static |
||
| 340 | ; |
||
| 341 | |||
| 342 | Collapse._getTargetFromElement = function _getTargetFromElement(element) { |
||
| 343 | var selector = Util.getSelectorFromElement(element); |
||
| 344 | return selector ? document.querySelector(selector) : null; |
||
| 345 | }; |
||
| 346 | |||
| 347 | Collapse._jQueryInterface = function _jQueryInterface(config) { |
||
| 348 | return this.each(function () { |
||
| 349 | var $this = $(this); |
||
| 350 | var data = $this.data(DATA_KEY); |
||
| 351 | |||
| 352 | var _config = _objectSpread({}, Default, $this.data(), typeof config === 'object' && config ? config : {}); |
||
| 353 | |||
| 354 | if (!data && _config.toggle && /show|hide/.test(config)) { |
||
| 355 | _config.toggle = false; |
||
| 356 | } |
||
| 357 | |||
| 358 | if (!data) { |
||
| 359 | data = new Collapse(this, _config); |
||
| 360 | $this.data(DATA_KEY, data); |
||
| 361 | } |
||
| 362 | |||
| 363 | if (typeof config === 'string') { |
||
| 364 | if (typeof data[config] === 'undefined') { |
||
| 365 | throw new TypeError("No method named \"" + config + "\""); |
||
| 366 | } |
||
| 367 | |||
| 368 | data[config](); |
||
| 369 | } |
||
| 370 | }); |
||
| 371 | }; |
||
| 372 | |||
| 373 | _createClass(Collapse, null, [{ |
||
| 374 | key: "VERSION", |
||
| 375 | get: function get() { |
||
| 376 | return VERSION; |
||
| 377 | } |
||
| 378 | }, { |
||
| 379 | key: "Default", |
||
| 380 | get: function get() { |
||
| 381 | return Default; |
||
| 382 | } |
||
| 383 | }]); |
||
| 384 | |||
| 385 | return Collapse; |
||
| 386 | }(); |
||
| 387 | /** |
||
| 388 | * ------------------------------------------------------------------------ |
||
| 389 | * Data Api implementation |
||
| 390 | * ------------------------------------------------------------------------ |
||
| 391 | */ |
||
| 392 | |||
| 393 | |||
| 394 | $(document).on(Event.CLICK_DATA_API, Selector.DATA_TOGGLE, function (event) { |
||
| 395 | // preventDefault only for <a> elements (which change the URL) not inside the collapsible element |
||
| 396 | if (event.currentTarget.tagName === 'A') { |
||
| 397 | event.preventDefault(); |
||
| 398 | } |
||
| 399 | |||
| 400 | var $trigger = $(this); |
||
| 401 | var selector = Util.getSelectorFromElement(this); |
||
| 402 | var selectors = [].slice.call(document.querySelectorAll(selector)); |
||
| 403 | $(selectors).each(function () { |
||
| 404 | var $target = $(this); |
||
| 405 | var data = $target.data(DATA_KEY); |
||
| 406 | var config = data ? 'toggle' : $trigger.data(); |
||
| 407 | |||
| 408 | Collapse._jQueryInterface.call($target, config); |
||
| 409 | }); |
||
| 410 | }); |
||
| 411 | /** |
||
| 412 | * ------------------------------------------------------------------------ |
||
| 413 | * jQuery |
||
| 414 | * ------------------------------------------------------------------------ |
||
| 415 | */ |
||
| 416 | |||
| 417 | $.fn[NAME] = Collapse._jQueryInterface; |
||
| 418 | $.fn[NAME].Constructor = Collapse; |
||
| 419 | |||
| 420 | $.fn[NAME].noConflict = function () { |
||
| 421 | $.fn[NAME] = JQUERY_NO_CONFLICT; |
||
| 422 | return Collapse._jQueryInterface; |
||
| 423 | }; |
||
| 424 | |||
| 425 | return Collapse; |
||
| 426 | |||
| 427 | })); |
||
| 428 | //# sourceMappingURL=collapse.js.map |
||
| 429 |
This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.
To learn more about declaring variables in Javascript, see the MDN.