]> git.mxchange.org Git - friendica.git/blob - view/theme/frio/js/hovercard.js
Merge branch 'develop' of https://github.com/friendica/friendica into develop
[friendica.git] / view / theme / frio / js / hovercard.js
1 // @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPLv3-or-later
2 /*
3  * The javascript for friendicas hovercard. Bootstraps popover is needed.
4  *
5  * Much parts of the code are from Hannes Mannerheims <h@nnesmannerhe.im>
6  * qvitter code (https://github.com/hannesmannerheim/qvitter)
7  *
8  * It is licensed under the GNU Affero General Public License <http://www.gnu.org/licenses/>
9  *
10  */
11 $(document).ready(function () {
12         let $body = $("body");
13         // Prevents normal click action on click hovercard elements
14         $body.on("click", ".userinfo.click-card", function (e) {
15                 e.preventDefault();
16         });
17         // This event listener needs to be declared before the one that removes
18         // all cards so that we can stop the immediate propagation of the event
19         // Since the manual popover appears instantly and the hovercard removal is
20         // on a 100ms delay, leaving event propagation immediately hides any click hovercard
21         $body.on("mousedown", ".userinfo.click-card", function (e) {
22                 e.stopImmediatePropagation();
23                 let timeNow = new Date().getTime();
24
25                 let contactUrl = false;
26                 let targetElement = $(this);
27
28                 // get href-attribute
29                 if (targetElement.is("[href]")) {
30                         contactUrl = targetElement.attr("href");
31                 } else {
32                         return true;
33                 }
34
35                 // no hovercard for anchor links
36                 if (contactUrl.substring(0, 1) === "#") {
37                         return true;
38                 }
39
40                 openHovercard(targetElement, contactUrl, timeNow);
41         });
42
43         // hover cards should be removed very easily, e.g. when any of these events happens
44         $body.on("mouseleave touchstart scroll mousedown submit keydown", function (e) {
45                 // remove hover card only for desktiop user, since on mobile we open the hovercards
46                 // by click event insteadof hover
47                 removeAllHovercards(e, new Date().getTime());
48         });
49
50         $body
51                 .on("mouseover", ".userinfo.hover-card, .wall-item-responses a, .wall-item-bottom .mention a", function (e) {
52                         let timeNow = new Date().getTime();
53                         removeAllHovercards(e, timeNow);
54                         let contactUrl = false;
55                         let targetElement = $(this);
56
57                         // get href-attribute
58                         if (targetElement.is("[href]")) {
59                                 contactUrl = targetElement.attr("href");
60                         } else {
61                                 return true;
62                         }
63
64                         // no hover card if the element has the no-hover-card class
65                         if (targetElement.hasClass("no-hover-card")) {
66                                 return true;
67                         }
68
69                         // no hovercard for anchor links
70                         if (contactUrl.substring(0, 1) === "#") {
71                                 return true;
72                         }
73
74                         targetElement.attr("data-awaiting-hover-card", timeNow);
75
76                         // Delay until the hover-card does appear
77                         setTimeout(function () {
78                                 if (
79                                         targetElement.is(":hover") &&
80                                         parseInt(targetElement.attr("data-awaiting-hover-card"), 10) === timeNow &&
81                                         $(".hovercard").length === 0
82                                 ) {
83                                         openHovercard(targetElement, contactUrl, timeNow);
84                                 }
85                         }, 500);
86                 })
87                 .on("mouseleave", ".userinfo.hover-card, .wall-item-responses a, .wall-item-bottom .mention a", function (e) {
88                         // action when mouse leaves the hover-card
89                         removeAllHovercards(e, new Date().getTime());
90                 });
91
92         // if we're hovering a hover card, give it a class, so we don't remove it
93         $body.on("mouseover", ".hovercard", function (e) {
94                 $(this).addClass("dont-remove-card");
95         });
96
97         $body.on("mouseleave", ".hovercard", function (e) {
98                 $(this).removeClass("dont-remove-card");
99                 $(this).popover("hide");
100         });
101 }); // End of $(document).ready
102
103 // removes all hover cards
104 function removeAllHovercards(event, priorTo) {
105         // 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)
106         setTimeout(function () {
107                 $.each($(".hovercard"), function () {
108                         let title = $(this).attr("data-orig-title");
109                         // don't remove card if it was created after removeAllhoverCards() was called
110                         if ($(this).data("card-created") < priorTo) {
111                                 // don't remove it if we're hovering it right now!
112                                 if (!$(this).hasClass("dont-remove-card")) {
113                                         let $handle = $('[data-hover-card-active="' + $(this).data("card-created") + '"]');
114                                         $handle.removeAttr("data-hover-card-active");
115
116                                         // Restoring the popover handle title
117                                         let title = $handle.attr("data-orig-title");
118                                         $handle.attr({ "data-orig-title": "", title: title });
119
120                                         $(this).popover("hide");
121                                 }
122                         }
123                 });
124         }, 100);
125 }
126
127 function openHovercard(targetElement, contactUrl, timeNow) {
128         // store the title in a data attribute because Bootstrap
129         // popover destroys the title attribute.
130         let title = targetElement.attr("title");
131         targetElement.attr({ "data-orig-title": title, title: "" });
132
133         // get an additional data atribute if the card is active
134         targetElement.attr("data-hover-card-active", timeNow);
135         // get the whole html content of the hover card and
136         // push it to the bootstrap popover
137         getHoverCardContent(contactUrl, function (data) {
138                 if (data) {
139                         targetElement
140                                 .popover({
141                                         html: true,
142                                         placement: function () {
143                                                 // Calculate the placement of the the hovercard (if top or bottom)
144                                                 // The placement depence on the distance between window top and the element
145                                                 // which triggers the hover-card
146                                                 let get_position = $(targetElement).offset().top - $(window).scrollTop();
147                                                 if (get_position < 270) {
148                                                         return "bottom";
149                                                 }
150                                                 return "top";
151                                         },
152                                         trigger: "manual",
153                                         template:
154                                                 '<div class="popover hovercard" data-card-created="' +
155                                                 timeNow +
156                                                 '"><div class="arrow"></div><div class="popover-content hovercard-content"></div></div>',
157                                         content: data,
158                                         container: "body",
159                                         sanitizeFn: function (content) {
160                                                 return DOMPurify.sanitize(content);
161                                         },
162                                 })
163                                 .popover("show");
164                 }
165         });
166 }
167
168 getHoverCardContent.cache = {};
169
170 function getHoverCardContent(contact_url, callback) {
171         let postdata = {
172                 url: contact_url,
173         };
174
175         // Normalize and clean the profile so we can use a standardized url
176         // as key for the cache
177         let nurl = cleanContactUrl(contact_url).normalizeLink();
178
179         // If the contact is already in the cache use the cached result instead
180         // of doing a new ajax request
181         if (nurl in getHoverCardContent.cache) {
182                 callback(getHoverCardContent.cache[nurl]);
183                 return;
184         }
185
186         $.ajax({
187                 url: baseurl + "/contact/hovercard",
188                 data: postdata,
189                 success: function (data, textStatus, request) {
190                         getHoverCardContent.cache[nurl] = data;
191                         callback(data);
192                 },
193         });
194 }
195 // @license-end