diff --git a/Gruntfile.js b/Gruntfile.js index f3c3af8f8..09268ecb1 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -56,27 +56,16 @@ module.exports = function (grunt) { docs: 'docs/dist' }, - jshint: { + babel: { options: { - jshintrc: 'js/.jshintrc' + sourceMap: true, + modules: 'ignore' }, - grunt: { - options: { - jshintrc: 'grunt/.jshintrc' - }, - src: ['Gruntfile.js', 'grunt/*.js'] - }, - core: { - src: 'js/*.js' - }, - test: { - options: { - jshintrc: 'js/tests/unit/.jshintrc' - }, - src: 'js/tests/unit/*.js' - }, - assets: { - src: ['docs/assets/js/src/*.js', 'docs/assets/js/*.js', '!docs/assets/js/*.min.js'] + dist: { + files: { + 'js/dist/util.js': 'js/src/util.js', + 'js/dist/alert.js': 'js/src/alert.js' + } } }, @@ -85,19 +74,22 @@ module.exports = function (grunt) { config: 'js/.jscsrc' }, grunt: { - src: '<%= jshint.grunt.src %>' + src: ['Gruntfile.js', 'grunt/*.js'] }, core: { - src: '<%= jshint.core.src %>' + src: 'js/*.js' + }, + es6: { + src: 'js/src/*.js' }, test: { - src: '<%= jshint.test.src %>' + src: 'js/tests/unit/*.js' }, assets: { options: { requireCamelCaseOrUpperCaseIdentifiers: null }, - src: '<%= jshint.assets.src %>' + src: ['docs/assets/js/src/*.js', 'docs/assets/js/*.js', '!docs/assets/js/*.min.js'] } }, @@ -301,12 +293,12 @@ module.exports = function (grunt) { watch: { src: { - files: '<%= jshint.core.src %>', - tasks: ['jshint:core', 'qunit', 'concat'] + files: '<%= jscs.core.src %>', + tasks: ['qunit', 'concat'] }, test: { - files: '<%= jshint.test.src %>', - tasks: ['jshint:test', 'qunit'] + files: '<%= jscs.test.src %>', + tasks: ['qunit'] }, sass: { files: 'scss/**/*.scss', @@ -398,7 +390,7 @@ module.exports = function (grunt) { testSubtasks.push('saucelabs-qunit'); } grunt.registerTask('test', testSubtasks); - grunt.registerTask('test-js', ['jshint:core', 'jshint:test', 'jshint:grunt', 'jscs:core', 'jscs:test', 'jscs:grunt', 'qunit']); + grunt.registerTask('test-js', ['jscs:core', 'jscs:test', 'jscs:grunt', 'qunit']); // JS distribution task. grunt.registerTask('dist-js', ['concat', 'uglify:core', 'commonjs']); @@ -434,7 +426,7 @@ module.exports = function (grunt) { // Docs task. grunt.registerTask('docs-css', ['autoprefixer:docs', 'autoprefixer:examples', 'csscomb:docs', 'csscomb:examples', 'cssmin:docs']); grunt.registerTask('docs-js', ['uglify:docsJs']); - grunt.registerTask('lint-docs-js', ['jshint:assets', 'jscs:assets']); + grunt.registerTask('lint-docs-js', ['jscs:assets']); grunt.registerTask('docs', ['docs-css', 'docs-js', 'lint-docs-js', 'clean:docs', 'copy:docs']); grunt.registerTask('docs-github', ['jekyll:github']); diff --git a/js/.jscsrc b/js/.jscsrc index 9544d2d2b..8d5ef3e56 100644 --- a/js/.jscsrc +++ b/js/.jscsrc @@ -1,4 +1,5 @@ { + "esnext": true, "disallowEmptyBlocks": true, "disallowKeywords": ["with"], "disallowMixedSpacesAndTabs": true, diff --git a/js/.jshintrc b/js/.jshintrc deleted file mode 100644 index a59e1d00a..000000000 --- a/js/.jshintrc +++ /dev/null @@ -1,15 +0,0 @@ -{ - "asi" : true, - "browser" : true, - "eqeqeq" : false, - "eqnull" : true, - "es3" : true, - "expr" : true, - "jquery" : true, - "latedef" : true, - "laxbreak" : true, - "nonbsp" : true, - "strict" : true, - "undef" : true, - "unused" : true -} diff --git a/js/alert.js b/js/alert.js deleted file mode 100644 index 1925ef011..000000000 --- a/js/alert.js +++ /dev/null @@ -1,94 +0,0 @@ -/* ======================================================================== - * Bootstrap: alert.js v3.3.4 - * http://getbootstrap.com/javascript/#alerts - * ======================================================================== - * Copyright 2011-2015 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - * ======================================================================== */ - - -+function ($) { - 'use strict'; - - // ALERT CLASS DEFINITION - // ====================== - - var dismiss = '[data-dismiss="alert"]' - var Alert = function (el) { - $(el).on('click', dismiss, this.close) - } - - Alert.VERSION = '3.3.4' - - Alert.TRANSITION_DURATION = 150 - - Alert.prototype.close = function (e) { - var $this = $(this) - var selector = $this.attr('data-target') - - if (!selector) { - selector = $this.attr('href') - selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7 - } - - var $parent = $(selector) - - if (e) e.preventDefault() - - if (!$parent.length) { - $parent = $this.closest('.alert') - } - - $parent.trigger(e = $.Event('close.bs.alert')) - - if (e.isDefaultPrevented()) return - - $parent.removeClass('in') - - function removeElement() { - // detach from parent, fire event then clean up data - $parent.detach().trigger('closed.bs.alert').remove() - } - - $.support.transition && $parent.hasClass('fade') ? - $parent - .one('bsTransitionEnd', removeElement) - .emulateTransitionEnd(Alert.TRANSITION_DURATION) : - removeElement() - } - - - // ALERT PLUGIN DEFINITION - // ======================= - - function Plugin(option) { - return this.each(function () { - var $this = $(this) - var data = $this.data('bs.alert') - - if (!data) $this.data('bs.alert', (data = new Alert(this))) - if (typeof option == 'string') data[option].call($this) - }) - } - - var old = $.fn.alert - - $.fn.alert = Plugin - $.fn.alert.Constructor = Alert - - - // ALERT NO CONFLICT - // ================= - - $.fn.alert.noConflict = function () { - $.fn.alert = old - return this - } - - - // ALERT DATA-API - // ============== - - $(document).on('click.bs.alert.data-api', dismiss, Alert.prototype.close) - -}(jQuery); diff --git a/js/dist/alert.js b/js/dist/alert.js new file mode 100644 index 000000000..0dbfe767b --- /dev/null +++ b/js/dist/alert.js @@ -0,0 +1,170 @@ +'use strict'; + +var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } + +/** + * -------------------------------------------------------------------------- + * Constants + * -------------------------------------------------------------------------- + */ + +var NAME = 'alert'; +var VERSION = '4.0.0'; +var DATA_KEY = 'bs.alert'; +var JQUERY_NO_CONFLICT = $.fn[NAME]; +var TRANSITION_DURATION = 150; + +var Selector = { + DISMISS: '[data-dismiss="alert"]' +}; + +var Event = { + CLOSE: 'close.bs.alert', + CLOSED: 'closed.bs.alert', + CLICK: 'click.bs.alert.data-api' +}; + +var ClassName = { + ALERT: 'alert', + FADE: 'fade', + IN: 'in' +}; + +/** + * -------------------------------------------------------------------------- + * Class Definition + * -------------------------------------------------------------------------- + */ + +var Alert = (function () { + function Alert(element) { + _classCallCheck(this, Alert); + + if (element) { + this.element = element; + } + } + + _createClass(Alert, [{ + key: 'close', + + // public + + value: function close(element) { + var rootElement = this._getRootElement(element); + var customEvent = this._triggerCloseEvent(rootElement); + + if (customEvent.isDefaultPrevented()) { + return; + } + + this._removeElement(rootElement); + } + }, { + key: '_getRootElement', + + // private + + value: function _getRootElement(element) { + var parent = false; + var selector = Util.getSelectorFromElement(element); + + if (selector) { + parent = $(selector)[0]; + } + + if (!parent) { + parent = $(element).closest('.' + ClassName.ALERT)[0]; + } + + return parent; + } + }, { + key: '_triggerCloseEvent', + value: function _triggerCloseEvent(element) { + var closeEvent = $.Event(Event.CLOSE); + $(element).trigger(closeEvent); + return closeEvent; + } + }, { + key: '_removeElement', + value: function _removeElement(element) { + $(element).removeClass(ClassName.IN); + + if (!Util.supportsTransitionEnd() || !$(element).hasClass(ClassName.FADE)) { + this._destroyElement(element); + return; + } + + $(element).one(Util.TRANSITION_END, this._destroyElement.bind(this, element)).emulateTransitionEnd(TRANSITION_DURATION); + } + }, { + key: '_destroyElement', + value: function _destroyElement(element) { + $(element).detach().trigger(Event.CLOSED).remove(); + } + }], [{ + key: '_jQueryInterface', + + // static + + value: function _jQueryInterface(config) { + return this.each(function () { + var $element = $(this); + var data = $element.data(DATA_KEY); + + if (!data) { + data = new Alert(this); + $element.data(DATA_KEY, data); + } + + if (config === 'close') { + data[config](this); + } + }); + } + }, { + key: '_handleDismiss', + value: function _handleDismiss(alertInstance) { + return function (event) { + if (event) { + event.preventDefault(); + } + + alertInstance.close(this); + }; + } + }]); + + return Alert; +})(); + +/** + * -------------------------------------------------------------------------- + * Data Api implementation + * -------------------------------------------------------------------------- + */ + +$(document).on(Event.CLICK, Selector.DISMISS, Alert._handleDismiss(new Alert())); + +/** + * -------------------------------------------------------------------------- + * jQuery + * -------------------------------------------------------------------------- + */ + +$.fn[NAME] = Alert._jQueryInterface; +$.fn[NAME].Constructor = Alert; +$.fn[NAME].noConflict = function () { + $.fn[NAME] = Alert._JQUERY_NO_CONFLICT; + return Alert._jQueryInterface; +}; +/** + * -------------------------------------------------------------------------- + * Bootstrap (v4.0.0): alert.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * -------------------------------------------------------------------------- + */ +//# sourceMappingURL=alert.js.map \ No newline at end of file diff --git a/js/dist/alert.js.map b/js/dist/alert.js.map new file mode 100644 index 000000000..d2d9be6b6 Binary files /dev/null and b/js/dist/alert.js.map differ diff --git a/js/dist/util.js b/js/dist/util.js new file mode 100644 index 000000000..48865c5d7 --- /dev/null +++ b/js/dist/util.js @@ -0,0 +1,124 @@ +/** + * -------------------------------------------------------------------------- + * Bootstrap (v4.0.0): util.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * -------------------------------------------------------------------------- + */ + +/** + * ------------------------------------------------------------------------ + * Public Util Api + * ------------------------------------------------------------------------ + */ + +'use strict'; + +var Util = { + + TRANSITION_END: 'bsTransitionEnd', + + getUID: function getUID(prefix) { + do prefix += ~ ~(Math.random() * 1000000); while (document.getElementById(prefix)); + return prefix; + }, + + getSelectorFromElement: function getSelectorFromElement(element) { + var selector = element.getAttribute('data-target'); + + if (!selector) { + selector = element.getAttribute('href') || ''; + selector = /^#[a-z]/i.test(selector) ? selector : null; + } + + return selector; + }, + + reflow: function reflow(element) { + new Function('bs', 'return bs')(element.offsetHeight); + }, + + supportsTransitionEnd: function supportsTransitionEnd() { + return !!transition; + } + +}; + +/** + * ------------------------------------------------------------------------ + * Private TransitionEnd Helpers + * ------------------------------------------------------------------------ + */ + +var transition = false; + +var TransitionEndEvent = { + WebkitTransition: 'webkitTransitionEnd', + MozTransition: 'transitionend', + OTransition: 'oTransitionEnd otransitionend', + transition: 'transitionend' +}; + +function getSpecialTransitionEndEvent() { + return { + bindType: transition.end, + delegateType: transition.end, + handle: function handle(event) { + if ($(event.target).is(this)) { + return event.handleObj.handler.apply(this, arguments); + } + } + }; +} + +function transitionEndTest() { + if (window.QUnit) { + return false; + } + + var el = document.createElement('bootstrap'); + + for (var name in TransitionEndEvent) { + if (el.style[name] !== undefined) { + return { end: TransitionEndEvent[name] }; + } + } + + return false; +} + +function transitionEndEmulator(duration) { + var _this = this; + + var called = false; + + $(this).one(Util.TRANSITION_END, function () { + called = true; + }); + + setTimeout(function () { + if (!called) { + $(_this).trigger(transition.end); + } + }, duration); + + return this; +} + +function setTransitionEndSupport() { + transition = transitionEndTest(); + + $.fn.emulateTransitionEnd = transitionEndEmulator; + + if (Util.supportsTransitionEnd()) { + $.event.special[Util.TRANSITION_END] = getSpecialTransitionEndEvent(); + } +} + +setTransitionEndSupport(); + +/** + * ------------------------------------------------------------------------ + * Export Api + * ------------------------------------------------------------------------ + */ +//# sourceMappingURL=util.js.map \ No newline at end of file diff --git a/js/dist/util.js.map b/js/dist/util.js.map new file mode 100644 index 000000000..5de0badbd Binary files /dev/null and b/js/dist/util.js.map differ diff --git a/js/src/alert.js b/js/src/alert.js new file mode 100644 index 000000000..bd12b1a55 --- /dev/null +++ b/js/src/alert.js @@ -0,0 +1,168 @@ +/** + * -------------------------------------------------------------------------- + * Bootstrap (v4.0.0): alert.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * -------------------------------------------------------------------------- + */ + +import Util from 'util' + + +/** + * -------------------------------------------------------------------------- + * Constants + * -------------------------------------------------------------------------- + */ + +const NAME = 'alert' +const VERSION = '4.0.0' +const DATA_KEY = 'bs.alert' +const JQUERY_NO_CONFLICT = $.fn[NAME] +const TRANSITION_DURATION = 150 + +const Selector = { + DISMISS : '[data-dismiss="alert"]' +} + +const Event = { + CLOSE : 'close.bs.alert', + CLOSED : 'closed.bs.alert', + CLICK : 'click.bs.alert.data-api' +} + +const ClassName = { + ALERT : 'alert', + FADE : 'fade', + IN : 'in' +} + + +/** + * -------------------------------------------------------------------------- + * Class Definition + * -------------------------------------------------------------------------- + */ + +export class Alert { + + constructor(element) { + if (element) { + this.element = element + } + } + + + // public + + close(element) { + let rootElement = this._getRootElement(element) + let customEvent = this._triggerCloseEvent(rootElement) + + if (customEvent.isDefaultPrevented()) { + return + } + + this._removeElement(rootElement) + } + + + // private + + _getRootElement(element) { + let parent = false + let selector = Util.getSelectorFromElement(element) + + if (selector) { + parent = $(selector)[0] + } + + if (!parent) { + parent = $(element).closest('.' + ClassName.ALERT)[0] + } + + return parent + } + + _triggerCloseEvent(element) { + var closeEvent = $.Event(Event.CLOSE) + $(element).trigger(closeEvent) + return closeEvent + } + + _removeElement(element) { + $(element).removeClass(ClassName.IN) + + if (!Util.supportsTransitionEnd() || !$(element).hasClass(ClassName.FADE)) { + this._destroyElement(element) + return + } + + $(element) + .one(Util.TRANSITION_END, this._destroyElement.bind(this, element)) + .emulateTransitionEnd(TRANSITION_DURATION) + } + + _destroyElement(element) { + $(element) + .detach() + .trigger(Event.CLOSED) + .remove() + } + + + // static + + static _jQueryInterface(config) { + return this.each(function () { + let $element = $(this) + let data = $element.data(DATA_KEY) + + if (!data) { + data = new Alert(this) + $element.data(DATA_KEY, data) + } + + if (config === 'close') { + data[config](this) + } + }) + } + + static _handleDismiss(alertInstance) { + return function (event) { + if (event) { + event.preventDefault() + } + + alertInstance.close(this) + } + } + +} + + +/** + * -------------------------------------------------------------------------- + * Data Api implementation + * -------------------------------------------------------------------------- + */ + +$(document).on( + Event.CLICK, + Selector.DISMISS, + Alert._handleDismiss(new Alert()) +) + + +/** + * -------------------------------------------------------------------------- + * jQuery + * -------------------------------------------------------------------------- + */ + +$.fn[NAME] = Alert._jQueryInterface +$.fn[NAME].Constructor = Alert +$.fn[NAME].noConflict = function () { + $.fn[NAME] = Alert._JQUERY_NO_CONFLICT + return Alert._jQueryInterface +} diff --git a/js/src/util.js b/js/src/util.js new file mode 100644 index 000000000..e9542149e --- /dev/null +++ b/js/src/util.js @@ -0,0 +1,118 @@ +/** + * -------------------------------------------------------------------------- + * Bootstrap (v4.0.0): util.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * -------------------------------------------------------------------------- + */ + + +/** + * -------------------------------------------------------------------------- + * Public Util Api + * -------------------------------------------------------------------------- + */ + +var Util = { + + TRANSITION_END: 'bsTransitionEnd', + + getUID(prefix) { + do prefix += ~~(Math.random() * 1000000) + while (document.getElementById(prefix)) + return prefix + }, + + getSelectorFromElement(element) { + let selector = element.getAttribute('data-target') + + if (!selector) { + selector = element.getAttribute('href') || '' + selector = /^#[a-z]/i.test(selector) ? selector : null + } + + return selector + }, + + reflow(element) { + new Function('bs', 'return bs')(element.offsetHeight) + }, + + supportsTransitionEnd() { + return !!transition + } + +} + +export default Util + + +/** + * -------------------------------------------------------------------------- + * Private TransitionEnd Helpers + * -------------------------------------------------------------------------- + */ + +let transition = false + +const TransitionEndEvent = { + WebkitTransition : 'webkitTransitionEnd', + MozTransition : 'transitionend', + OTransition : 'oTransitionEnd otransitionend', + transition : 'transitionend' +} + +function getSpecialTransitionEndEvent() { + return { + bindType: transition.end, + delegateType: transition.end, + handle: function (event) { + if ($(event.target).is(this)) { + return event.handleObj.handler.apply(this, arguments) + } + } + } +} + +function transitionEndTest() { + if (window.QUnit) { + return false + } + + let el = document.createElement('bootstrap') + + for (var name in TransitionEndEvent) { + if (el.style[name] !== undefined) { + return { end: TransitionEndEvent[name] } + } + } + + return false +} + +function transitionEndEmulator(duration) { + let called = false + + $(this).one(Util.TRANSITION_END, function () { + called = true + }) + + setTimeout(() => { + if (!called) { + $(this).trigger(transition.end) + } + }, duration) + + return this +} + +function setTransitionEndSupport() { + transition = transitionEndTest() + + $.fn.emulateTransitionEnd = transitionEndEmulator + + if (Util.supportsTransitionEnd()) { + $.event.special[Util.TRANSITION_END] = getSpecialTransitionEndEvent() + } +} + +setTransitionEndSupport() diff --git a/js/tests/index.html b/js/tests/index.html index fab2ebc16..536e34c74 100644 --- a/js/tests/index.html +++ b/js/tests/index.html @@ -129,9 +129,11 @@ })(); - - - + + + + + diff --git a/js/tests/unit/alert.js b/js/tests/unit/alert.js index 6be990a51..97818960a 100644 --- a/js/tests/unit/alert.js +++ b/js/tests/unit/alert.js @@ -38,7 +38,8 @@ $(function () { + '×' + '

Holy guacamole! Best check yo self, you\'re not looking too good.

' + '' - var $alert = $(alertHTML).bootstrapAlert() + + var $alert = $(alertHTML).bootstrapAlert().appendTo($('#qunit-fixture')) $alert.find('.close').trigger('click') diff --git a/js/tests/visual/alert.html b/js/tests/visual/alert.html index 0070315a2..9e6a939e9 100644 --- a/js/tests/visual/alert.html +++ b/js/tests/visual/alert.html @@ -41,8 +41,8 @@ - - + + diff --git a/package.json b/package.json index 107325a25..5e062297f 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "glob": "~5.0.3", "grunt": "~0.4.5", "grunt-autoprefixer": "~2.2.0", + "grunt-babel": "^5.0.0", "grunt-banner": "~0.3.1", "grunt-contrib-clean": "~0.6.0", "grunt-contrib-compress": "~0.13.0", @@ -85,6 +86,10 @@ "exports": "$" } }, - "files": ["css", "fonts", "js"] + "files": [ + "css", + "fonts", + "js" + ] } }