From 485b1e55213263bb01a73b79add1a3956b125c72 Mon Sep 17 00:00:00 2001
From: rabuzarus <>
Date: Fri, 6 May 2016 16:37:04 +0200
Subject: [PATCH] hovercard: add cache for contact data results

---
 js/hovercard.js | 179 +++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 171 insertions(+), 8 deletions(-)

diff --git a/js/hovercard.js b/js/hovercard.js
index 0fccebc34b..72273ba19b 100644
--- a/js/hovercard.js
+++ b/js/hovercard.js
@@ -40,8 +40,8 @@ $(document).ready(function(){
 
 			targetElement.attr('data-awaiting-hover-card',timeNow);
 
-			// The serach term is the url 
-			var term = hrefAttr;
+			// Take link href attribute as link to the profile
+			var profileurl = hrefAttr;
 			// the url to get the contact and template data
 			var url = baseurl + "/frio_hovercard";
 
@@ -58,7 +58,7 @@ $(document).ready(function(){
 						targetElement.attr('data-hover-card-active',timeNow);
 						// get the whole html content of the hover card and
 						// push it to the bootstrap popover
-						getHoverCardContent(term, url, function(data){
+						getHoverCardContent(profileurl, url, function(data){
 							if(data) {
 								targetElement.popover({
 									html: true,
@@ -119,18 +119,41 @@ $('body').on('mouseleave','.hovercard', function(e) {
 });
 
 // Ajax request to get json contact data
-function getHoverCardData(term, url, actionOnSuccess) {
+function getContactData(purl, url, actionOnSuccess) {
 	var postdata = {
 		mode		: 'modal',
-		profileurl	: term,
+		profileurl	: purl,
 		datatype	: 'json',
 	};
 
+	// Normalize and clean the profile so we can use a standardized url
+	// as key for the cache
+	var nurl = cleanContactUrl(purl).normalizeLink();
+
+	// If the contact is allready in the cache use the cached result instead
+	// of doing a new ajax request
+	if(nurl in getContactData.cache) {
+		setTimeout(function() { actionOnSuccess(getContactData.cache[nurl]); } , 1);
+		return;
+	}
+
 	$.ajax({
 		url: url,
 		data: postdata,
 		dataType: "json",
 		success: function(data, textStatus, request){
+			// Check if the nurl (normalized profile url) is present and store it to the cache
+			// The nurl will be the identifier in the object
+			if(data.nurl.length > 0) {
+				// Test if the contact is allready connected with the user (if url containing
+				// the expression ("redir/") We will store different cache keys 
+				if((data.url.search("redir/")) >= 0 ) {
+					var key = data.url;
+				} else {
+					var key = data.nurl;
+				}
+				getContactData.cache[key] = data;
+			}
 			actionOnSuccess(data, url, request);
 		},
 		error: function(data) {
@@ -138,20 +161,159 @@ function getHoverCardData(term, url, actionOnSuccess) {
 		}
 	});
 }
+getContactData.cache = {};
+
 // current time in milliseconds, to send each request to make sure
 // we 're not getting 304 response
 function timeNow() {
 	return new Date().getTime();
 }
+
+String.prototype.normalizeLink = function () {
+	var ret = this.replace('https:', 'http:');
+	var ret = ret.replace('//www', '//');
+	return ret.rtrim();
+};
+
+
+
+function cleanContactUrl(url) {
+	var parts = parseUrl(url);
+
+	if(! ("scheme" in parts) || ! ("host" in parts)) {
+		return url;
+	}
+
+	var newUrl =parts["scheme"] + "://" + parts["host"];
+
+	if("port" in parts) {
+		newUrl += ":" + parts["port"];
+	}
+
+	if("path" in parts) {
+		newUrl += parts["path"];
+	}
+
+//	if(url != newUrl) {
+//		console.log("Cleaned contact url " + url + " to " + newUrl);
+//	}
+
+	return newUrl;
+}
+
+function parseUrl (str, component) { // eslint-disable-line camelcase
+	//       discuss at: http://locutusjs.io/php/parse_url/
+	//      original by: Steven Levithan (http://blog.stevenlevithan.com)
+	// reimplemented by: Brett Zamir (http://brett-zamir.me)
+	//         input by: Lorenzo Pisani
+	//         input by: Tony
+	//      improved by: Brett Zamir (http://brett-zamir.me)
+	//           note 1: original by http://stevenlevithan.com/demo/parseuri/js/assets/parseuri.js
+	//           note 1: blog post at http://blog.stevenlevithan.com/archives/parseuri
+	//           note 1: demo at http://stevenlevithan.com/demo/parseuri/js/assets/parseuri.js
+	//           note 1: Does not replace invalid characters with '_' as in PHP,
+	//           note 1: nor does it return false with
+	//           note 1: a seriously malformed URL.
+	//           note 1: Besides function name, is essentially the same as parseUri as
+	//           note 1: well as our allowing
+	//           note 1: an extra slash after the scheme/protocol (to allow file:/// as in PHP)
+	//        example 1: parse_url('http://user:pass@host/path?a=v#a')
+	//        returns 1: {scheme: 'http', host: 'host', user: 'user', pass: 'pass', path: '/path', query: 'a=v', fragment: 'a'}
+	//        example 2: parse_url('http://en.wikipedia.org/wiki/%22@%22_%28album%29')
+	//        returns 2: {scheme: 'http', host: 'en.wikipedia.org', path: '/wiki/%22@%22_%28album%29'}
+	//        example 3: parse_url('https://host.domain.tld/a@b.c/folder')
+	//        returns 3: {scheme: 'https', host: 'host.domain.tld', path: '/a@b.c/folder'}
+	//        example 4: parse_url('https://gooduser:secretpassword@www.example.com/a@b.c/folder?foo=bar')
+	//        returns 4: { scheme: 'https', host: 'www.example.com', path: '/a@b.c/folder', query: 'foo=bar', user: 'gooduser', pass: 'secretpassword' }
+
+	var query
+
+	var mode = (typeof require !== 'undefined' ? require('../info/ini_get')('locutus.parse_url.mode') : undefined) || 'php'
+
+	var key = [
+		'source',
+		'scheme',
+		'authority',
+		'userInfo',
+		'user',
+		'pass',
+		'host',
+		'port',
+		'relative',
+		'path',
+		'directory',
+		'file',
+		'query',
+		'fragment'
+	]
+
+	// For loose we added one optional slash to post-scheme to catch file:/// (should restrict this)
+	var parser = {
+		php: new RegExp([
+			'(?:([^:\\/?#]+):)?',
+			'(?:\\/\\/()(?:(?:()(?:([^:@\\/]*):?([^:@\\/]*))?@)?([^:\\/?#]*)(?::(\\d*))?))?',
+			'()',
+			'(?:(()(?:(?:[^?#\\/]*\\/)*)()(?:[^?#]*))(?:\\?([^#]*))?(?:#(.*))?)'
+		].join('')),
+		strict: new RegExp([
+			'(?:([^:\\/?#]+):)?',
+			'(?:\\/\\/((?:(([^:@\\/]*):?([^:@\\/]*))?@)?([^:\\/?#]*)(?::(\\d*))?))?',
+			'((((?:[^?#\\/]*\\/)*)([^?#]*))(?:\\?([^#]*))?(?:#(.*))?)'
+		].join('')),
+		loose: new RegExp([
+			'(?:(?![^:@]+:[^:@\\/]*@)([^:\\/?#.]+):)?',
+			'(?:\\/\\/\\/?)?',
+			'((?:(([^:@\\/]*):?([^:@\\/]*))?@)?([^:\\/?#]*)(?::(\\d*))?)',
+			'(((\\/(?:[^?#](?![^?#\\/]*\\.[^?#\\/.]+(?:[?#]|$)))*\\/?)?([^?#\\/]*))',
+			'(?:\\?([^#]*))?(?:#(.*))?)'
+		].join(''))
+	}
+
+	var m = parser[mode].exec(str)
+	var uri = {}
+	var i = 14
+
+	while (i--) {
+		if (m[i]) {
+			uri[key[i]] = m[i]
+		}
+	}
+
+	if (component) {
+		return uri[component.replace('PHP_URL_', '').toLowerCase()]
+	}
+
+	if (mode !== 'php') {
+		var name = (typeof require !== 'undefined' ? require('../info/ini_get')('locutus.parse_url.queryKey') : undefined) || 'queryKey'
+		parser = /(?:^|&)([^&=]*)=?([^&]*)/g
+		uri[name] = {}
+		query = uri[key[12]] || ''
+		query.replace(parser, function ($0, $1, $2) {
+			if ($1) {
+				uri[name][$1] = $2
+			}
+		})
+	}
+
+	delete uri.source
+	return uri
+}
+
+// trim function to replace whithespace after the string
+String.prototype.rtrim = function() {
+	var trimmed = this.replace(/\s+$/g, '');
+	return trimmed;
+};
+
 // Get hover-card template data and the contact-data and transform it with
 // the help of jSmart. At the end we have full html content of the hovercard
-function getHoverCardContent(term, url, callback) {
+function getHoverCardContent(purl, url, callback) {
 	// fetch the raw content of the template
 	getHoverCardTemplate(url, function(stpl) {
 		var template = unescape(stpl);
 
 		// get the contact data
-		getHoverCardData (term, url, function(data) {
+		getContactData (purl, url, function(data) {
 			if(typeof template != 'undefined') {
 				// get the hover-card variables
 				var variables = getHoverCardVariables(data);
@@ -179,7 +341,7 @@ function getHoverCardContent(term, url, callback) {
 // https://lostechies.com/joshuaflanagan/2011/10/20/coordinating-multiple-ajax-requests-with-jquery-when/
 //	$.when(
 //		getHoverCardTemplate(url),
-//		getHoverCardData (term, url )
+//		getContactData (term, url )
 //
 //	).done(function(template, profile){
 //		if(typeof template != 'undefined') {
@@ -230,6 +392,7 @@ function getHoverCardVariables(object) {
 			addr:		object.addr,
 			thumb:		object.thumb,
 			url:		object.url,
+			nurl:		object.nurl,
 			location:	object.location,
 			gender:		object.gender,
 			about:		object.about,
-- 
2.39.5