mivita/_static/assets/plugins/widget.dribbble/jribbble.js
2018-10-29 09:39:31 +01:00

407 lines
12 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* @preserve
* Jribbble v2.0.3 | Tue May 26 21:23:05 2015 -0400
* Copyright (c) 2015, Tyler Gaw me@tylergaw.com
* Released under the ISC-LICENSE
*/
;(function($, window, document, undefined) {
'use strict';
// This is our public access point.
$.jribbble = {};
var ACCESS_TOKEN = null;
var API_URL = 'https://api.dribbble.com/v1';
// The types of shot lists that are available through the API.
// The default shot listretrieved by shots()is any type.
var SHOT_LIST_TYPES = [
'animated',
'attachments',
'debuts',
'playoffs',
'rebounds',
'teams'
];
// Rather than pepper the code with error messages, we use this object to store
// them. There are also a number of convenience methods here for creating
// common error messages for different resources.
var ERROR_MSGS = {
token: 'Jribbble: Missing Dribbble access token. Set one with ' +
'$.jribbble.accessToken = YOUR_ACCESS_TOKEN. If you do not have an ' +
'access token, you must register a new application at ' +
'https://dribbble.com/account/applications/new',
singular: function(str) {
return str.substr(0, str.length - 1);
},
idRequired: function(resource) {
return 'Jribbble: You have to provide a ' + this.singular(resource) +
' ID. ex: $.jribbble.%@("1234").'.replace(/%@/g, resource);
},
subResource: function(resource) {
return 'Jribbble: You have to provide a ' + this.singular(resource) +
' ID to get %@. ex: $.jribbble.%@("1234").%@()'.replace(/%@/g, resource);
},
// A shot ID is required to get shot sub-resources.
shotId: function(resource) {
return 'Jribbble: You have to provide a shot ID to get %@. ex: ' +
' $.jribbble.shots("1234").%@()'.replace(/%@/g, resource);
},
commentLikes: 'Jribbble: You have to provide a comment ID to get likes. ex: ' +
' $.jribbble.shots("1234").comments("456").likes()'
};
// A number of resources do not allow for bare calls to them, they require a
// resource ID. If the ID is not provided, things will not function so we err.
var checkId = function(id, resource) {
if (!id || typeof id === 'object') {
throw new Error(ERROR_MSGS.idRequired(resource));
} else {
return id;
}
};
// Many top-level resourcesusers,buckets, etchave subresources that all behave
// in a similar way. We use this function to create new methods on the top-level
// object's prototype.
var createSubResources = function(subResources) {
var obj = {};
subResources.forEach(function(resource) {
obj[resource] = subResourceWithOpts.call(this, resource);
}.bind(this));
return obj;
};
// Provide an object of key: value params. Get back a URL encoded string if
// params has keys.
var parseParams = function(params) {
var p = $.param(params);
if (p) {
return '?' + p;
} else {
return '';
}
};
// Because we want our API to be as flexible as possible, there are a number
// of methods where we'll allow different types of arguments to be passed as
// the first argument and then we'll figure out here what the user meant.
var negotiateArgs = function(args) {
// If there's nothing here, just bail, that's OK. Calls like `shots()` are
// fine on their own.
if (args.length !== 0) {
var firstArg = args[0];
var type = typeof firstArg;
var params = {};
// If the first argument is a number or string, we're going to assume that
// the user wants a resource by id or name.
if (type === 'number' || type === 'string') {
var list = SHOT_LIST_TYPES.indexOf(firstArg);
// Shots can be retrieved by the shot name. The Dribbble API wants this
// passed as a query paramter, but as a conveinence, you can pass the
// name of a shot list to shots() and we'll check here if that name
// is a valid shot list name.
if (list > -1) {
params.list = firstArg;
} else {
params.resource = firstArg;
}
// If we see an object as the first parameter, we assume the user is
// providing options to be passed as query parameters.
} else if (type === 'object') {
params = firstArg;
}
return params;
}
};
// All initial Jribbble API methodsshots, buckets, project, etcshare common
// functionality. We mix this base functionality into each one.
var jribbbleBase = function() {
var ext = $.extend({}, $.Deferred());
var Queue = function() {
this.methods = [];
this.response = null;
this.flushed = false;
this.add = function(fn) {
if (this.flushed) {
fn(this.scope);
} else {
this.methods.push(fn);
}
};
this.flush = function(scope) {
if (this.flushed) {
return;
}
this.scope = scope;
this.flushed = true;
while(this.methods[0]) {
this.methods.shift()(scope);
}
return scope;
};
return this;
};
ext.queue = new Queue();
ext.url = API_URL;
ext.get = function() {
if (!ACCESS_TOKEN) {
console.error(ERROR_MSGS.token);
return false;
}
$.ajax({
type: 'GET',
url: this.url,
beforeSend: function(jqxhr) {
jqxhr.setRequestHeader('Authorization', 'Bearer ' + ACCESS_TOKEN);
},
success: function(res) {
this.resolve(res);
}.bind(this),
error: function(jqxhr) {
this.reject(jqxhr);
}.bind(this)
});
return this;
};
return ext;
};
// Because a number of API resources are set up the same way, we can create
// new Jribble API methods for them using currying. This extends the method
// with the JribbbleBase, adds a method to the queue, and sets the needed
// timeout for flushing the queue.
// See jribbble.buckets for example usage.
var resourceWithoutOpts = function(resource) {
return function(resourceId) {
$.extend(this, jribbbleBase());
this.queue.add(function(self) {
self.url += '/' + resource + '/' + resourceId;
});
setTimeout(function() {
this.queue.flush(this).get();
}.bind(this));
return this;
};
};
// Because a number of API resources are set up the same way, we can create
// new Jribble API methods for them using currying. This function returns a
// function that allows for creating URLS like:
// /resource/subresource/?foo=1&bar=2
var subResourceWithOpts = function(resource) {
return function(opts) {
this.queue.add(function(self) {
self.url += '/' + resource + '/' + parseParams(opts || {});
});
return this;
};
};
$.jribbble.shots = function(undefined, opts) {
var shotArgsNegotiated = negotiateArgs([].slice.call(arguments)) || {};
var shotsParams = opts || {};
// Because most shot subresources; likes, projects, buckets, etc. all do
// pretty much the same thing, we can avoid repeating code by using
// currying. For each subresource we call this function and pass it the name
// of the resource, it returns jribbble API method for that resource.
// Yay programming!
var shotSubResource = function(resource) {
return function(undefined, opts) {
var negotiated = negotiateArgs([].slice.call(arguments)) || {};
var params = opts || {};
this.queue.add(function(self) {
if (!self.shotId) {
throw new Error(ERROR_MSGS.shotId(resource));
}
self.url += '/' + resource + '/';
if (negotiated.resource) {
self.url += negotiated.resource;
delete negotiated.resource;
}
self.url += parseParams($.extend(negotiated, params));
});
return this;
};
};
var Shots = function() {
$.extend(this, jribbbleBase());
this.url += '/shots/';
this.queue.add(function(self) {
if (shotArgsNegotiated.resource) {
self.shotId = shotArgsNegotiated.resource;
self.url += shotArgsNegotiated.resource;
delete shotArgsNegotiated.resource;
}
self.url += parseParams($.extend(shotArgsNegotiated, shotsParams));
});
// Jribbble seems to need an async queue, because we need to run the
// server request at the end of the chain, but we will never know how
// long the chain is. This is a super hack way of "waiting" to make sure
// the queue is stocked before we flush it.
setTimeout(function() {
this.queue.flush(this).get();
}.bind(this));
return this;
};
Shots.prototype.attachments = shotSubResource('attachments');
Shots.prototype.buckets = shotSubResource('buckets');
Shots.prototype.likes = shotSubResource('likes');
Shots.prototype.projects = shotSubResource('projects');
Shots.prototype.rebounds = shotSubResource('rebounds');
// Comments is a slightly different subresource because it has it's own
// likes subresource. Comments shares a number of things with the other
// shot subresources, but I haven't been able to figure out how to use
// the shotSubResource currying function here to reduce repitition because
// of the likes subresource.
// I think I could get that to work if I created comments as a new Object
// like comments = new Comments(). Then likes could be added to the
// prototype of the Comments instance?
// TODO: Figure that out.
Shots.prototype.comments = function(undefined, opts) {
var commentsArgsNegotiated = negotiateArgs([].slice.call(arguments)) || {};
var commentsParams = opts || {};
this.queue.add(function(self) {
if (!self.shotId) {
throw new Error(ERROR_MSGS.shotId('comments'));
}
self.url += '/comments/';
// If we're looking for a specific comment by its ID.
if (commentsArgsNegotiated.resource) {
self.commentId = commentsArgsNegotiated.resource;
self.url += commentsArgsNegotiated.resource + '/';
delete commentsArgsNegotiated.resource;
}
self.url += parseParams($.extend(commentsArgsNegotiated, commentsParams));
});
this.likes = function(opts) {
var params = opts || {};
this.queue.add(function(self) {
if (!self.commentId) {
throw new Error(ERROR_MSGS.commentLikes);
}
self.url += 'likes/' + parseParams(params);
});
return this;
};
return this;
};
return new Shots();
};
$.jribbble.teams = function(id) {
var resource = 'teams';
var resourceId = checkId(id, resource);
var Teams = resourceWithoutOpts.call(this, resource);
Teams.prototype = createSubResources.call(this, [
'members',
'shots'
]);
return new Teams(resourceId);
};
$.jribbble.users = function(id) {
var resource = 'users';
var resourceId = checkId(id, resource);
var Users = resourceWithoutOpts.call(this, resource);
Users.prototype = createSubResources.call(this, [
'buckets',
'followers',
'following',
'likes',
'projects',
'shots',
'teams'
]);
Users.prototype.isFollowing = function(targetUser) {
this.queue.add(function(self) {
self.url += '/following/' + targetUser;
});
return this;
};
return new Users(resourceId);
};
$.jribbble.buckets = function(id) {
var resource = 'buckets';
var resourceId = checkId(id, resource);
var Buckets = resourceWithoutOpts.call(this, resource);
Buckets.prototype = createSubResources.call(this, ['shots']);
return new Buckets(resourceId);
};
$.jribbble.projects = function(id) {
var resource = 'projects';
var resourceId = checkId(id, resource);
var Projects = resourceWithoutOpts.call(this, resource);
Projects.prototype = createSubResources.call(this, ['shots']);
return new Projects(resourceId);
};
$.jribbble.setToken = function(token) {
ACCESS_TOKEN = token;
return this;
};
})(jQuery, window , document);