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