]> git.mxchange.org Git - friendica.git/blob - view/theme/frio/js/hovercard.js
Merge remote-tracking branch 'upstream/develop' into aria
[friendica.git] / view / theme / frio / js / hovercard.js
1 /*
2  * The javascript for friendicas hovercard. Bootstraps popover is needed.
3  *
4  * Much parts of the code are from Hannes Mannerheims <h@nnesmannerhe.im>
5  * qvitter code (https://github.com/hannesmannerheim/qvitter)
6  *
7  * It is licensed under the GNU Affero General Public License <http://www.gnu.org/licenses/>
8  *
9  */
10 $(document).ready(function(){
11         // Elements with the class "userinfo" will get a hover-card.
12         // Note that this elements does need a href attribute which links to
13         // a valid profile url
14         $("body").on("mouseover", ".userinfo, .wall-item-responses a, .wall-item-bottom .mention a", function(e) {
15                         var timeNow = new Date().getTime();
16                         removeAllhoverCards(e,timeNow);
17                         var hoverCardData = false;
18                         var hrefAttr = false;
19                         var targetElement = $(this);
20
21                         // get href-attribute
22                         if(targetElement.is('[href]')) {
23                                 hrefAttr = targetElement.attr('href');
24                         } else {
25                                 return true;
26                         }
27
28                         // no hover card if the element has the no-hover-card class
29                         if(targetElement.hasClass('no-hover-card')) {
30                                 return true;
31                         }
32
33                         // no hovercard for anchor links
34                         if(hrefAttr.substring(0,1) == '#') {
35                                 return true;
36                         }
37
38                         targetElement.attr('data-awaiting-hover-card',timeNow);
39
40                         // Take link href attribute as link to the profile
41                         var profileurl = hrefAttr;
42                         // the url to get the contact and template data
43                         var url = baseurl + "/hovercard";
44
45                         // store the title in an other data attribute beause bootstrap
46                         // popover destroys the title.attribute. We can restore it later
47                         var title = targetElement.attr("title");
48                         targetElement.attr({"data-orig-title": title, title: ""});
49
50                         // if the device is a mobile open the hover card by click and not by hover
51                         if(typeof is_mobile != "undefined") {
52                                         targetElement[0].removeAttribute("href");
53                                         var hctrigger = 'click';
54                                 } else {
55                                         var hctrigger = 'manual';
56                         };
57
58                         // Timeout until the hover-card does appear
59                         setTimeout(function(){
60                                 if(targetElement.is(":hover") && parseInt(targetElement.attr('data-awaiting-hover-card'),10) == timeNow) {
61                                         if($('.hovercard').length == 0) {       // no card if there already is one open
62                                                 // get an additional data atribute if the card is active
63                                                 targetElement.attr('data-hover-card-active',timeNow);
64                                                 // get the whole html content of the hover card and
65                                                 // push it to the bootstrap popover
66                                                 getHoverCardContent(profileurl, url, function(data){
67                                                         if(data) {
68                                                                 targetElement.popover({
69                                                                         html: true,
70                                                                         placement: function () {
71                                                                                 // Calculate the placement of the the hovercard (if top or bottom)
72                                                                                 // The placement depence on the distance between window top and the element
73                                                                                 // which triggers the hover-card
74                                                                                 var get_position = $(targetElement).offset().top - $(window).scrollTop();
75                                                                                 if (get_position < 270 ){
76                                                                                         return "bottom";
77                                                                                 }
78                                                                                 return "top";
79                                                                         },
80                                                                         trigger: hctrigger,
81                                                                         template: '<div class="popover hovercard" data-card-created="' + timeNow + '"><div class="arrow"></div><div class="popover-content hovercard-content"></div></div>',
82                                                                         content: data,
83                                                                         container: "body",
84                                                                         sanitizeFn: function (content) {
85                                                                                 return DOMPurify.sanitize(content)
86                                                                         },
87                                                                 }).popover('show');
88                                                         }
89                                                 });
90                                         }
91                                 }
92                         }, 500);
93         }).on("mouseleave", ".userinfo, .wall-item-responses a, .wall-item-bottom .mention a", function(e) { // action when mouse leaves the hover-card
94                 var timeNow = new Date().getTime();
95                 // copy the original title to the title atribute
96                 var title = $(this).attr("data-orig-title");
97                 $(this).attr({"data-orig-title": "", title: title});
98                 removeAllhoverCards(e,timeNow);
99         });
100
101
102
103         // hover cards should be removed very easily, e.g. when any of these events happen
104         $('body').on("mouseleave touchstart scroll click dblclick mousedown mouseup submit keydown keypress keyup", function(e){
105                 // remove hover card only for desktiop user, since on mobile we openen the hovercards
106                 // by click event insteadof hover
107                 if(typeof is_mobile == "undefined") {
108                         var timeNow = new Date().getTime();
109                         removeAllhoverCards(e,timeNow);
110                 };
111         });
112
113         // if we're hovering a hover card, give it a class, so we don't remove it
114         $('body').on('mouseover','.hovercard', function(e) {
115                 $(this).addClass('dont-remove-card');
116         });
117         $('body').on('mouseleave','.hovercard', function(e) {
118                 $(this).removeClass('dont-remove-card');
119                 $(this).popover("hide");
120         });
121
122 }); // End of $(document).ready
123
124 // removes all hover cards
125 function removeAllhoverCards(event,priorTo) {
126         // 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)
127         setTimeout(function(){
128                 $.each($('.hovercard'),function(){
129                         var title = $(this).attr("data-orig-title");
130                         // don't remove card if it was created after removeAllhoverCards() was called
131                         if($(this).data('card-created') < priorTo) {
132                                 // don't remove it if we're hovering it right now!
133                                 if(!$(this).hasClass('dont-remove-card')) {
134                                         $('[data-hover-card-active="' + $(this).data('card-created') + '"]').removeAttr('data-hover-card-active');
135                                         $(this).popover("hide");
136                                 }
137                         }
138                 });
139         },100);
140 }
141
142 // Ajax request to get json contact data
143 function getContactData(purl, url, actionOnSuccess) {
144         var postdata = {
145                 mode            : 'none',
146                 profileurl      : purl,
147                 datatype        : 'json',
148         };
149
150         // Normalize and clean the profile so we can use a standardized url
151         // as key for the cache
152         var nurl = cleanContactUrl(purl).normalizeLink();
153
154         // If the contact is allready in the cache use the cached result instead
155         // of doing a new ajax request
156         if(nurl in getContactData.cache) {
157                 setTimeout(function() { actionOnSuccess(getContactData.cache[nurl]); } , 1);
158                 return;
159         }
160
161         $.ajax({
162                 url: url,
163                 data: postdata,
164                 dataType: "json",
165                 success: function(data, textStatus, request){
166                         // Check if the nurl (normalized profile url) is present and store it to the cache
167                         // The nurl will be the identifier in the object
168                         if(data.nurl.length > 0) {
169                                 // Test if the contact is allready connected with the user (if url containing
170                                 // the expression ("redir/") We will store different cache keys
171                                 if((data.url.search("redir/")) >= 0 ) {
172                                         var key = data.url;
173                                 } else {
174                                         var key = data.nurl;
175                                 }
176                                 getContactData.cache[key] = data;
177                         }
178                         actionOnSuccess(data, url, request);
179                 },
180                 error: function(data) {
181                         actionOnSuccess(false, data, url);
182                 }
183         });
184 }
185 getContactData.cache = {};
186
187 // Get hover-card template data and the contact-data and transform it with
188 // the help of jSmart. At the end we have full html content of the hovercard
189 function getHoverCardContent(purl, url, callback) {
190         // fetch the raw content of the template
191         getHoverCardTemplate(url, function(stpl) {
192                 var template = unescape(stpl);
193
194                 // get the contact data
195                 getContactData (purl, url, function(data) {
196                         if(typeof template != 'undefined') {
197                                 // get the hover-card variables
198                                 var variables = getHoverCardVariables(data);
199                                 var tpl;
200
201                                 // use friendicas template delimiters instead of
202                                 // the original one
203                                 jSmart.prototype.left_delimiter = '{{';
204                                 jSmart.prototype.right_delimiter = '}}';
205
206                                 // create a new jSmart instant with the raw content
207                                 // of the template
208                                 var tpl = new jSmart (template);
209                                 // insert the variables content into the template content
210                                 var HoverCardContent = tpl.fetch(variables);
211
212                                 callback(HoverCardContent);
213                         }
214                 });
215         });
216
217 // This is interisting. this pice of code ajax request are done asynchron.
218 // To make it work getHOverCardTemplate() and getHOverCardData have to return it's
219 // data (no succes handler for each of this). I leave it here, because it could be useful.
220 // https://lostechies.com/joshuaflanagan/2011/10/20/coordinating-multiple-ajax-requests-with-jquery-when/
221 //      $.when(
222 //              getHoverCardTemplate(url),
223 //              getContactData (term, url )
224 //
225 //      ).done(function(template, profile){
226 //              if(typeof template != 'undefined') {
227 //                      var variables = getHoverCardVariables(profile);
228 //
229 //                      jSmart.prototype.left_delimiter = '{{';
230 //                      jSmart.prototype.right_delimiter = '}}';
231 //                      var tpl = new jSmart (template);
232 //                      var html = tpl.fetch(variables);
233 //
234 //                      return html;
235 //              }
236 //      });
237 }
238
239
240 // Ajax request to get the raw template content
241 function getHoverCardTemplate (url, callback) {
242         var postdata = {
243                 mode: 'none',
244                 datatype: 'tpl'
245         };
246
247         // Look if we have the template already in the cace, so we don't have
248         // request it again
249         if('hovercard' in getHoverCardTemplate.cache) {
250                 setTimeout(function() { callback(getHoverCardTemplate.cache['hovercard']); } , 1);
251                 return;
252         }
253
254         $.ajax({
255                 url: url,
256                 data: postdata,
257                 success: function(data, textStatus) {
258                         // write the data in the cache
259                         getHoverCardTemplate.cache['hovercard'] = data;
260                         callback(data);
261                 }
262         }).fail(function () {callback([]); });
263 }
264 getHoverCardTemplate.cache = {};
265
266 // The Variables used for the template
267 function getHoverCardVariables(object) {
268         var profile = {
269                         name:           object.name,
270                         nick:           object.nick,
271                         addr:           object.addr,
272                         thumb:          object.thumb,
273                         url:            object.url,
274                         nurl:           object.nurl,
275                         location:       object.location,
276                         gender:         object.gender,
277                         about:          object.about,
278                         network:        object.network,
279                         tags:           object.tags,
280                         bd:             object.bd,
281                         account_type:   object.account_type,
282                         actions:        object.actions
283         };
284
285         var variables = { profile:  profile};
286
287         return variables;
288 }