openstack-manuals/www/static/common/js/webui-popover.js
Tom Fifield c7ed201cf5 New homepage design
This commit adds all files for the new landing design by
Wes Wilson, modifies templates and pages to use the new
design.

Change-Id: I3447172321731989703849b7c99eb030b04fd74f
2015-01-30 15:36:50 +08:00

435 lines
20 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

;(function ( $, window, document, undefined ) {
// Create the defaults once
var pluginName = 'webuiPopover';
var pluginClass = 'webui-popover';
var pluginType = 'webui.popover';
var defaults = {
placement:'auto',
width:'auto',
height:'auto',
trigger:'click',
style:'',
delay:300,
cache:true,
multi:false,
arrow:true,
title:'',
content:'',
closeable:false,
padding:true,
url:'',
type:'html',
template:'<div class="webui-popover">'+
'<div class="arrow"></div>'+
'<div class="webui-popover-inner">'+
'<a href="#" class="close">x</a>'+
'<h3 class="webui-popover-title"></h3>'+
'<div class="webui-popover-content"><i class="icon-refresh"></i> <p>&nbsp;</p></div>'+
'</div>'+
'</div>'
};
// The actual plugin constructor
function WebuiPopover ( element, options ) {
this.$element = $(element);
this.options = $.extend( {}, defaults, options );
this._defaults = defaults;
this._name = pluginName;
this.init();
}
WebuiPopover.prototype = {
//init webui popover
init: function () {
//init the event handlers
if (this.options.trigger==='click'){
this.$element.off('click').on('click',$.proxy(this.toggle,this));
}else{
this.$element.off('mouseenter mouseleave')
.on('mouseenter',$.proxy(this.mouseenterHandler,this))
.on('mouseleave',$.proxy(this.mouseleaveHandler,this));
}
this._poped = false;
this._inited = true;
},
/* api methods and actions */
destroy:function(){
this.hide();
this.$element.data('plugin_'+pluginName,null);
this.$element.off();
if (this.$target){
this.$target.remove();
}
},
hide:function(event){
if (event){
event.preventDefault();
event.stopPropagation();
}
var e = $.Event('hide.' + pluginType);
this.$element.trigger(e);
if (this.$target){this.$target.removeClass('in').hide();}
this.$element.trigger('hidden.'+pluginType);
},
toggle:function(e){
if (e) {
e.preventDefault();
e.stopPropagation();
}
this[this.getTarget().hasClass('in') ? 'hide' : 'show']();
},
hideAll:function(){
$('div.webui-popover').not('.webui-popover-fixed').removeClass('in').hide();
},
/*core method ,show popover */
show:function(){
var
$target = this.getTarget().removeClass().addClass(pluginClass);
if (!this.options.multi){
this.hideAll();
}
// use cache by default, if not cache set, reInit the contents
if (!this.options.cache||!this._poped){
this.setTitle(this.getTitle());
if (!this.options.closeable){
$target.find('.close').off('click').remove();
}
if (!this.isAsync()){
this.setContent(this.getContent());
}else{
this.setContentASync(this.options.content);
this.displayContent();
return;
}
$target.show();
}
this.displayContent();
this.bindBodyEvents();
},
displayContent:function(){
var
//element postion
elementPos = this.getElementPosition(),
//target postion
$target = this.getTarget().removeClass().addClass(pluginClass),
//target content
$targetContent = this.getContentElement(),
//target Width
targetWidth = $target[0].offsetWidth,
//target Height
targetHeight = $target[0].offsetHeight,
//placement
placement = 'bottom',
e = $.Event('show.' + pluginType);
//if (this.hasContent()){
this.$element.trigger(e);
//}
if (this.options.width!=='auto') {$target.width(this.options.width);}
if (this.options.height!=='auto'){$targetContent.height(this.options.height);}
//init the popover and insert into the document body
if (!this.options.arrow){
$target.find('.arrow').remove();
}
$target.remove().css({ top: -1000, left: -1000, display: 'block' }).appendTo(document.body);
targetWidth = $target[0].offsetWidth;
targetHeight = $target[0].offsetHeight;
placement = this.getPlacement(elementPos,targetHeight);
this.initTargetEvents();
var postionInfo = this.getTargetPositin(elementPos,placement,targetWidth,targetHeight);
this.$target.css(postionInfo.position).addClass(placement).addClass('in');
if (this.options.type==='iframe'){
var $iframe = $target.find('iframe');
$iframe.width($target.width()).height($iframe.parent().height());
}
if (this.options.style){
this.$target.addClass(pluginClass+'-'+this.options.style);
}
if (!this.options.padding){
$targetContent.css('height',$targetContent.outerHeight());
this.$target.addClass('webui-no-padding');
}
if (!this.options.arrow){
this.$target.css({'margin':0});
}
if (this.options.arrow){
var $arrow = this.$target.find('.arrow');
$arrow.removeAttr('style');
if (postionInfo.arrowOffset){
$arrow.css(postionInfo.arrowOffset);
}
}
this._poped = true;
this.$element.trigger('shown.'+pluginType);
},
isTargetLoaded:function(){
return this.getTarget().find('i.glyphicon-refresh').length===0;
},
/*getter setters */
getTarget:function(){
if (!this.$target){
this.$target = $(this.options.template);
}
return this.$target;
},
getTitleElement:function(){
return this.getTarget().find('.'+pluginClass+'-title');
},
getContentElement:function(){
return this.getTarget().find('.'+pluginClass+'-content');
},
getTitle:function(){
return this.options.title||this.$element.attr('data-title')||this.$element.attr('title');
},
setTitle:function(title){
var $titleEl = this.getTitleElement();
if (title){
$titleEl.html(title);
}else{
$titleEl.remove();
}
},
hasContent:function () {
return this.getContent();
},
getContent:function(){
if (this.options.url){
if (this.options.type==='iframe'){
this.content = $('<iframe frameborder="0"></iframe>').attr('src',this.options.url);
}
}else if (!this.content){
var content='';
if ($.isFunction(this.options.content)){
content = this.options.content.apply(this.$element[0],arguments);
}else{
content = this.options.content;
}
this.content = this.$element.attr('data-content')||content;
}
return this.content;
},
setContent:function(content){
var $target = this.getTarget();
this.getContentElement().html(content);
this.$target = $target;
},
isAsync:function(){
return this.options.type==='async';
},
setContentASync:function(content){
var that = this;
$.ajax({
url:this.options.url,
type:'GET',
cache:this.options.cache,
success:function(data){
if (content&&$.isFunction(content)){
that.content = content.apply(that.$element[0],[data]);
}else{
that.content = data;
}
that.setContent(that.content);
var $targetContent = that.getContentElement();
$targetContent.removeAttr('style');
that.displayContent();
}
});
},
bindBodyEvents:function(){
$('body').off('keyup.webui-popover').on('keyup.webui-popover',$.proxy(this.escapeHandler,this));
$('body').off('click.webui-popover').on('click.webui-popover',$.proxy(this.bodyClickHandler,this));
},
/* event handlers */
mouseenterHandler:function(){
var self = this;
if (self._timeout){clearTimeout(self._timeout);}
if (!self.getTarget().is(':visible')){self.show();}
},
mouseleaveHandler:function(){
var self = this;
//key point, set the _timeout then use clearTimeout when mouse leave
self._timeout = setTimeout(function(){
self.hide();
},self.options.delay);
},
escapeHandler:function(e){
if (e.keyCode===27){
this.hideAll();
}
},
bodyClickHandler:function(){
this.hideAll();
},
targetClickHandler:function(e){
e.stopPropagation();
},
//reset and init the target events;
initTargetEvents:function(){
if (this.options.trigger!=='click'){
this.$target.off('mouseenter mouseleave')
.on('mouseenter',$.proxy(this.mouseenterHandler,this))
.on('mouseleave',$.proxy(this.mouseleaveHandler,this));
}
this.$target.find('.close').off('click').on('click', $.proxy(this.hide,this));
this.$target.off('click.webui-popover').on('click.webui-popover',$.proxy(this.targetClickHandler,this));
},
/* utils methods */
//caculate placement of the popover
getPlacement:function(pos,targetHeight){
var
placement,
de = document.documentElement,
db = document.body,
clientWidth = de.clientWidth,
clientHeight = de.clientHeight,
scrollTop = Math.max(db.scrollTop,de.scrollTop),
scrollLeft = Math.max(db.scrollLeft,de.scrollLeft),
pageX = Math.max(0,pos.left - scrollLeft),
pageY = Math.max(0,pos.top - scrollTop),
arrowSize = 20;
//if placement equals autocaculate the placement by element information;
if (typeof(this.options.placement)==='function'){
placement = this.options.placement.call(this, this.getTarget()[0], this.$element[0]);
}else{
placement = this.$element.data('placement')||this.options.placement;
}
if (placement==='auto'){
if (pageX<clientWidth/3){
if (pageY<clientHeight/3){
placement = 'bottom-right';
}else if (pageY<clientHeight*2/3){
placement = 'right';
}else{
placement = 'top-right';
}
//placement= pageY>targetHeight+arrowSize?'top-right':'bottom-right';
}else if (pageX<clientWidth*2/3){
if (pageY<clientHeight/3){
placement = 'bottom';
}else if (pageY<clientHeight*2/3){
placement = 'bottom';
}else{
placement = 'top';
}
}else{
placement = pageY>targetHeight+arrowSize?'top-left':'bottom-left';
if (pageY<clientHeight/3){
placement = 'bottom-left';
}else if (pageY<clientHeight*2/3){
placement = 'left';
}else{
placement = 'top-left';
}
}
}
return placement;
},
getElementPosition:function(){
return $.extend({},this.$element.offset(), {
width: this.$element[0].offsetWidth,
height: this.$element[0].offsetHeight
});
},
getTargetPositin:function(elementPos,placement,targetWidth,targetHeight){
var pos = elementPos,
elementW = this.$element.outerWidth(),
elementH = this.$element.outerHeight(),
position={},
arrowOffset=null,
arrowSize = this.options.arrow?28:0,
fixedW = elementW<arrowSize+10?arrowSize:0,
fixedH = elementH<arrowSize+10?arrowSize:0;
switch (placement) {
case 'bottom':
position = {top: pos.top + pos.height, left: pos.left + pos.width / 2 - targetWidth / 2};
break;
case 'top':
position = {top: pos.top - targetHeight, left: pos.left + pos.width / 2 - targetWidth / 2};
break;
case 'left':
position = {top: pos.top + pos.height / 2 - targetHeight / 2, left: pos.left - targetWidth};
break;
case 'right':
position = {top: pos.top + pos.height / 2 - targetHeight / 2, left: pos.left + pos.width};
break;
case 'top-right':
position = {top: pos.top - targetHeight, left: pos.left-fixedW};
arrowOffset = {left: elementW/2 + fixedW};
break;
case 'top-left':
position = {top: pos.top - targetHeight, left: pos.left -targetWidth +pos.width + fixedW};
arrowOffset = {left: targetWidth - elementW /2 -fixedW};
break;
case 'bottom-right':
position = {top: pos.top + pos.height, left: pos.left-fixedW};
arrowOffset = {left: elementW /2+fixedW};
break;
case 'bottom-left':
position = {top: pos.top + pos.height, left: pos.left -targetWidth +pos.width+fixedW};
arrowOffset = {left: targetWidth- elementW /2 - fixedW};
break;
case 'right-top':
position = {top: pos.top -targetHeight + pos.height + fixedH, left: pos.left + pos.width};
arrowOffset = {top: targetHeight - elementH/2 -fixedH};
break;
case 'right-bottom':
position = {top: pos.top - fixedH, left: pos.left + pos.width};
arrowOffset = {top: elementH /2 +fixedH };
break;
case 'left-top':
position = {top: pos.top -targetHeight + pos.height+fixedH, left: pos.left - targetWidth};
arrowOffset = {top: targetHeight - elementH/2 - fixedH};
break;
case 'left-bottom':
position = {top: pos.top , left: pos.left -targetWidth};
arrowOffset = {top: elementH /2 };
break;
}
return {position:position,arrowOffset:arrowOffset};
}
};
$.fn[ pluginName ] = function ( options ) {
return this.each(function() {
var webuiPopover = $.data( this, 'plugin_' + pluginName );
if (!webuiPopover) {
if (!options){
webuiPopover = new WebuiPopover( this, null);
}else if (typeof options ==='string'){
if (options!=='destroy'){
webuiPopover = new WebuiPopover( this, null );
webuiPopover[options]();
}
}else if (typeof options ==='object'){
webuiPopover = new WebuiPopover( this, options );
}
$.data( this, 'plugin_' + pluginName, webuiPopover);
}else{
if (options==='destroy'){
webuiPopover.destroy();
}else if (typeof options ==='string'){
webuiPopover[options]();
}
}
});
};
})( jQuery, window, document );