Skip to content

Instantly share code, notes, and snippets.

@jimmynotjim
Last active December 27, 2015 08:39
Show Gist options
  • Save jimmynotjim/7297939 to your computer and use it in GitHub Desktop.
Save jimmynotjim/7297939 to your computer and use it in GitHub Desktop.
Social Deck Source
I'm working on these social "cards" for my personal site and decided it'd me a good learning lesson to create a plugin for them. This is still a work in progress, but you can see where I'm going with it. I want to move the network related settings and functions into their own extension and keep just the core intact. I think I need to move the default settings and whatever global function I need out to the methods object. Not sure if that's correct though.
This an even cruder version, but let's you visually see what I'm doing http://jsfiddle.net/jimmynotjim/2Kg3g/embedded/result/
* Social Deck
*
*
* Copyright (c) 2013 James Wilson
* Licensed under the MIT license.
*/
(function ($) {
// Truncate text and append ellipsis
var trunc = function(string, n) {
return (string.length > n) ? string.substr(0, n - 1) + '…' : string;
};
var str_to_title = function(string) {
return string.replace(/\w\S*/g, function(txt){ return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase(); });
};
// Return the date in long form for printing
var format_date = function(date) {
var m_names = new Array('January', 'February', 'March',
'April', 'May', 'June', 'July', 'August', 'September',
'October', 'November', 'December');
var d = new Date(date);
var curr_date = d.getDate();
var curr_month = d.getMonth();
var curr_year = d.getFullYear();
var sup = '';
if (curr_date === 1 || curr_date === 21 || curr_date === 31) { sup = 'st'; }
else if (curr_date === 2 || curr_date === 22) { sup = 'nd'; }
else if (curr_date === 3 || curr_date === 23) { sup = 'rd'; }
else { sup = 'th'; }
return m_names[curr_month] + ' ' + curr_date + sup + ', ' + curr_year;
};
// Fetch the network data
var fetch_data = function($obj, module, url) {
var settings = $obj.data('socialdeck');
$.ajax({
dataType : 'json',
url : url,
success : function(data) {
var total_modules = settings.total_modules;
var saved_modules = settings.saved_modules;
save_module_data($obj, data, module);
// Update and save the number of saved modules so far
saved_modules = saved_modules + 1;
settings = $.extend(settings, {saved_modules: saved_modules});
$obj.data('socialdeck', settings);
// if we've finished getting all modules data, render the modules
if (total_modules === saved_modules) {
render_modules($obj);
}
}
});
};
// Fetch and normalize data for each module
var setup_modules = function($obj) {
var settings = $obj.data('socialdeck');
var user_name = settings.user_name;
var network = settings.network;
var net_settings = settings.networks[network];
var modules = settings.modules;
var i = 0;
var l = modules.length;
settings = $.extend(settings, {total_modules : l, saved_modules: 0});
$obj.data('socialdeck', settings);
for (i; i < l; i++) {
var module = modules[i];
var url = net_settings.api_urls[module] + user_name;
fetch_data($obj, module, url);
}
};
// Render the saved data to the initialized object
var render_modules = function($obj) {
var settings = $obj.data('socialdeck');
var network = settings.network;
var net_settings = settings.networks[network];
var module_data = net_settings.module_data;
var modules = settings.modules;
var i = 0;
var l = modules.length;
var $heading = $('<h3 />', {'class': 'social-card__network-name'}).text( str_to_title(network) );
$obj.append($heading);
for (i; i < l; i++) {
var module = modules[i];
if (network === 'github') {
if (module === 'user') {
$obj.append( render_gh_user(module_data.user) );
} else if (module === 'contribs') {
$obj.append( render_gh_contribs(module_data.contribs) );
} else if (module === 'activity') {
$obj.append( render_gh_activity(module_data.activity) );
}
}
}
};
// Render for the GH user module
var render_gh_user = function(data) {
var $user_details = $('<div />', {'class': 'social-card__user'});
var $user_name = $('<a />', {'href': data.user_url, 'class': 'social-card__user-name'}).text('@' + data.user_name);
var $user_loc = $('<span />', {'class': 'social-card__user-location'}).text(data.user_loc);
var $user_stats = $('<ul />', {'class': 'social-card__user-stats'});
var user_repos = data.user_details.repos;
var user_followers = data.user_details.followers;
var user_following = data.user_details.following;
var $repo_item = $('<li />')
.append( $('<a />', {'href': user_repos.url})
.append( $('<span />', {'class': 'user-stat__total'}).text(user_repos.total) )
.append( $('<span />', {'class': 'user-stat__label'}).text(' Repos') )
);
var $followers_item = $('<li />')
.append( $('<a />', {'href': user_followers.url})
.append( $('<span />', {'class': 'user-stat__total'}).text(user_followers.total) )
.append( $('<span />', {'class': 'user-stat__label'}).text(' Followers') )
);
var $following_item = $('<li />')
.append( $('<a />', {'href': user_following.url})
.append( $('<span />', {'class': 'user-stat__total'}).text(user_following.total) )
.append( $('<span />', {'class': 'user-stat__label'}).text(' Following') )
);
$user_stats
.append($repo_item)
.append($followers_item)
.append($following_item);
$user_details
.append($user_name)
.append($user_loc)
.append($user_stats);
return $user_details;
};
// Render for the GH contribs module
var render_gh_contribs = function(data) {
var $contrib_list = $('<ul />', {'class': 'social-card__contribs'});
var i = 0;
var l = data.length;
for (i; i < l; i++) {
var this_contrib = data[i];
var $item = $('<li />', {'class': 'rating-' + this_contrib.rating + ' js-contrib-link'});
var $link = $('<a />', {'href': this_contrib.url}).text(this_contrib.rating);
var $tooltip = $('<span />', {'class': 'contribution-tooltip'})
.html('<strong>' + this_contrib.total + ' contributions</strong> on ' + this_contrib.print_date);
$item
.append($link)
.append($tooltip);
$contrib_list
.append($item);
}
return $contrib_list;
};
// Render the GH events module
var render_gh_activity = function(data) {
var $activity_list = $('<ul />', {'class': 'social-card__activity'});
var i = 0;
var l = data.length;
for (i; i < l; i++) {
var this_event = data[i];
var $item = $('<li />')
.append( $('<span />', {'class': 'time-ago'}).text(this_event.time_ago) )
.append( $('<h3 />').html(this_event.title_html) )
.append(this_event.addl_details);
$activity_list
.append($item);
}
return $activity_list;
};
// Organize and save the returned data
var save_module_data = function($obj, data, module) {
var settings = $obj.data('socialdeck');
var network = settings.network;
var module_data = settings.networks[network].module_data;
if (network === 'github') {
if (module === 'user') {
module_data.user = parse_gh_user(data.user.data);
} else if (module === 'contribs') {
module_data.contribs = parse_gh_contribs(data.contrib.data, $obj);
} else if (module === 'activity') {
module_data.activity = parse_gh_activity(data.events.data, $obj);
}
}
settings = $.extend(settings, {module_data: module_data});
$obj.data('socialdeck', settings);
};
// Parse and organize the GH user data
var parse_gh_user = function(data) {
var this_user = data;
var user = {
user_name : this_user.login,
user_id : this_user.id,
user_url : this_user.html_url,
user_loc : this_user.location,
user_details : {
repos : {
total : this_user.public_repos,
url : this_user.html_url + '?tab=repositories'
},
followers : {
total : this_user.followers,
url : this_user.html_url + '/followers'
},
following : {
total : this_user.following,
url : this_user.html_url + '/following'
}
}
};
return user;
};
//Parse and organize the GH contribs data
var parse_gh_contribs = function(data, $obj) {
var contribs = [];
var this_user = $obj.data('socialdeck').user_name;
var this_month = data.slice( Math.max(data.length - 30, 0) );
var i = 0;
var l = this_month.length;
for (i; i < l; i++) {
var rating = 0;
var total = this_month[i][1];
var date = this_month[i][0];
var url_date = date.replace(/\//g, '-');
var print_date = format_date(date);
var url = 'https://github.com/' + this_user + '?tab=contributions&from=' + url_date + '#contribution-activity';
if (total > 0 && total < 4) {
rating = 1;
}
else if (total >= 4 && total < 10) {
rating = 2;
}
else if (total >= 10 && total < 18) {
rating = 3;
}
else if (total >= 18) {
rating = 4;
}
var item = {
total : total,
rating : rating,
url : url,
date : date,
print_date : print_date
};
contribs.push(item);
}
return contribs;
};
// Parse and organize the GH activity data
var parse_gh_activity = function(data, $obj) {
var events = [];
var returned_items = $obj.data('socialdeck').returned_items;
var this_user = $obj.data('socialdeck').user_name;
var these_events = data.slice(0, returned_items);
var i = 0;
var l = these_events.length;
for (i; i < l; i++) {
var gh_action, user_action, event_url, event_text, event_html, icon_type, addl_details, this_issue, issue_number, commit_sha, commit_id;
var this_event = these_events[i];
var type = this_event.type;
var repo_name = this_event.repo.name;
var repo_url = this_event.repo.url.replace('api.', '');
repo_url = repo_url.replace('repos/', '');
var time = this_event.created_at;
// var time_ago = $.timeago(time);
if (type === 'FollowEvent') {
icon_type = 'follow';
user_action = 'started following';
event_url = this_event.payload.target.html_url;
event_text = this_event.payload.target.name;
event_html = ' <a href="' + event_url + '">' + event_text + '</a>';
addl_details = '';
}
else if (type === 'WatchEvent') {
icon_type = 'star';
user_action = 'starred';
event_url = repo_url;
event_text = repo_name;
event_html = ' <a href="' + event_url + '">' + event_text + '</a>';
addl_details = '';
}
else if (type === 'IssueCommentEvent' || type === 'CommitCommentEvent') {
var this_comment = this_event.payload.comment;
var comment_text = trunc(this_comment.body, 140);
commit_sha = this_comment.commit_id;
commit_id = (commit_sha) ? commit_sha.substr(0, 7) : null;
this_issue = this_event.payload.issue;
issue_number = (this_issue) ? this_issue.number : null;
var is_pr = (this_issue) ? this_issue.pull_request.diff_url : null;
var issue_type = (is_pr === null) ? 'issue' : 'pull request';
icon_type = 'comment';
user_action = (type === 'IssueCommentEvent') ? 'commented on ' + issue_type : 'commented on commit';
event_url = this_comment.html_url;
event_text = (type === 'IssueCommentEvent') ? repo_name + '#' + issue_number : repo_name + '@' + commit_id;
event_html = ' <a href="' + event_url + '">' + event_text + '</a>';
addl_details = '<p>' + comment_text + '</p>';
}
else if (type === 'ReleaseEvent') {
var this_release = this_event.payload.release;
var tag_name = this_release.tag_name;
var zip_url = repo_url + '/zipball/' + tag_name;
icon_type = 'release';
event_url = repo_url;
event_text = repo_name;
event_html = ' <a href="' + event_url + '">' + event_text + '</a>';
user_action = 'released <a href="' + event_url + '/releases/tag/v2.0.2">' + tag_name + '</a> at';
addl_details = '<a href="' + zip_url + '" class="zipball">Source Code (zip)</a>';
}
else if (type === 'PushEvent') {
var this_push = this_event.payload;
var push_ref = (this_push.ref).replace('refs/heads/', '');
var push_commits = this_event.payload.commits;
var $commit_list = $('<ul />', {'class': 'commit-list'});
var $commit_item = $('<li />');
var ci = 0;
var cl = push_commits.length;
for (ci; ci < cl; ci++) {
if (ci > 2) { break; }
var this_commit = push_commits[ci];
commit_sha = this_commit.sha;
commit_id = commit_sha.substr(0, 7);
var commit_messages = (this_commit.message).split('\n');
var commit_message = commit_messages[0];
var commit_url = repo_url + '/commit/' + commit_sha;
$commit_item.append(
$('<code />', {'class': 'commit-id'}).html('<a href="' + commit_url + '">' + commit_id + '</a>')
)
.append(
$('<blockquote />', {'class': 'commit-message'}).text(commit_message)
);
$commit_list.append($commit_item);
}
if (cl > 3) {
var start_id = push_commits[0].sha.substr(0, 7);
var end_id = push_commits[cl - 1].sha.substr(0, 7);
$commit_item.append(
$('<a />', {
'class': 'more-commits',
'href': repo_url + '/compare/' + start_id + '...' + end_id
}).text('more')
);
$commit_list.append($commit_item);
}
icon_type = 'push';
event_url = repo_url;
event_text = repo_name;
event_html = ' <a href="' + event_url + '">' + event_text + '</a>';
user_action = 'pushed to <a href="' + event_url + '/tree/' + push_ref + '">' + push_ref + '</a> at';
addl_details = '';
addl_details = $commit_list.prop('outerHTML');
}
else if (type === 'CreateEvent' || type === 'DeleteEvent') {
var this_ref = this_event.payload;
var ref_type = this_ref.ref_type;
var ref_text = (ref_type === 'tag' && type === 'CreateEvent') ? ' <a href="#">' + this_ref.ref + '</a>' : ' ' + this_ref.ref;
icon_type = ref_type;
user_action = (type === 'CreateEvent') ? 'created ' : 'deleted ';
user_action = (ref_type === 'repository') ? user_action + 'repository' : user_action + ref_type + ref_text + ' at';
event_url = repo_url;
event_text = repo_name;
event_html = ' <a href="' + event_url + '">' + event_text + '</a>';
addl_details = '';
}
else if (type === 'PullRequestEvent') {
var this_pr = this_event.payload.pull_request;
gh_action = (this_pr.merged) ? 'merged' : this_event.payload.action;
var pr_number = this_pr.number;
var pr_text = this_pr.title;
var pr_commits = this_pr.commits;
var commit_text = (pr_commits > 1) ? 'commits' : 'commit';
icon_type = 'pull_request';
user_action = gh_action + ' pull request';
event_url = this_pr.html_url;
event_text = repo_name + '#' + pr_number;
event_html = ' <a href="' + event_url + '">' + event_text + '</a>';
addl_details = '<p>' + pr_text + '</p>\n' +
'<p class="pull-info">' + pr_commits + ' ' + commit_text + ' with ' + this_pr.additions + ' addition and ' + this_pr.deletions + ' deletions</p>';
}
else if (type === 'ForkEvent') {
var this_fork = this_event.payload.forkee;
var dest_url = this_fork.html_url;
var dest_text = this_fork.full_name;
icon_type = 'fork';
user_action = 'forked';
event_url = repo_url;
event_text = repo_name;
event_html = ' <a href="' + event_url + '">' + event_text + '</a> to ' + '<a href="' + dest_url + '">' + dest_text + '</a>';
addl_details = '';
}
else if (type === 'IssuesEvent') {
gh_action = this_event.payload.action;
this_issue = this_event.payload.issue;
issue_number = this_issue.number;
icon_type = 'issue';
user_action = gh_action + ' issue';
event_url = this_issue.html_url;
event_text = repo_name + '#' + issue_number;
event_html = ' <a href="' + event_url + '">' + event_text + '</a>';
addl_details = '';
}
else {
icon_type = '';
addl_details = '';
}
var event = {
icon_type : icon_type,
time_ago : time,
title_html : this_user + ' ' + user_action + event_html,
addl_details : addl_details
};
events.push(event);
}
return events;
};
var methods = {
init: function(options) {
return this.each(function () {
var $this = $(this);
// Any existing settings
var settings = $this.data('socialdeck');
if (settings === undefined) {
// If there's no existing settings, set defaults
var defaults = {
user_name : null,
returned_items : 4,
network : null,
networks : {
github: {
api_urls : {
all : 'http://jimmynotjim.com/gh-data/?api=all&user=',
user : 'http://jimmynotjim.com/gh-data/?api=all&user=',
contribs : 'http://jimmynotjim.com/gh-data/?api=all&user=',
activity : 'http://jimmynotjim.com/gh-data/?api=all&user=',
repos : 'http://jimmynotjim.com/gh-data/?api=all&user=',
repos_pop : 'http://jimmynotjim.com/gh-data/?api=all&user=',
repos_contrib : 'http://jimmynotjim.com/gh-data/?api=all&user='
},
module_data : {},
templates : {
user : 'foo',
contribs : 'foo',
activity : 'foo',
repos : 'foo',
repos_pop : 'foo',
repos_contrib : 'foo'
}
}
},
modules : [
'user'
]
};
// Merge default settings with user defined options
settings = $.extend({}, defaults, options);
// Save the updated settings
$this.data('socialdeck', settings);
} else {
// Merge existing settings with user defined options
settings = $.extend({}, settings, options);
// Save the updated settings
$this.data('socialdeck', settings);
}
console.log($this.data('socialdeck'));
setup_modules($this);
});
},
destroy: function() {
return this.each(function() {
var $this = $(this);
var modules = $this.data('socialdeck').modules;
var i = 0;
var l = modules.length;
// Remove modules
for (i; i < l; i++) {
var module = modules[i];
var $module = $this.find('.social-card__' + module);
$module.remove();
}
// Remove the saved settings
$this.removeData('socialdeck');
});
},
val: function() {
return this.each(function(i) {
var $this = $(this);
return $this.eq(i).html();
});
}
};
$.fn.socialdeck = function () {
var method = arguments[0];
if (methods[method]) {
method = methods[method];
arguments = Array.prototype.slice.call(arguments, 1);
} else if (typeof(method) === 'object' || !method) {
method = methods.init;
} else {
$.error( 'Method ' + method + ' does not exist on jQuery.socialdeck' );
return this;
}
return method.apply(this, arguments);
};
}(jQuery));
@cz
Copy link

cz commented Nov 4, 2013

I don't think I fully understand what the intended usage is here, but it seems like passing method names to the plugin would be less useful than being able to do something like:

$('.gh-div').socialdeck('github');
$('.tw-div').socialdeck('twitter');

Totally guessing on what you're after here, but the plugin could have a method that registers a new 'network' or 'module' and accepts an object with its default settings, methods, etc.:

 $.fn.socialdeck.register_module = function(name, module) {};

Each 'module' could expose a few basic methods like init() and render() or whatever, which could be called when needed by the core plugin, and the core itself would only be responsible for some high-level bootstrapping.

So then adding a new module would work something like:

 var fb_module = {
     'init': function() {
     },
     'render': function() {
     }
 };

 $.fn.socialdeck.register('facebook', fb_module);

 $('.fb-div').socialdeck('facebook');

Know what I mean? Is that sort of what you're after?

@jimmynotjim
Copy link
Author

Ah, exactly what I was thinking. This sort of grew out from some half thought through ideas, so I didn't think about building modularly from the beginning (if you look at the code in the fiddle, it's atrocious). I'll hack at it some more next weekend and see what I can do. Appreciate the feedback.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment