Last active
December 27, 2015 08:39
-
-
Save jimmynotjim/7297939 to your computer and use it in GitHub Desktop.
Social Deck Source
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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/ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
* 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)); |
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
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:
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.:
Each 'module' could expose a few basic methods like
init()
andrender()
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:
Know what I mean? Is that sort of what you're after?