Propchange: incubator/shindig/trunk/php/js/People.js
------------------------------------------------------------------------------
svn:executable = *
Added: incubator/shindig/trunk/php/js/ShindigContainer.js
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/php/js/ShindigContainer.js?rev=606463&view=auto
==============================================================================
--- incubator/shindig/trunk/php/js/ShindigContainer.js (added)
+++ incubator/shindig/trunk/php/js/ShindigContainer.js Sat Dec 22 08:11:57 2007
@@ -0,0 +1,589 @@
+/**
+* @constructor
+*/
+opensocial.ShindigContainer = function() {
+ opensocial.Container.call(this);
+};
+opensocial.ShindigContainer.inherits(opensocial.Container);
+
+Array.prototype.each = function(fun) {
+ for (var cnt=0; cnt<this.length; cnt++) {
+ fun(this[cnt]);
+ };
+};
+/**
+* Initialize the container with the specified state.
+*
+* @param {Object} viewer Person object that corresponds to the viewer.
+* @param {Object} opt_owner Person object that corresponds to the owner.
+* @param {Map<String, String>} opt_friends A map of friends of the viewer,
+* keyed by their Id.
+* @param {Array<Object>} opt_activities An array of activity objects.
+* @param {Map<String, String>} opt_nonFriends A map of contacts who are
+* not friends.
+*/
+opensocial.ShindigContainer.prototype.init = function(appName) {
+ /*
+ if (appName = '/46/o') {
+ this.appName = hashAppName;
+ }
+ else {
+ this.appName = appName;
+ }
+ */
+ if (this.containerInitialized) {
+ return;
+ }
+ this.containerInitialized = true;
+
+
+
+ this.appName = hashAppName;
+
+
+ this.viewerFriends = {};
+ this.viewerActivities = {};
+ this.ownerActivities = {};
+ this.ownerFriends ={};
+
+ //the framework calls this function passing a URL from which to load
the data?
+ //looks like the google.opensocial.Container.get().init("/46/o");
call ends up passing the /46/o as viewer...
+ //interesting -- for us the person that embedded the object is hard to
ascertain, while for social networks profile pages
+ //are a first-class concept... it's going to be tricky to map this in a
non-fragile way
+ //each widget will have to track who inserted it
+ this.owner = xnGetOwner();
+ //TODO load the profile that owns the gadget I'm looking at...
+ this.viewer = xnGetViewer();
+
+ if (xnGetViewerFriends().contacts &&
xnGetViewerFriends().contacts.values) {
+ var contactsArr = xnGetViewerFriends().contacts.values;
+ var resultArr = new Array();
+ for (var i = 0; i < contactsArr.length; i++) {
+ fObj = contactsArr[i];
+ //todo load the app data
+ resultArr[resultArr.length] = new
opensocial.ShindigPerson(fObj.id, fObj.name, '', fObj.photoUrl, '', new
Array(),'');
+ }
+ this.viewerFriends = resultArr;
+ //console.log('viewerFriends ' + resultArr.length);
+ //console.log('viewerFriendsx ' + contactsArr);
+ }
+ if (xnGetOwnerFriends().contacts &&
xnGetOwnerFriends().contacts.values) {
+ var contacts2Arr = xnGetOwnerFriends().contacts.values;
+ var result2Arr = new Array();
+ for (var i = 0; i < contacts2Arr.length; i++) {
+ fObj = contacts2Arr[i];
+ //todo load the app data
+ result2Arr[result2Arr.length] = new
opensocial.ShindigPerson(fObj.id, fObj.name, '', fObj.photoUrl, '', new
Array(),'');
+ }
+ this.ownerFriends = result2Arr;
+ }
+ this.containerInitialized = true;
+ this.globalAppData = {};
+ this.instanceAppData = {};
+
+ this.owner.appData = preloadedUserData;
+ this.viewer.appData = preloadedViewerData;
+
+ this.activities = new Array();
+
+
+};
+opensocial.ShindigContainer.prototype.getAppName = function() {
+ return this.appName;
+};
+
+opensocial.ShindigContainer.prototype.isContainerInitialized = function() {
+ return this.containerInitialized;
+}
+opensocial.ShindigContainer.prototype.internalLoadAppData = function(person,
callback) {
+ if (person) {
+ onSuccess = function(evaldObj) {
+ //this.appData = evaldObj;//console.log('success!');
+ //console.log('obj=');
+ //console.log(evaldObj);
+ //console.log('nummsg=' + evaldObj["numMessages"]);
+ person.appData = evaldObj;
+ //console.log(person.getAppField("numMessages"));
+ person.isAppDataLoaded = true;
+ callback();
+ };
+ onError = function(context, msg) {
+ console.log('error = ' + msg);
+ };
+ var handlers = {
+ success: onSuccess,
+ failure: onError
+ };
+ //var form = new Array();
+ content = {'user': person.id, 'op':'get-app-data', 'app':
this.appName, 'xark': Shindig._.os.xark, 'origin': Shindig._.os.origin};
+ Shindig.api.post("/gadgets/index/api", content, handlers);
+ }
+};
+
+/**
+* Deprecated.
+*/
+opensocial.ShindigContainer.prototype.requestInstallApp = function(appUrl,
+opt_onSuccess, opt_onFailure) {
+ opt_onSuccess();
+};
+
+/**
+* Deprecated.
+*/
+opensocial.ShindigContainer.prototype.requestUninstallApp = function(appUrl,
+opt_onSuccess, opt_onFailure) {
+ opt_onSuccess();
+};
+
+/**
+* Make the specified person as friend by moving them from the nonFriends
+* list to the friends list.
+*
+* @param {String} id The id associated with the person to be made a friend.
+* @param {Function} opt_onSuccess The callback method on successful completion.
+* @param {Function} opt_onFailure The callback method on failure.
+*/
+opensocial.ShindigContainer.prototype.requestMakePersonAFriend = function(id,
+onSuccess, onError) {
+ if (!Shindig.CurrentProfile) {
+ alert("signin", "You must sign in to make someone your
friend.");
+ return false;
+ }
+ if (Shindig.CurrentProfile.id.toLowerCase() == friendId.toLowerCase()) {
+ alert("You can't be your own friend!");
+ return false;
+ }
+ onSuccess = onSuccess || function() {
+ alert("Friend request sent!");
+ };
+ onError = onError || function(msg) {
+ if (msg == "Sorry, that person has blocked you") {
+ // hide blocking from user
+ onSuccess();
+ }
+ else {
+ alert("make person friend error: " + msg);
+ }
+ };
+ Shindig.social.sendFriendRequest(friendId, {
+ success: onSuccess,
+ error: onError
+ });
+ return false;
+};
+
+opensocial.ShindigContainer.prototype.requestCreateActivity =
function(activity,
+priority, opt_callback) {
+ // TODO(doll): We need to include simulating opeShindig a user dialog
if the
+ // stream does not exist. The templates should also be translated at
this
+ // point.
+ this.activities.push(activity);
+ if (opt_callback) {
+ opt_callback();
+ }
+};
+
+/**
+* Make the specified person as nonfriend by moving them from the friends
+* list to the nonFriends list.
+*
+* @param {String} id The id associated with the person to be removed
+* as a friend.
+* @param {Function} opt_onSuccess The callback method on successful completion.
+* @param {Function} opt_onFailure The callback method on failure.
+*/
+opensocial.ShindigContainer.prototype.requestMakePersonNotAFriend =
function(id, opt_onSuccess, opt_onFailure) {
+
+ var url =
dojo.string.substituteParams("/xn/rest/1.0/profile:%{id}/contact?xn_method=PUT",
{
+ id: Shindig.CurrentProfile.id
+ });
+
+ var handlers = {
+ success: opt_onSuccess,
+ failure: opt_onFailure
+ };
+
+ context = null;
+ var post_data = {
+ contact_relationship: "not-friend",
+ contact_id: profileId
+ };
+
+ Shindig.api.post(url, post_data, handlers, context);
+
+};
+
+/**
+* This method returns the data requested about the viewer and his/her friends.
+* Since this is an in memory container, it is merely returShindig the member
+* variables. In a real world container, this would involve making an ajax
+* request to fetch the values from the server.
+*
+* @param {Object} dataRequest The object that specifies the data requested.
+* @param {Function} callback The callback method on completion.
+*/
+opensocial.ShindigContainer.prototype.requestData = function(dataRequest,
callback) {
+
+ var myself = this;
+ /*
+ if (!this.owner.isDataLoaded()) {
+ console.log('calling load data for owner');
+ this.internalLoadAppData(this.owner, function()
{myself.requestData(dataRequest, callback);} );
+ return;
+ }
+ if (!this.viewer.isDataLoaded()) {
+ console.log('calling load data for current profile');
+ this.internalLoadAppData(this.viewer, function()
{myself.requestData(dataRequest, callback);} );
+ return;
+ }
+ */
+
+ //TODO the requests right now include "BogusAppname" clearly a
placeholder... wouldn't it be better to specify
+ //the appID/name in the init() like I'm doing?
+
+ var requestObjects = dataRequest.getRequestObjects();
+ //var appName = dataRequest['AppName'];
+ var dataResponseValues = {};
+ //TODO for lists of friends requests we handle everything in-memory and
return that data in this loop...
+ //the expected behavior for multiple requests simultaneously is a bit
unclear.
+ for (var i = 0; i < requestObjects.length; i++) {
+ var request = requestObjects[i].request;
+ var responseName = requestObjects[i].key;
+ var requestedValue;
+ console.log('request type: ' + request.type);
+ console.log('request response name: ' + responseName);
+
+ switch(request.type) {
+ case
opensocial.DataRequest.RequestType.FETCH_GLOBAL_DATA :
+ console.log('FETCH_GLOBAL_DATA');
+ // TODO(doll): Filter by key
+ requestedValue = this.globalAppData;
+ break;
+
+ case
opensocial.DataRequest.RequestType.FETCH_INSTANCE_DATA :
+ console.log('FETCH_INSTANCE_DATA');
+ // TODO(doll): Filter by key
+ requestedValue = this.instanceAppData;
+ break;
+
+ case
opensocial.DataRequest.RequestType.UPDATE_INSTANCE_DATA :
+ console.log('UPDATE_INSTANCE_DATA');
+ this.instanceAppData[request.parameters.key] =
request.parameters.value;
+ break;
+
+ case opensocial.DataRequest.RequestType.FETCH_PEOPLE :
+ console.log('FETCH_PEOPLE');
+ console.log(request);
+ var groupId = request.parameters.idSpec;
+ if (groupId ==
opensocial.DataRequest.Group.VIEWER_FRIENDS && this.viewer.getId() !=
'xn_anonymous') {
+ console.log('FETCH_PEOPLE: viewerFriends');
+ console.log(this.viewerFriends);
+ //requestedValue = new
s2.data.ArrayWrapper(this.viewerFriends);
+ requestedValue = this.viewerFriends;
+ }
+ else if (groupId ==
opensocial.DataRequest.Group.OWNER_FRIENDS) {
+ console.log('FETCH_PEOPLE: ownerFriends');
+ //requestedValue = new
s2.data.ArrayWrapper(this.ownerFriends);
+ requestedValue = this.ownerFriends;
+ }
+
+ break;
+
+ case opensocial.DataRequest.RequestType.FETCH_PERSON :
+
+ //opensocial.Container.PersonId.VIEWER returns person:
viewer -- not clear in the docs
+ var personId = request.parameters.id;
+ if (personId == opensocial.DataRequest.PersonId.VIEWER)
{
+ console.log('FETCH_PERSON: viewer');
+ console.log(this.viewer);
+ requestedValue = this.viewer;
+ }
+ else if (personId ==
opensocial.DataRequest.PersonId.OWNER) {
+ console.log('FETCH_PERSON: owner');
+ requestedValue = this.owner;
+ } else {
+ console.log('FETCH_PERSON: specific friend');
+ requestedValue =
this.viewerFriends.get(personId)
+ || this.ownerFriends.get(personId);
+ }
+
+
+ //todo -- add waiting in case they request data from
another profile
+ break;
+
+ case
opensocial.DataRequest.RequestType.FETCH_ACTIVITIES :
+ console.log('FETCH_ACTIVITIES');
+
//http://gadgets.Shindig.com/activity/log/list?fmt=json&screenName=diego
+
+ onSuccess = function(evaldObj) {
+ friendCount = evaldObj.total;
+ //
console.log('count='+friendCount);
+ //
console.log('intct='+evaldObj.contacts.values.length);
+ var activitiesArr = evaldObj.values;
+ var resultArr = {};
+ if (responseName == "viewer" ||
request.responseName == "activities") {
+ this.viewerActivities = new Array();
+ resultArr = this.viewerActivities;
+ }
+ if (responseName == "owner") {
+ this.ownerActivities = new Array();
+ resultArr = this.ownerActivities;
+ }
+
+/*
+ for(var i = 0;i < activityResponse.length;i++) {
+ var activity = activityResponse[i];
+ activities.push(new opensocial.Person.Activity(activity.ApplicationId,
activity.Body, activity.Title, activity.Url, activity.Summary,
activity.FolderId))
+ }
+ opensocial.DataRequest.ActivityRequestFields = {APP_ID:"appId",
FOLDER_ID:"folderId"};
+ opensocial.DataRequest.prototype.newFetchActivitiesRequest =
function(idSpec, opt_params) {
+ opt_params = opt_params || {};
+ var fields = opensocial.DataRequest.ActivityRequestFields, requestParams =
{idSpec:idSpec, appId:opt_params[fields.APP_ID],
folderId:opt_params[fields.FOLDER_ID]};
+ return new
opensocial.DataRequest.BaseDataRequest(opensocial.DataRequest.RequestType.FETCH_ACTIVITIES,
requestParams)
+ opensocial.Activity = function(stream, title, opt_params) {
+};
+opensocial.Activity.Field = {ID:"id", EXTERNAL_ID:"externalId",
STREAM:"stream", TITLE:"title", SUMMARY:"summary", BODY:"body", URL:"url",
MEDIA_ITEMS:"mediaItems", POSTED_TIME:"postedTime",
CUSTOM_VALUES:"customValues"};
+
+*/
+ for (var i = 0; i < activitiesArr.length; i++) {
+ fObj = activitiesArr[i];
+ resultArr[resultArr.length] = new
opensocial.Person.Activity(fObj.id, fObj.description, fObj.title, fObj.link);
+ }
+
+ dataResponseValues[responseName] = new
opensocial.ResponseItem(request, resultArr);
+ callback(new
opensocial.DataResponse(dataRequest, dataResponseValues));
+ };
+ onError = function(context, msg) {
+
+ };
+ var handlers = {
+ success: onSuccess,
+ failure: onError
+ };
+ if (this.viewer.getName() == 'xn_anonymous') {
+
Shindig.api.get("/activity/log/list?fmt=json&screenName=" +
this.viewer.getId(), handlers);
+ }
+ else {
+ console.log('not logged in');
+ }
+ return;
+
+ case
opensocial.DataRequest.RequestType.FETCH_PERSON_DATA:
+ // TODO(doll): Filter by person and key
+ console.log('FETCH_PERSON_DATA: ' +
request.parameters.idSpec);
+ var personId = request.parameters.idSpec ||
request.parameters.id;//request.parameters.id;
+ if (personId == opensocial.DataRequest.PersonId.VIEWER)
{
+ requestedValue = this.viewer.getAppData();
+ }
+ else if (personId ==
opensocial.DataRequest.PersonId.OWNER) {
+ requestedValue = this.owner.getAppData();
+ }
+ break;
+
+ case
opensocial.DataRequest.RequestType.UPDATE_PERSON_DATA :
+ console.log('FETCH_PERSON_DATA: ' +
request.parameters.person.id);
+ var key = request.parameters.field;
+ if (request.parameters.person.appData[key] !=
request.parameters.value) {
+ request.parameters.person.appData[key] =
request.parameters.value;
+ if (this.viewer) {
+ onSuccess = function(evaldObj) {
+ console.log('success!');
+
dataResponseValues[responseName] = new opensocial.ResponseItem(request,
requestedValue);
+ callback(new
opensocial.DataResponse(dataRequest, dataResponseValues));
+ };
+ onError = function(context, msg) {
+ console.log('error = ' + msg);
+ };
+ var handlers = {
+ success: onSuccess,
+ failure: onError
+ };
+ var userid =
request.parameters.person.id;
+ var value = request.parameters.value;
+ //var form = new Array();
+ content = {'user': userid,
'op':'update-app-data', 'app': this.appName, 'key': key, 'value': value,
'xark': Shindig._.os.xark, 'origin': Shindig._.os.origin};
+ Shindig.api.post("/gadgets/index/api",
content, handlers);
+ }
+ else {
+ console.log('not logged in');
+ }
+ }
+ return;
+
+ case opensocial.DataRequest.RequestType.POST_ACTIVITY :
+ console.log('POST_ACTIVITY');
+ console.log(request);
+ console.log('end POST_ACTIVITY');
+
+ if (this.viewer) {
+ onSuccess = function(evaldObj) {
+ console.log('success!');
+
dataResponseValues[responseName] = new opensocial.ResponseItem(request,
requestedValue);
+ callback(new
opensocial.DataResponse(dataRequest, dataResponseValues));
+ };
+ onError = function(context, msg) {
+ console.log('error = ' + msg);
+ };
+ var handlers = {
+ success: onSuccess,
+ failure: onError
+ };
+ var userid =
request.parameters.person.id;
+ var value = request.parameters.value;
+ //var form = new Array();
+ content = {'user': userid,
'op':'insert-activity', 'app': this.appName, 'description':
request.parameters.body, 'title': request.parameters.title, 'link':
request.parameters.url, 'xark': Shindig._.os.xark, 'origin':
Shindig._.os.origin};
+ Shindig.api.post("/gadgets/index/api",
content, handlers);
+ }
+ else {
+ console.log('not logged in');
+ }
+
+ /*
+ this.activities.push(new opensocial.Person.Activity(
+ request.parameters.appId, request.parameters.body,
+ request.parameters.title, request.parameters.url));
+ */
+ return;
+ }
+ console.log('setting response to '+responseName);
+ dataResponseValues[responseName] = new
opensocial.ResponseItem(request, requestedValue);
+ }
+
+ console.log('viewer ==========')
+ //console.log(dataResponseValues.get('viewer'))
+ console.log('end viewer ========== ')
+
+ callback(new opensocial.DataResponse(dataResponseValues));
+};
+
+
+
+/**
+* Create a person.
+*
+* @param {String} id The id of the person
+* @param {String} name The name of the person
+* @param {String} email The email of the person
+* @param {String} pictureUrl The url of the person's photo
+* @param {Array<String>} installedApps An array of strings representing which
+* apps are installed
+* @param {Map<String, String>} appData A map from app fields to app values
+* @param {Boolean} isViewerObject If true, this person is the currently logged
+* in user.
+* @constructor
+*/
+opensocial.ShindigPerson = function(id, opt_name, opt_email, opt_pictureUrl,
opt_installedApps, opt_appData, opt_isViewerObject) {
+ this.id = id;
+ this.name = opt_name;
+ this.email = opt_email;
+
+ this.pictureUrl = opt_pictureUrl;
+ if (this.pictureUrl.indexOf('?') == -1) {
+ this.pictureUrl += '?';
+ }
+ else {
+ this.pictureUrl += '&';
+ }
+ // this.pictureUrl += 'width=32&height=32'; // Ning specific
+ this.installedApps = opt_installedApps;
+ this.appData = opt_appData;
+ this.isViewerObject = opt_isViewerObject;
+ this.isAppDataLoaded = false;
+ this.isAppDataLoading = false;
+};
+
+
+opensocial.ShindigPerson.prototype.getField = function(key) {
+ if (key == opensocial.Person.Field.ID) {
+ return this.id;
+ }
+ if (key == opensocial.Person.Field.NAME) {
+ return this.name;
+ }
+ if (key == opensocial.Person.Field.EMAIL) {
+ return this.email;
+ }
+ return null;
+ //return this.fields[key];
+};
+
+opensocial.ShindigPerson.prototype.isDataLoaded = function() {
+ return this.isAppDataLoaded;
+};
+
+
+opensocial.ShindigPerson.prototype.isViewer = function() {
+ return this.isViewerObject;
+};
+
+
+opensocial.ShindigPerson.prototype.getId = function() {
+ return this.id;
+};
+
+
+opensocial.ShindigPerson.prototype.getName = function() {
+ return this.name;
+};
+
+
+opensocial.ShindigPerson.prototype.getEmail = function() {
+ return this.email;
+};
+
+/**
+* If the display name field is set, then return it. If not try to do
+* something meaShindigful using the email address.
+*/
+opensocial.ShindigPerson.prototype.getDisplayName = function() {
+ if (this.getName()) {
+ return this.getName();
+ } else {
+ var email = this.getEmail();
+ if (email && email.indexOf('@') > 0) {
+ return email.substring(0, email.indexOf('@'));
+ }
+ }
+ return '';
+};
+
+
+opensocial.ShindigPerson.prototype.getPicture = function() {
+ return this.pictureUrl;
+};
+
+
+opensocial.ShindigPerson.prototype.hasApp = function(appName) {
+ for (var i = 0; i < this.installedApps.length; i++) {
+ if (this.installedApps[i] == appName) {
+ return true;
+ }
+ }
+
+ return false;
+};
+
+
+
+opensocial.ShindigPerson.prototype.getAppData = function(field) {
+ return this.appData;
+}
+opensocial.ShindigPerson.prototype.getAppField = function(field) {
+ if (typeof(this.appData[field]) == "undefined") {
+ return '';
+ }
+ return this.appData[field];
+};
+
+if(window.magic_orkut) {
+ opensocial.Container.setContainer(new opensocial.OrkutContainer)
+}
+else if(window.magic_shindig) {
+ opensocial.Container.setContainer(new opensocial.ShindigContainer)
+}
+else {
+ opensocial.Container.setContainer(new opensocial.IGoogleContainer)
+};
+
+
Propchange: incubator/shindig/trunk/php/js/ShindigContainer.js
------------------------------------------------------------------------------
svn:executable = *
Added: incubator/shindig/trunk/php/js/hash.js
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/php/js/hash.js?rev=606463&view=auto
==============================================================================
--- incubator/shindig/trunk/php/js/hash.js (added)
+++ incubator/shindig/trunk/php/js/hash.js Sat Dec 22 08:11:57 2007
@@ -0,0 +1,257 @@
+/*
+ * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
+ * Digest Algorithm, as defined in RFC 1321.
+ * Version 2.1 Copyright (C) Paul Johnston 1999 - 2002.
+ * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
+ * Distributed under the BSD License
+ * See http://pajhome.org.uk/crypt/md5 for more info.
+ */
+
+/*
+ * Configurable variables. You may need to tweak these to be compatible with
+ * the server-side, but the defaults work in most cases.
+ */
+var hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase */
+var b64pad = ""; /* base-64 pad character. "=" for strict RFC compliance */
+var chrsz = 8; /* bits per input character. 8 - ASCII; 16 - Unicode */
+
+/*
+ * These are the functions you'll usually want to call
+ * They take string arguments and return either hex or base-64 encoded strings
+ */
+function hex_md5(s){ return binl2hex(core_md5(str2binl(s), s.length * chrsz));}
+function b64_md5(s){ return binl2b64(core_md5(str2binl(s), s.length * chrsz));}
+function str_md5(s){ return binl2str(core_md5(str2binl(s), s.length * chrsz));}
+function hex_hmac_md5(key, data) { return binl2hex(core_hmac_md5(key, data)); }
+function b64_hmac_md5(key, data) { return binl2b64(core_hmac_md5(key, data)); }
+function str_hmac_md5(key, data) { return binl2str(core_hmac_md5(key, data)); }
+
+/*
+ * Perform a simple self-test to see if the VM is working
+ */
+function md5_vm_test()
+{
+ return hex_md5("abc") == "900150983cd24fb0d6963f7d28e17f72";
+}
+
+/*
+ * Calculate the MD5 of an array of little-endian words, and a bit length
+ */
+function core_md5(x, len)
+{
+ /* append padding */
+ x[len >> 5] |= 0x80 << ((len) % 32);
+ x[(((len + 64) >>> 9) << 4) + 14] = len;
+
+ var a = 1732584193;
+ var b = -271733879;
+ var c = -1732584194;
+ var d = 271733878;
+
+ for(var i = 0; i < x.length; i += 16)
+ {
+ var olda = a;
+ var oldb = b;
+ var oldc = c;
+ var oldd = d;
+
+ a = md5_ff(a, b, c, d, x[i+ 0], 7 , -680876936);
+ d = md5_ff(d, a, b, c, x[i+ 1], 12, -389564586);
+ c = md5_ff(c, d, a, b, x[i+ 2], 17, 606105819);
+ b = md5_ff(b, c, d, a, x[i+ 3], 22, -1044525330);
+ a = md5_ff(a, b, c, d, x[i+ 4], 7 , -176418897);
+ d = md5_ff(d, a, b, c, x[i+ 5], 12, 1200080426);
+ c = md5_ff(c, d, a, b, x[i+ 6], 17, -1473231341);
+ b = md5_ff(b, c, d, a, x[i+ 7], 22, -45705983);
+ a = md5_ff(a, b, c, d, x[i+ 8], 7 , 1770035416);
+ d = md5_ff(d, a, b, c, x[i+ 9], 12, -1958414417);
+ c = md5_ff(c, d, a, b, x[i+10], 17, -42063);
+ b = md5_ff(b, c, d, a, x[i+11], 22, -1990404162);
+ a = md5_ff(a, b, c, d, x[i+12], 7 , 1804603682);
+ d = md5_ff(d, a, b, c, x[i+13], 12, -40341101);
+ c = md5_ff(c, d, a, b, x[i+14], 17, -1502002290);
+ b = md5_ff(b, c, d, a, x[i+15], 22, 1236535329);
+
+ a = md5_gg(a, b, c, d, x[i+ 1], 5 , -165796510);
+ d = md5_gg(d, a, b, c, x[i+ 6], 9 , -1069501632);
+ c = md5_gg(c, d, a, b, x[i+11], 14, 643717713);
+ b = md5_gg(b, c, d, a, x[i+ 0], 20, -373897302);
+ a = md5_gg(a, b, c, d, x[i+ 5], 5 , -701558691);
+ d = md5_gg(d, a, b, c, x[i+10], 9 , 38016083);
+ c = md5_gg(c, d, a, b, x[i+15], 14, -660478335);
+ b = md5_gg(b, c, d, a, x[i+ 4], 20, -405537848);
+ a = md5_gg(a, b, c, d, x[i+ 9], 5 , 568446438);
+ d = md5_gg(d, a, b, c, x[i+14], 9 , -1019803690);
+ c = md5_gg(c, d, a, b, x[i+ 3], 14, -187363961);
+ b = md5_gg(b, c, d, a, x[i+ 8], 20, 1163531501);
+ a = md5_gg(a, b, c, d, x[i+13], 5 , -1444681467);
+ d = md5_gg(d, a, b, c, x[i+ 2], 9 , -51403784);
+ c = md5_gg(c, d, a, b, x[i+ 7], 14, 1735328473);
+ b = md5_gg(b, c, d, a, x[i+12], 20, -1926607734);
+
+ a = md5_hh(a, b, c, d, x[i+ 5], 4 , -378558);
+ d = md5_hh(d, a, b, c, x[i+ 8], 11, -2022574463);
+ c = md5_hh(c, d, a, b, x[i+11], 16, 1839030562);
+ b = md5_hh(b, c, d, a, x[i+14], 23, -35309556);
+ a = md5_hh(a, b, c, d, x[i+ 1], 4 , -1530992060);
+ d = md5_hh(d, a, b, c, x[i+ 4], 11, 1272893353);
+ c = md5_hh(c, d, a, b, x[i+ 7], 16, -155497632);
+ b = md5_hh(b, c, d, a, x[i+10], 23, -1094730640);
+ a = md5_hh(a, b, c, d, x[i+13], 4 , 681279174);
+ d = md5_hh(d, a, b, c, x[i+ 0], 11, -358537222);
+ c = md5_hh(c, d, a, b, x[i+ 3], 16, -722521979);
+ b = md5_hh(b, c, d, a, x[i+ 6], 23, 76029189);
+ a = md5_hh(a, b, c, d, x[i+ 9], 4 , -640364487);
+ d = md5_hh(d, a, b, c, x[i+12], 11, -421815835);
+ c = md5_hh(c, d, a, b, x[i+15], 16, 530742520);
+ b = md5_hh(b, c, d, a, x[i+ 2], 23, -995338651);
+
+ a = md5_ii(a, b, c, d, x[i+ 0], 6 , -198630844);
+ d = md5_ii(d, a, b, c, x[i+ 7], 10, 1126891415);
+ c = md5_ii(c, d, a, b, x[i+14], 15, -1416354905);
+ b = md5_ii(b, c, d, a, x[i+ 5], 21, -57434055);
+ a = md5_ii(a, b, c, d, x[i+12], 6 , 1700485571);
+ d = md5_ii(d, a, b, c, x[i+ 3], 10, -1894986606);
+ c = md5_ii(c, d, a, b, x[i+10], 15, -1051523);
+ b = md5_ii(b, c, d, a, x[i+ 1], 21, -2054922799);
+ a = md5_ii(a, b, c, d, x[i+ 8], 6 , 1873313359);
+ d = md5_ii(d, a, b, c, x[i+15], 10, -30611744);
+ c = md5_ii(c, d, a, b, x[i+ 6], 15, -1560198380);
+ b = md5_ii(b, c, d, a, x[i+13], 21, 1309151649);
+ a = md5_ii(a, b, c, d, x[i+ 4], 6 , -145523070);
+ d = md5_ii(d, a, b, c, x[i+11], 10, -1120210379);
+ c = md5_ii(c, d, a, b, x[i+ 2], 15, 718787259);
+ b = md5_ii(b, c, d, a, x[i+ 9], 21, -343485551);
+
+ a = safe_add(a, olda);
+ b = safe_add(b, oldb);
+ c = safe_add(c, oldc);
+ d = safe_add(d, oldd);
+ }
+ return Array(a, b, c, d);
+
+}
+
+/*
+ * These functions implement the four basic operations the algorithm uses.
+ */
+function md5_cmn(q, a, b, x, s, t)
+{
+ return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s),b);
+}
+function md5_ff(a, b, c, d, x, s, t)
+{
+ return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t);
+}
+function md5_gg(a, b, c, d, x, s, t)
+{
+ return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t);
+}
+function md5_hh(a, b, c, d, x, s, t)
+{
+ return md5_cmn(b ^ c ^ d, a, b, x, s, t);
+}
+function md5_ii(a, b, c, d, x, s, t)
+{
+ return md5_cmn(c ^ (b | (~d)), a, b, x, s, t);
+}
+
+/*
+ * Calculate the HMAC-MD5, of a key and some data
+ */
+function core_hmac_md5(key, data)
+{
+ var bkey = str2binl(key);
+ if(bkey.length > 16) bkey = core_md5(bkey, key.length * chrsz);
+
+ var ipad = Array(16), opad = Array(16);
+ for(var i = 0; i < 16; i++)
+ {
+ ipad[i] = bkey[i] ^ 0x36363636;
+ opad[i] = bkey[i] ^ 0x5C5C5C5C;
+ }
+
+ var hash = core_md5(ipad.concat(str2binl(data)), 512 + data.length * chrsz);
+ return core_md5(opad.concat(hash), 512 + 128);
+}
+
+/*
+ * Add integers, wrapping at 2^32. This uses 16-bit operations internally
+ * to work around bugs in some JS interpreters.
+ */
+function safe_add(x, y)
+{
+ var lsw = (x & 0xFFFF) + (y & 0xFFFF);
+ var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
+ return (msw << 16) | (lsw & 0xFFFF);
+}
+
+/*
+ * Bitwise rotate a 32-bit number to the left.
+ */
+function bit_rol(num, cnt)
+{
+ return (num << cnt) | (num >>> (32 - cnt));
+}
+
+/*
+ * Convert a string to an array of little-endian words
+ * If chrsz is ASCII, characters >255 have their hi-byte silently ignored.
+ */
+function str2binl(str)
+{
+ var bin = Array();
+ var mask = (1 << chrsz) - 1;
+ for(var i = 0; i < str.length * chrsz; i += chrsz)
+ bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (i%32);
+ return bin;
+}
+
+/*
+ * Convert an array of little-endian words to a string
+ */
+function binl2str(bin)
+{
+ var str = "";
+ var mask = (1 << chrsz) - 1;
+ for(var i = 0; i < bin.length * 32; i += chrsz)
+ str += String.fromCharCode((bin[i>>5] >>> (i % 32)) & mask);
+ return str;
+}
+
+/*
+ * Convert an array of little-endian words to a hex string.
+ */
+function binl2hex(binarray)
+{
+ var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
+ var str = "";
+ for(var i = 0; i < binarray.length * 4; i++)
+ {
+ str += hex_tab.charAt((binarray[i>>2] >> ((i%4)*8+4)) & 0xF) +
+ hex_tab.charAt((binarray[i>>2] >> ((i%4)*8 )) & 0xF);
+ }
+ return str;
+}
+
+/*
+ * Convert an array of little-endian words to a base-64 string
+ */
+function binl2b64(binarray)
+{
+ var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+ var str = "";
+ for(var i = 0; i < binarray.length * 4; i += 3)
+ {
+ var triplet = (((binarray[i >> 2] >> 8 * ( i %4)) & 0xFF) << 16)
+ | (((binarray[i+1 >> 2] >> 8 * ((i+1)%4)) & 0xFF) << 8 )
+ | ((binarray[i+2 >> 2] >> 8 * ((i+2)%4)) & 0xFF);
+ for(var j = 0; j < 4; j++)
+ {
+ if(i * 8 + j * 6 > binarray.length * 32) str += b64pad;
+ else str += tab.charAt((triplet >> 6*(3-j)) & 0x3F);
+ }
+ }
+ return str;
+}
+