diff --git a/src/social-likes.js b/src/social-likes.js
index 73cf4bb..c107dc1 100644
--- a/src/social-likes.js
+++ b/src/social-likes.js
@@ -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($('
', {'class': prefix + '_single-w'}));
- container.wrapInner($('
', {'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 = $('
', {
- 'class': getElementClassNames('widget', 'single')
- });
- var button = $(template(
- '
' +
- '' +
- '{title}' +
- '
',
- {
- 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 = '
'+
+ this.options.singleTitle+'
';
+ 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 = $('
', {
- '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 = $('', {
- '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 = $('', {
- 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($('', {'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 = $('', 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]);
});
-
-}));
+})();