Initial commit
This commit is contained in:
commit
639cab1bff
|
@ -0,0 +1,262 @@
|
|||
(function() {
|
||||
var SHOW_TIMEOUT = 1000,
|
||||
LOAD_TIMEOUT = SHOW_TIMEOUT / 4,
|
||||
STORAGE = {};
|
||||
|
||||
var links = document.getElementsByTagName('a');
|
||||
for (var i = 0; i < links.length; i++) {
|
||||
var el = links[i],
|
||||
href = el.getAttribute('href');
|
||||
|
||||
// Skipping elements that has no href and anchor links
|
||||
if (href === null || href[0] === '#') {
|
||||
continue;
|
||||
}
|
||||
|
||||
STORAGE[i] = {};
|
||||
(function(i) {
|
||||
var el = links[i];
|
||||
|
||||
el.addEventListener('mouseover', function(e) {
|
||||
log('The mouse is OVER!');
|
||||
|
||||
if (STORAGE[i].result === undefined) {
|
||||
STORAGE[i].load_timeout = after(LOAD_TIMEOUT, function() {
|
||||
STORAGE[i].xhr = loadPageInfo(el.href, function(res) {
|
||||
log('Result loaded:', res);
|
||||
STORAGE[i].result = res;
|
||||
delete STORAGE[i].xhr;
|
||||
|
||||
var tt = document.getElementById('whats-there-tooltip');
|
||||
if (tt === null) {
|
||||
showTooltip(el, STORAGE[i].result, STORAGE[i].e);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
STORAGE[i].show_timeout = after(SHOW_TIMEOUT, function() {
|
||||
log('Time for result!');
|
||||
if (STORAGE[i].result !== undefined) {
|
||||
showTooltip(el, STORAGE[i].result, STORAGE[i].e);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
el.addEventListener('mouseout', function(e) {
|
||||
hideTooltip();
|
||||
log('The mouse is OUT!');
|
||||
|
||||
if (STORAGE[i].xhr !== undefined) {
|
||||
STORAGE[i].xhr.abort();
|
||||
log('XHR aborted!');
|
||||
}
|
||||
window.clearTimeout(STORAGE[i].load_timeout);
|
||||
window.clearTimeout(STORAGE[i].show_timeout);
|
||||
});
|
||||
|
||||
el.addEventListener('mousemove', function(e) {
|
||||
STORAGE[i].e = e;
|
||||
});
|
||||
})(i);
|
||||
}
|
||||
|
||||
function showTooltip(el, info, e) {
|
||||
var tt = document.getElementById('whats-there-tooltip');
|
||||
if (tt !== null) {
|
||||
// Don't show a tooltip if there's one already
|
||||
return;
|
||||
}
|
||||
|
||||
if (info.title === undefined && info.description === undefined) {
|
||||
// Don't show a tooltip if there's no title nor descriptions for the page
|
||||
return;
|
||||
}
|
||||
|
||||
var tt = document.createElement('div'),
|
||||
body = document.getElementsByTagName('body')[0],
|
||||
bounds = el.getBoundingClientRect();
|
||||
|
||||
tt.setAttribute('class', 'whats-there-tooltip');
|
||||
tt.setAttribute('id', 'whats-there-tooltip');
|
||||
|
||||
if (info.image_url !== undefined) {
|
||||
var div = document.createElement('div');
|
||||
div.setAttribute('class', 'whats-there-img');
|
||||
div.style.backgroundImage = 'url(' + info.image_url + ')';
|
||||
tt.appendChild(div);
|
||||
}
|
||||
|
||||
if (info.title !== undefined) {
|
||||
var div = document.createElement('div');
|
||||
div.setAttribute('class', 'whats-there-title');
|
||||
div.innerText = info.title;
|
||||
tt.appendChild(div);
|
||||
}
|
||||
|
||||
if (info.description !== undefined) {
|
||||
var div = document.createElement('div');
|
||||
div.setAttribute('class', 'whats-there-description');
|
||||
div.innerText = info.description;
|
||||
tt.appendChild(div);
|
||||
}
|
||||
|
||||
var site_name;
|
||||
if (info.site_name === undefined) {
|
||||
site_name = info.host;
|
||||
} else {
|
||||
site_name = info.site_name + ' (' + info.host + ')';
|
||||
}
|
||||
var div = document.createElement('div');
|
||||
div.setAttribute('class', 'whats-there-site');
|
||||
div.innerText = site_name;
|
||||
tt.appendChild(div);
|
||||
|
||||
body.appendChild(tt);
|
||||
moveTooltip(e);
|
||||
}
|
||||
|
||||
function hideTooltip() {
|
||||
var tt = document.getElementById('whats-there-tooltip');
|
||||
if (tt !== null) {
|
||||
tt.parentNode.removeChild(tt);
|
||||
}
|
||||
}
|
||||
|
||||
function moveTooltip(e) {
|
||||
var tt = document.getElementById('whats-there-tooltip'),
|
||||
tt_width = tt.clientWidth,
|
||||
tt_height = tt.clientHeight,
|
||||
w_width = window.innerWidth,
|
||||
w_height = window.innerHeight,
|
||||
s_top = document.documentElement.scrollTop,
|
||||
s_left = document.documentElement.scrollLeft,
|
||||
e_top = e.clientY,
|
||||
e_left = e.clientX;
|
||||
|
||||
var tt_top = s_top + e_top - tt_height - 20,
|
||||
tt_left = s_left + e_left - tt_width / 2;
|
||||
|
||||
// Vertical positioning fix
|
||||
if (tt_top < 10) {
|
||||
tt_top = 10;
|
||||
|
||||
if (e_left > w_width / 2) {
|
||||
tt_left = s_left + e_left - tt_width - 20;
|
||||
} else {
|
||||
tt_left = s_left + e_left + 20;
|
||||
}
|
||||
} else {
|
||||
// Horizontal positioning fixes
|
||||
if (e_left - tt_width / 2 - 10 < s_left) {
|
||||
tt_left = 10;
|
||||
} else if (tt_left + tt_width + 10 > w_width) {
|
||||
tt_left = w_width - tt_width - 10;
|
||||
}
|
||||
}
|
||||
|
||||
tt.style.top = tt_top + 'px';
|
||||
tt.style.left = tt_left + 'px';
|
||||
}
|
||||
|
||||
function loadPageInfo(url, callback) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
|
||||
xhr.onreadystatechange = function() {
|
||||
if (this.readyState === this.HEADERS_RECEIVED) {
|
||||
var ct = this.getResponseHeader('Content-Type');
|
||||
|
||||
// Aborting page load if it's not HTML
|
||||
if (ct === null || ct.indexOf('text/html') != 0) {
|
||||
this.abort();
|
||||
}
|
||||
} else if (this.readyState === this.DONE && this.status === 200) {
|
||||
var result = parseTags(this.responseText),
|
||||
tokens = url.split('/');
|
||||
|
||||
result.protocol = tokens[0];
|
||||
result.host = tokens[2];
|
||||
if (result.image_url !== undefined) {
|
||||
result.image_url = makeAbsoluteURL(url, result.image_url);
|
||||
}
|
||||
|
||||
callback(result);
|
||||
}
|
||||
}
|
||||
|
||||
xhr.open('GET', url, true);
|
||||
xhr.send(null);
|
||||
|
||||
return xhr;
|
||||
}
|
||||
|
||||
function parseTags(html) {
|
||||
var parser = new DOMParser(),
|
||||
result = {};
|
||||
|
||||
var doc = parser.parseFromString(html, 'text/html');
|
||||
|
||||
var title = doc.getElementsByTagName('title')[0];
|
||||
if (title !== undefined) {
|
||||
result.title = title.innerText;
|
||||
}
|
||||
|
||||
var tags = doc.getElementsByTagName('meta');
|
||||
for (var i = 0; i < tags.length; i++) {
|
||||
var el = tags[i],
|
||||
name = el.getAttribute('name'),
|
||||
property = el.getAttribute('property'),
|
||||
content = el.getAttribute('content'),
|
||||
has_content = (content !== null && content.length > 0);
|
||||
|
||||
if (property === 'og:title' && has_content) {
|
||||
result.title = content;
|
||||
} else if (name === 'description' && has_content) {
|
||||
result.description = content;
|
||||
} else if (property === 'og:description' && has_content) {
|
||||
result.description = content;
|
||||
} else if (property === 'og:image' && has_content) {
|
||||
result.image_url = content;
|
||||
} else if (property === 'og:site_name' && has_content) {
|
||||
result.site_name = content;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function makeAbsoluteURL(page_url, link_url) {
|
||||
var tokens = link_url.split('/'),
|
||||
page = document.createElement('a'),
|
||||
link = document.createElement('a');
|
||||
|
||||
page.href = page_url;
|
||||
link.href = link_url;
|
||||
|
||||
if ((tokens[0] === 'http:' || tokens[0] === 'https:') && tokens[1] === '') {
|
||||
// Assuming it's an absolute URL already
|
||||
return link_url;
|
||||
} else if (link_url.slice(0, 2) === '//') {
|
||||
// Relative protocol
|
||||
return page.protocol + link_url;
|
||||
} else {
|
||||
return [
|
||||
page.protocol,
|
||||
'//',
|
||||
page.host,
|
||||
link.pathname,
|
||||
link.search
|
||||
].join('')
|
||||
}
|
||||
}
|
||||
|
||||
function after(timeout, func) {
|
||||
return window.setTimeout(func, timeout);
|
||||
}
|
||||
|
||||
function log() {
|
||||
if (false && console) {
|
||||
console.log.apply(console, arguments);
|
||||
}
|
||||
}
|
||||
})();
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"manifest_version": 2,
|
||||
|
||||
"name": "Whats There",
|
||||
"description": "Shows nice link previews based on the page's Open Graph tags",
|
||||
"version": "1.0",
|
||||
|
||||
"permissions": [
|
||||
"http://*/*",
|
||||
"https://*/*"
|
||||
],
|
||||
"content_scripts": [
|
||||
{
|
||||
"all_frames": true,
|
||||
"js": [ "background.js" ],
|
||||
"css": [ "tooltip.css" ],
|
||||
"matches": [ "\u003Call_urls>" ]
|
||||
}
|
||||
]
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 79 KiB |
|
@ -0,0 +1,41 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Playground</title>
|
||||
<meta charset="utf8">
|
||||
<style type="text/css">
|
||||
a { position: absolute; }
|
||||
|
||||
#l1{ top: 20px; left: 20px; }
|
||||
#l2{ top: 120px; left: 20px; }
|
||||
#l3{ top: 220px; left: 20px; }
|
||||
#l4{ top: 320px; left: 20px; }
|
||||
#l5{ top: 420px; left: 20px; }
|
||||
#l6{ top: 420px; left: 320px; }
|
||||
|
||||
#r1{ top: 20px; right: 20px; }
|
||||
#r2{ top: 120px; right: 20px; }
|
||||
#r3{ top: 220px; right: 20px; }
|
||||
#r4{ top: 320px; right: 20px; }
|
||||
#r5{ top: 420px; right: 20px; }
|
||||
#r6{ top: 520px; right: 20px; }
|
||||
#r7{ top: 520px; right: 320px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<a id="l1" href="http://localhots.xxx/">localhots.xxx</a>
|
||||
<a id="l2" href="http://erem.in/">erem.in</a>
|
||||
<a id="l3" href="https://www.facebook.com/">facebook.com</a>
|
||||
<a id="l4">bad link</a>
|
||||
<a id="l5" href="../../..">wtf</a>
|
||||
<a id="l6" href="http://www.aviasales.ru/">aviasales.ru</a>
|
||||
|
||||
<a id="r1" href="https://twitter.com/">twitter.com</a>
|
||||
<a id="r2" href="https://github.com/">github.com</a>
|
||||
<a id="r3" href="https://www.travelpayouts.com/">travelpayouts.com</a>
|
||||
<a id="r4" href="http://hotellook.ru/">hotellook.ru</a>
|
||||
<a id="r5" href="http://www.jetradar.com/">jetradar.com</a>
|
||||
<a id="r6" href="http://www.youtube.com/">youtube.com</a>
|
||||
<a id="r7" href="http://habrahabr.ru/">habrahabr.ru</a>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,47 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Themes Demo</title>
|
||||
<meta charset="utf8">
|
||||
<style type="text/css">
|
||||
.whats-there-img {
|
||||
background-image: url(aviasales.jpg);
|
||||
}
|
||||
#tt1 { left: 20px; top: 20px; }
|
||||
#tt2 { left: 20px; top: 190px; }
|
||||
#tt3 { left: 20px; top: 360px; }
|
||||
#tt4 { left: 450px; top: 20px; }
|
||||
#tt5 { left: 450px; top: 190px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="whats-there-tooltip" id="tt1">
|
||||
<div class="whats-there-img"></div>
|
||||
<div class="whats-there-title">Дешевые авиабилеты онлайн. Поиск билетов на самолет по 728 авиакомпаниям, включая low-cost - Aviasales</div>
|
||||
<div class="whats-there-description">Aviasales.ru помогает найти авиабилеты дешево. Мы ищем билеты на самолет по ведущим российским и зарубежным авиакассам. С помощью нашего поиска вы найдете лучшие цены на авиабилеты. Мы не продаем авиабилеты, мы помогаем вам найти лучшее предложение.</div>
|
||||
<div class="whats-there-site">Aviasales.ru (aviasales.ru)</div>
|
||||
</div>
|
||||
|
||||
<div class="whats-there-tooltip" id="tt2">
|
||||
<div class="whats-there-title">Дешевые авиабилеты онлайн. Поиск билетов на самолет по 728 авиакомпаниям, включая low-cost - Aviasales</div>
|
||||
<div class="whats-there-description">Aviasales.ru помогает найти авиабилеты дешево. Мы ищем билеты на самолет по ведущим российским и зарубежным авиакассам. С помощью нашего поиска вы найдете лучшие цены на авиабилеты. Мы не продаем авиабилеты, мы помогаем вам найти лучшее предложение.</div>
|
||||
<div class="whats-there-site">Aviasales.ru (aviasales.ru)</div>
|
||||
</div>
|
||||
|
||||
<div class="whats-there-tooltip" id="tt3">
|
||||
<div class="whats-there-img"></div>
|
||||
<div class="whats-there-title">Дешевые авиабилеты онлайн. Поиск билетов на самолет по 728 авиакомпаниям, включая low-cost - Aviasales</div>
|
||||
<div class="whats-there-site">Aviasales.ru (aviasales.ru)</div>
|
||||
</div>
|
||||
|
||||
<div class="whats-there-tooltip" id="tt4">
|
||||
<div class="whats-there-title">Дешевые авиабилеты онлайн. Поиск билетов на самолет по 728 авиакомпаниям, включая low-cost - Aviasales</div>
|
||||
<div class="whats-there-site">Aviasales.ru (aviasales.ru)</div>
|
||||
</div>
|
||||
|
||||
<div class="whats-there-tooltip" id="tt5">
|
||||
<div class="whats-there-description">Aviasales.ru помогает найти авиабилеты дешево. Мы ищем билеты на самолет по ведущим российским и зарубежным авиакассам. С помощью нашего поиска вы найдете лучшие цены на авиабилеты. Мы не продаем авиабилеты, мы помогаем вам найти лучшее предложение.</div>
|
||||
<div class="whats-there-site">Aviasales.ru (aviasales.ru)</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,63 @@
|
|||
.whats-there-tooltip
|
||||
{
|
||||
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
||||
font-size: 14px;
|
||||
line-height: 18px;
|
||||
|
||||
position: absolute;
|
||||
z-index: 999999;
|
||||
|
||||
overflow: hidden;
|
||||
|
||||
width: 400px;
|
||||
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
border-color: #ccc;
|
||||
border-radius: 5px;
|
||||
background-color: #fff;
|
||||
box-shadow: 0 1px 3px 1px rgba(0, 0, 0, .2);
|
||||
}
|
||||
.whats-there-img
|
||||
{
|
||||
float: left;
|
||||
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
margin-right: 10px;
|
||||
|
||||
background-repeat: no-repeat;
|
||||
background-position: center center;
|
||||
background-size: auto 100%;
|
||||
}
|
||||
.whats-there-title
|
||||
{
|
||||
font-weight: 600;
|
||||
|
||||
overflow: hidden;
|
||||
|
||||
height: 18px;
|
||||
margin: 5px 10px;
|
||||
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.whats-there-description
|
||||
{
|
||||
font-size: 13px;
|
||||
line-height: 16px;
|
||||
|
||||
overflow: hidden;
|
||||
|
||||
height: 94px;
|
||||
margin: 5px 10px;
|
||||
|
||||
color: #666;
|
||||
}
|
||||
.whats-there-site
|
||||
{
|
||||
overflow: hidden;
|
||||
|
||||
height: 18px;
|
||||
margin: 5px 10px;
|
||||
}
|
Loading…
Reference in New Issue