zui
Version:
一个基于 Bootstrap 深度定制开源前端实践方案,帮助你快速构建现代跨屏应用。
436 lines (355 loc) • 14.8 kB
JavaScript
/* ========================================================================
* Bootstrap: modal.js v3.2.0
* http://getbootstrap.com/javascript/#modals
*
* ZUI: The file has been changed in ZUI. It will not keep update with the
* Bootsrap version in the future.
* http://zui.sexy
* ========================================================================
* Copyright 2011-2014 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* ========================================================================
* Updates in ZUI:
* 1. changed event namespace to *.zui.modal
* 2. added position option to ajust poisition of modal
* 3. added event 'escaping.zui.modal' with an param 'esc' to judge the esc
* key down
* 4. get moveable options value from '.modal-moveable' on '.modal-dialog'
* 5. add setMoveable method to make modal dialog moveable
* ======================================================================== */
+ function($, undefined) {
'use strict';
// MODAL CLASS DEFINITION
// ======================
var zuiname = 'zui.modal'
var Modal = function(element, options) {
var that = this;
that.options = options
that.$body = $(document.body)
that.$element = $(element)
that.$backdrop =
that.isShown = null
that.scrollbarWidth = 0
if(options.moveable === undefined) {
that.options.moveable = that.$element.hasClass('modal-moveable');
}
if(options.remote) {
that.$element
.find('.modal-content')
.load(options.remote, function() {
that.$element.trigger('loaded.' + zuiname)
})
}
}
Modal.VERSION = '3.2.0'
Modal.TRANSITION_DURATION = 300
Modal.BACKDROP_TRANSITION_DURATION = 150
Modal.DEFAULTS = {
backdrop: true,
keyboard: true,
show: true,
// rememberPos: false,
// moveable: false,
position: 'fit', // 'center' or '40px' or '10%',
// scrollInside: false,
// headerHeight: 'auto',
};
var setDialogPos = function($dialog, pos) {
var $window = $(window);
pos.left = Math.max(0, Math.min(pos.left, $window.width() - $dialog.outerWidth()));
pos.top = Math.max(0, Math.min(pos.top, $window.height() - $dialog.outerHeight()));
$dialog.css(pos);
};
Modal.prototype.toggle = function(_relatedTarget, position) {
return this.isShown ? this.hide() : this.show(_relatedTarget, position)
}
Modal.prototype.ajustPosition = function(position) {
var that = this;
var options = that.options;
if(position === undefined) position = options.position;
if(position === undefined || position === null) return;
if ($.isFunction(position)) {
position = position(that);
}
var $dialog = that.$element.find('.modal-dialog');
var winHeight = $(window).height();
var bodyCss = {maxHeight: 'initial', overflow: 'visible'};
var $body = $dialog.find('.modal-body').css(bodyCss);
if (options.scrollInside) {
var headerHeight = options.headerHeight;
if (typeof headerHeight !== 'number') {
headerHeight = $dialog.find('.modal-header').height();
} else if ($.isFunction(headerHeight)) {
headerHeight = headerHeight($header);
}
bodyCss.maxHeight = winHeight - headerHeight;
if ($body.outerHeight() > bodyCss.maxHeight) {
bodyCss.overflow = 'auto';
}
}
$body.css(bodyCss);
var half = Math.max(0, (winHeight - $dialog.outerHeight()) / 2);
if (position === 'fit') {
position = {top: half > 50 ? Math.floor(half * 2 / 3) : half};
} else if (position === 'center') {
position = {top: half};
} else if (!$.isPlainObject(position)) {
position = {top: position};
}
if($dialog.hasClass('modal-moveable')) {
var pos = null;
var rememberPos = options.rememberPos;
if(rememberPos) {
if(rememberPos === true) {
pos = that.$element.data('modal-pos');
} else if($.zui.store) {
pos = $.zui.store.pageGet(zuiname + '.rememberPos.' + rememberPos);
}
}
position = $.extend(position, {left: Math.max(0, ($(window).width() - $dialog.outerWidth()) / 2)}, pos);
if (options.moveable === 'inside') {
setDialogPos($dialog, position);
} else {
$dialog.css(position);
}
} else {
$dialog.css(position);
}
}
Modal.prototype.setMoveale = function() {
if(!$.fn.draggable) console.error('Moveable modal requires draggable.js.');
var that = this;
var options = that.options;
var $dialog = that.$element.find('.modal-dialog').removeClass('modal-dragged');
$dialog.toggleClass('modal-moveable', !!options.moveable);
if(!that.$element.data('modal-moveable-setup')) {
$dialog.draggable({
container: that.$element,
handle: '.modal-header',
before: function() {
var marginTop = $dialog.css('margin-top');
if (marginTop && marginTop !== '0px') {
$dialog.css('top', marginTop).css('margin-top', '').addClass('modal-dragged');
}
},
finish: function(e) {
var rememberPos = options.rememberPos;
if(rememberPos) {
that.$element.data('modal-pos', e.pos);
if($.zui.store && rememberPos !== true) {
$.zui.store.pageSet(zuiname + '.rememberPos.' + rememberPos, e.pos);
}
}
},
move: options.moveable === 'inside' ? function (dragPos) {
setDialogPos($dialog, dragPos);
} : true
});
}
}
Modal.prototype.show = function(_relatedTarget, position) {
var that = this
var e = $.Event('show.' + zuiname, {
relatedTarget: _relatedTarget
})
that.$element.trigger(e)
that.$element.toggleClass('modal-scroll-inside', !!that.options.scrollInside);
if(that.isShown || e.isDefaultPrevented()) return
that.isShown = true
if(that.options.moveable) that.setMoveale();
that.checkScrollbar()
if (that.options.backdrop !== false) {
that.$body.addClass('modal-open')
that.setScrollbar()
}
that.escape()
that.$element.on('click.dismiss.' + zuiname, '[data-dismiss="modal"]',function(e) {
that.hide();
e.stopPropagation();
})
that.backdrop(function() {
var transition = $.support.transition && that.$element.hasClass('fade')
if(!that.$element.parent().length) {
that.$element.appendTo(that.$body) // don't move modals dom position
}
that.$element
.show()
.scrollTop(0)
if(transition) {
that.$element[0].offsetWidth // force reflow
}
that.$element
.addClass('in')
.attr('aria-hidden', false)
that.ajustPosition(position);
that.enforceFocus()
var e = $.Event('shown.' + zuiname, {
relatedTarget: _relatedTarget
})
transition ?
that.$element.find('.modal-dialog') // wait for modal to slide in
.one('bsTransitionEnd', function() {
that.$element.trigger('focus').trigger(e)
})
.emulateTransitionEnd(Modal.TRANSITION_DURATION) :
that.$element.trigger('focus').trigger(e)
})
}
Modal.prototype.hide = function(e) {
if(e) e.preventDefault()
var that = this;
e = $.Event('hide.' + zuiname)
that.$element.trigger(e)
if(!that.isShown || e.isDefaultPrevented()) return
that.isShown = false
if (that.options.backdrop !== false) {
that.$body.removeClass('modal-open')
that.resetScrollbar()
}
that.escape()
$(document).off('focusin.' + zuiname)
that.$element
.removeClass('in')
.attr('aria-hidden', true)
.off('click.dismiss.' + zuiname)
$.support.transition && that.$element.hasClass('fade') ?
that.$element
.one('bsTransitionEnd', $.proxy(that.hideModal, that))
.emulateTransitionEnd(Modal.TRANSITION_DURATION) :
that.hideModal()
}
Modal.prototype.enforceFocus = function() {
$(document)
.off('focusin.' + zuiname) // guard against infinite focus loop
.on('focusin.' + zuiname, $.proxy(function(e) {
if(this.$element[0] !== e.target && !this.$element.has(e.target).length) {
this.$element.trigger('focus')
}
}, this))
}
Modal.prototype.escape = function() {
if(this.isShown && this.options.keyboard) {
$(document).on('keydown.dismiss.' + zuiname, $.proxy(function(e) {
if(e.which == 27) {
var et = $.Event('escaping.' + zuiname)
var result = this.$element.triggerHandler(et, 'esc')
if(result != undefined && (!result)) return
this.hide()
}
}, this))
} else if(!this.isShown) {
$(document).off('keydown.dismiss.' + zuiname)
}
}
Modal.prototype.hideModal = function() {
var that = this
this.$element.hide()
this.backdrop(function() {
that.$element.trigger('hidden.' + zuiname)
})
}
Modal.prototype.removeBackdrop = function() {
this.$backdrop && this.$backdrop.remove()
this.$backdrop = null
}
Modal.prototype.backdrop = function(callback) {
var that = this
var animate = this.$element.hasClass('fade') ? 'fade' : ''
if(this.isShown && this.options.backdrop) {
var doAnimate = $.support.transition && animate
this.$backdrop = $('<div class="modal-backdrop ' + animate + '" />')
.appendTo(this.$body)
this.$element.on('mousedown.dismiss.' + zuiname, $.proxy(function(e) {
if(e.target !== e.currentTarget) return
this.options.backdrop == 'static' ? this.$element[0].focus.call(this.$element[0]) : this.hide.call(this)
}, this))
if(doAnimate) this.$backdrop[0].offsetWidth // force reflow
this.$backdrop.addClass('in')
if(!callback) return
doAnimate ?
this.$backdrop
.one('bsTransitionEnd', callback)
.emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) :
callback()
} else if(!this.isShown && this.$backdrop) {
this.$backdrop.removeClass('in')
var callbackRemove = function() {
that.removeBackdrop()
callback && callback()
}
$.support.transition && this.$element.hasClass('fade') ?
this.$backdrop
.one('bsTransitionEnd', callbackRemove)
.emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) :
callbackRemove()
} else if(callback) {
callback()
}
}
Modal.prototype.checkScrollbar = function() {
if(document.body.clientWidth >= window.innerWidth) return
this.scrollbarWidth = this.scrollbarWidth || this.measureScrollbar()
}
Modal.prototype.setScrollbar = function() {
var bodyPad = parseInt((this.$body.css('padding-right') || 0), 10)
if(this.scrollbarWidth) this.$body.css('padding-right', bodyPad + this.scrollbarWidth)
}
Modal.prototype.resetScrollbar = function() {
this.$body.css('padding-right', '')
}
Modal.prototype.measureScrollbar = function() { // thx walsh
var scrollDiv = document.createElement('div')
scrollDiv.className = 'modal-scrollbar-measure'
this.$body.append(scrollDiv)
var scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth
this.$body[0].removeChild(scrollDiv)
return scrollbarWidth
}
// MODAL PLUGIN DEFINITION
// =======================
function Plugin(option, _relatedTarget, position) {
return this.each(function() {
var $this = $(this)
var data = $this.data(zuiname)
var options = $.extend({}, Modal.DEFAULTS, $this.data(), typeof option == 'object' && option)
if(!data) $this.data(zuiname, (data = new Modal(this, options)))
if(typeof option == 'string') data[option](_relatedTarget, position)
else if(options.show) data.show(_relatedTarget, position)
})
}
var old = $.fn.modal
$.fn.modal = Plugin
$.fn.modal.Constructor = Modal
// MODAL NO CONFLICT
// =================
$.fn.modal.noConflict = function() {
$.fn.modal = old
return this
}
// MODAL DATA-API
// ==============
$(document).on('click.' + zuiname + '.data-api', '[data-toggle="modal"]', function(e) {
var $this = $(this)
var href = $this.attr('href')
var $target = null
try {
// strip for ie7
$target = $($this.attr('data-target') || (href && href.replace(/.*(?=#[^\s]+$)/, '')));
} catch(ex) {
return
}
if(!$target.length) return;
var option = $target.data(zuiname) ? 'toggle' : $.extend({
remote: !/#/.test(href) && href
}, $target.data(), $this.data())
if($this.is('a')) e.preventDefault()
$target.one('show.' + zuiname, function(showEvent) {
// only register focus restorer if modal will actually get shown
if(showEvent.isDefaultPrevented()) return
$target.one('hidden.' + zuiname, function() {
$this.is(':visible') && $this.trigger('focus')
})
})
Plugin.call($target, option, this, $this.data('position'))
})
}(jQuery, undefined);