*
*/
$(document).ready(function () {
- // Elements with the class "userinfo" will get a hover-card.
- // Note that this elements does need a href attribute which links to
- // a valid profile url
- $("body").on("mouseover", ".userinfo, .wall-item-responses a, .wall-item-bottom .mention a", function (e) {
+ let $body = $('body');
+ // Prevents normal click action on click hovercard elements
+ $body.on('click', '.userinfo.click-card', function (e) {
+ e.preventDefault();
+ });
+ // This event listener needs to be declared before the one that removes
+ // all cards so that we can stop the immediate propagation of the event
+ // Since the manual popover appears instantly and the hovercard removal is
+ // on a 100ms delay, leaving event propagation immediately hides any click hovercard
+ $body.on('mousedown', '.userinfo.click-card', function (e) {
+ e.stopImmediatePropagation();
+ let timeNow = new Date().getTime();
+
+ let contactUrl = false;
+ let targetElement = $(this);
+
+ // get href-attribute
+ if (targetElement.is('[href]')) {
+ contactUrl = targetElement.attr('href');
+ } else {
+ return true;
+ }
+
+ // no hovercard for anchor links
+ if (contactUrl.substring(0, 1) === '#') {
+ return true;
+ }
+
+ openHovercard(targetElement, contactUrl, timeNow);
+ });
+
+ // hover cards should be removed very easily, e.g. when any of these events happens
+ $body.on('mouseleave touchstart scroll mousedown submit keydown', function (e) {
+ // remove hover card only for desktiop user, since on mobile we open the hovercards
+ // by click event insteadof hover
+ removeAllHovercards(e, new Date().getTime());
+ });
+
+ $body.on('mouseover', '.userinfo.hover-card, .wall-item-responses a, .wall-item-bottom .mention a', function (e) {
let timeNow = new Date().getTime();
removeAllHovercards(e, timeNow);
- let contact_url = false;
+ let contactUrl = false;
let targetElement = $(this);
// get href-attribute
if (targetElement.is('[href]')) {
- contact_url = targetElement.attr('href');
+ contactUrl = targetElement.attr('href');
} else {
return true;
}
}
// no hovercard for anchor links
- if (contact_url.substring(0, 1) === '#') {
+ if (contactUrl.substring(0, 1) === '#') {
return true;
}
targetElement.attr('data-awaiting-hover-card', timeNow);
- // store the title in an other data attribute beause bootstrap
- // popover destroys the title.attribute. We can restore it later
- let title = targetElement.attr("title");
- targetElement.attr({"data-orig-title": title, title: ""});
-
- // if the device is a mobile open the hover card by click and not by hover
- if (typeof is_mobile != "undefined") {
- targetElement[0].removeAttribute("href");
- var hctrigger = 'click';
- } else {
- var hctrigger = 'manual';
- }
-
- // Timeout until the hover-card does appear
+ // Delay until the hover-card does appear
setTimeout(function () {
if (
- targetElement.is(":hover")
+ targetElement.is(':hover')
&& parseInt(targetElement.attr('data-awaiting-hover-card'), 10) === timeNow
&& $('.hovercard').length === 0
- ) { // no card if there already is one open
- // get an additional data atribute if the card is active
- targetElement.attr('data-hover-card-active', timeNow);
- // get the whole html content of the hover card and
- // push it to the bootstrap popover
- getHoverCardContent(contact_url, function (data) {
- if (data) {
- targetElement.popover({
- html: true,
- placement: function () {
- // Calculate the placement of the the hovercard (if top or bottom)
- // The placement depence on the distance between window top and the element
- // which triggers the hover-card
- var get_position = $(targetElement).offset().top - $(window).scrollTop();
- if (get_position < 270) {
- return "bottom";
- }
- return "top";
- },
- trigger: hctrigger,
- template: '<div class="popover hovercard" data-card-created="' + timeNow + '"><div class="arrow"></div><div class="popover-content hovercard-content"></div></div>',
- content: data,
- container: "body",
- sanitizeFn: function (content) {
- return DOMPurify.sanitize(content)
- },
- }).popover('show');
- }
- });
+ ) {
+ openHovercard(targetElement, contactUrl, timeNow);
}
}, 500);
- }).on("mouseleave", ".userinfo, .wall-item-responses a, .wall-item-bottom .mention a", function (e) { // action when mouse leaves the hover-card
- var timeNow = new Date().getTime();
- // copy the original title to the title atribute
- var title = $(this).attr("data-orig-title");
- $(this).attr({"data-orig-title": "", title: title});
- removeAllHovercards(e, timeNow);
- });
-
- // hover cards should be removed very easily, e.g. when any of these events happen
- $('body').on("mouseleave touchstart scroll click dblclick mousedown mouseup submit keydown keypress keyup", function (e) {
- // remove hover card only for desktiop user, since on mobile we openen the hovercards
- // by click event insteadof hover
- if (typeof is_mobile == "undefined") {
- var timeNow = new Date().getTime();
- removeAllHovercards(e, timeNow);
- }
+ }).on('mouseleave', '.userinfo.hover-card, .wall-item-responses a, .wall-item-bottom .mention a', function (e) { // action when mouse leaves the hover-card
+ removeAllHovercards(e, new Date().getTime());
});
// if we're hovering a hover card, give it a class, so we don't remove it
- $('body').on('mouseover', '.hovercard', function (e) {
+ $body.on('mouseover', '.hovercard', function (e) {
$(this).addClass('dont-remove-card');
});
- $('body').on('mouseleave', '.hovercard', function (e) {
+ $body.on('mouseleave', '.hovercard', function (e) {
$(this).removeClass('dont-remove-card');
- $(this).popover("hide");
+ $(this).popover('hide');
});
}); // End of $(document).ready
// don't remove hovercards until after 100ms, so user have time to move the cursor to it (which gives it the dont-remove-card class)
setTimeout(function () {
$.each($('.hovercard'), function () {
- var title = $(this).attr("data-orig-title");
+ let title = $(this).attr('data-orig-title');
// don't remove card if it was created after removeAllhoverCards() was called
if ($(this).data('card-created') < priorTo) {
// don't remove it if we're hovering it right now!
if (!$(this).hasClass('dont-remove-card')) {
- $('[data-hover-card-active="' + $(this).data('card-created') + '"]').removeAttr('data-hover-card-active');
- $(this).popover("hide");
+ let $handle = $('[data-hover-card-active="' + $(this).data('card-created') + '"]');
+ $handle.removeAttr('data-hover-card-active');
+
+ // Restoring the popover handle title
+ let title = $handle.attr('data-orig-title');
+ $handle.attr({'data-orig-title': '', title: title});
+
+ $(this).popover('hide');
}
}
});
}, 100);
}
+function openHovercard(targetElement, contactUrl, timeNow) {
+ // store the title in a data attribute because Bootstrap
+ // popover destroys the title attribute.
+ let title = targetElement.attr('title');
+ targetElement.attr({'data-orig-title': title, title: ''});
+
+ // get an additional data atribute if the card is active
+ targetElement.attr('data-hover-card-active', timeNow);
+ // get the whole html content of the hover card and
+ // push it to the bootstrap popover
+ getHoverCardContent(contactUrl, function (data) {
+ if (data) {
+ targetElement.popover({
+ html: true,
+ placement: function () {
+ // Calculate the placement of the the hovercard (if top or bottom)
+ // The placement depence on the distance between window top and the element
+ // which triggers the hover-card
+ let get_position = $(targetElement).offset().top - $(window).scrollTop();
+ if (get_position < 270) {
+ return 'bottom';
+ }
+ return 'top';
+ },
+ trigger: 'manual',
+ template: '<div class="popover hovercard" data-card-created="' + timeNow + '"><div class="arrow"></div><div class="popover-content hovercard-content"></div></div>',
+ content: data,
+ container: 'body',
+ sanitizeFn: function (content) {
+ return DOMPurify.sanitize(content)
+ },
+ }).popover('show');
+ }
+ });
+}
+
getHoverCardContent.cache = {};
function getHoverCardContent(contact_url, callback) {
}
$.ajax({
- url: baseurl + "/contact/hovercard",
+ url: baseurl + '/contact/hovercard',
data: postdata,
success: function (data, textStatus, request) {
getHoverCardContent.cache[nurl] = data;
{{* The avatar picture and the photo-menu *}}
<div class="dropdown pull-left"><!-- Dropdown -->
<div class="hidden-sm hidden-xs contact-photo-wrapper mframe{{if $item.owner_url}} wwfrom{{/if}}">
- <a href="{{$item.profile_url}}" class="userinfo u-url" id="wall-item-photo-menu-{{$item.id}}">
+ <a href="{{$item.profile_url}}" class="userinfo click-card u-url" id="wall-item-photo-menu-{{$item.id}}">
<div class="contact-photo-image-wrapper">
<img src="{{$item.thumb}}" class="contact-photo media-object {{$item.sparkle}}" id="wall-item-photo-{{$item.id}}" alt="{{$item.name}}" />
</div>
</a>
</div>
<div class="hidden-lg hidden-md contact-photo-wrapper mframe{{if $item.owner_url}} wwfrom{{/if}}">
- <a href="{{$item.profile_url}}" class="userinfo u-url" id="wall-item-photo-menu-xs-{{$item.id}}">
+ <a href="{{$item.profile_url}}" class="userinfo click-card u-url" id="wall-item-photo-menu-xs-{{$item.id}}">
<div class="contact-photo-image-wrapper">
<img src="{{$item.thumb}}" class="contact-photo-xs media-object {{$item.sparkle}}" id="wall-item-photo-xs-{{$item.id}}" alt="{{$item.name}}" />
</div>
{{* contact info header*}}
- <div role="heading " class="contact-info hidden-sm hidden-xs media-body"><!-- <= For computer -->
- <h4 class="media-heading"><a href="{{$item.profile_url}}" title="{{$item.linktitle}}" class="wall-item-name-link userinfo"><span class="wall-item-name {{$item.sparkle}}">{{$item.name}}</span></a>
- {{if $item.owner_url}}{{$item.via}} <a href="{{$item.owner_url}}" target="redir" title="{{$item.olinktitle}}" class="wall-item-name-link userinfo"><span class="wall-item-name {{$item.osparkle}}" id="wall-item-ownername-{{$item.id}}">{{$item.owner_name}}</span></a>{{/if}}
- {{if $item.lock}}<span class="navicon lock fakelink" onClick="lockview(event, {{$item.id}});" title="{{$item.lock}}"> <small><i class="fa fa-lock" aria-hidden="true"></i></small></span>{{/if}}
+ <div role="heading" class="contact-info hidden-sm hidden-xs media-body"><!-- <= For computer -->
+ <h4 class="media-heading">
+ <a href="{{$item.profile_url}}" title="{{$item.linktitle}}" class="wall-item-name-link userinfo hover-card">
+ <span class="wall-item-name {{$item.sparkle}}">{{$item.name}}</span>
+ </a>
+ {{if $item.owner_url}}
+ {{$item.via}}
+ <a href="{{$item.owner_url}}" target="redir" title="{{$item.olinktitle}}" class="wall-item-name-link userinfo hover-card">
+ <span class="wall-item-name {{$item.osparkle}}" id="wall-item-ownername-{{$item.id}}">{{$item.owner_name}}</span>
+ </a>
+ {{/if}}
+ {{if $item.lock}}
+ <span class="navicon lock fakelink" onClick="lockview(event, {{$item.id}});" title="{{$item.lock}}">
+ <small><i class="fa fa-lock" aria-hidden="true"></i></small>
+ </span>
+ {{/if}}
<div class="additional-info text-muted">
<div id="wall-item-ago-{{$item.id}}" class="wall-item-ago">
{{* contact info header for smartphones *}}
<div role="heading " class="contact-info-xs hidden-lg hidden-md">
<h5 class="media-heading">
- <a href="{{$item.profile_url}}" title="{{$item.linktitle}}" class="wall-item-name-link userinfo"><span>{{$item.name}}</span></a>
+ <a href="{{$item.profile_url}}" title="{{$item.linktitle}}" class="wall-item-name-link userinfo hover-card"><span>{{$item.name}}</span></a>
<p class="text-muted"><small>
<span class="wall-item-ago">{{$item.ago}}</span> {{if $item.location}} — ({{$item.location nofilter}}){{/if}}</small>
</p>
<div class="dropdown pull-left"><!-- Dropdown -->
{{if $item.thread_level==1}}
<div class="hidden-sm hidden-xs contact-photo-wrapper mframe{{if $item.owner_url}} wwfrom{{/if}} p-author h-card">
- <a class="userinfo u-url" id="wall-item-photo-menu-{{$item.id}}" href="{{$item.profile_url}}">
+ <a class="userinfo click-card u-url" id="wall-item-photo-menu-{{$item.id}}" href="{{$item.profile_url}}">
<div class="contact-photo-image-wrapper">
<img src="{{$item.thumb}}" class="contact-photo media-object {{$item.sparkle}} p-name u-photo" id="wall-item-photo-{{$item.id}}" alt="{{$item.name}}" />
</div>
</a>
</div>
<div class="hidden-lg hidden-md contact-photo-wrapper mframe{{if $item.owner_url}} wwfrom{{/if}}">
- <a class="userinfo u-url" id="wall-item-photo-menu-xs-{{$item.id}}" href="{{$item.profile_url}}">
+ <a class="userinfo click-card u-url" id="wall-item-photo-menu-xs-{{$item.id}}" href="{{$item.profile_url}}">
<div class="contact-photo-image-wrapper">
<img src="{{$item.thumb}}" class="contact-photo-xs media-object {{$item.sparkle}}" id="wall-item-photo-xs-{{$item.id}}" alt="{{$item.name}}" />
</div>
{{* The avatar picture for comments *}}
{{if $item.thread_level!=1}}
<div class="contact-photo-wrapper mframe{{if $item.owner_url}} wwfrom{{/if}} p-author h-card">
- <a class="userinfo u-url" id="wall-item-photo-menu-{{$item.id}}" href="{{$item.profile_url}}">
+ <a class="userinfo click-card u-url" id="wall-item-photo-menu-{{$item.id}}" href="{{$item.profile_url}}">
<div class="contact-photo-image-wrapper">
<img src="{{$item.thumb}}" class="contact-photo-xs media-object {{$item.sparkle}} p-name u-photo" id="wall-item-photo-comment-{{$item.id}}" alt="{{$item.name}}" />
</div>
{{* contact info header*}}
{{if $item.thread_level==1}}
<div role="heading " aria-level="{{$item.thread_level}}" class="contact-info hidden-sm hidden-xs media-body"><!-- <= For computer -->
- <h4 class="media-heading"><a href="{{$item.profile_url}}" title="{{$item.linktitle}}" class="wall-item-name-link userinfo"><span class="wall-item-name {{$item.sparkle}}">{{$item.name}}</span></a>
- {{if $item.owner_url}}{{$item.via}} <a href="{{$item.owner_url}}" target="redir" title="{{$item.olinktitle}}" class="wall-item-name-link userinfo"><span class="wall-item-name {{$item.osparkle}}" id="wall-item-ownername-{{$item.id}}">{{$item.owner_name}}</span></a>{{/if}}
- {{if $item.lock}}<span class="navicon lock fakelink" onClick="lockview(event,{{$item.id}});" title="{{$item.lock}}" data-toggle="tooltip"> <small><i class="fa fa-lock" aria-hidden="true"></i></small></span>{{/if}}
+ <h4 class="media-heading">
+ <a href="{{$item.profile_url}}" title="{{$item.linktitle}}" class="wall-item-name-link userinfo hover-card">
+ <span class="wall-item-name {{$item.sparkle}}">{{$item.name}}</span>
+ </a>
+ {{if $item.owner_url}}
+ {{$item.via}}
+ <a href="{{$item.owner_url}}" target="redir" title="{{$item.olinktitle}}" class="wall-item-name-link userinfo hover-card">
+ <span class="wall-item-name {{$item.osparkle}}" id="wall-item-ownername-{{$item.id}}">{{$item.owner_name}}</span>
+ </a>
+ {{/if}}
+ {{if $item.lock}}
+ <span class="navicon lock fakelink" onClick="lockview(event,{{$item.id}});" title="{{$item.lock}}" data-toggle="tooltip">
+ <small><i class="fa fa-lock" aria-hidden="true"></i></small>
+ </span>
+ {{/if}}
</h4>
<div class="additional-info text-muted">
{{* contact info header for smartphones *}}
<div role="heading " aria-level="{{$item.thread_level}}" class="contact-info-xs hidden-lg hidden-md"><!-- <= For smartphone (responsive) -->
<h5 class="media-heading">
- <a href="{{$item.profile_url}}" title="{{$item.linktitle}}" class="wall-item-name-link userinfo"><span>{{$item.name}}</span></a>
+ <a href="{{$item.profile_url}}" title="{{$item.linktitle}}" class="wall-item-name-link userinfo hover-card"><span>{{$item.name}}</span></a>
<p class="text-muted">
<small>
<a class="time" href="{{$item.plink.orig}}"><span class="wall-item-ago">{{$item.ago}}</span></a>
<div class="media-body">{{*this is the media body for comments - this div must be closed at the end of the file *}}
<div role="heading " aria-level="{{$item.thread_level}}" class="contact-info-comment">
<h5 class="media-heading">
- <a href="{{$item.profile_url}}" title="{{$item.linktitle}}" class="wall-item-name-link userinfo"><span class="fakelink">{{$item.name}}</span></a>
+ <a href="{{$item.profile_url}}" title="{{$item.linktitle}}" class="wall-item-name-link userinfo hover-card"><span class="fakelink">{{$item.name}}</span></a>
<span class="text-muted">
<small>
<a class="time" href="{{$item.plink.orig}}" title="{{$item.localtime}}" data-toggle="tooltip">{{$item.ago}}</a>