]> git.mxchange.org Git - friendica.git/blob - view/theme/frio/js/hovercard.js
Merge remote-tracking branch 'refs/remotes/friendica/develop' into develop
[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                         // Timeoute 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                                                                 }).popover('show');
85                                                         }
86                                                 });
87                                         }
88                                 }
89                         }, 500);
90         }).on("mouseleave", ".userinfo, .wall-item-responses a, .wall-item-bottom .mention a", function(e) { // action when mouse leaves the hover-card
91                 var timeNow = new Date().getTime();
92                 // copy the original title to the title atribute
93                 var title = $(this).attr("data-orig-title");
94                 $(this).attr({"data-orig-title": "", title: title});
95                 removeAllhoverCards(e,timeNow);
96         });
97
98
99
100         // hover cards should be removed very easily, e.g. when any of these events happen
101         $('body').on("mouseleave touchstart scroll click dblclick mousedown mouseup submit keydown keypress keyup", function(e){
102                 // remove hover card only for desktiop user, since on mobile we openen the hovercards
103                 // by click event insteadof hover
104                 if(typeof is_mobile == "undefined") {
105                         var timeNow = new Date().getTime();
106                         removeAllhoverCards(e,timeNow);
107                 };
108         });
109
110         // if we're hovering a hover card, give it a class, so we don't remove it
111         $('body').on('mouseover','.hovercard', function(e) {
112                 $(this).addClass('dont-remove-card');
113         });
114         $('body').on('mouseleave','.hovercard', function(e) {
115                 $(this).removeClass('dont-remove-card');
116                 $(this).popover("hide");
117         });
118
119 }); // End of $(document).ready
120
121 // removes all hover cards
122 function removeAllhoverCards(event,priorTo) {
123         // 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)
124         setTimeout(function(){
125                 $.each($('.hovercard'),function(){
126                         var title = $(this).attr("data-orig-title");
127                         // don't remove card if it was created after removeAllhoverCards() was called
128                         if($(this).data('card-created') < priorTo) {
129                                 // don't remove it if we're hovering it right now!
130                                 if(!$(this).hasClass('dont-remove-card')) {
131                                         $('[data-hover-card-active="' + $(this).data('card-created') + '"]').removeAttr('data-hover-card-active');
132                                         $(this).popover("hide");
133                                 }
134                         }
135                 });
136         },100);
137 }
138
139 // Ajax request to get json contact data
140 function getContactData(purl, url, actionOnSuccess) {
141         var postdata = {
142                 mode            : 'none',
143                 profileurl      : purl,
144                 datatype        : 'json',
145         };
146
147         // Normalize and clean the profile so we can use a standardized url
148         // as key for the cache
149         var nurl = cleanContactUrl(purl).normalizeLink();
150
151         // If the contact is allready in the cache use the cached result instead
152         // of doing a new ajax request
153         if(nurl in getContactData.cache) {
154                 setTimeout(function() { actionOnSuccess(getContactData.cache[nurl]); } , 1);
155                 return;
156         }
157
158         $.ajax({
159                 url: url,
160                 data: postdata,
161                 dataType: "json",
162                 success: function(data, textStatus, request){
163                         // Check if the nurl (normalized profile url) is present and store it to the cache
164                         // The nurl will be the identifier in the object
165                         if(data.nurl.length > 0) {
166                                 // Test if the contact is allready connected with the user (if url containing
167                                 // the expression ("redir/") We will store different cache keys
168                                 if((data.url.search("redir/")) >= 0 ) {
169                                         var key = data.url;
170                                 } else {
171                                         var key = data.nurl;
172                                 }
173                                 getContactData.cache[key] = data;
174                         }
175                         actionOnSuccess(data, url, request);
176                 },
177                 error: function(data) {
178                         actionOnSuccess(false, data, url);
179                 }
180         });
181 }
182 getContactData.cache = {};
183
184 // Get hover-card template data and the contact-data and transform it with
185 // the help of jSmart. At the end we have full html content of the hovercard
186 function getHoverCardContent(purl, url, callback) {
187         // fetch the raw content of the template
188         getHoverCardTemplate(url, function(stpl) {
189                 var template = unescape(stpl);
190
191                 // get the contact data
192                 getContactData (purl, url, function(data) {
193                         if(typeof template != 'undefined') {
194                                 // get the hover-card variables
195                                 var variables = getHoverCardVariables(data);
196                                 var tpl;
197
198                                 // use friendicas template delimiters instead of
199                                 // the original one
200                                 jSmart.prototype.left_delimiter = '{{';
201                                 jSmart.prototype.right_delimiter = '}}';
202
203                                 // create a new jSmart instant with the raw content
204                                 // of the template
205                                 var tpl = new jSmart (template);
206                                 // insert the variables content into the template content
207                                 var HoverCardContent = tpl.fetch(variables);
208
209                                 callback(HoverCardContent);
210                         }
211                 });
212         });
213
214 // This is interisting. this pice of code ajax request are done asynchron.
215 // To make it work getHOverCardTemplate() and getHOverCardData have to return it's
216 // data (no succes handler for each of this). I leave it here, because it could be useful.
217 // https://lostechies.com/joshuaflanagan/2011/10/20/coordinating-multiple-ajax-requests-with-jquery-when/
218 //      $.when(
219 //              getHoverCardTemplate(url),
220 //              getContactData (term, url )
221 //
222 //      ).done(function(template, profile){
223 //              if(typeof template != 'undefined') {
224 //                      var variables = getHoverCardVariables(profile);
225 //
226 //                      jSmart.prototype.left_delimiter = '{{';
227 //                      jSmart.prototype.right_delimiter = '}}';
228 //                      var tpl = new jSmart (template);
229 //                      var html = tpl.fetch(variables);
230 //
231 //                      return html;
232 //              }
233 //      });
234 }
235
236
237 // Ajax request to get the raw template content
238 function getHoverCardTemplate (url, callback) {
239         var postdata = {
240                 mode: 'none',
241                 datatype: 'tpl'
242         };
243
244         // Look if we have the template already in the cace, so we don't have
245         // request it again
246         if('hovercard' in getHoverCardTemplate.cache) {
247                 setTimeout(function() { callback(getHoverCardTemplate.cache['hovercard']); } , 1);
248                 return;
249         }
250
251         $.ajax({
252                 url: url,
253                 data: postdata,
254                 success: function(data, textStatus) {
255                         // write the data in the cache
256                         getHoverCardTemplate.cache['hovercard'] = data;
257                         callback(data);
258                 }
259         }).fail(function () {callback([]); });
260 }
261 getHoverCardTemplate.cache = {};
262
263 // The Variables used for the template
264 function getHoverCardVariables(object) {
265         var profile = {
266                         name:           object.name,
267                         nick:           object.nick,
268                         addr:           object.addr,
269                         thumb:          object.thumb,
270                         url:            object.url,
271                         nurl:           object.nurl,
272                         location:       object.location,
273                         gender:         object.gender,
274                         about:          object.about,
275                         network:        object.network,
276                         tags:           object.tags,
277                         bd:             object.bd,
278                         account_type:   object.account_type,
279                         actions:        object.actions
280         };
281
282         var variables = { profile:  profile};
283
284         return variables;
285 }