jquery.webui-popover.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358
  1. /*
  2. * webui popover plugin - v1.0.5
  3. * A lightWeight popover plugin with jquery ,enchance the popover plugin of bootstrap with some awesome new features. It works well with bootstrap ,but bootstrap is not necessary!
  4. * https://github.com/sandywalker/webui-popover
  5. *
  6. * Made by Sandy Duan
  7. * Under MIT License
  8. */
  9. ;(function ( $, window, document, undefined ) {
  10. // Create the defaults once
  11. var pluginName = 'webuiPopover';
  12. var pluginClass = 'webui-popover';
  13. var pluginType = 'webui.popover';
  14. var defaults = {
  15. placement:'auto',
  16. width:'auto',
  17. height:'auto',
  18. trigger:'click',
  19. style:'',
  20. delay:300,
  21. cache:true,
  22. multi:false,
  23. arrow:true,
  24. title:'',
  25. content:'',
  26. closeable:false,
  27. padding:true,
  28. url:'',
  29. type:'html',
  30. template:'<div class="webui-popover">'+
  31. '<div class="arrow"></div>'+
  32. '<div class="webui-popover-inner">'+
  33. '<a href="#" class="close">x</a>'+
  34. '<h3 class="webui-popover-title"></h3>'+
  35. '<div class="webui-popover-content"><i class="glyphicon glyphicon-refresh"></i> <p></p></div>'+
  36. '</div>'+
  37. '</div>'
  38. };
  39. // The actual plugin constructor
  40. function WebuiPopover ( element, options ) {
  41. this.$element = $(element);
  42. this.options = $.extend( {}, defaults, options );
  43. this._defaults = defaults;
  44. this._name = pluginName;
  45. this.init();
  46. }
  47. WebuiPopover.prototype = {
  48. //init webui popover
  49. init: function () {
  50. //init the event handlers
  51. if (this.options.trigger==='click'){
  52. this.$element.off('click').on('click',$.proxy(this.toggle,this));
  53. }else{
  54. this.$element.off('mouseenter mouseleave')
  55. .on('mouseenter',$.proxy(this.mouseenterHandler,this))
  56. .on('mouseleave',$.proxy(this.mouseleaveHandler,this));
  57. }
  58. this._poped = false;
  59. this._inited = true;
  60. },
  61. /* api methods and actions */
  62. destroy:function(){
  63. this.hide();
  64. this.$element.data('plugin_'+pluginName,null);
  65. this.$element.off();
  66. if (this.$target){
  67. this.$target.remove();
  68. }
  69. },
  70. hide:function(event){
  71. if (event){event.preventDefault();}
  72. var e = $.Event('hide.' + pluginType);
  73. this.$element.trigger(e);
  74. if (this.$target){this.$target.removeClass('in').hide();}
  75. this.$element.trigger('hidden.'+pluginType);
  76. },
  77. toggle:function(e){
  78. if (e) {e.preventDefault();}
  79. this[this.getTarget().hasClass('in') ? 'hide' : 'show']();
  80. },
  81. hideAll:function(){
  82. $('div.webui-popover').removeClass('in').hide();
  83. },
  84. /*core method ,show popover */
  85. show:function(){
  86. var
  87. //element postion
  88. elementPos = this.getElementPosition(),
  89. //target postion
  90. $target = this.getTarget().removeClass().addClass(pluginClass),
  91. //target content
  92. $targetContent = this.getContentElement(),
  93. //target Width
  94. targetWidth = $target[0].offsetWidth,
  95. //target Height
  96. targetHeight = $target[0].offsetHeight,
  97. //placement
  98. placement = 'bottom',
  99. e = $.Event('show.' + pluginType);
  100. if (!this.options.multi){
  101. this.hideAll();
  102. }
  103. //if (this.hasContent()){
  104. this.$element.trigger(e);
  105. //}
  106. // use cache by default, if not cache setted , reInit the contents
  107. if (!this.options.cache||!this._poped){
  108. if (!this.isAsync()){
  109. this.setContent(this.getContent());
  110. }else{
  111. this.setContentASync(this.options.content);
  112. }
  113. this.setTitle(this.getTitle());
  114. if (!this.options.closeable){
  115. $target.find('.close').off('click').remove();
  116. }
  117. $target.show();
  118. }
  119. if (this.options.width!=='auto') {$target.width(this.options.width);}
  120. if (this.options.height!=='auto'){$targetContent.height(this.options.height);}
  121. //init the popover and insert into the document body
  122. if (!this.options.arrow){
  123. $target.find('.arrow').remove();
  124. }
  125. $target.remove().css({ top: -1000, left: -1000, display: 'block' }).appendTo(document.body);
  126. targetWidth = $target[0].offsetWidth;
  127. targetHeight = $target[0].offsetHeight;
  128. placement = this.getPlacement(elementPos,targetHeight);
  129. this.initTargetEvents();
  130. var postionInfo = this.getTargetPositin(elementPos,placement,targetWidth,targetHeight);
  131. this.$target.css(postionInfo.position).addClass(placement).addClass('in');
  132. if (this.options.type==='iframe'){
  133. var $iframe = $target.find('iframe');
  134. $iframe.width($target.width()).height($iframe.parent().height());
  135. }
  136. if (this.options.style){
  137. this.$target.addClass(pluginClass+'-'+this.options.style);
  138. }
  139. if (!this.options.padding){
  140. //fixed position offset bug
  141. $targetContent.css('height',$targetContent.outerHeight());
  142. this.$target.addClass('webui-no-padding');
  143. }
  144. if (!this.options.arrow){
  145. this.$target.css({'margin':0});
  146. }
  147. if (this.options.arrow&&postionInfo.arrowOffset){
  148. this.$target.find('.arrow').css(postionInfo.arrowOffset);
  149. }
  150. this._poped = true;
  151. this.$element.trigger('shown.'+pluginType);
  152. },
  153. /*getter setters */
  154. getTarget:function(){
  155. if (!this.$target){
  156. this.$target = $(this.options.template);
  157. }
  158. return this.$target;
  159. },
  160. getTitleElement:function(){
  161. return this.getTarget().find('.'+pluginClass+'-title');
  162. },
  163. getContentElement:function(){
  164. return this.getTarget().find('.'+pluginClass+'-content');
  165. },
  166. getTitle:function(){
  167. return this.options.title||this.$element.attr('data-title')||this.$element.attr('title');
  168. },
  169. setTitle:function(title){
  170. var $titleEl = this.getTitleElement();
  171. if (title){
  172. $titleEl.html(title);
  173. }else{
  174. $titleEl.remove();
  175. }
  176. },
  177. hasContent:function () {
  178. return this.getContent();
  179. },
  180. getContent:function(){
  181. if (this.options.url){
  182. if (this.options.type==='iframe'){
  183. this.content = $('<iframe frameborder="0"></iframe>').attr('src',this.options.url);
  184. }
  185. }else if (!this.content){
  186. var content='';
  187. if ($.isFunction(this.options.content)){
  188. content = this.options.content.apply(this.$element[0],arguments);
  189. }else{
  190. content = this.options.content;
  191. }
  192. this.content = this.$element.attr('data-content')||content;
  193. }
  194. return this.content;
  195. },
  196. setContent:function(content){
  197. var $target = this.getTarget();
  198. this.getContentElement().html(content);
  199. this.$target = $target;
  200. },
  201. isAsync:function(){
  202. return this.options.type==='async';
  203. },
  204. setContentASync:function(content){
  205. var that = this;
  206. $.ajax({
  207. url:this.options.url,
  208. type:'GET',
  209. cache:this.options.cache,
  210. success:function(data){
  211. if (content&&$.isFunction(content)){
  212. that.content = content.apply(that.$element[0],[data]);
  213. }else{
  214. that.content = data;
  215. }
  216. that.setContent(that.content);
  217. }
  218. });
  219. },
  220. /* event handlers */
  221. mouseenterHandler:function(){
  222. var self = this;
  223. if (self._timeout){clearTimeout(self._timeout);}
  224. if (!self.getTarget().is(':visible')){self.show();}
  225. },
  226. mouseleaveHandler:function(){
  227. var self = this;
  228. //key point, set the _timeout then use clearTimeout when mouse leave
  229. self._timeout = setTimeout(function(){
  230. self.hide();
  231. },self.options.delay);
  232. },
  233. //reset and init the target events;
  234. initTargetEvents:function(){
  235. if (this.options.trigger!=='click'){
  236. this.$target.off('mouseenter mouseleave')
  237. .on('mouseenter',$.proxy(this.mouseenterHandler,this))
  238. .on('mouseleave',$.proxy(this.mouseleaveHandler,this));
  239. }
  240. this.$target.find('.close').off('click').on('click', $.proxy(this.hide,this));
  241. },
  242. /* utils methods */
  243. //caculate placement of the popover
  244. getPlacement:function(pos,targetHeight){
  245. var
  246. placement,
  247. de = document.documentElement,
  248. db = document.body,
  249. clientWidth = de.clientWidth,
  250. //clientHeight = de.clientHeight,
  251. scrollTop = Math.max(db.scrollTop,de.scrollTop),
  252. scrollLeft = Math.max(db.scrollLeft,de.scrollLeft),
  253. pageX = Math.max(0,pos.left - scrollLeft),
  254. pageY = Math.max(0,pos.top - scrollTop),
  255. arrowSize = 20;
  256. //if placement equals auto,caculate the placement by element information;
  257. if (typeof(this.options.placement)==='function'){
  258. placement = this.options.placement.call(this, this.getTarget()[0], this.$element[0]);
  259. }else{
  260. placement = this.$element.data('placement')||this.options.placement;
  261. }
  262. if (placement==='auto'){
  263. if (pageX<clientWidth/3){
  264. placement= pageY>targetHeight+arrowSize?'top-right':'bottom-right';
  265. }else if (pageX<clientWidth*2/3){
  266. placement = pageY>targetHeight+arrowSize?'top':'bottom';
  267. }else{
  268. placement = pageY>targetHeight+arrowSize?'top-left':'bottom-left';
  269. }
  270. }
  271. return placement;
  272. },
  273. getElementPosition:function(){
  274. return $.extend({},this.$element.offset(), {
  275. width: this.$element[0].offsetWidth,
  276. height: this.$element[0].offsetHeight
  277. });
  278. },
  279. getTargetPositin:function(elementPos,placement,targetWidth,targetHeight){
  280. var pos = elementPos,
  281. elementW = this.$element.outerWidth(),
  282. position={},
  283. arrowOffset={},
  284. arrowSize = this.options.arrow?0:0;
  285. switch (placement) {
  286. case 'bottom':
  287. position = {top: pos.top + pos.height, left: pos.left + pos.width / 2 - targetWidth / 2};
  288. break;
  289. case 'top':
  290. position = {top: pos.top - targetHeight-arrowSize, left: pos.left + pos.width / 2 - targetWidth / 2};
  291. break;
  292. case 'left':
  293. position = {top: pos.top + pos.height / 2 - targetHeight / 2, left: pos.left - targetWidth -arrowSize};
  294. break;
  295. case 'right':
  296. position = {top: pos.top + pos.height / 2 - targetHeight / 2, left: pos.left + pos.width};
  297. break;
  298. case 'top-right':
  299. position = {top: pos.top - targetHeight -arrowSize, left: pos.left};
  300. arrowOffset = {left: elementW /2 };
  301. break;
  302. case 'top-left':
  303. position = {top: pos.top - targetHeight -arrowSize, left: pos.left -targetWidth +pos.width};
  304. arrowOffset = {left: targetWidth - elementW /2 };
  305. break;
  306. case 'bottom-right':
  307. position = {top: pos.top + pos.height, left: pos.left};
  308. arrowOffset = {left: elementW /2};
  309. break;
  310. case 'bottom-left':
  311. position = {top: pos.top + pos.height, left: pos.left -targetWidth +pos.width};
  312. arrowOffset = {left: targetWidth- elementW /2};
  313. break;
  314. }
  315. return {position:position,arrowOffset:arrowOffset};
  316. }
  317. };
  318. $.fn[ pluginName ] = function ( options ) {
  319. return this.each(function() {
  320. var webuiPopover = $.data( this, 'plugin_' + pluginName );
  321. if (!webuiPopover) {
  322. if (!options){
  323. webuiPopover = new WebuiPopover( this, null);
  324. }else if (typeof options ==='string'){
  325. if (options!=='destroy'){
  326. webuiPopover = new WebuiPopover( this, null );
  327. webuiPopover[options]();
  328. }
  329. }else if (typeof options ==='object'){
  330. webuiPopover = new WebuiPopover( this, options );
  331. }
  332. $.data( this, 'plugin_' + pluginName, webuiPopover);
  333. }else{
  334. if (options==='destroy'){
  335. webuiPopover.destroy();
  336. }else if (typeof options ==='string'){
  337. webuiPopover[options]();
  338. }
  339. }
  340. });
  341. };
  342. })( jQuery, window, document );