Created
November 23, 2013 12:07
-
-
Save patricklx/7613893 to your computer and use it in GitHub Desktop.
Ember.js Bootstrap Popover
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var template = '' + | |
'<div class="arrow"></div>' + | |
' <h3 class="popover-title">{{title}}</h3>' + | |
'<div class="popover-content">' + | |
'{{#if content}}' + | |
' {{content}}' + | |
'{{else}}' + | |
'{{yield}}' + | |
'{{/if}}' + | |
' </div>'; | |
Ember.TEMPLATES["components/bs-popover"] = Ember.Handlebars.compile(template); | |
App.BsPopoverComponent = Ember.Component.extend({ | |
classNames: 'popover', | |
classNameBindings: ['fade', 'in', 'top', 'left', 'right', 'bottom'], | |
top: function(){ | |
return this.get('realPlacement')=='top'; | |
}.property('realPlacement'), | |
left: function(){ | |
return this.get('realPlacement')=='left'; | |
}.property('realPlacement'), | |
right: function(){ | |
return this.get('realPlacement')=='right'; | |
}.property('realPlacement'), | |
bottom: function(){ | |
return this.get('realPlacement')=='bottom'; | |
}.property('realPlacement'), | |
title: '', | |
content: '', | |
html: false, | |
delay: 0, | |
isVisible: false, | |
animation: true, | |
fade: function(){ | |
return this.get('animation'); | |
}.property('animation'), | |
in: function(){ | |
return this.get('isVisible'); | |
}.property('isVisible'), | |
triggers: 'hover focus', | |
placement: 'top', | |
onElement: null, | |
$element: null, | |
$tip: null, | |
inserted: false, | |
styleUpdater: function(){ | |
if( !this.$tip || !this.get('isVisible')){ | |
return; | |
} | |
this.$tip.css('display','block'); | |
var placement = this.get('realPlacement'); | |
var pos = this.getPosition(); | |
var actualWidth = this.$tip[0].offsetWidth; | |
var actualHeight = this.$tip[0].offsetHeight; | |
var calculatedOffset = this.getCalculatedOffset(placement, pos, actualWidth, actualHeight); | |
this.$tip.css('top',calculatedOffset.top); | |
this.$tip.css('left',calculatedOffset.left); | |
if(this.firstTime){ | |
this.firstTime = false; | |
this.styleUpdater(); | |
this.firstTime = true; | |
} | |
}.observes('content','realPlacement','inserted', 'isVisible'), | |
didInsertElement: function(){ | |
this.$tip = this.$(); | |
if(this.get('onElement')){ | |
this.$element=$('#'+this.get('onElement')); | |
}else if(this.$tip.prev(':not(script)').length){ | |
this.$element = this.$tip.prev(':not(script)'); | |
}else{ | |
this.$element = this.$tip.parent(':not(script)'); | |
} | |
var triggers = this.triggers.split(' '); | |
for (var i = triggers.length; i--;) { | |
var trigger = triggers[i]; | |
if (trigger == 'click') { | |
this.$element.on('click',$.proxy(this.toggle, this)); | |
} else if (trigger != 'manual') { | |
var eventIn = trigger == 'hover' ? 'mouseenter' : 'focus'; | |
var eventOut = trigger == 'hover' ? 'mouseleave' : 'blur'; | |
this.$element.on(eventIn, $.proxy(this.enter, this)); | |
this.$element.on(eventOut, $.proxy(this.leave, this)); | |
} | |
} | |
this.set('inserted',true); | |
}, | |
toggle: function(){ | |
this.toggleProperty('isVisible'); | |
}, | |
enter: function(){ | |
this.set('isVisible',true); | |
}, | |
leave: function(){ | |
this.set('isVisible',false); | |
}, | |
afterRender: function(){ | |
this.notifyPropertyChange('content'); | |
}, | |
realPlacement: function(){ | |
if(!this.$tip) return null; | |
var placement = this.get('placement') || ''; | |
var autoToken = /\s?auto?\s?/i; | |
var autoPlace = autoToken.test(placement); | |
if (autoPlace) | |
placement = placement.replace(autoToken, '') || 'top'; | |
var pos = this.getPosition(); | |
var actualWidth = this.$tip[0].offsetWidth; | |
var actualHeight = this.$tip[0].offsetHeight; | |
if (autoPlace) { | |
var $parent = this.$element.parent(); | |
var orgPlacement = placement; | |
var docScroll = document.documentElement.scrollTop || document.body.scrollTop; | |
var parentWidth = $parent.outerWidth(); | |
var parentHeight = $parent.outerHeight(); | |
var parentLeft = $parent.offset().left; | |
placement = placement == 'bottom' && pos.top + pos.height + actualHeight - docScroll > parentHeight ? 'top' : | |
placement == 'top' && pos.top - docScroll - actualHeight < 0 ? 'bottom' : | |
placement == 'right' && pos.right + actualWidth > parentWidth ? 'left' : | |
placement == 'left' && pos.left - actualWidth < parentLeft ? 'right' : | |
placement; | |
} | |
return placement; | |
}.property('placement','inserted'), | |
hasContent: function () { | |
return this.get('title'); | |
}, | |
getPosition: function () { | |
var el = this.$element[0]; | |
return $.extend({}, (typeof el.getBoundingClientRect == 'function') ? el.getBoundingClientRect() : { | |
width: el.offsetWidth, height: el.offsetHeight | |
}, this.$element.offset()); | |
}, | |
getCalculatedOffset: function (placement, pos, actualWidth, actualHeight) { | |
return placement == 'bottom' ? { top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2 } : | |
placement == 'top' ? { top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2 } : | |
placement == 'left' ? { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth } : | |
/* placement == 'right' */ { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width } | |
} | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment