Remove jquery :)

master
Vitaliy Filippov 2016-04-30 21:17:19 +03:00
parent a52691b048
commit e77851ef60
1 changed files with 268 additions and 195 deletions

View File

@ -3,24 +3,14 @@
* http://sapegin.github.com/social-likes * http://sapegin.github.com/social-likes
* *
* Sharing buttons for Russian and worldwide social networks. * Sharing buttons for Russian and worldwide social networks.
* jQuery removed, only requires simple utilities: GET, onDomReady, http_build_query, addListener, removeListener
* *
* @requires jQuery
* @author Artem Sapegin * @author Artem Sapegin
* @copyright 2014 Artem Sapegin (sapegin.me) * @copyright 2014 Artem Sapegin (sapegin.me)
* @license MIT * @license MIT
*/ */
/*global define:false, socialLikesButtons:false */ (function() {
(function(factory) { // Try to register as an anonymous AMD module
if (typeof define === 'function' && define.amd) {
define(['jquery'], factory);
}
else {
factory(jQuery);
}
}(function($, undefined) {
'use strict'; 'use strict';
var prefix = 'social-likes'; var prefix = 'social-likes';
@ -29,6 +19,43 @@
var protocol = location.protocol === 'https:' ? 'https:' : 'http:'; var protocol = location.protocol === 'https:' ? 'https:' : 'http:';
var isHttps = protocol === 'https:'; var isHttps = protocol === 'https:';
function hasClass(e, cls, remove)
{
var p = -1;
while ((p = e.className.indexOf(cls, p+1)) != -1)
{
if ((!p || /\s/.exec(e.className.charAt(p-1))) &&
(p == e.className.length-cls.length || /\s/.exec(e.className.charAt(p+cls.length))))
{
if (remove)
e.className = e.className.substr(0, p-1)+e.className.substr(p+cls.length);
else
return true;
}
}
return false;
}
function getScript(url, onsuccess, onerror)
{
var node = document.createElement('script');
node.type = 'text/javascript';
node.src = url;
node.onreadystatechange = function(node)
{
if (node.readyState == 'complete')
{
var head = document.head || document.getElementsByTagName('head')[0];
head.appendChild(node);
onsuccess && onsuccess();
}
else if (node.readyState == 'loaded')
{
node.children; // IE hack
if (node.readyState == 'loading')
onerror && onerror();
}
};
}
/** /**
* Buttons * Buttons
@ -83,8 +110,7 @@
var index = options._.length; var index = options._.length;
options._.push(deferred); options._.push(deferred);
$.getScript(makeUrl(jsonUrl, {index: index})) getScript(makeUrl(jsonUrl, {index: index}), null, function() { deferred.reject() });
.fail(deferred.reject);
}, },
popupUrl: 'https://vk.com/share.php?url={url}&title={title}', popupUrl: 'https://vk.com/share.php?url={url}&title={title}',
popupWidth: 655, popupWidth: 655,
@ -104,8 +130,7 @@
var index = options._.length; var index = options._.length;
options._.push(deferred); options._.push(deferred);
$.getScript(makeUrl(jsonUrl, {index: index})) getScript(makeUrl(jsonUrl, {index: index}), null, function() { deferred.reject() });
.fail(deferred.reject);
}, },
popupUrl: 'https://connect.ok.ru/dk?st.cmd=WidgetSharePreview&service=odnoklassniki&st.shareUrl={url}', popupUrl: 'https://connect.ok.ru/dk?st.cmd=WidgetSharePreview&service=odnoklassniki&st.shareUrl={url}',
popupWidth: 580, popupWidth: 580,
@ -145,34 +170,42 @@
return servicePromises[url]; return servicePromises[url];
} }
else { else {
var options = $.extend({}, services[service], extraOptions); var options = {};
var deferred = $.Deferred(); for (var i in services[service])
options[i] = services[service][i];
for (var i in extraOptions)
options[i] = extraOptions[i];
var deferred = { resolve: function(v) {}, reject: function() {} };
var jsonUrl = options.counterUrl && makeUrl(options.counterUrl, {url: url}); var jsonUrl = options.counterUrl && makeUrl(options.counterUrl, {url: url});
if (jsonUrl && $.isFunction(options.counter)) { if (jsonUrl && typeof options.counter == 'function')
options.counter(jsonUrl, deferred); options.counter(jsonUrl, deferred);
} else if (options.counterUrl)
else if (options.counterUrl) { {
$.getJSON(jsonUrl) GET(jsonUrl, function(r, d)
.done(function(data) { {
try { if (!r.responseText)
var number = data; deferred.reject();
if ($.isFunction(options.convertNumber)) { else
{
var number = d||r.responseText;
try
{
if (typeof options.convertNumber == 'function')
number = options.convertNumber(data); number = options.convertNumber(data);
}
deferred.resolve(number); deferred.resolve(number);
} }
catch (e) { catch (e)
{
deferred.reject(); deferred.reject();
} }
}) }
.fail(deferred.reject); });
} }
else { else
deferred.reject(); deferred.reject();
}
servicePromises[url] = deferred.promise(); servicePromises[url] = deferred;
return servicePromises[url]; return servicePromises[url];
} }
} }
@ -182,23 +215,27 @@
/** /**
* jQuery plugin * jQuery plugin
*/ */
$.fn.socialLikes = function(options) { window.socialLikes = function(element, options) {
return this.each(function() { var instance = element['__'+prefix];
var elem = $(this); if (instance) {
var instance = elem.data(prefix); if (typeof options == 'object') {
if (instance) { instance.update(options);
if ($.isPlainObject(options)) {
instance.update(options);
}
} }
else { }
instance = new SocialLikes(elem, $.extend({}, $.fn.socialLikes.defaults, options, dataToOptions(elem))); else {
elem.data(prefix, instance); var c = {}, o = dataToOptions(element);
} for (var i in window.socialLikes.defaults)
}); c[i] = window.socialLikes.defaults[i];
for (var i in options)
c[i] = options[i];
for (var i in o)
c[i] = o[i];
instance = new SocialLikes(element, c);
element['__'+prefix] = instance;
}
}; };
$.fn.socialLikes.defaults = { window.socialLikes.defaults = {
url: window.location.href.replace(window.location.hash, ''), url: window.location.href.replace(window.location.hash, ''),
title: document.title, title: document.title,
counters: true, counters: true,
@ -217,39 +254,46 @@
SocialLikes.prototype = { SocialLikes.prototype = {
init: function() { init: function() {
// Add class in case of manual initialization var self = this;
this.container.addClass(prefix);
this.single = this.container.hasClass(prefix + '_single'); // Add class in case of manual initialization
if (!hasClass(this.container, prefix))
this.container.className += ' '+prefix;
this.single = hasClass(this.container, prefix+'_single');
this.initUserButtons(); this.initUserButtons();
this.countersLeft = 0; this.countersLeft = 0;
this.number = 0; this.number = 0;
this.container.on('counter.' + prefix, $.proxy(this.updateCounter, this)); this.container['on_counter.' + prefix] = function(e) { return self.updateCounter(e); };
var buttons = this.container.children();
this.makeSingleButton(); this.makeSingleButton();
this.buttons = []; this.buttons = [];
buttons.each($.proxy(function(idx, elem) { for (var i = 0; i < this.container.children.length; i++)
var button = new Button($(elem), this.options); {
var button = new Button(this.container.children[i], this.options);
this.buttons.push(button); this.buttons.push(button);
if (button.options.counterUrl) this.countersLeft++; if (button.options.counterUrl)
}, this)); this.countersLeft++;
}
if (this.options.counters) { if (this.options.counters)
this.timer = setTimeout($.proxy(this.appear, this), this.options.wait); {
this.timeout = setTimeout($.proxy(this.ready, this, true), this.options.timeout); this.timer = setTimeout(function() { self.appear() }, this.options.wait);
this.timeout = setTimeout(function() { self.ready() }, this.options.timeout);
} }
else { else
this.appear(); this.appear();
}
}, },
initUserButtons: function() { initUserButtons: function() {
if (!this.userButtonInited && window.socialLikesButtons) { if (!this.userButtonInited && window.socialLikesButtons) {
$.extend(true, services, socialLikesButtons); for (var i in socialLikesButtons) {
services[i] = services[i] || {};
for (var j in socialLikesButtons[i])
services[i][j] = socialLikesButtons[i][j];
}
} }
this.userButtonInited = true; this.userButtonInited = true;
}, },
@ -257,41 +301,39 @@
if (!this.single) return; if (!this.single) return;
var container = this.container; var container = this.container;
container.addClass(prefix + '_vertical'); container.className += ' ' + prefix + '_vertical';
container.wrap($('<div>', {'class': prefix + '_single-w'})); var wrapper = document.createElement('div');
container.wrapInner($('<div>', {'class': prefix + '__single-container'})); wrapper.className = prefix + '_single-w';
var wrapper = container.parent(); container.parentNode.insertBefore(wrapper, container);
wrapper.appendChild(container);
var d = document.createElement('div');
d.className = prefix + '__single-container';
while (container.firstChild)
d.appendChild(container.firstChild);
container.appendChild(d);
// Widget // Widget
var widget = $('<div>', { var widget = document.createElement('div');
'class': getElementClassNames('widget', 'single') widget.className = getElementClassNames('widget', 'single');
}); widget.innerHTML = '<div class="'+getElementClassNames('button', 'single')+
var button = $(template( '"><span class="'+getElementClassNames('icon', 'single')+'"></span>'+
'<div class="{buttonCls}">' + this.options.singleTitle+'</div>';
'<span class="{iconCls}"></span>' + wrapper.appendChild(widget);
'{title}' +
'</div>',
{
buttonCls: getElementClassNames('button', 'single'),
iconCls: getElementClassNames('icon', 'single'),
title: this.options.singleTitle
}
));
widget.append(button);
wrapper.append(widget);
widget.on('click', function() { addListener(widget, 'click', function() {
var activeClass = prefix + '__widget_active'; var activeClass = prefix+'__widget_active';
widget.toggleClass(activeClass); if (!hasClass(widget, activeClass)) {
if (widget.hasClass(activeClass)) { widget.className += ' '+activeClass;
container.css({left: -(container.width()-widget.width())/2, top: -container.height()}); container.style.left = ((widget.offsetWidth-container.offsetWidth)/2)+'px';
container.style.top = -container.offsetHeight+'px';
showInViewport(container); showInViewport(container);
closeOnClick(container, function() { closeOnClick(container, function() {
widget.removeClass(activeClass); hasClass(widget, activeClass, true);
}); });
} }
else { else {
container.removeClass(openClass); widget.className = widget.className.substr(0, i);
hasClass(container, openClass, true);
} }
return false; return false;
}); });
@ -304,13 +346,17 @@
// Reset counters // Reset counters
this.number = 0; this.number = 0;
this.countersLeft = this.buttons.length; this.countersLeft = this.buttons.length;
if (this.widget) this.widget.find('.' + prefix + '__counter').remove(); if (this.widget)
{
var e = this.widget.querySelector('.' + prefix + '__counter');
if (e) e.parentNode.removeChild(e);
}
// Update options // Update options
$.extend(this.options, options); for (var i in options)
for (var buttonIdx = 0; buttonIdx < this.buttons.length; buttonIdx++) { this.options[i] = options[i];
for (var buttonIdx = 0; buttonIdx < this.buttons.length; buttonIdx++)
this.buttons[buttonIdx].update(options); this.buttons[buttonIdx].update(options);
}
}, },
updateCounter: function(e, service, number) { updateCounter: function(e, service, number) {
number = number || 0; number = number || 0;
@ -329,23 +375,23 @@
this.countersLeft--; this.countersLeft--;
}, },
appear: function() { appear: function() {
this.container.addClass(prefix + '_visible'); this.container.className += ' '+prefix+'_visible';
}, },
ready: function(silent) { ready: function(silent) {
if (this.timeout) { if (this.timeout) {
clearTimeout(this.timeout); clearTimeout(this.timeout);
} }
this.container.addClass(prefix + '_ready'); this.container.className += ' '+prefix+'_ready';
if (!silent) { if (!silent) {
this.container.trigger('ready.' + prefix, this.number); var e = this.container['on_ready.'+prefix];
if (e) e(this.number);
} }
}, },
getCounterElem: function() { getCounterElem: function() {
var counterElem = this.widget.find('.' + classPrefix + 'counter_single'); var counterElem = this.widget.querySelector('.' + classPrefix + 'counter_single');
if (!counterElem.length) { if (!counterElem.length) {
counterElem = $('<span>', { counterElem = document.createElement('span');
'class': getElementClassNames('counter', 'single') counterElem.className = getElementClassNames('counter', 'single');
});
this.widget.append(counterElem); this.widget.append(counterElem);
} }
return counterElem; return counterElem;
@ -355,7 +401,9 @@
function Button(widget, options) { function Button(widget, options) {
this.widget = widget; this.widget = widget;
this.options = $.extend({}, options); this.options = {};
for (var i in options)
this.options[i] = options[i];
this.detectService(); this.detectService();
if (this.service) { if (this.service) {
this.init(); this.init();
@ -366,21 +414,24 @@
init: function() { init: function() {
this.detectParams(); this.detectParams();
this.initHtml(); this.initHtml();
setTimeout($.proxy(this.initCounter, this), 0); var self = this;
setTimeout(function() { self.initCounter() }, 0);
}, },
update: function(options) { update: function(options) {
$.extend(this.options, {forceUpdate: false}, options); this.options.forceUpdate = false;
this.widget.find('.' + prefix + '__counter').remove(); // Remove old counter for (var i in options)
this.options[i] = options[i];
var e = this.widget.querySelector('.' + prefix + '__counter');
if (e) e.parentNode.removeChild(e); // Remove old counter
this.initCounter(); this.initCounter();
}, },
detectService: function() { detectService: function() {
var service = this.widget.data('service'); var service = this.widget.getAttribute('data-service');
if (!service) { if (!service) {
// class="facebook" // class="facebook"
var node = this.widget[0]; var classes = this.widget.classList || this.widget.className.split(' ');
var classes = node.classList || node.className.split(' ');
for (var classIdx = 0; classIdx < classes.length; classIdx++) { for (var classIdx = 0; classIdx < classes.length; classIdx++) {
var cls = classes[classIdx]; var cls = classes[classIdx];
if (services[cls]) { if (services[cls]) {
@ -391,72 +442,71 @@
if (!service) return; if (!service) return;
} }
this.service = service; this.service = service;
$.extend(this.options, services[service]); for (var i in services[service])
this.options[i] = services[service][i];
}, },
detectParams: function() { detectParams: function() {
var data = this.widget.data();
// Custom page counter URL or number // Custom page counter URL or number
if (data.counter) { var c = this.widget.getAttribute('data-counter');
var number = parseInt(data.counter, 10); if (c) {
if (isNaN(number)) { var number = parseInt(c, 10);
this.options.counterUrl = data.counter; if (isNaN(number))
} this.options.counterUrl = c;
else { else
this.options.counterNumber = number; this.options.counterNumber = number;
}
} }
// Custom page title // Custom page title
if (data.title) { c = this.widget.getAttribute('data-title');
this.options.title = data.title; if (c)
} this.options.title = c;
// Custom page URL // Custom page URL
if (data.url) { c = this.widget.getAttribute('data-url');
this.options.url = data.url; if (c)
} this.options.url = c;
}, },
initHtml: function() { initHtml: function() {
var self = this;
var options = this.options; var options = this.options;
var widget = this.widget; var widget = this.widget;
// Old initialization HTML // Old initialization HTML
var a = widget.find('a'); var a = widget.querySelector('a');
if (a.length) { if (a)
this.cloneDataAttrs(a, widget); this.cloneDataAttrs(a, widget);
}
// Button // Button
var button = $('<span>', { var button = document.createElement('span');
'class': this.getElementClassNames('button'), button.className = this.getElementClassNames('button');
'text': widget.text() button.innerHTML = widget.innerHTML;
});
if (options.clickUrl) { if (options.clickUrl) {
var url = makeUrl(options.clickUrl, { var url = makeUrl(options.clickUrl, {
url: options.url, url: options.url,
title: options.title title: options.title
}); });
var link = $('<a>', { var link = document.createElement('a');
href: url link.href = url;
});
this.cloneDataAttrs(widget, link); this.cloneDataAttrs(widget, link);
widget.replaceWith(link); widget.parentNode.insertBefore(link, widget);
widget.parentNode.removeChild(widget);
this.widget = widget = link; this.widget = widget = link;
} }
else { else {
widget.on('click', $.proxy(this.click, this)); addListener(widget, 'click', function() { self.click() });
} }
widget.removeClass(this.service); widget.className = widget.className.replace(' '+this.service, '') + ' '+this.getElementClassNames('widget');
widget.addClass(this.getElementClassNames('widget'));
// Icon // Icon
button.prepend($('<span>', {'class': this.getElementClassNames('icon')})); var s = document.createElement('span');
s.className = this.getElementClassNames('icon');
button.children.length ? button.insertBefore(s, button.firstChild) : button.appendChild(s);
widget.empty().append(button); widget.innerHTML = '';
widget.appendChild(button);
this.button = button; this.button = button;
}, },
@ -470,19 +520,17 @@
counterUrl: this.options.counterUrl, counterUrl: this.options.counterUrl,
forceUpdate: this.options.forceUpdate forceUpdate: this.options.forceUpdate
}; };
counters.fetch(this.service, this.options.url, extraOptions) var self = this;
.always($.proxy(this.updateCounter, this)); var r = counters.fetch(this.service, this.options.url, extraOptions);
r.reject = r.resolve = function(n) { self.updateCounter(n) };
} }
} }
}, },
cloneDataAttrs: function(source, destination) { cloneDataAttrs: function(source, destination) {
var data = source.data(); for (var i = 0; i < source.attributes.length; i++)
for (var key in data) { if (source.attributes[i].name.substr(0, 5) == 'data-')
if (data.hasOwnProperty(key)) { destination.setAttribute(source.attributes[i].name, source.attributes[i].value);
destination.data(key, data[key]);
}
}
}, },
getElementClassNames: function(elem) { getElementClassNames: function(elem) {
@ -492,24 +540,23 @@
updateCounter: function(number) { updateCounter: function(number) {
number = parseInt(number, 10) || 0; number = parseInt(number, 10) || 0;
var params = { var counterElem = document.createElement('span');
'class': this.getElementClassNames('counter'),
'text': number
};
if (!number && !this.options.zeroes) { if (!number && !this.options.zeroes) {
params['class'] += ' ' + prefix + '__counter_empty'; counterElem.className = this.getElementClassNames('counter') + ' ' + prefix + '__counter_empty';
params.text = ''; } else {
counterElem.innerHTML = number;
counterElem.className = this.getElementClassNames('counter');
} }
var counterElem = $('<span>', params); this.widget.appendChild(counterElem);
this.widget.append(counterElem);
this.widget.trigger('counter.' + prefix, [this.service, number]); var e = this.widget['on_counter.'+prefix];
if (e) e([this.service, number]);
}, },
click: function(e) { click: function(e) {
var options = this.options; var options = this.options;
var process = true; var process = true;
if ($.isFunction(options.click)) { if (typeof options.click == 'function') {
process = options.click.call(this, e); process = options.click.call(this, e);
} }
if (process) { if (process) {
@ -527,10 +574,17 @@
}, },
addAdditionalParamsToUrl: function(url) { addAdditionalParamsToUrl: function(url) {
var params = $.param($.extend(this.widget.data(), this.options.data)); var params = dataToOptions(this.widget);
if ($.isEmptyObject(params)) return url; for (var i in this.options.data)
var glue = url.indexOf('?') === -1 ? '?' : '&'; params[i] = this.options.data[i];
return url + glue + params; var s = '';
for (var i in params)
s += '&'+encodeURIComponent(i)+'='+encodeURIComponent(params[i]);
if (!s)
return url;
if (!url.indexOf('?'))
s = '?'+s.substr(1);
return url + s;
}, },
openPopup: function(url, params) { openPopup: function(url, params) {
@ -544,12 +598,15 @@
'width=' + params.width + ',height=' + params.height + ',personalbar=0,toolbar=0,scrollbars=1,resizable=1'); 'width=' + params.width + ',height=' + params.height + ',personalbar=0,toolbar=0,scrollbars=1,resizable=1');
if (win) { if (win) {
win.focus(); win.focus();
this.widget.trigger('popup_opened.' + prefix, [this.service, win]); var e = this.widget['on_popup_opened.' + prefix];
var timer = setInterval($.proxy(function() { if (e) e([this.service, win]);
var self = this;
var timer = setInterval(function() {
if (!win.closed) return; if (!win.closed) return;
clearInterval(timer); clearInterval(timer);
this.widget.trigger('popup_closed.' + prefix, this.service); var e = self.widget['on_popup_closed.' + prefix];
}, this), this.options.popupCheckInterval); if (e) e(self.service);
}, this.options.popupCheckInterval);
} }
else { else {
location.href = url; location.href = url;
@ -562,18 +619,25 @@
* Helpers * Helpers
*/ */
// Camelize data-attributes // Camelize data-attributes
function dataToOptions(elem) { function dataToOptions(elem, nocamel)
function upper(m, l) { {
function upper(m, l)
{
return l.toUpper(); return l.toUpper();
} }
var options = {}; var options = {};
var data = elem.data(); for (var i = 0; i < elem.attributes.length; i++)
for (var key in data) { {
var value = data[key]; var key = elem.attributes[i].name;
if (value === 'yes') value = true; if (key.substr(0, 5) == 'data-')
else if (value === 'no') value = false; {
options[key.replace(/-(\w)/g, upper)] = value; key = key.substr(5);
var value = elem.attributes[i].value;
if (value === 'yes') value = true;
else if (value === 'no') value = false;
options[nocamel ? key : key.replace(/-(\w)/g, upper)] = value;
}
} }
return options; return options;
} }
@ -594,44 +658,53 @@
return cls + ' ' + cls + '_' + mod; return cls + ' ' + cls + '_' + mod;
} }
function closeOnClick(elem, callback) { function closeOnClick(elem, callback)
function handler(e) { {
if ((e.type === 'keydown' && e.which !== 27) || $(e.target).closest(elem).length) return; function handler(e)
elem.removeClass(openClass); {
doc.off(events, handler); if (e.type === 'keydown' && e.which !== 27)
if ($.isFunction(callback)) callback(); return;
for (var i = e; i && i != elem; i = i.parentNode) {}
if (i == elem)
return;
hasClass(elem, openClass, true);
removeListener(document, 'click', handler);
removeListener(document, 'touchstart', handler);
removeListener(document, 'keydown', handler);
callback();
} }
var doc = $(document);
var events = 'click touchstart keydown'; var events = 'click touchstart keydown';
doc.on(events, handler); addListener(document, 'click', handler);
addListener(document, 'touchstart', handler);
addListener(document, 'keydown', handler);
} }
function showInViewport(elem) { function showInViewport(elem) {
var offset = 10; var offset = 10;
if (document.documentElement.getBoundingClientRect) { if (document.documentElement.getBoundingClientRect) {
var left = parseInt(elem.css('left'), 10); var left = parseInt(elem.style.left, 10);
var top = parseInt(elem.css('top'), 10); var top = parseInt(elem.style.top, 10);
var rect = elem[0].getBoundingClientRect(); var rect = elem[0].getBoundingClientRect();
if (rect.left < offset) if (rect.left < offset)
elem.css('left', offset - rect.left + left); elem.stype.left = (offset - rect.left + left)+'px';
else if (rect.right > window.innerWidth - offset) else if (rect.right > window.innerWidth - offset)
elem.css('left', window.innerWidth - rect.right - offset + left); elem.style.left = (window.innerWidth - rect.right - offset + left)+'px';
if (rect.top < offset) if (rect.top < offset)
elem.css('top', offset - rect.top + top); elem.style.top = (offset - rect.top + top)+'px';
else if (rect.bottom > window.innerHeight - offset) else if (rect.bottom > window.innerHeight - offset)
elem.css('top', window.innerHeight - rect.bottom - offset + top); elem.style.top = (window.innerHeight - rect.bottom - offset + top)+'px';
} }
elem.addClass(openClass); elem.className += ' '+openClass;
} }
/** /**
* Auto initialization * Auto initialization
*/ */
$(function() { onDomReady(function() {
$('.' + prefix).socialLikes(); var es = document.querySelectorAll('.' + prefix);
for (var i = 0; i < es.length; i++)
socialLikes(es[i]);
}); });
})();
}));