octocatstartv

Pen Settings

CSS Base

Vendor Prefixing

Add External CSS

These stylesheets will be added in this order and before the code you write in the CSS editor. You can also add another Pen here, and it will pull the CSS from it. Try typing "font" or "ribbon" below.

Quick-add: + add another resource

Add External JavaScript

These scripts will run in this order and before the code in the JavaScript editor. You can also link to another Pen here, and it will run the JavaScript from it. Also try typing the name of any popular library.

Quick-add: + add another resource

Code Indentation

     

Save Automatically?

If active, Pens will autosave every 30 seconds after being saved once.

Auto-Updating Preview

If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.

            
              <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>
            
          
!
            
              @import "compass/css3";

@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-in-out $kudos_duration_finish / 4);
  
  &: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;
  }
}
            
          
!
            
               /**
  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'
    }
  });
});
            
          
!
999px
Close

Asset uploading is a PRO feature.

As a PRO member, you can drag-and-drop upload files here to use as resources. Images, Libraries, JSON data... anything you want. You can even edit them anytime, like any other code on CodePen.

Go PRO

Loading ..................

Console