Skip to content

Instantly share code, notes, and snippets.

@ilumin
Created April 12, 2013 03:05
Show Gist options
  • Save ilumin/5369004 to your computer and use it in GitHub Desktop.
Save ilumin/5369004 to your computer and use it in GitHub Desktop.
A CodePen by Tim Pietrusky. Kudos Please - A simple kudos widget without any external lib and it works with touch & mouse devices. Inspired by [dcurt.is](http://dcurt.is/).
<div class="kudos" data-amount="0" data-url="codepen.io/TimPietrusky/pen/acBCf"></div>
<footer>
2013 by
<a href="http://twitter.com/TimPietrusky" target="_blank">@TimPietrusky</a>
</footer>
/**
Kudos Please
A simple kudos widget without any external lib and it works
with touch & mouse devices.
Inspired by dcurt.is
The heart in this example is served by weloveiconfonts.com,
but you can just add a value manually (.finish:before{content:''}).
# 2013 by Tim Pietrusky
# timpietrusky.com
**/
KudosPlease = (function() {
var _$;
// Constructor
function KudosPlease(args) {
_$ = this;
// All widgets
this.elements = document.querySelectorAll(args.el);
// Set the status
this.status = args.status;
// Is localStorage enabled?
this.persistent = (args.persistent != undefined && args.persistent && localStorage != undefined);
// Duration of activation
this.duration = args.duration;
// setTimeout-ID's
this.timer = {};
// @TODO [TimPietrusky] - This should be an array
this.currentStatus = '';
for (var i = 0; i < this.elements.length; i++) {
var el = this.elements[i];
// Delete all elements from localStorage
// localStorage.setItem('kudos:saved:'+el.getAttribute('data-url'), 0);
// Identify element
el.setAttribute('data-id', i);
// Load kudos via ajax
_$.request(el, 'GET');
// Amount is 0
if (this.loadAmount(i) == 0) {
// Set kudos amount
el.setAttribute('data-amount', 0);
// Init timer id
this.timer[i] = -1;
// Events
if (this.isTouch()) {
this.on(el, 'touchstart', this.enter);
this.on(el, 'touchend', this.out);
} else {
this.on(el, 'mouseover', this.enter);
this.on(el, 'mouseout', this.out);
}
// Load the amount and display it, because user already voted
} else {
this.finish(el);
}
}
};
/*
* Enter the element
*/
KudosPlease.prototype.enter = function(e) {
var that = this,
id = -1;
// Do the kudo twist
if (!_$.hasClass(this, 'finish')) {
// Activate the kudo twist
_$.addClass(that, 'active');
// Start timeout
id = setTimeout(function() {
_$.removeClass(that, 'active');
_$.finish(that, true);
}, _$.duration);
// Add timeout id to global object
_$.timer[that.getAttribute('data-id')] = id;
}
};
// Leave the element
KudosPlease.prototype.out = function(e) {
if (!_$.hasClass(this, 'finish')) {
_$.removeClass(this, 'active');
clearTimeout(_$.timer[this.getAttribute('data-id')]);
}
};
/*
* State: finished (kudos given)
*/
KudosPlease.prototype.finish = function(el, increase) {
// Finished
_$.addClass(el, 'finish');
_$.changeStatus(el, 'gamma');
increase = increase || false;
amount = _$.loadAmount(parseInt(el.getAttribute('data-id'), 10));
if (increase) {
++amount;
// Update kudos via ajax
_$.request(el, 'POST');
}
};
/*
* Change the status of the widget and
* aply 3 different classes for the icon
* in the middle.
*/
KudosPlease.prototype.changeStatus = function(el, state) {
if (_$.status != undefined) {
_$.removeClass(el, _$.currentStatus);
_$.addClass(el, _$.status[state]);
_$.currentStatus = _$.status[state];
}
};
/**
* Helper functions
*/
/*
* Bind event
*/
KudosPlease.prototype.on = function(el, event, func) {
try {
el.addEventListener(event, func, false);
} catch(e) {
el.attachEvent('on' + event, func);
}
};
/*
* Add <CODE>class</CODE> to <CODE>el</CODE>
*/
KudosPlease.prototype.addClass = function(el, classes) {
classes = classes.split(',');
for (var i=0; i < classes.length; i++) {
if (el.className.indexOf(classes[i]) == -1) {
el.className = el.className.trim() + ' ' + classes[i];
}
}
};
/*
* Remove <CODE>class</CODE> to <CODE>el</CODE>
*/
KudosPlease.prototype.removeClass = function(el, classes) {
classes = classes.split(',');
for (var i = 0; i < classes.length; i++) {
el.className = el.className.replace(classes[i], '').trim();
}
};
/*
* Returns <CODE>true</CODE> if <CODE>el</CODE> has
* the <CODE>class</CODE>, <CODE>false</CODE> otherwise
*/
KudosPlease.prototype.hasClass = function(el, className) {
var classes = el.className.split(' '),
result = false;
for (var i = 0; i < classes.length; i++) {
if (classes[i] == className) {
result = true;
}
}
return result;
};
/*
* Returns <CODE>true</CODE> if the actual
* device is a touch device, <CODE>false</CODE> otherwise
*
* http://stackoverflow.com/a/4819886/1012875
*/
KudosPlease.prototype.isTouch = function() {
return !!('ontouchstart' in window)
|| !!('onmsgesturechange' in window);
};
/*
* Saves the amount of a specific widget into localStorage
* when <CODE>persistent</CODE> is <CODE>true</CODE>.
*/
KudosPlease.prototype.save = function(el, amount) {
if (_$.persistent) {
localStorage.setItem('kudos:saved:' + el.getAttribute('data-url'), amount);
}
};
/*
* Loads the amount of a specific widget from the localStorage
* when <CODE>persistent</CODE> is <CODE>true</CODE>.
*/
KudosPlease.prototype.loadAmount = function(id) {
var result = _$.elements[id].getAttribute('data-amount') || 0;
if (_$.persistent) {
if ((amount = localStorage.getItem('kudos:saved:' + _$.elements[id].getAttribute('data-url'))) != null) {
result = amount;
}
}
return result;
};
/*
* Create a ajax request to a backend
* which just keeps track of the kudos counter
* via php & mysql
*/
KudosPlease.prototype.request = function(el, type) {
var xhr;
// Initialize
try {
xhr = new ActiveXObject("Microsoft.XMLHTTP");
} catch(e) {
xhr = new XMLHttpRequest();
}
// Change the amount
xhr.onreadystatechange = function() {
if (xhr.readyState == 4 && xhr.status == 200) {
var amount = xhr.responseText;
el.setAttribute('data-amount', amount);
if (type == 'GET') {
_$.changeStatus(el, amount == 0 ? 'alpha' : 'beta');
if (_$.persistent
&& localStorage.getItem('kudos:saved:' + el.getAttribute('data-url')) != null) {
_$.changeStatus(el, 'gamma');
}
}
if (type == 'POST') {
_$.save(el, amount);
}
}
}
var url = "?url="+encodeURIComponent(el.getAttribute('data-url'));
// Open request
xhr.open(type, "http://api.kudosplease.com/" + url, true);
xhr.send();
};
// trim polyfill
''.trim || (String.prototype.trim = function(){
return this.replace(/^\s+|\s+$/g,'');
});
return KudosPlease;
})();
/*
* DOM ready function
* http://dustindiaz.com/smallest-domready-ever
*/
function r(f){/in/.test(document.readyState)?setTimeout('r('+f+')',9):f()}
r(function() {
/*
* Create Kudos Please widget
*/
var kudosPlease = new KudosPlease({
el : '.kudos',
duration : 1500,
persistent : false,
status : {
alpha : 'fontawesome-star',
beta : 'fontawesome-glass',
gamma : 'fontawesome-bolt'
}
});
});
@import "compass";
@import url(http://weloveiconfonts.com/api/?family=fontawesome);
/*
* Use FontAwesome from weloveiconfonts.com
*/
[class*="fontawesome-"]:before {
font-family: 'FontAwesome', sans-serif;
font-weight:normal;
}
/*
* Default styles
*/
body {
font: 1.5em sans-serif;
margin:.5em;
text-align:center;
}
footer {
margin:3.75em 0 0 0;
font-size:.65em;
a {
color:rgba(#000, .7);
text-decoration:none;
&:hover {
color:#000;
}
}
}
/*
* Kudos
*/
$kudos_duration: 1.5s;
$kudos_duration_finish: .45s;
$kudos_width: 6em;
$kudos_height: 6em;
$kudos_color_alpha: #fff; // default
$kudos_color_beta: #cc3d39; // active
$kudos_color_gamma: #000; // default border
.kudos {
position:relative;
width:$kudos_width;
height:$kudos_height;
margin:0 auto;
background:$kudos_color_alpha;
box-shadow:
inset 0 0 0 .25em $kudos_color_gamma,
inset 0 0 0 $kudos_width / 3 $kudos_color_alpha,
inset 0 0 0 $kudos_width #000
;
line-height:$kudos_height;
text-align:center;
border-radius:50%;
@include transition(box-shadow $kudos_duration_finish / 2 ease-out);
&:before {
@include transition(font-size $kudos_duration_finish ease-in);
font-size:1.75em;
color:$kudos_color_alpha;
line-height:$kudos_height / 1.725;
}
&.active {
@include transition(box-shadow $kudos_duration linear);
box-shadow:
inset 0 0 0 .25em $kudos_color_gamma,
inset 0 0 0 0 $kudos_color_alpha,
inset 0 0 0 .75em rgba($kudos_color_beta, .75),
inset 0 0 0 $kudos_width $kudos_color_beta
;
&:before {
@include transition(color $kudos_duration linear);
color: $kudos-color-beta;
}
&:after {
content: 'Don\'t move!';
}
}
&.finish {
@include transition(
box-shadow $kudos_duration_finish linear,
transform $kudos_duration_finish * 1.25 ease-in-out
);
box-shadow:
inset 0 0 0 .25em rgba($kudos_color_beta, .5),
inset 0 0 0 .5em $kudos_color_alpha,
inset 0 0 0 .75em rgba($kudos_color_beta, .75),
inset 0 0 0 1em $kudos_color_alpha,
inset 0 0 0 0 $kudos_color_alpha,
inset 0 0 0 $kudos_width $kudos_color_beta
;
&:before {
font-size:2.25em;
color:$kudos_color_alpha;
line-height:$kudos_height / 2.125;
}
}
&:after {
position:absolute;
content: attr(data-amount) ' Kudos';
bottom:-1.25em;
left:0;
width:$kudos_width;
text-align:center;
line-height:1em;
font-variant:small-caps;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment