|
|
|
@ -5,13 +5,17 @@
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
if (typeof jQuery === 'undefined') {
|
|
|
|
|
// 如果未定义jQuery,则抛出错误
|
|
|
|
|
throw new Error('Bootstrap\'s JavaScript requires jQuery')
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
+function ($) {
|
|
|
|
|
// 使用严格模式
|
|
|
|
|
'use strict';
|
|
|
|
|
var version = $.fn.jquery.split(' ')[0].split('.')
|
|
|
|
|
// 获取jQuery版本号并分割成数组
|
|
|
|
|
if ((version[0] < 2 && version[1] < 9) || (version[0] == 1 && version[1] == 9 && version[2] < 1) || (version[0] > 3)) {
|
|
|
|
|
// 检查jQuery版本是否在1.9.1以上但低于4之间
|
|
|
|
|
throw new Error('Bootstrap\'s JavaScript requires jQuery version 1.9.1 or higher, but lower than version 4')
|
|
|
|
|
}
|
|
|
|
|
}(jQuery);
|
|
|
|
@ -32,6 +36,8 @@ if (typeof jQuery === 'undefined') {
|
|
|
|
|
// ============================================================
|
|
|
|
|
|
|
|
|
|
function transitionEnd() {
|
|
|
|
|
// 创建一个临时元素用于检测CSS过渡支持
|
|
|
|
|
|
|
|
|
|
var el = document.createElement('bootstrap')
|
|
|
|
|
|
|
|
|
|
var transEndEventNames = {
|
|
|
|
@ -40,14 +46,16 @@ if (typeof jQuery === 'undefined') {
|
|
|
|
|
OTransition : 'oTransitionEnd otransitionend',
|
|
|
|
|
transition : 'transitionend'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 定义不同浏览器的过渡结束事件名称
|
|
|
|
|
for (var name in transEndEventNames) {
|
|
|
|
|
if (el.style[name] !== undefined) {
|
|
|
|
|
return { end: transEndEventNames[name] }
|
|
|
|
|
// 如果找到支持的过渡结束事件,返回对应的事件名称
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false // explicit for ie8 ( ._.)
|
|
|
|
|
// 对于不支持过渡的浏览器,显式返回false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// http://blog.alexmaccaw.com/css-transitions
|
|
|
|
@ -55,23 +63,30 @@ if (typeof jQuery === 'undefined') {
|
|
|
|
|
var called = false
|
|
|
|
|
var $el = this
|
|
|
|
|
$(this).one('bsTransitionEnd', function () { called = true })
|
|
|
|
|
// 绑定一次性事件,当过渡结束时设置called为true
|
|
|
|
|
var callback = function () { if (!called) $($el).trigger($.support.transition.end) }
|
|
|
|
|
// 定义回调函数,如果called为false,触发过渡结束事件
|
|
|
|
|
setTimeout(callback, duration)
|
|
|
|
|
// 设置定时器,在指定持续时间后执行回调函数
|
|
|
|
|
return this
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$(function () {
|
|
|
|
|
$.support.transition = transitionEnd()
|
|
|
|
|
// 检测并设置CSS过渡支持情况
|
|
|
|
|
|
|
|
|
|
if (!$.support.transition) return
|
|
|
|
|
// 如果不支持过渡,直接返回
|
|
|
|
|
|
|
|
|
|
$.event.special.bsTransitionEnd = {
|
|
|
|
|
bindType: $.support.transition.end,
|
|
|
|
|
delegateType: $.support.transition.end,
|
|
|
|
|
handle: function (e) {
|
|
|
|
|
if ($(e.target).is(this)) return e.handleObj.handler.apply(this, arguments)
|
|
|
|
|
// 如果事件目标与当前元素匹配,调用处理函数
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// 定义自定义事件bsTransitionEnd,用于处理过渡结束事件
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
}(jQuery);
|
|
|
|
@ -94,38 +109,42 @@ if (typeof jQuery === 'undefined') {
|
|
|
|
|
var dismiss = '[data-dismiss="alert"]'
|
|
|
|
|
var Alert = function (el) {
|
|
|
|
|
$(el).on('click', dismiss, this.close)
|
|
|
|
|
// 绑定点击事件,当点击带有data-dismiss属性的元素时,调用close方法
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Alert.VERSION = '3.3.7'
|
|
|
|
|
|
|
|
|
|
// 定义Alert类的版本号
|
|
|
|
|
Alert.TRANSITION_DURATION = 150
|
|
|
|
|
// 定义过渡动画的持续时间为150毫秒
|
|
|
|
|
|
|
|
|
|
Alert.prototype.close = function (e) {
|
|
|
|
|
var $this = $(this)
|
|
|
|
|
var selector = $this.attr('data-target')
|
|
|
|
|
|
|
|
|
|
// 获取data-target属性的值作为选择器
|
|
|
|
|
if (!selector) {
|
|
|
|
|
selector = $this.attr('href')
|
|
|
|
|
selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 如果data-target不存在,则获取href属性的值作为选择器,并去除URL中的其他部分以兼容IE7
|
|
|
|
|
var $parent = $(selector === '#' ? [] : 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')
|
|
|
|
|
|
|
|
|
|
// 移除父元素的'in'类,表示隐藏元素
|
|
|
|
|
function removeElement() {
|
|
|
|
|
// detach from parent, fire event then clean up data
|
|
|
|
|
$parent.detach().trigger('closed.bs.alert').remove()
|
|
|
|
|
// 从父元素中分离,触发关闭事件,然后移除元素及其数据
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$.support.transition && $parent.hasClass('fade') ?
|
|
|
|
@ -133,6 +152,7 @@ if (typeof jQuery === 'undefined') {
|
|
|
|
|
.one('bsTransitionEnd', removeElement)
|
|
|
|
|
.emulateTransitionEnd(Alert.TRANSITION_DURATION) :
|
|
|
|
|
removeElement()
|
|
|
|
|
// 如果支持过渡且父元素有'fade'类,则在过渡结束后移除元素;否则立即移除元素
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -140,34 +160,36 @@ if (typeof jQuery === 'undefined') {
|
|
|
|
|
// =======================
|
|
|
|
|
|
|
|
|
|
function Plugin(option) {
|
|
|
|
|
// 遍历每一个匹配的元素,并执行回调函数
|
|
|
|
|
return this.each(function () {
|
|
|
|
|
var $this = $(this)
|
|
|
|
|
var data = $this.data('bs.alert')
|
|
|
|
|
var $this = $(this); // 将当前元素包装成jQuery对象
|
|
|
|
|
var data = $this.data('bs.'); // 获取元素的'bs.'数据
|
|
|
|
|
|
|
|
|
|
if (!data) $this.data('bs.alert', (data = new Alert(this)))
|
|
|
|
|
if (typeof option == 'string') data[option].call($this)
|
|
|
|
|
})
|
|
|
|
|
// 如果元素没有'bs.'数据,则创建一个新的Alert实例并存储在'bs.'数据中
|
|
|
|
|
if (!data) $this.data('bs.', (data = new Alert(this)));
|
|
|
|
|
// 如果传入的option是字符串,则调用对应的方法
|
|
|
|
|
if (typeof option == 'string') data[option].call($this);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var old = $.fn.alert
|
|
|
|
|
var old = $.fn.button// 保存旧的插件定义
|
|
|
|
|
|
|
|
|
|
$.fn.alert = Plugin
|
|
|
|
|
$.fn.alert.Constructor = Alert
|
|
|
|
|
$.fn.button = Plugin// 将Plugin函数赋值给jQuery的fn属性
|
|
|
|
|
$.fn.button.Constructor = Button// 将Alert构造函数赋值给jQuery的fn属性的Constructor属性
|
|
|
|
|
|
|
|
|
|
// ALERT NO CONFLICT
|
|
|
|
|
// =================
|
|
|
|
|
|
|
|
|
|
// ALERT NO CONFLICT
|
|
|
|
|
// =================
|
|
|
|
|
|
|
|
|
|
$.fn.alert.noConflict = function () {
|
|
|
|
|
$.fn.alert = old
|
|
|
|
|
$.fn.button.noConflict = function () {
|
|
|
|
|
$.fn.button = old
|
|
|
|
|
return this
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// ALERT DATA-API
|
|
|
|
|
// ==============
|
|
|
|
|
// ALERT DATA-API
|
|
|
|
|
// ==============
|
|
|
|
|
|
|
|
|
|
$(document).on('click.bs.alert.data-api', dismiss, Alert.prototype.close)
|
|
|
|
|
$(document).on('click.bs..data-api', dismiss, Alert.prototype.close); // 为文档绑定点击事件,当点击dismiss时,调用Alert原型的close方法
|
|
|
|
|
|
|
|
|
|
}(jQuery);
|
|
|
|
|
|
|
|
|
@ -179,7 +201,6 @@ if (typeof jQuery === 'undefined') {
|
|
|
|
|
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
|
|
|
|
* ======================================================================== */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
+function ($) {
|
|
|
|
|
'use strict';
|
|
|
|
|
|
|
|
|
@ -187,115 +208,122 @@ if (typeof jQuery === 'undefined') {
|
|
|
|
|
// ==============================
|
|
|
|
|
|
|
|
|
|
var Button = function (element, options) {
|
|
|
|
|
this.$element = $(element)
|
|
|
|
|
this.options = $.extend({}, Button.DEFAULTS, options)
|
|
|
|
|
this.isLoading = false
|
|
|
|
|
this.$element = $(element); // 将DOM元素包装成jQuery对象
|
|
|
|
|
this.options = $.extend({}, Button.DEFAULTS, options); // 合并默认选项和用户选项
|
|
|
|
|
this.isLoading = false; // 初始化加载状态为false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Button.VERSION = '3.3.7'
|
|
|
|
|
Button.VERSION = '3.3.7'; // 设置版本号
|
|
|
|
|
|
|
|
|
|
Button.DEFAULTS = {
|
|
|
|
|
loadingText: 'loading...'
|
|
|
|
|
loadingText: 'loading...' // 设置默认加载文本
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Button.prototype.setState = function (state) {
|
|
|
|
|
var d = 'disabled'
|
|
|
|
|
var $el = this.$element
|
|
|
|
|
var val = $el.is('input') ? 'val' : 'html'
|
|
|
|
|
var data = $el.data()
|
|
|
|
|
var d = 'disabled'; // 禁用状态的CSS类名
|
|
|
|
|
var $el = this.$element; // 按钮的jQuery对象
|
|
|
|
|
var val = $el.is('input') ? 'val' : 'html'; // 根据元素类型选择使用val或html方法
|
|
|
|
|
var data = $el.data(); // 获取元素的数据
|
|
|
|
|
|
|
|
|
|
state += 'Text'
|
|
|
|
|
state += 'Text'; // 拼接状态文本后缀
|
|
|
|
|
|
|
|
|
|
if (data.resetText == null) $el.data('resetText', $el[val]())
|
|
|
|
|
// 如果resetText数据不存在,则保存当前值到resetText数据中
|
|
|
|
|
if (data.resetText == null) $el.data('resetText', $el[val]());
|
|
|
|
|
|
|
|
|
|
// push to event loop to allow forms to submit
|
|
|
|
|
setTimeout($.proxy(function () {
|
|
|
|
|
$el[val](data[state] == null ? this.options[state] : data[state])
|
|
|
|
|
// 设置按钮文本或HTML内容
|
|
|
|
|
$el[val](data[state] == null ? this.options[state] : data[state]);
|
|
|
|
|
|
|
|
|
|
// 如果状态是loadingText,则设置加载状态和禁用样式
|
|
|
|
|
if (state == 'loadingText') {
|
|
|
|
|
this.isLoading = true
|
|
|
|
|
$el.addClass(d).attr(d, d).prop(d, true)
|
|
|
|
|
this.isLoading = true;
|
|
|
|
|
$el.addClass(d).attr(d, d).prop(d, true);
|
|
|
|
|
} else if (this.isLoading) {
|
|
|
|
|
this.isLoading = false
|
|
|
|
|
$el.removeClass(d).removeAttr(d).prop(d, false)
|
|
|
|
|
// 如果当前状态不是loadingText且之前是加载状态,则取消加载状态和禁用样式
|
|
|
|
|
this.isLoading = false;
|
|
|
|
|
$el.removeClass(d).removeAttr(d).prop(d, false);
|
|
|
|
|
}
|
|
|
|
|
}, this), 0)
|
|
|
|
|
}, this), 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Button.prototype.toggle = function () {
|
|
|
|
|
var changed = true
|
|
|
|
|
var $parent = this.$element.closest('[data-toggle="buttons"]')
|
|
|
|
|
var changed = true; // 标记状态是否改变
|
|
|
|
|
var $parent = this.$element.closest('[data-toggle="buttons"]'); // 查找最近的父级按钮组容器
|
|
|
|
|
|
|
|
|
|
if ($parent.length) {
|
|
|
|
|
var $input = this.$element.find('input')
|
|
|
|
|
var $input = this.$element.find('input'); // 查找按钮内的输入元素
|
|
|
|
|
if ($input.prop('type') == 'radio') {
|
|
|
|
|
if ($input.prop('checked')) changed = false
|
|
|
|
|
$parent.find('.active').removeClass('active')
|
|
|
|
|
this.$element.addClass('active')
|
|
|
|
|
// 如果输入类型是单选按钮且已选中,则不改变状态
|
|
|
|
|
if ($input.prop('checked')) changed = false;
|
|
|
|
|
$parent.find('.active').removeClass('active'); // 移除其他按钮的激活状态
|
|
|
|
|
this.$element.addClass('active'); // 设置当前按钮为激活状态
|
|
|
|
|
} else if ($input.prop('type') == 'checkbox') {
|
|
|
|
|
if (($input.prop('checked')) !== this.$element.hasClass('active')) changed = false
|
|
|
|
|
this.$element.toggleClass('active')
|
|
|
|
|
// 如果输入类型是复选框且状态与激活状态不一致,则不改变状态
|
|
|
|
|
if (($input.prop('checked')) !== this.$element.hasClass('active')) changed = false;
|
|
|
|
|
this.$element.toggleClass('active'); // 切换当前按钮的激活状态
|
|
|
|
|
}
|
|
|
|
|
$input.prop('checked', this.$element.hasClass('active'))
|
|
|
|
|
if (changed) $input.trigger('change')
|
|
|
|
|
$input.prop('checked', this.$element.hasClass('active')); // 同步输入元素的选中状态
|
|
|
|
|
if (changed) $input.trigger('change'); // 如果状态改变,触发change事件
|
|
|
|
|
} else {
|
|
|
|
|
this.$element.attr('aria-pressed', !this.$element.hasClass('active'))
|
|
|
|
|
this.$element.toggleClass('active')
|
|
|
|
|
// 如果按钮不在按钮组内,直接切换aria-pressed属性和激活状态
|
|
|
|
|
this.$element.attr('aria-pressed', !this.$element.hasClass('active'));
|
|
|
|
|
this.$element.toggleClass('active');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// BUTTON PLUGIN DEFINITION
|
|
|
|
|
// ========================
|
|
|
|
|
|
|
|
|
|
function Plugin(option) {
|
|
|
|
|
return this.each(function () {
|
|
|
|
|
var $this = $(this)
|
|
|
|
|
var data = $this.data('bs.button')
|
|
|
|
|
var options = typeof option == 'object' && option
|
|
|
|
|
var $this = $(this); // 将当前元素包装成jQuery对象
|
|
|
|
|
var data = $this.data('bs.button'); // 获取元素的bs.button数据
|
|
|
|
|
var options = typeof option == 'object' && option; // 如果option是对象,则赋值给options变量
|
|
|
|
|
|
|
|
|
|
if (!data) $this.data('bs.button', (data = new Button(this, options)))
|
|
|
|
|
// 如果元素没有bs.button数据,则创建新的Button实例并存储在bs.button数据中
|
|
|
|
|
if (!data) $this.data('bs.button', (data = new Button(this, options)));
|
|
|
|
|
|
|
|
|
|
if (option == 'toggle') data.toggle()
|
|
|
|
|
else if (option) data.setState(option)
|
|
|
|
|
})
|
|
|
|
|
// 如果option是toggle,则调用toggle方法;否则如果option存在,则调用setState方法
|
|
|
|
|
if (option == 'toggle') data.toggle();
|
|
|
|
|
else if (option) data.setState(option);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var old = $.fn.button
|
|
|
|
|
|
|
|
|
|
$.fn.button = Plugin
|
|
|
|
|
$.fn.button.Constructor = Button
|
|
|
|
|
var old = $.fn.button; // 保存旧的按钮插件定义
|
|
|
|
|
|
|
|
|
|
$.fn.button = Plugin; // 将Plugin函数赋值给jQuery的fn属性的button方法
|
|
|
|
|
$.fn.button.Constructor = Button; // 将Button构造函数赋值给jQuery的fn属性的button方法的Constructor属性
|
|
|
|
|
|
|
|
|
|
// BUTTON NO CONFLICT
|
|
|
|
|
// ==================
|
|
|
|
|
|
|
|
|
|
$.fn.button.noConflict = function () {
|
|
|
|
|
$.fn.button = old
|
|
|
|
|
return this
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$.fn.button = old; // 恢复旧的按钮插件定义
|
|
|
|
|
return this; // 返回当前对象以支持链式调用
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// BUTTON DATA-API
|
|
|
|
|
// ===============
|
|
|
|
|
|
|
|
|
|
$(document)
|
|
|
|
|
.on('click.bs.button.data-api', '[data-toggle^="button"]', function (e) {
|
|
|
|
|
var $btn = $(e.target).closest('.btn')
|
|
|
|
|
Plugin.call($btn, 'toggle')
|
|
|
|
|
var $btn = $(e.target).closest('.btn'); // 查找点击目标最近的按钮元素
|
|
|
|
|
Plugin.call($btn, 'toggle'); // 调用Plugin的toggle方法
|
|
|
|
|
if (!($(e.target).is('input[type="radio"], input[type="checkbox"]'))) {
|
|
|
|
|
// Prevent double click on radios, and the double selections (so cancellation) on checkboxes
|
|
|
|
|
e.preventDefault()
|
|
|
|
|
// The target component still receive the focus
|
|
|
|
|
if ($btn.is('input,button')) $btn.trigger('focus')
|
|
|
|
|
else $btn.find('input:visible,button:visible').first().trigger('focus')
|
|
|
|
|
// 如果点击的不是单选按钮或复选框,则阻止默认行为(防止双击)
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
// 如果目标组件是按钮或输入元素,则触发焦点事件
|
|
|
|
|
if ($btn.is('input,button')) $btn.trigger('focus');
|
|
|
|
|
else $btn.find('input:visible,button:visible').first().trigger('focus');
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
.on('focus.bs.button.data-api blur.bs.button.data-api', '[data-toggle^="button"]', function (e) {
|
|
|
|
|
$(e.target).closest('.btn').toggleClass('focus', /^focus(in)?$/.test(e.type))
|
|
|
|
|
})
|
|
|
|
|
// 根据事件类型切换按钮的焦点样式
|
|
|
|
|
$(e.target).closest('.btn').toggleClass('focus', /^focus(in)?$/.test(e.type));
|
|
|
|
|
});
|
|
|
|
|
}(jQuery); // 立即执行函数,传入jQuery对象作为参数,确保代码在jQuery加载后执行。
|
|
|
|
|
|
|
|
|
|
}(jQuery);
|
|
|
|
|
|
|
|
|
|
/* ========================================================================
|
|
|
|
|
* Bootstrap: carousel.js v3.3.7
|
|
|
|
@ -307,168 +335,182 @@ if (typeof jQuery === 'undefined') {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
+function ($) {
|
|
|
|
|
'use strict';
|
|
|
|
|
'use strict'; // 使用严格模式,避免一些常见的错误
|
|
|
|
|
|
|
|
|
|
// CAROUSEL CLASS DEFINITION
|
|
|
|
|
// =========================
|
|
|
|
|
|
|
|
|
|
var Carousel = function (element, options) {
|
|
|
|
|
this.$element = $(element)
|
|
|
|
|
this.$indicators = this.$element.find('.carousel-indicators')
|
|
|
|
|
this.options = options
|
|
|
|
|
this.paused = null
|
|
|
|
|
this.sliding = null
|
|
|
|
|
this.interval = null
|
|
|
|
|
this.$active = null
|
|
|
|
|
this.$items = null
|
|
|
|
|
|
|
|
|
|
this.$element = $(element) // 将传入的元素转换为jQuery对象
|
|
|
|
|
this.$indicators = this.$element.find('.carousel-indicators') // 查找轮播图的指示器元素
|
|
|
|
|
this.options = options // 保存传入的选项参数
|
|
|
|
|
this.paused = null // 初始化暂停状态为null
|
|
|
|
|
this.sliding = null // 初始化滑动状态为null
|
|
|
|
|
this.interval = null // 初始化定时器为null
|
|
|
|
|
this.$active = null // 初始化当前活动项为null
|
|
|
|
|
this.$items = null // 初始化所有项目为null
|
|
|
|
|
|
|
|
|
|
// 如果选项中启用了键盘控制,绑定键盘事件处理函数
|
|
|
|
|
this.options.keyboard && this.$element.on('keydown.bs.carousel', $.proxy(this.keydown, this))
|
|
|
|
|
|
|
|
|
|
// 如果选项中设置了鼠标悬停暂停,并且设备不支持触摸事件,绑定鼠标进入和离开事件处理函数
|
|
|
|
|
this.options.pause == 'hover' && !('ontouchstart' in document.documentElement) && this.$element
|
|
|
|
|
.on('mouseenter.bs.carousel', $.proxy(this.pause, this))
|
|
|
|
|
.on('mouseleave.bs.carousel', $.proxy(this.cycle, this))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Carousel.VERSION = '3.3.7'
|
|
|
|
|
Carousel.VERSION = '3.3.7' // 定义轮播组件的版本号
|
|
|
|
|
|
|
|
|
|
Carousel.TRANSITION_DURATION = 600
|
|
|
|
|
Carousel.TRANSITION_DURATION = 600 // 定义过渡动画的持续时间
|
|
|
|
|
|
|
|
|
|
Carousel.DEFAULTS = {
|
|
|
|
|
interval: 5000,
|
|
|
|
|
pause: 'hover',
|
|
|
|
|
wrap: true,
|
|
|
|
|
keyboard: true
|
|
|
|
|
interval: 5000, // 默认自动播放间隔时间为5秒
|
|
|
|
|
pause: 'hover', // 默认在鼠标悬停时暂停
|
|
|
|
|
wrap: true, // 默认支持循环播放
|
|
|
|
|
keyboard: true // 默认启用键盘控制
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 键盘事件处理函数
|
|
|
|
|
Carousel.prototype.keydown = function (e) {
|
|
|
|
|
if (/input|textarea/i.test(e.target.tagName)) return
|
|
|
|
|
if (/input|textarea/i.test(e.target.tagName)) return // 如果焦点在输入框或文本区域,不处理键盘事件
|
|
|
|
|
switch (e.which) {
|
|
|
|
|
case 37: this.prev(); break
|
|
|
|
|
case 39: this.next(); break
|
|
|
|
|
default: return
|
|
|
|
|
case 37: this.prev(); break // 左箭头键切换到上一个项目
|
|
|
|
|
case 39: this.next(); break // 右箭头键切换到下一个项目
|
|
|
|
|
default: return // 其他按键不处理
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
e.preventDefault()
|
|
|
|
|
e.preventDefault() // 阻止默认行为
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 开始自动播放
|
|
|
|
|
Carousel.prototype.cycle = function (e) {
|
|
|
|
|
e || (this.paused = false)
|
|
|
|
|
e || (this.paused = false) // 如果没有传入事件对象,设置暂停状态为false
|
|
|
|
|
|
|
|
|
|
this.interval && clearInterval(this.interval)
|
|
|
|
|
this.interval && clearInterval(this.interval) // 清除之前的定时器
|
|
|
|
|
|
|
|
|
|
this.options.interval
|
|
|
|
|
&& !this.paused
|
|
|
|
|
&& (this.interval = setInterval($.proxy(this.next, this), this.options.interval))
|
|
|
|
|
&& (this.interval = setInterval($.proxy(this.next, this), this.options.interval)) // 根据选项中的间隔时间设置新的定时器
|
|
|
|
|
|
|
|
|
|
return this
|
|
|
|
|
return this // 返回当前实例以支持链式调用
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 获取项目的索引
|
|
|
|
|
Carousel.prototype.getItemIndex = function (item) {
|
|
|
|
|
this.$items = item.parent().children('.item')
|
|
|
|
|
return this.$items.index(item || this.$active)
|
|
|
|
|
this.$items = item.parent().children('.item') // 获取所有项目
|
|
|
|
|
return this.$items.index(item || this.$active) // 返回指定项目的索引,如果未指定则返回当前活动的项目索引
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 根据方向获取下一个项目
|
|
|
|
|
Carousel.prototype.getItemForDirection = function (direction, active) {
|
|
|
|
|
var activeIndex = this.getItemIndex(active)
|
|
|
|
|
var willWrap = (direction == 'prev' && activeIndex === 0)
|
|
|
|
|
|| (direction == 'next' && activeIndex == (this.$items.length - 1))
|
|
|
|
|
if (willWrap && !this.options.wrap) return active
|
|
|
|
|
var delta = direction == 'prev' ? -1 : 1
|
|
|
|
|
var itemIndex = (activeIndex + delta) % this.$items.length
|
|
|
|
|
return this.$items.eq(itemIndex)
|
|
|
|
|
var activeIndex = this.getItemIndex(active) // 获取当前活动的项目索引
|
|
|
|
|
var willWrap = (direction == 'prev' && activeIndex === 0) // 判断是否要循环到最后一个项目
|
|
|
|
|
|| (direction == 'next' && activeIndex == (this.$items.length - 1)) // 判断是否要循环到第一个项目
|
|
|
|
|
if (willWrap && !this.options.wrap) return active // 如果不允许循环播放,直接返回当前活动项目
|
|
|
|
|
var delta = direction == 'prev' ? -1 : 1 // 根据方向计算索引变化量
|
|
|
|
|
var itemIndex = (activeIndex + delta) % this.$items.length // 计算下一个项目的索引
|
|
|
|
|
return this.$items.eq(itemIndex) // 返回下一个项目
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 切换到指定位置的项目
|
|
|
|
|
Carousel.prototype.to = function (pos) {
|
|
|
|
|
var that = this
|
|
|
|
|
var activeIndex = this.getItemIndex(this.$active = this.$element.find('.item.active'))
|
|
|
|
|
var that = this // 保存当前实例的引用
|
|
|
|
|
var activeIndex = this.getItemIndex(this.$active = this.$element.find('.item.active')) // 获取当前活动的项目索引并更新活动项目
|
|
|
|
|
|
|
|
|
|
if (pos > (this.$items.length - 1) || pos < 0) return
|
|
|
|
|
if (pos > (this.$items.length - 1) || pos < 0) return // 如果指定的位置超出范围,直接返回
|
|
|
|
|
if (this.sliding) return this.$element.one('slid.bs.carousel', function () { that.to(pos) }) // 如果正在滑动,等待滑动完成后再切换到指定位置
|
|
|
|
|
if (activeIndex == pos) return this.pause().cycle() // 如果已经是指定位置,暂停并重新开始自动播放
|
|
|
|
|
|
|
|
|
|
if (this.sliding) return this.$element.one('slid.bs.carousel', function () { that.to(pos) }) // yes, "slid"
|
|
|
|
|
if (activeIndex == pos) return this.pause().cycle()
|
|
|
|
|
|
|
|
|
|
return this.slide(pos > activeIndex ? 'next' : 'prev', this.$items.eq(pos))
|
|
|
|
|
return this.slide(pos > activeIndex ? 'next' : 'prev', this.$items.eq(pos)) // 根据位置关系切换到指定位置的项目
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 暂停自动播放
|
|
|
|
|
Carousel.prototype.pause = function (e) {
|
|
|
|
|
e || (this.paused = true)
|
|
|
|
|
e || (this.paused = true) // 如果没有传入事件对象,设置暂停状态为true
|
|
|
|
|
|
|
|
|
|
if (this.$element.find('.next, .prev').length && $.support.transition) {
|
|
|
|
|
this.$element.trigger($.support.transition.end)
|
|
|
|
|
this.cycle(true)
|
|
|
|
|
this.$element.trigger($.support.transition.end) // 如果支持CSS3过渡效果,触发过渡结束事件
|
|
|
|
|
this.cycle(true) // 重新开始自动播放
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this.interval = clearInterval(this.interval)
|
|
|
|
|
this.interval = clearInterval(this.interval) // 清除定时器
|
|
|
|
|
|
|
|
|
|
return this
|
|
|
|
|
return this // 返回当前实例以支持链式调用
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 切换到下一个或上一个项目
|
|
|
|
|
Carousel.prototype.next = function () {
|
|
|
|
|
if (this.sliding) return
|
|
|
|
|
return this.slide('next')
|
|
|
|
|
if (this.sliding) return // 如果正在滑动,直接返回
|
|
|
|
|
return this.slide('next') // 切换到下一个项目
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 切换到上一个项目
|
|
|
|
|
Carousel.prototype.prev = function () {
|
|
|
|
|
if (this.sliding) return
|
|
|
|
|
return this.slide('prev')
|
|
|
|
|
if (this.sliding) return // 如果正在滑动,直接返回
|
|
|
|
|
return this.slide('prev') // 切换到上一个项目
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 执行滑动操作
|
|
|
|
|
Carousel.prototype.slide = function (type, next) {
|
|
|
|
|
var $active = this.$element.find('.item.active')
|
|
|
|
|
var $next = next || this.getItemForDirection(type, $active)
|
|
|
|
|
var isCycling = this.interval
|
|
|
|
|
var direction = type == 'next' ? 'left' : 'right'
|
|
|
|
|
var that = this
|
|
|
|
|
var $active = this.$element.find('.item.active') // 获取当前活动的项目
|
|
|
|
|
var $next = next || this.getItemForDirection(type, $active) // 获取下一个项目,如果没有指定则根据方向获取
|
|
|
|
|
var isCycling = this.interval // 检查是否正在自动播放
|
|
|
|
|
var direction = type == 'next' ? 'left' : 'right' // 根据类型确定滑动方向
|
|
|
|
|
var that = this // 保存当前实例的引用
|
|
|
|
|
|
|
|
|
|
if ($next.hasClass('active')) return (this.sliding = false)
|
|
|
|
|
if ($next.hasClass('active')) return (this.sliding = false) // 如果下一个项目已经是活动状态,直接返回
|
|
|
|
|
|
|
|
|
|
var relatedTarget = $next[0]
|
|
|
|
|
var slideEvent = $.Event('slide.bs.carousel', {
|
|
|
|
|
relatedTarget: relatedTarget,
|
|
|
|
|
direction: direction
|
|
|
|
|
var slideEvent = $.Event('slide.bs.carousel', { // 创建滑动事件对象
|
|
|
|
|
relatedTarget: $next[0], // 设置相关目标为下一个项目
|
|
|
|
|
direction: direction // 设置滑动方向
|
|
|
|
|
})
|
|
|
|
|
this.$element.trigger(slideEvent)
|
|
|
|
|
if (slideEvent.isDefaultPrevented()) return
|
|
|
|
|
this.$element.trigger(slideEvent) // 触发滑动事件
|
|
|
|
|
if (slideEvent.isDefaultPrevented()) return // 如果事件被阻止,直接返回
|
|
|
|
|
|
|
|
|
|
this.sliding = true
|
|
|
|
|
this.sliding = true // 设置滑动状态为true
|
|
|
|
|
|
|
|
|
|
isCycling && this.pause()
|
|
|
|
|
isCycling && this.pause() // 如果正在自动播放,暂停自动播放
|
|
|
|
|
|
|
|
|
|
if (this.$indicators.length) {
|
|
|
|
|
this.$indicators.find('.active').removeClass('active')
|
|
|
|
|
var $nextIndicator = $(this.$indicators.children()[this.getItemIndex($next)])
|
|
|
|
|
$nextIndicator && $nextIndicator.addClass('active')
|
|
|
|
|
if (this.$indicators.length) { // 如果存在指示器元素
|
|
|
|
|
this.$indicators.find('.active').removeClass('active') // 移除当前活动指示器的激活状态
|
|
|
|
|
var $nextIndicator = $(this.$indicators.children()[this.getItemIndex($next)]) // 获取下一个项目对应的指示器元素
|
|
|
|
|
$nextIndicator && $nextIndicator.addClass('active') // 如果存在下一个指示器元素,添加激活状态
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var slidEvent = $.Event('slid.bs.carousel', { relatedTarget: relatedTarget, direction: direction }) // yes, "slid"
|
|
|
|
|
if ($.support.transition && this.$element.hasClass('slide')) {
|
|
|
|
|
$next.addClass(type)
|
|
|
|
|
$next[0].offsetWidth // force reflow
|
|
|
|
|
$active.addClass(direction)
|
|
|
|
|
$next.addClass(direction)
|
|
|
|
|
var slidEvent = $.Event('slid.bs.carousel', { // 创建已滑动事件对象
|
|
|
|
|
relatedTarget: $next[0], // 设置相关目标为下一个项目
|
|
|
|
|
direction: direction // 设置滑动方向
|
|
|
|
|
}) // yes, "slid"
|
|
|
|
|
if ($.support.transition && this.$element.hasClass('slide')) { // 如果支持CSS3过渡效果且轮播图元素具有slide类名
|
|
|
|
|
$next.addClass(type) // 添加滑动类型类名(next或prev)
|
|
|
|
|
$next[0].offsetWidth // 强制浏览器重新计算元素的宽度和高度,以触发重绘和过渡效果
|
|
|
|
|
$active.addClass(direction) // 添加滑动方向类名(left或right)
|
|
|
|
|
$next.addClass(direction) // 添加滑动方向类名(left或right)
|
|
|
|
|
$active
|
|
|
|
|
.one('bsTransitionEnd', function () {
|
|
|
|
|
$next.removeClass([type, direction].join(' ')).addClass('active')
|
|
|
|
|
$active.removeClass(['active', direction].join(' '))
|
|
|
|
|
that.sliding = false
|
|
|
|
|
setTimeout(function () {
|
|
|
|
|
that.$element.trigger(slidEvent)
|
|
|
|
|
.one('bsTransitionEnd', function () { // 监听过渡结束事件
|
|
|
|
|
$next.removeClass([type, direction].join(' ')).addClass('active') // 移除滑动类型和方向类名,添加激活状态
|
|
|
|
|
$active.removeClass(['active', direction].join(' ')) // 移除激活状态和方向类名
|
|
|
|
|
that.sliding = false // 设置滑动状态为false
|
|
|
|
|
setTimeout(function () { // 延迟触发已滑动事件,以确保过渡效果完成
|
|
|
|
|
that.$element.trigger(slidEvent) // 触发已滑动事件
|
|
|
|
|
}, 0)
|
|
|
|
|
})
|
|
|
|
|
.emulateTransitionEnd(Carousel.TRANSITION_DURATION)
|
|
|
|
|
} else {
|
|
|
|
|
$active.removeClass('active')
|
|
|
|
|
$next.addClass('active')
|
|
|
|
|
this.sliding = false
|
|
|
|
|
this.$element.trigger(slidEvent)
|
|
|
|
|
.emulateTransitionEnd(Carousel.TRANSITION_DURATION) // 模拟过渡结束事件,以触发过渡效果
|
|
|
|
|
} else { // 如果不支持CSS3过渡效果或轮播图元素没有slide类名
|
|
|
|
|
$active.removeClass('active') // 移除当前活动项目的激活状态
|
|
|
|
|
$next.addClass('active') // 添加下一个项目的激活状态
|
|
|
|
|
this.sliding = false // 设置滑动状态为false
|
|
|
|
|
this.$element.trigger(slidEvent) // 触发已滑动事件
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
isCycling && this.cycle()
|
|
|
|
|
isCycling && this.cycle() // 如果正在自动播放,重新开始自动播放
|
|
|
|
|
|
|
|
|
|
return this
|
|
|
|
|
return this // 返回当前实例以支持链式调用
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// CAROUSEL PLUGIN DEFINITION
|
|
|
|
|
// ==========================
|
|
|
|
|
|
|
|
|
|