1. data-counter can be a number.

2. New button API option clickUrl which opens a link when button clicked.
3. Ability to create custom buttons.
4. Single button mode.
5. Fix popups in IE.
6. New icons for Code and Mail.ru bottons.
7. jsHint.
8. Refactorings.
master
Artem Sapegin 2012-05-25 00:08:05 +04:00
parent 61197b0cd2
commit c6eb5f81a4
7 changed files with 365 additions and 74 deletions

View File

@ -13,21 +13,71 @@
<p>&nbsp;</p>
<p>&nbsp;</p>
<h3>Standard</h3>
<ul class="social-likes" data-title="!!!!">
<ul class="social-likes" data-url="http://mail.ru/">
<li class="facebook" title="Опубликовать ссылку на Фейсбуке">Facebook</li>
<li class="twitter" data-via="sapegin" data-related="DessiTeckel:Моя такса" title="Опубликовать ссылку в Твиттере">Twitter</li>
<li class="mailru" title="Опубликовать ссылку в Моём мире">Мой мир</li>
<li class="vkontakte" title="Опубликовать ссылку во Вконтакте">Вконтакте</li>
<li class="odnoklassniki" title="Опубликовать ссылку в Одноклассниках">Одноклассники</li>
<li class="plusone" data-counter-url="http://sapegin.ru/api/social-likes/googleplusonecount.php?url={PAGE_URL}&callback=?" title="Плюсануть в Гугле">Google+</li>
<li class="plusone" data-counter="http://sapegin.ru/api/social-likes/googleplusonecount.php?url={PAGE_URL}&callback=?" title="Плюсануть в Гугле">Google+</li>
<li class="livejournal" title="Опубликовать ссылку в ЖЖ">LiveJournal</li>
<li class="code" title="Получить код для публикации ссылки в другом сервисе" data-prompt="Скопируйте код в буфер обмена:">Код</li>
</ul>
<h3>Vertical (no counters)</h3>
<ul class="social-likes social-likes_vertical" data-url="http://mail.ru/" data-counters="no">
<li class="facebook" title="Опубликовать ссылку на Фейсбуке">Facebook</li>
<li class="twitter" data-via="sapegin" data-related="DessiTeckel:Моя такса" title="Опубликовать ссылку в Твиттере">Twitter</li>
<li class="mailru" title="Опубликовать ссылку в Моём мире">Мой мир</li>
<li class="vkontakte" title="Опубликовать ссылку во Вконтакте">Вконтакте</li>
<li class="odnoklassniki" title="Опубликовать ссылку в Одноклассниках">Одноклассники</li>
<li class="plusone" data-counter="http://sapegin.ru/api/social-likes/googleplusonecount.php?url={PAGE_URL}&callback=?" title="Плюсануть в Гугле">Google+</li>
<li class="livejournal" title="Опубликовать ссылку в ЖЖ">LiveJournal</li>
<li class="code" title="Получить код для публикации ссылки в другом сервисе" data-prompt="Скопируйте код в буфер обмена:">Код</li>
</ul>
<h3>Single button</h3>
<ul class="social-likes social-likes_single" data-url="http://mail.ru/">
<li class="facebook" title="Опубликовать ссылку на Фейсбуке">Facebook</li>
<li class="twitter" data-via="sapegin" data-related="DessiTeckel:Моя такса" title="Опубликовать ссылку в Твиттере">Twitter</li>
<li class="mailru" title="Опубликовать ссылку в Моём мире">Мой мир</li>
<li class="vkontakte" title="Опубликовать ссылку во Вконтакте">Вконтакте</li>
<li class="odnoklassniki" title="Опубликовать ссылку в Одноклассниках">Одноклассники</li>
<li class="plusone" data-counter="http://sapegin.ru/api/social-likes/googleplusonecount.php?url={PAGE_URL}&callback=?" title="Плюсануть в Гугле">Google+</li>
<li class="livejournal" title="Опубликовать ссылку в ЖЖ">LiveJournal</li>
<li class="code" title="Получить код для публикации ссылки в другом сервисе" data-prompt="Скопируйте код в буфер обмена:">Код</li>
</ul>
<h3>Old initialization HTML</h3>
<ul class="social-likes">
<li class="facebook"><a href="#" title="Опубликовать ссылку на Фейсбуке">Facebook</a></li>
<li class="twitter"><a href="#" data-via="sapegin" data-related="DessiTeckel:Моя такса" title="Опубликовать ссылку в Твиттере">Twitter</a></li>
</ul>
<h3>User button &amp; custom count</h3>
<script>
var socialLikesButtons = {
surfingbird: {
clickUrl: 'http://surfingbird.ru/share?url={url}',
//popupUrl: 'http://surfingbird.ru/share?url={url}',
pupupWidth: 650,
popupHeight: 500
}
};
</script>
<style>
.social-likes__button_surfingbird {
background: #f2f3f5;
color: #596e7e;
border-color: #ced5e2;
}
.social-likes__icon_surfingbird {
background: url(http://surfingbird.ru/img/share-icon.png) no-repeat 2px 3px;
}
</style>
<ul class="social-likes">
<li class="surfingbird" data-counter="666">Surf</li>
</ul>
</body>
</html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 739 B

After

Width:  |  Height:  |  Size: 313 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 806 B

After

Width:  |  Height:  |  Size: 714 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 264 B

View File

@ -11,19 +11,28 @@
* @license MIT
*/
/*global define:false, socialLikesButtons:false */
(function (factory) { // Try to register as an anonymous AMD module
if (typeof define === 'function' && define.amd) {
define(['jquery'], factory);
} else {
factory(jQuery);
}
}(function ($) {
}(function ($) { 'use strict';
var prefix = 'social-likes__',
fadeSpeed = 'fast';
/**
* Buttons
*/
var services = {
facebook: {
counterUrl: 'http://graph.facebook.com/?ids={url}&callback=?',
convertNumber: function(data) {
for (var url in data) {
for (var url in data) if (data.hasOwnProperty(url)) {
return data[url].shares;
}
},
@ -44,7 +53,7 @@ var services = {
mailru: {
counterUrl: 'http://connect.mail.ru/share_count?url_list={url}&callback=1&func=?',
convertNumber: function(data) {
for (var url in data) {
for (var url in data) if (data.hasOwnProperty(url)) {
return data[url].shares;
}
},
@ -105,14 +114,17 @@ var services = {
click: function(e) {
var balloon = this._codeBalloon;
if (balloon) {
balloon.toggle();
if (balloon.is(':visible')) {
balloon.fadeOut(fadeSpeed);
return;
}
}
else {
balloon = $(template(
'<div class="social-likes__balloon">' +
'<div class="social-likes__balloon__arrow"></div>' +
'<div class="' + prefix + 'balloon">' +
'<div class="' + prefix + 'balloon__arrow"></div>' +
'{prompt}<br>' +
'<textarea class="social-likes__balloon__code">{html}</textarea>' +
'<textarea class="' + prefix + 'balloon__code">{html}</textarea>' +
'</div>',
{
prompt: (this.widget.data('prompt') || 'Copy code to clipboard:'),
@ -122,18 +134,16 @@ var services = {
this.widget.append(balloon);
this._codeBalloon = balloon;
}
balloon.fadeIn(fadeSpeed);
balloon.find('textarea').select();
if (balloon.is(':visible')) {
var doc = $(document),
clickEvent = 'click.social-likes__hide-code';
doc.on(clickEvent, function(e) {
if (e.target.tagName !== 'TEXTAREA') {
balloon.hide();
doc.off(clickEvent);
}
});
balloon.removeClass(prefix + 'balloon_right');
if (balloon.offset().left < 0) {
balloon.addClass(prefix + 'balloon_right');
}
closeOnClick(balloon);
}
}
},
@ -164,6 +174,9 @@ var services = {
};
/**
* Counters manager
*/
var counters = {
promises: {},
fetch: function(service, url) {
@ -178,7 +191,7 @@ var counters = {
deferred = $.Deferred(),
jsonUrl = options.counterUrl && makeUrl(options.counterUrl, {url: url});
if (typeof options.counter === 'function') {
if ($.isFunction(options.counter)) {
options.counter(jsonUrl, deferred);
}
else if (options.counterUrl) {
@ -186,13 +199,12 @@ var counters = {
.done(function(data) {
try {
var number = data;
if (typeof options.convertNumber === 'function') {
if ($.isFunction(options.convertNumber)) {
number = options.convertNumber(data);
}
deferred.resolve(number);
}
catch (e) {
throw e;
deferred.reject(e);
}
});
@ -205,6 +217,9 @@ var counters = {
};
/**
* jQuery plugin
*/
$.fn.socialLikes = function() {
return this.each(function() {
new SocialLikes($(this));
@ -218,26 +233,112 @@ function SocialLikes(container) {
}
SocialLikes.prototype = {
optionsMap: {
pageUrl: {
attr: 'url',
default: function() { return window.location.href.replace(window.location.hash, ''); }
},
pageTitle: {
attr: 'title',
default: function() { return document.title; }
},
pageHtml: {
attr: 'html',
default: function() { return '<a href="' + this.options.pageUrl + '">' + this.options.pageTitle + '</a>'; }
},
pageCounters: {
attr: 'counters',
default: 'yes',
convert: function(value) { return value === 'yes'; }
}
},
init: function() {
var options = {
pageUrl: this.pageUrl(),
pageTitle: this.pageTitle(),
pageHtml: this.pageHtml()
};
this.readOptions();
this.single = this.container.hasClass('social-likes_single');
this.initUserButtons();
if (this.single)
this.container.on('counter.social-likes', $.proxy(this.updateCounter, this));
var options = this.options;
this.container.find('li').each(function() {
new Button($(this), options);
});
if (this.single)
this.makeSingleButton();
},
pageUrl: function() {
return 'http://mail.ru';
//return this.container.data('url') || window.location.href.replace(window.location.hash, '');
readOptions: function() {
this.options = {};
for (var key in this.optionsMap) {
var option = this.optionsMap[key];
this.options[key] = this.container.data(option.attr) ||
($.isFunction(option.default) ? $.proxy(option.default, this)() : option.default);
if ($.isFunction(option.convert))
this.options[key] = option.convert(this.options[key]);
}
},
pageTitle: function() {
return this.container.data('title') || document.title;
initUserButtons: function() {
if (!this.userButtonInited && window.socialLikesButtons) {
$.extend(services, socialLikesButtons);
}
this.userButtonInited = true;
},
pageHtml: function() {
return (this.container.data('html') ||
'<a href="' + this.pageUrl() + '">' + this.pageTitle() + '</a>');
makeSingleButton: function() {
var container = this.container;
container.addClass('social-likes_vertical');
container.wrap($('<div>', {'class': 'social-likes_single-w'}));
var wrapper = container.parent();
var defaultLeft = parseInt(container.css('left'), 10),
defaultTop = parseInt(container.css('top'), 10);
container.hide();
var button = $('<div>', {
'class': getElementClassNames('button', 'single'),
'text': container.data('single-title') || 'Share'
});
button.prepend($('<span>', {'class': getElementClassNames('icon', 'single')}));
wrapper.append(button);
var close = $('<li>', {
'class': prefix + 'close',
'html': '&times;'
});
container.append(close);
this.number = 0;
button.click(function() {
container.css({ left: defaultLeft, top: defaultTop });
showInViewport(container, 20);
closeOnClick(container);
return false;
});
close.click(function() {
container.fadeOut(fadeSpeed);
});
this.wrapper = wrapper;
},
updateCounter: function(e, number) {
if (!number) return;
this.number += number;
this.getCounterElem().text(this.number);
},
getCounterElem: function() {
var counterElem = this.wrapper.find('.' + prefix + 'counter_single');
if (!counterElem.length) {
counterElem = $('<span>', {
'class': getElementClassNames('counter', 'single')
});
this.wrapper.append(counterElem);
}
return counterElem;
}
};
@ -252,14 +353,19 @@ function Button(widget, options) {
}
Button.prototype = {
prefix: 'social-likes__',
init: function() {
this.detectParams();
this.initHtml();
this.setEventHandlers();
counters.fetch(this.service, this.options.pageUrl)
.done($.proxy(this.updateCounter, this));
if (this.options.pageCounters) {
if (this.options.counterNumber) {
this.updateCounter(this.options.counterNumber);
}
else {
counters.fetch(this.service, this.options.pageUrl)
.done($.proxy(this.updateCounter, this));
}
}
},
detectService: function() {
@ -275,15 +381,21 @@ Button.prototype = {
},
detectParams: function() {
// Custom page counter URL
var counterUrl = this.widget.data('counter-url');
if (counterUrl) {
this.options.counterUrl = counterUrl;
// Custom page counter URL or number
var counter = this.widget.data('counter');
if (counter) {
var number = parseInt(counter);
if (isNaN(number))
this.options.counterUrl = counter;
else
this.options.counterNumber = number;
}
},
initHtml: function() {
var widget = this.widget;
var options = this.options,
widget = this.widget;
var isLink = !!options.clickUrl;
widget.removeClass(this.service);
widget.addClass(this.getElementClassNames('widget'));
@ -295,10 +407,22 @@ Button.prototype = {
}
// Button
var button = $('<span>', {
var button = $(isLink ? '<a>' : '<span>', {
'class': this.getElementClassNames('button'),
'text': widget.text()
})
});
if (isLink) {
var url = makeUrl(options.clickUrl, {
url: options.pageUrl,
title: options.pageTitle
});
button.attr('href', url);
}
else {
button.click($.proxy(this.click, this));
}
// Icon
button.prepend($('<span>', {'class': this.getElementClassNames('icon')}));
widget.empty().append(button);
@ -307,18 +431,13 @@ Button.prototype = {
cloneDataAttrs: function(source, destination) {
var data = source.data();
for (var key in data) {
for (var key in data) if (data.hasOwnProperty(key)) {
destination.data(key, data[key]);
}
},
getElementClassNames: function(elem) {
var cls = this.prefix + elem;
return cls + ' ' + cls + '_' + this.service;
},
setEventHandlers: function() {
this.button.click($.proxy(this.click, this));
return getElementClassNames(elem, this.service);
},
updateCounter: function(number) {
@ -327,14 +446,16 @@ Button.prototype = {
var counterElem = $('<span>', {
'class': this.getElementClassNames('counter'),
'html': number
'text': number
});
this.widget.append(counterElem);
this.widget.trigger('counter.social-likes', number);
},
click: function(e) {
var options = this.options;
if (typeof options.click === 'function') {
if ($.isFunction(options.click)) {
options.click.call(this, e);
}
else {
@ -369,13 +490,13 @@ Button.prototype = {
},
openPopup: function(url, params) {
var left = Math.round(screen.width/2 - params.width/2);
var left = Math.round(screen.width/2 - params.width/2),
top = 0;
if (screen.height > params.height) {
top = Math.round(screen.height/3 - params.height/2);
}
var win = window.open(url, this.prefix + this.service, 'left=' + left + ',top=' + top + ',' +
var win = window.open(url, 'sl_' + this.service, 'left=' + left + ',top=' + top + ',' +
'width=' + params.width + ',height=' + params.height + ',personalbar=0,toolbar=0,scrollbars=1,resizable=1');
if (win) {
win.focus();
@ -386,24 +507,66 @@ Button.prototype = {
};
// Helpers
/**
* Helpers
*/
function makeUrl(url, context) {
for (var key in context) {
url = url.replace('{' + key + '}', encodeURIComponent(context[key]).replace(/\+/g, '%2B'));
for (var key in context) if (context.hasOwnProperty(key)) {
url = url.replace('{' + key + '}', encodeURIComponent(context[key]));
}
return url;
};
}
function template(tmpl, context) {
for (var key in context) {
for (var key in context) if (context.hasOwnProperty(key)) {
tmpl = tmpl.replace('{' + key + '}', context[key]);
}
return tmpl;
};
}
function getElementClassNames(elem, mod) {
var cls = prefix + elem;
return cls + ' ' + cls + '_' + mod;
}
function closeOnClick(elem) {
var doc = $(document),
clickEvent = 'click.social-likes' + Math.random();
doc.on(clickEvent, function(e) {
if (!$(e.target).closest(elem).length) {
elem.fadeOut(fadeSpeed);
doc.off(clickEvent);
}
});
}
function showInViewport(elem, offset) {
if (document.documentElement.getBoundingClientRect) {
var left = parseInt(elem.css('left'), 10),
top = parseInt(elem.css('top'), 10);
elem.css('visibility', 'hidden').show();
var rect = elem[0].getBoundingClientRect();
if (rect.left < offset)
elem.css('left', offset - rect.left + left);
else if (rect.right > window.innerWidth - offset)
elem.css('left', window.innerWidth - rect.right - offset + left);
if (rect.top < offset)
elem.css('top', offset - rect.top + top);
else if (rect.bottom > window.innerHeight - offset)
elem.css('top', window.innerHeight - rect.bottom - offset + top);
elem.hide().css('visibility', 'visible');
}
elem.fadeIn(fadeSpeed);
}
// Auto initialization
/**
* Auto initialization
*/
$(function() {
$('.social-likes').socialLikes();
});

View File

@ -28,10 +28,6 @@ overflow-scrolling()
-webkit-overflow-scrolling arguments
overflow-scrolling arguments
// Preserve '%'s in hsla() -- strange hack
hsla()
'hsla(%s)' % unquote(join(', ', arguments))
// Gradients
// https://github.com/LearnBoost/stylus/issues/245

View File

@ -11,13 +11,10 @@
font-size:14px;
}
// foo
/* bar */
/*! baz */
.social-likes {
margin-right:-8px;
line-height:26px;
list-style:none;
*zoom:1;
&__widget {
@ -62,6 +59,7 @@
// Gradient transition dont work yet in any browser. This is for future :)
transition:border .1s ease-in-out, color .2s ease-in-out, background .2s ease-in-out;
cursor:pointer;
user-select:none;
}
&__icon {
@ -106,6 +104,78 @@
border-left:0;
border-right:5px solid;
}
&__close {
position:absolute;
right:0;
top:0;
padding:2px 4px;
font:16px Verdana, Geneva, Tahoma, sans-serif;
color:#999;
cursor:pointer;
}
&__close:hover {
color:#111;
}
&__close:before {
display:none; // Remove nice bullets
}
// Vertical mode
&_vertical {
margin-right:0;
.social-likes__widget {
display:block;
margin-bottom:8px;
margin-right:0;
}
}
// Single button nmode
&_single-w {
position:relative;
}
&_single {
position:absolute;
top:-19px;
left:-15px;
padding:15px 15px 9px 15px;
background:#fff;
box-shadow:0 0 15px hsla(0,0%,0%,.3);
z-index:99999;
}
}
// Single button
.social-likes__button_single {
position:relative;
padding-left:19px;
background:#e2e2e2;
background:linear-gradient(top, #f7f7f7, #e2e2e2);
color:#444;
border-color:#ccc;
border-color:hsla(0,0%,70%,.8);
border-bottom-color:hsla(0,0%,60%,.8);
}
.social-likes__button_single:hover {
background:#f4f4f4;
background:linear-gradient(top, #f0f0f0, #cfcfcf);
color:#222;
border-color:#bbb;
border-bottom-color:#9f9f9f;
}
.social-likes__icon_single {
background-image:embedurl('../icons/single.png');
background-position:2px 3px;
}
.social-likes__counter_single {
background:#f6f6f6;
border-color:#ddd;
}
.social-likes__counter_single:after {
border-right-color:#f6f6f6;
}
@ -201,6 +271,7 @@
// Mail.ru
.social-likes__button_mailru {
padding-left:18px;
background:#004584;
background:linear-gradient(top, #5d90ba, #004584);
color:#fff;
@ -220,7 +291,7 @@
}
.social-likes__icon_mailru {
background-image:embedurl('../icons/mailru.png');
background-position:2px 2px;
background-position:1px 2px;
}
.social-likes__counter_mailru {
background:#fff1c2;
@ -335,12 +406,13 @@
}
.social-likes__icon_code {
background-image:embedurl('../icons/code.png');
background-position:2px 3px;
background-position:2px 2px;
}
// Code balloon
.social-likes__balloon {
position:absolute;
display:none;
bottom:29px;
right:0;
padding:6px;
@ -374,4 +446,14 @@
border-bottom:none;
border-top:6px solid #666;
}
&_right {
left:0;
right:auto;
.social-likes__balloon__arrow {
left:8px;
right:auto;
}
}
}