You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@usergrid.apache.org by sn...@apache.org on 2014/01/27 23:20:52 UTC

[06/27] git commit: split up SDK into separate files for logical pieces. Added Grunt build to put them together. Added mocha tests

split up SDK into separate files for logical pieces. Added Grunt build to put them together. Added mocha tests


Project: http://git-wip-us.apache.org/repos/asf/incubator-usergrid/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-usergrid/commit/ef53b3b5
Tree: http://git-wip-us.apache.org/repos/asf/incubator-usergrid/tree/ef53b3b5
Diff: http://git-wip-us.apache.org/repos/asf/incubator-usergrid/diff/ef53b3b5

Branch: refs/pull/34/head
Commit: ef53b3b5f0e9469af4688991e5bb0bbd35e0fee4
Parents: 4def2d7
Author: ryan bridges <rb...@apigee.com>
Authored: Fri Jan 17 11:32:43 2014 -0500
Committer: ryan bridges <rb...@apigee.com>
Committed: Fri Jan 17 11:32:43 2014 -0500

----------------------------------------------------------------------
 sdks/html5-javascript/Gruntfile.js              |   83 +
 sdks/html5-javascript/lib/Client.js             |  864 +++
 sdks/html5-javascript/lib/Collection.js         |  446 ++
 sdks/html5-javascript/lib/Entity.js             |  646 +++
 sdks/html5-javascript/lib/Event.js              |  163 +
 sdks/html5-javascript/lib/Group.js              |  233 +
 sdks/html5-javascript/lib/Usergrid.js           |   87 +
 sdks/html5-javascript/package.json              |   22 +
 sdks/html5-javascript/tests/mocha/index.html    |   49 +
 sdks/html5-javascript/tests/mocha/test.js       |  348 ++
 .../tests/qunit/apigee_test.html                |   14 +
 sdks/html5-javascript/tests/qunit/tests.js      |    3 +
 .../resources/css/bootstrap-combined.min.css    |   18 +
 .../tests/resources/css/mocha.css               |  270 +
 .../tests/resources/css/styles.css              |   91 +
 .../tests/resources/images/apigee.png           |  Bin 0 -> 6010 bytes
 .../tests/resources/js/blanket_mocha.min.js     |    1 +
 .../tests/resources/js/json2.js                 |  486 ++
 .../tests/resources/js/mocha.js                 | 5341 ++++++++++++++++++
 sdks/html5-javascript/tests/test.html           |   37 +
 sdks/html5-javascript/tests/test.js             |  910 +++
 21 files changed, 10112 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ef53b3b5/sdks/html5-javascript/Gruntfile.js
----------------------------------------------------------------------
diff --git a/sdks/html5-javascript/Gruntfile.js b/sdks/html5-javascript/Gruntfile.js
new file mode 100644
index 0000000..e1d297a
--- /dev/null
+++ b/sdks/html5-javascript/Gruntfile.js
@@ -0,0 +1,83 @@
+module.exports = function(grunt) {
+  var files = ['lib/Usergrid.js', 'lib/Client.js', 'lib/Entity.js', 'lib/Collection.js', 'lib/Group.js', 'lib/Event.js'];
+  var tests = [ 'tests/mocha/index.html','tests/mocha/test_*.html' ];
+   // Project configuration.
+  grunt.initConfig({
+    //pkg: grunt.file.readJSON('package.json'),
+    meta: {
+      package: grunt.file.readJSON('package.json'),
+    },
+    clean: ['usergrid.js', 'usergrid.min.js'],
+    uglify: {
+      build: {
+        options: {
+          banner: '/*! <%= meta.package.name %>@<%= meta.package.version %> <%= grunt.template.today("yyyy-mm-dd") %> */\n',
+            mangle: false,
+            compress: false,
+            beautify: true,
+            preserveComments: "all"
+        },
+        files: {
+          'usergrid.js': files
+        }
+      },
+      buildmin: {
+        options: {
+          banner: '/*! <%= meta.package.name %>@<%= meta.package.version %> <%= grunt.template.today("yyyy-mm-dd") %> */\n',
+            mangle: false,
+            compress: true,
+            beautify: false,
+            preserveComments: "some"
+        },
+        files: {
+          'usergrid.min.js': files
+        }
+      }
+    },
+    connect: {
+      server: {
+        options: {
+          port: 3000,
+          base: '.'
+        }
+      },
+      test: {
+        options: {
+          port: 8000,
+          base: '.'
+        }
+      }
+    },
+    watch : {
+      files : files,
+      tasks : ['default']
+    },
+    blanket_mocha: {
+      all: tests,
+      //urls: [ 'http://localhost:8000/tests/mocha/index.html' ],
+      options: {
+          dest: 'report/coverage.html',
+          reporter: 'Spec',
+          threshold: 70
+      }
+    },
+  });
+  grunt.loadNpmTasks('grunt-contrib-clean');
+  grunt.loadNpmTasks('grunt-contrib-uglify');
+  grunt.loadNpmTasks('grunt-contrib-watch'); 
+  grunt.loadNpmTasks('grunt-contrib-connect');
+  grunt.loadNpmTasks('grunt-blanket-mocha');
+grunt.registerTask('default', [
+    'clean',
+    'uglify'
+  ]);
+  grunt.registerTask('dev', [
+  	'connect:server',
+    'watch'
+  ]);
+  grunt.registerTask('test', [
+    'connect:test',
+    'blanket_mocha',
+  ]);
+};
+

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ef53b3b5/sdks/html5-javascript/lib/Client.js
----------------------------------------------------------------------
diff --git a/sdks/html5-javascript/lib/Client.js b/sdks/html5-javascript/lib/Client.js
new file mode 100644
index 0000000..3fc4338
--- /dev/null
+++ b/sdks/html5-javascript/lib/Client.js
@@ -0,0 +1,864 @@
+
+Usergrid.Client = function(options) {
+  //usergrid enpoint
+  this.URI = options.URI || 'https://api.usergrid.com';
+
+  //Find your Orgname and Appname in the Admin portal (http://apigee.com/usergrid)
+  if (options.orgName) {
+    this.set('orgName', options.orgName);
+  }
+  if (options.appName) {
+    this.set('appName', options.appName);
+  }
+
+  //other options
+  this.buildCurl = options.buildCurl || false;
+  this.logging = options.logging || false;
+
+  //timeout and callbacks
+  this._callTimeout =  options.callTimeout || 30000; //default to 30 seconds
+  this._callTimeoutCallback =  options.callTimeoutCallback || null;
+  this.logoutCallback =  options.logoutCallback || null;
+};
+
+/*
+ *  Main function for making requests to the API.  Can be called directly.
+ *
+ *  options object:
+ *  `method` - http method (GET, POST, PUT, or DELETE), defaults to GET
+ *  `qs` - object containing querystring values to be appended to the uri
+ *  `body` - object containing entity body for POST and PUT requests
+ *  `endpoint` - API endpoint, for example 'users/fred'
+ *  `mQuery` - boolean, set to true if running management query, defaults to false
+ *
+ *  @method request
+ *  @public
+ *  @params {object} options
+ *  @param {function} callback
+ *  @return {callback} callback(err, data)
+ */
+Usergrid.Client.prototype.request = function (options, callback) {
+  var self = this;
+  var method = options.method || 'GET';
+  var endpoint = options.endpoint;
+  var body = options.body || {};
+  var qs = options.qs || {};
+  var mQuery = options.mQuery || false; //is this a query to the management endpoint?
+  var orgName = this.get('orgName');
+  var appName = this.get('appName');
+  if(!mQuery && !orgName && !appName){
+    if (typeof(this.logoutCallback) === 'function') {
+      return this.logoutCallback(true, 'no_org_or_app_name_specified');
+    }
+  }
+  if (mQuery) {
+    uri = this.URI + '/' + endpoint;
+  } else {
+    uri = this.URI + '/' + orgName + '/' + appName + '/' + endpoint;
+  }
+
+  if (self.getToken()) {
+    qs.access_token = self.getToken();
+    /* //could also use headers for the token
+     xhr.setRequestHeader("Authorization", "Bearer " + self.getToken());
+     xhr.withCredentials = true;
+     */
+  }
+
+  //append params to the path
+  var encoded_params = encodeParams(qs);
+  if (encoded_params) {
+    uri += "?" + encoded_params;
+  }
+
+  //stringify the body object
+  body = JSON.stringify(body);
+
+  //so far so good, so run the query
+  var xhr = new XMLHttpRequest();
+  xhr.open(method, uri, true);
+  //add content type = json if there is a json payload
+  if (body) {
+    xhr.setRequestHeader("Content-Type", "application/json");
+    xhr.setRequestHeader("Accept", "application/json");
+  }
+
+  // Handle response.
+  xhr.onerror = function(response) {
+    self._end = new Date().getTime();
+    if (self.logging) {
+      console.log('success (time: ' + self.calcTimeDiff() + '): ' + method + ' ' + uri);
+    }
+    if (self.logging) {
+      console.log('Error: API call failed at the network level.');
+    }
+    //network error
+    clearTimeout(timeout);
+    var err = true;
+    if (typeof(callback) === 'function') {
+      callback(err, response);
+    }
+  };
+
+  xhr.onload = function(response) {
+    //call timing, get time, then log the call
+    self._end = new Date().getTime();
+    if (self.logging) {
+      console.log('success (time: ' + self.calcTimeDiff() + '): ' + method + ' ' + uri);
+    }
+    //call completed
+    clearTimeout(timeout);
+    //decode the response
+    try{
+      response = JSON.parse(xhr.responseText);
+    }catch (e){
+      response = {error:'unhandled_error',error_description:xhr.responseText};
+      xhr.status = xhr.status === 200 ? 400 : xhr.status;
+      console.error(e);
+    }
+    if (xhr.status != 200)   {
+      //there was an api error
+      var error = response.error;
+      var error_description = response.error_description;
+      if (self.logging) {
+        console.log('Error (' + xhr.status + ')(' + error + '): ' + error_description);
+      }
+      if ( (error == "auth_expired_session_token") ||
+        (error == "auth_missing_credentials")   ||
+        (error == "auth_unverified_oath")       ||
+        (error == "expired_token")              ||
+        (error == "unauthorized")               ||
+        (error == "auth_invalid")) {
+        //these errors mean the user is not authorized for whatever reason. If a logout function is defined, call it
+        //if the user has specified a logout callback:
+        if (typeof(self.logoutCallback) === 'function') {
+          return self.logoutCallback(true, response);
+        }
+      }
+      if (typeof(callback) === 'function') {
+        callback(true, response);
+      }
+    } else {
+      if (typeof(callback) === 'function') {
+        callback(false, response);
+      }
+    }
+  };
+
+  var timeout = setTimeout(
+    function() {
+      xhr.abort();
+      if (self._callTimeoutCallback === 'function') {
+        self._callTimeoutCallback('API CALL TIMEOUT');
+      } else {
+        self.callback('API CALL TIMEOUT');
+      }
+    },
+    self._callTimeout); //set for 30 seconds
+
+  if (this.logging) {
+    console.log('calling: ' + method + ' ' + uri);
+  }
+  if (this.buildCurl) {
+    var curlOptions = {
+      uri:uri,
+      body:body,
+      method:method
+    }
+    this.buildCurlCall(curlOptions);
+  }
+  this._start = new Date().getTime();
+  xhr.send(body);
+}
+
+/*
+ *  function for building asset urls
+ *
+ *  @method buildAssetURL
+ *  @public
+ *  @params {string} uuid
+ *  @return {string} assetURL
+ */
+Usergrid.Client.prototype.buildAssetURL = function(uuid) {
+  var self = this;
+  var qs = {};
+  var assetURL = this.URI + '/' + this.orgName + '/' + this.appName + '/assets/' + uuid + '/data';
+
+  if (self.getToken()) {
+    qs.access_token = self.getToken();
+  }
+
+  //append params to the path
+  var encoded_params = encodeParams(qs);
+  if (encoded_params) {
+    assetURL += "?" + encoded_params;
+  }
+
+  return assetURL;
+};
+
+/*
+ *  Main function for creating new groups. Call this directly.
+ *
+ *  @method createGroup
+ *  @public
+ *  @params {string} path
+ *  @param {function} callback
+ *  @return {callback} callback(err, data)
+ */
+Usergrid.Client.prototype.createGroup = function(options, callback) {
+  var getOnExist = options.getOnExist || false;
+
+  options = {
+    path: options.path,
+    client: this,
+    data: options
+  };
+
+  var group = new Usergrid.Group(options);
+  group.fetch(function(err, data){
+    var okToSave = (err && 'service_resource_not_found' === data.error || 'no_name_specified' === data.error || 'null_pointer' === data.error) || (!err && getOnExist);
+    if (okToSave) {
+      group.save(function(err, data){
+        if (typeof(callback) === 'function') {
+          callback(err, group);
+        }
+      });
+    } else {
+      if(typeof(callback) === 'function') {
+        callback(err, group);
+      }
+    }
+  });
+};
+
+/*
+ *  Main function for creating new entities - should be called directly.
+ *
+ *  options object: options {data:{'type':'collection_type', 'key':'value'}, uuid:uuid}}
+ *
+ *  @method createEntity
+ *  @public
+ *  @params {object} options
+ *  @param {function} callback
+ *  @return {callback} callback(err, data)
+ */
+Usergrid.Client.prototype.createEntity = function (options, callback) {
+  // todo: replace the check for new / save on not found code with simple save
+  // when users PUT on no user fix is in place.
+  /*
+   var options = {
+   client:this,
+   data:options
+   }
+   var entity = new Usergrid.Entity(options);
+   entity.save(function(err, data) {
+   if (typeof(callback) === 'function') {
+   callback(err, entity);
+   }
+   });
+   */
+  var getOnExist = options.getOnExist || false; //if true, will return entity if one already exists
+  var options = {
+    client:this,
+    data:options
+  };
+  var entity = new Usergrid.Entity(options);
+  entity.fetch(function(err, data) {
+    //if the fetch doesn't find what we are looking for, or there is no error, do a save
+    var okToSave = (err && 'service_resource_not_found' === data.error || 'no_name_specified' === data.error || 'null_pointer' === data.error) || (!err && getOnExist);
+    if(okToSave) {
+      entity.set(options.data); //add the data again just in case
+      entity.save(function(err, data) {
+        if (typeof(callback) === 'function') {
+          callback(err, entity, data);
+        }
+      });
+    } else {
+      if (typeof(callback) === 'function') {
+        callback(err, entity, data);
+      }
+    }
+  });
+
+};
+
+/*
+ *  Main function for getting existing entities - should be called directly.
+ *
+ *  You must supply a uuid or (username or name). Username only applies to users.
+ *  Name applies to all custom entities
+ *
+ *  options object: options {data:{'type':'collection_type', 'name':'value', 'username':'value'}, uuid:uuid}}
+ *
+ *  @method createEntity
+ *  @public
+ *  @params {object} options
+ *  @param {function} callback
+ *  @return {callback} callback(err, data)
+ */
+Usergrid.Client.prototype.getEntity = function (options, callback) {
+  var options = {
+    client:this,
+    data:options
+  }
+  var entity = new Usergrid.Entity(options);
+  entity.fetch(function(err, data) {
+    if (typeof(callback) === 'function') {
+      callback(err, entity, data);
+    }
+  });
+};
+
+/*
+ *  Main function for restoring an entity from serialized data.
+ *
+ *  serializedObject should have come from entityObject.serialize();
+ *
+ *  @method restoreEntity
+ *  @public
+ *  @param {string} serializedObject
+ *  @return {object} Entity Object
+ */
+Usergrid.Client.prototype.restoreEntity = function (serializedObject) {
+  var data = JSON.parse(serializedObject);
+  var options = {
+    client:this,
+    data:data
+  }
+  var entity = new Usergrid.Entity(options);
+  return entity;
+};
+
+/*
+ *  Main function for creating new collections - should be called directly.
+ *
+ *  options object: options {client:client, type: type, qs:qs}
+ *
+ *  @method createCollection
+ *  @public
+ *  @params {object} options
+ *  @param {function} callback
+ *  @return {callback} callback(err, data)
+ */
+Usergrid.Client.prototype.createCollection = function (options, callback) {
+  options.client = this;
+  var collection = new Usergrid.Collection(options, function(err, data) {
+    if (typeof(callback) === 'function') {
+      callback(err, collection, data);
+    }
+  });
+};
+
+/*
+ *  Main function for restoring a collection from serialized data.
+ *
+ *  serializedObject should have come from collectionObject.serialize();
+ *
+ *  @method restoreCollection
+ *  @public
+ *  @param {string} serializedObject
+ *  @return {object} Collection Object
+ */
+Usergrid.Client.prototype.restoreCollection = function (serializedObject) {
+  var data = JSON.parse(serializedObject);
+  data.client = this;
+  var collection = new Usergrid.Collection(data);
+  return collection;
+};
+
+/*
+ *  Main function for retrieving a user's activity feed.
+ *
+ *  @method getFeedForUser
+ *  @public
+ *  @params {string} username
+ *  @param {function} callback
+ *  @return {callback} callback(err, data, activities)
+ */
+Usergrid.Client.prototype.getFeedForUser = function(username, callback) {
+  var options = {
+    method: "GET",
+    endpoint: "users/"+username+"/feed"
+  };
+
+  this.request(options, function(err, data){
+    if(typeof(callback) === "function") {
+      if(err) {
+        callback(err);
+      } else {
+        callback(err, data, data.entities);
+      }
+    }
+  });
+};
+
+/*
+ *  Function for creating new activities for the current user - should be called directly.
+ *
+ *  //user can be any of the following: "me", a uuid, a username
+ *  Note: the "me" alias will reference the currently logged in user (e.g. 'users/me/activties')
+ *
+ *  //build a json object that looks like this:
+ *  var options =
+ *  {
+ *    "actor" : {
+ *      "displayName" :"myusername",
+ *      "uuid" : "myuserid",
+ *      "username" : "myusername",
+ *      "email" : "myemail",
+ *      "picture": "http://path/to/picture",
+ *      "image" : {
+ *          "duration" : 0,
+ *          "height" : 80,
+ *          "url" : "http://www.gravatar.com/avatar/",
+ *          "width" : 80
+ *      },
+ *    },
+ *    "verb" : "post",
+ *    "content" : "My cool message",
+ *    "lat" : 48.856614,
+ *    "lon" : 2.352222
+ *  }
+ *
+ *  @method createEntity
+ *  @public
+ *  @params {string} user // "me", a uuid, or a username
+ *  @params {object} options
+ *  @param {function} callback
+ *  @return {callback} callback(err, data)
+ */
+Usergrid.Client.prototype.createUserActivity = function (user, options, callback) {
+  options.type = 'users/'+user+'/activities';
+  var options = {
+    client:this,
+    data:options
+  }
+  var entity = new Usergrid.Entity(options);
+  entity.save(function(err, data) {
+    if (typeof(callback) === 'function') {
+      callback(err, entity);
+    }
+  });
+};
+
+/*
+ *  Function for creating user activities with an associated user entity.
+ *
+ *  user object:
+ *  The user object passed into this function is an instance of Usergrid.Entity.
+ *
+ *  @method createUserActivityWithEntity
+ *  @public
+ *  @params {object} user
+ *  @params {string} content
+ *  @param {function} callback
+ *  @return {callback} callback(err, data)
+ */
+Usergrid.Client.prototype.createUserActivityWithEntity = function(user, content, callback) {
+  var username = user.get("username");
+  var options = {
+    actor: {
+      "displayName":username,
+      "uuid":user.get("uuid"),
+      "username":username,
+      "email":user.get("email"),
+      "picture":user.get("picture"),
+      "image": {
+        "duration":0,
+        "height":80,
+        "url":user.get("picture"),
+        "width":80
+      },
+    },
+    "verb":"post",
+    "content":content
+  };
+
+  this.createUserActivity(username, options, callback);
+
+};
+
+/*
+ *  A private method to get call timing of last call
+ */
+Usergrid.Client.prototype.calcTimeDiff = function () {
+  var seconds = 0;
+  var time = this._end - this._start;
+  try {
+    seconds = ((time/10) / 60).toFixed(2);
+  } catch(e) {
+    return 0;
+  }
+  return seconds;
+};
+
+/*
+ *  A public method to store the OAuth token for later use - uses localstorage if available
+ *
+ *  @method setToken
+ *  @public
+ *  @params {string} token
+ *  @return none
+ */
+Usergrid.Client.prototype.setToken = function (token) {
+  this.set('token', token);
+};
+
+/*
+ *  A public method to get the OAuth token
+ *
+ *  @method getToken
+ *  @public
+ *  @return {string} token
+ */
+Usergrid.Client.prototype.getToken = function () {
+  return this.get('token');
+};
+
+Usergrid.Client.prototype.setObject = function(key, value) {
+  if (value) {
+    value = JSON.stringify(value);
+  }
+  this.set(key, value);
+};
+
+Usergrid.Client.prototype.set = function (key, value) {
+  var keyStore =  'apigee_' + key;
+  this[key] = value;
+  if(typeof(Storage)!=="undefined"){
+    if (value) {
+      localStorage.setItem(keyStore, value);
+    } else {
+      localStorage.removeItem(keyStore);
+    }
+  }
+};
+
+Usergrid.Client.prototype.getObject = function(key) {
+  return JSON.parse(this.get(key));
+};
+
+Usergrid.Client.prototype.get = function (key) {
+  var keyStore = 'apigee_' + key;
+  if (this[key]) {
+    return this[key];
+  } else if(typeof(Storage)!=="undefined") {
+    return localStorage.getItem(keyStore);
+  }
+  return null;
+};
+
+/*
+ * A public facing helper method for signing up users
+ *
+ * @method signup
+ * @public
+ * @params {string} username
+ * @params {string} password
+ * @params {string} email
+ * @params {string} name
+ * @param {function} callback
+ * @return {callback} callback(err, data)
+ */
+Usergrid.Client.prototype.signup = function(username, password, email, name, callback) {
+  var self = this;
+  var options = {
+    type:"users",
+    username:username,
+    password:password,
+    email:email,
+    name:name
+  };
+
+  this.createEntity(options, callback);
+};
+
+/*
+ *
+ *  A public method to log in an app user - stores the token for later use
+ *
+ *  @method login
+ *  @public
+ *  @params {string} username
+ *  @params {string} password
+ *  @param {function} callback
+ *  @return {callback} callback(err, data)
+ */
+Usergrid.Client.prototype.login = function (username, password, callback) {
+  var self = this;
+  var options = {
+    method:'POST',
+    endpoint:'token',
+    body:{
+      username: username,
+      password: password,
+      grant_type: 'password'
+    }
+  };
+  this.request(options, function(err, data) {
+    var user = {};
+    if (err && self.logging) {
+      console.log('error trying to log user in');
+    } else {
+      var options = {
+        client:self,
+        data:data.user
+      };
+      user = new Usergrid.Entity(options);
+      self.setToken(data.access_token);
+    }
+    if (typeof(callback) === 'function') {
+      callback(err, data, user);
+    }
+  });
+};
+
+
+Usergrid.Client.prototype.reAuthenticateLite = function (callback) {
+  var self = this;
+  var options = {
+    method:'GET',
+    endpoint:'management/me',
+    mQuery:true
+  };
+  this.request(options, function(err, response) {
+    if (err && self.logging) {
+      console.log('error trying to re-authenticate user');
+    } else {
+
+      //save the re-authed token and current email/username
+      self.setToken(response.access_token);
+
+    }
+    if (typeof(callback) === 'function') {
+      callback(err);
+    }
+  });
+};
+
+
+Usergrid.Client.prototype.reAuthenticate = function (email, callback) {
+  var self = this;
+  var options = {
+    method:'GET',
+    endpoint:'management/users/'+email,
+    mQuery:true
+  };
+  this.request(options, function(err, response) {
+    var organizations = {};
+    var applications = {};
+    var user = {};
+    var data;
+    if (err && self.logging) {
+      console.log('error trying to full authenticate user');
+    } else {
+      data = response.data;
+      self.setToken(data.token);
+      self.set('email', data.email);
+
+      //delete next block and corresponding function when iframes are refactored
+      localStorage.setItem('accessToken', data.token);
+      localStorage.setItem('userUUID', data.uuid);
+      localStorage.setItem('userEmail', data.email);
+      //end delete block
+
+
+      var userData = {
+        "username" : data.username,
+        "email" : data.email,
+        "name" : data.name,
+        "uuid" : data.uuid
+      };
+      var options = {
+        client:self,
+        data:userData
+      };
+      user = new Usergrid.Entity(options);
+
+      organizations = data.organizations;
+      var org = '';
+      try {
+        //if we have an org stored, then use that one. Otherwise, use the first one.
+        var existingOrg = self.get('orgName');
+        org = (organizations[existingOrg])?organizations[existingOrg]:organizations[Object.keys(organizations)[0]];
+        self.set('orgName', org.name);
+      } catch(e) {
+        err = true;
+        if (self.logging) {
+          console.log('error selecting org');
+        }
+      } //should always be an org
+
+      applications = self.parseApplicationsArray(org);
+      self.selectFirstApp(applications);
+
+      self.setObject('organizations', organizations);
+      self.setObject('applications', applications);
+
+    }
+    if (typeof(callback) === 'function') {
+      callback(err, data, user, organizations, applications);
+    }
+  });
+};
+
+/*
+ *  A public method to log in an app user with facebook - stores the token for later use
+ *
+ *  @method loginFacebook
+ *  @public
+ *  @params {string} username
+ *  @params {string} password
+ *  @param {function} callback
+ *  @return {callback} callback(err, data)
+ */
+Usergrid.Client.prototype.loginFacebook = function (facebookToken, callback) {
+  var self = this;
+  var options = {
+    method:'GET',
+    endpoint:'auth/facebook',
+    qs:{
+      fb_access_token: facebookToken
+    }
+  };
+  this.request(options, function(err, data) {
+    var user = {};
+    if (err && self.logging) {
+      console.log('error trying to log user in');
+    } else {
+      var options = {
+        client: self,
+        data: data.user
+      }
+      user = new Usergrid.Entity(options);
+      self.setToken(data.access_token);
+    }
+    if (typeof(callback) === 'function') {
+      callback(err, data, user);
+    }
+  });
+};
+
+/*
+ *  A public method to get the currently logged in user entity
+ *
+ *  @method getLoggedInUser
+ *  @public
+ *  @param {function} callback
+ *  @return {callback} callback(err, data)
+ */
+Usergrid.Client.prototype.getLoggedInUser = function (callback) {
+  if (!this.getToken()) {
+    callback(true, null, null);
+  } else {
+    var self = this;
+    var options = {
+      method:'GET',
+      endpoint:'users/me'
+    };
+    this.request(options, function(err, data) {
+      if (err) {
+        if (self.logging) {
+          console.log('error trying to log user in');
+        }
+        if (typeof(callback) === 'function') {
+          callback(err, data, null);
+        }
+      } else {
+        var options = {
+          client:self,
+          data:data.entities[0]
+        };
+        var user = new Usergrid.Entity(options);
+        if (typeof(callback) === 'function') {
+          callback(err, data, user);
+        }
+      }
+    });
+  }
+};
+
+/*
+ *  A public method to test if a user is logged in - does not guarantee that the token is still valid,
+ *  but rather that one exists
+ *
+ *  @method isLoggedIn
+ *  @public
+ *  @return {boolean} Returns true the user is logged in (has token and uuid), false if not
+ */
+Usergrid.Client.prototype.isLoggedIn = function () {
+  if (this.getToken() && this.getToken() != 'null') {
+    return true;
+  }
+  return false;
+};
+
+/*
+ *  A public method to log out an app user - clears all user fields from client
+ *
+ *  @method logout
+ *  @public
+ *  @return none
+ */
+Usergrid.Client.prototype.logout = function () {
+  this.setToken(null);
+};
+
+/*
+ *  A private method to build the curl call to display on the command line
+ *
+ *  @method buildCurlCall
+ *  @private
+ *  @param {object} options
+ *  @return {string} curl
+ */
+Usergrid.Client.prototype.buildCurlCall = function (options) {
+  var curl = 'curl';
+  var method = (options.method || 'GET').toUpperCase();
+  var body = options.body || {};
+  var uri = options.uri;
+
+  //curl - add the method to the command (no need to add anything for GET)
+  if (method === 'POST') {
+    curl += ' -X POST';
+  } else if (method === 'PUT') {
+    curl += ' -X PUT';
+  } else if (method === 'DELETE') {
+    curl += ' -X DELETE';
+  } else {
+    curl += ' -X GET';
+  }
+
+  //curl - append the path
+  curl += ' ' + uri;
+
+  //curl - add the body
+  if("undefined"!== typeof window){body = JSON.stringify(body);}//only in node module
+  if (body !== '"{}"' && method !== 'GET' && method !== 'DELETE') {
+    //curl - add in the json obj
+    curl += " -d '" + body + "'";
+  }
+
+  //log the curl command to the console
+  console.log(curl);
+
+  return curl;
+}
+
+Usergrid.Client.prototype.getDisplayImage = function (email, picture, size) {
+  try {
+    if (picture) {
+      return picture;
+    }
+    var size = size || 50;
+    if (email.length) {
+      return 'https://secure.gravatar.com/avatar/' + MD5(email) + '?s=' + size + encodeURI("&d=https://apigee.com/usergrid/images/user_profile.png");
+    } else {
+      return 'https://apigee.com/usergrid/images/user_profile.png';
+    }
+  } catch(e) {
+    return 'https://apigee.com/usergrid/images/user_profile.png';
+  }
+}
+

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ef53b3b5/sdks/html5-javascript/lib/Collection.js
----------------------------------------------------------------------
diff --git a/sdks/html5-javascript/lib/Collection.js b/sdks/html5-javascript/lib/Collection.js
new file mode 100644
index 0000000..57fdf06
--- /dev/null
+++ b/sdks/html5-javascript/lib/Collection.js
@@ -0,0 +1,446 @@
+
+/*
+ *  The Collection class models Usergrid Collections.  It essentially
+ *  acts as a container for holding Entity objects, while providing
+ *  additional funcitonality such as paging, and saving
+ *
+ *  @constructor
+ *  @param {string} options - configuration object
+ *  @param {function} callback
+ *  @return {callback} callback(err, data)
+ */
+Usergrid.Collection = function(options, callback) {
+
+  if (options) {
+    this._client = options.client;
+    this._type = options.type;
+    this.qs = options.qs || {};
+
+    //iteration
+    this._list = options.list || [];
+    this._iterator = options.iterator || -1; //first thing we do is increment, so set to -1
+
+    //paging
+    this._previous = options.previous || [];
+    this._next = options.next || null;
+    this._cursor = options.cursor || null;
+
+    //restore entities if available
+    if (options.list) {
+      var count = options.list.length;
+      for(var i=0;i<count;i++){
+        //make new entity with
+        var entity = this._client.restoreEntity(options.list[i]);
+        this._list[i] = entity;
+      }
+    }
+  }
+  if (callback) {
+    //populate the collection
+    this.fetch(callback);
+  }
+
+};
+
+
+/*
+ *  gets the data from the collection object for serialization
+ *
+ *  @method serialize
+ *  @return {object} data
+ */
+Usergrid.Collection.prototype.serialize = function () {
+
+  //pull out the state from this object and return it
+  var data = {}
+  data.type = this._type;
+  data.qs = this.qs;
+  data.iterator = this._iterator;
+  data.previous = this._previous;
+  data.next = this._next;
+  data.cursor = this._cursor;
+
+  this.resetEntityPointer();
+  var i=0;
+  data.list = [];
+  while(this.hasNextEntity()) {
+    var entity = this.getNextEntity();
+    data.list[i] = entity.serialize();
+    i++;
+  }
+
+  data = JSON.stringify(data);
+  return data;
+};
+
+Usergrid.Collection.prototype.addCollection = function (collectionName, options, callback) {
+  self = this;
+  options.client = this._client;
+  var collection = new Usergrid.Collection(options, function(err, data) {
+    if (typeof(callback) === 'function') {
+
+      collection.resetEntityPointer();
+      while(collection.hasNextEntity()) {
+        var user = collection.getNextEntity();
+        var email = user.get('email');
+        var image = self._client.getDisplayImage(user.get('email'), user.get('picture'));
+        user._portal_image_icon = image;
+      }
+
+      self[collectionName] = collection;
+      callback(err, collection);
+    }
+  });
+};
+
+/*
+ *  Populates the collection from the server
+ *
+ *  @method fetch
+ *  @param {function} callback
+ *  @return {callback} callback(err, data)
+ */
+Usergrid.Collection.prototype.fetch = function (callback) {
+  var self = this;
+  var qs = this.qs;
+
+  //add in the cursor if one is available
+  if (this._cursor) {
+    qs.cursor = this._cursor;
+  } else {
+    delete qs.cursor;
+  }
+  var options = {
+    method:'GET',
+    endpoint:this._type,
+    qs:this.qs
+  };
+  this._client.request(options, function (err, data) {
+    if(err && self._client.logging) {
+      console.log('error getting collection');
+    } else {
+      //save the cursor if there is one
+      var cursor = data.cursor || null;
+      self.saveCursor(cursor);
+      if (data.entities) {
+        self.resetEntityPointer();
+        var count = data.entities.length;
+        //save entities locally
+        self._list = []; //clear the local list first
+        for (var i=0;i<count;i++) {
+          var uuid = data.entities[i].uuid;
+          if (uuid) {
+            var entityData = data.entities[i] || {};
+            self._baseType = data.entities[i].type; //store the base type in the collection
+            entityData.type = self._type;//make sure entities are same type (have same path) as parent collection.
+            var entityOptions = {
+              type:self._type,
+              client:self._client,
+              uuid:uuid,
+              data:entityData
+            };
+
+            var ent = new Usergrid.Entity(entityOptions);
+            ent._json = JSON.stringify(entityData, null, 2);
+            var ct = self._list.length;
+            self._list[ct] = ent;
+          }
+        }
+      }
+    }
+    if (typeof(callback) === 'function') {
+      callback(err, data);
+    }
+  });
+};
+
+/*
+ *  Adds a new Entity to the collection (saves, then adds to the local object)
+ *
+ *  @method addNewEntity
+ *  @param {object} entity
+ *  @param {function} callback
+ *  @return {callback} callback(err, data, entity)
+ */
+Usergrid.Collection.prototype.addEntity = function (options, callback) {
+  var self = this;
+  options.type = this._type;
+
+  //create the new entity
+  this._client.createEntity(options, function (err, entity) {
+    if (!err) {
+      //then add the entity to the list
+      var count = self._list.length;
+      self._list[count] = entity;
+    }
+    if (typeof(callback) === 'function') {
+      callback(err, entity);
+    }
+  });
+};
+
+Usergrid.Collection.prototype.addExistingEntity = function (entity) {
+  //entity should already exist in the db, so just add it to the list
+  var count = this._list.length;
+  this._list[count] = entity;
+};
+
+/*
+ *  Removes the Entity from the collection, then destroys the object on the server
+ *
+ *  @method destroyEntity
+ *  @param {object} entity
+ *  @param {function} callback
+ *  @return {callback} callback(err, data)
+ */
+Usergrid.Collection.prototype.destroyEntity = function (entity, callback) {
+  var self = this;
+  entity.destroy(function(err, data) {
+    if (err) {
+      if (self._client.logging) {
+        console.log('could not destroy entity');
+      }
+      if (typeof(callback) === 'function') {
+        callback(err, data);
+      }
+    } else {
+      //destroy was good, so repopulate the collection
+      self.fetch(callback);
+    }
+  });
+  //remove entity from the local store
+  this.removeEntity(entity);
+};
+
+
+Usergrid.Collection.prototype.removeEntity = function (entity) {
+  var uuid = entity.get('uuid');
+  for (var key in this._list) {
+    var listItem = this._list[key];
+    if (listItem.get('uuid') === uuid) {
+      return this._list.splice(key, 1);
+    }
+  }
+  return false;
+};
+
+/*
+ *  Looks up an Entity by UUID
+ *
+ *  @method getEntityByUUID
+ *  @param {string} UUID
+ *  @param {function} callback
+ *  @return {callback} callback(err, data, entity)
+ */
+Usergrid.Collection.prototype.getEntityByUUID = function (uuid, callback) {
+
+  for (var key in this._list) {
+    var listItem = this._list[key];
+    if (listItem.get('uuid') === uuid) {
+      return listItem;
+    }
+  }
+
+  //get the entity from the database
+  var options = {
+    data: {
+      type: this._type,
+      uuid:uuid
+    },
+    client: this._client
+  }
+  var entity = new Usergrid.Entity(options);
+  entity.fetch(callback);
+};
+
+/*
+ *  Returns the first Entity of the Entity list - does not affect the iterator
+ *
+ *  @method getFirstEntity
+ *  @return {object} returns an entity object
+ */
+Usergrid.Collection.prototype.getFirstEntity = function () {
+  var count = this._list.length;
+  if (count > 0) {
+    return this._list[0];
+  }
+  return null;
+};
+
+/*
+ *  Returns the last Entity of the Entity list - does not affect the iterator
+ *
+ *  @method getLastEntity
+ *  @return {object} returns an entity object
+ */
+Usergrid.Collection.prototype.getLastEntity = function () {
+  var count = this._list.length;
+  if (count > 0) {
+    return this._list[count-1];
+  }
+  return null;
+};
+
+/*
+ *  Entity iteration -Checks to see if there is a "next" entity
+ *  in the list.  The first time this method is called on an entity
+ *  list, or after the resetEntityPointer method is called, it will
+ *  return true referencing the first entity in the list
+ *
+ *  @method hasNextEntity
+ *  @return {boolean} true if there is a next entity, false if not
+ */
+Usergrid.Collection.prototype.hasNextEntity = function () {
+  var next = this._iterator + 1;
+  var hasNextElement = (next >=0 && next < this._list.length);
+  if(hasNextElement) {
+    return true;
+  }
+  return false;
+};
+
+/*
+ *  Entity iteration - Gets the "next" entity in the list.  The first
+ *  time this method is called on an entity list, or after the method
+ *  resetEntityPointer is called, it will return the,
+ *  first entity in the list
+ *
+ *  @method hasNextEntity
+ *  @return {object} entity
+ */
+Usergrid.Collection.prototype.getNextEntity = function () {
+  this._iterator++;
+  var hasNextElement = (this._iterator >= 0 && this._iterator <= this._list.length);
+  if(hasNextElement) {
+    return this._list[this._iterator];
+  }
+  return false;
+};
+
+/*
+ *  Entity iteration - Checks to see if there is a "previous"
+ *  entity in the list.
+ *
+ *  @method hasPrevEntity
+ *  @return {boolean} true if there is a previous entity, false if not
+ */
+Usergrid.Collection.prototype.hasPrevEntity = function () {
+  var previous = this._iterator - 1;
+  var hasPreviousElement = (previous >=0 && previous < this._list.length);
+  if(hasPreviousElement) {
+    return true;
+  }
+  return false;
+};
+
+/*
+ *  Entity iteration - Gets the "previous" entity in the list.
+ *
+ *  @method getPrevEntity
+ *  @return {object} entity
+ */
+Usergrid.Collection.prototype.getPrevEntity = function () {
+  this._iterator--;
+  var hasPreviousElement = (this._iterator >= 0 && this._iterator <= this._list.length);
+  if(hasPreviousElement) {
+    return this._list[this._iterator];
+  }
+  return false;
+};
+
+/*
+ *  Entity iteration - Resets the iterator back to the beginning
+ *  of the list
+ *
+ *  @method resetEntityPointer
+ *  @return none
+ */
+Usergrid.Collection.prototype.resetEntityPointer = function () {
+  this._iterator  = -1;
+};
+
+/*
+ * Method to save off the cursor just returned by the last API call
+ *
+ * @public
+ * @method saveCursor
+ * @return none
+ */
+Usergrid.Collection.prototype.saveCursor = function(cursor) {
+  //if current cursor is different, grab it for next cursor
+  if (this._next !== cursor) {
+    this._next = cursor;
+  }
+};
+
+/*
+ * Resets the paging pointer (back to original page)
+ *
+ * @public
+ * @method resetPaging
+ * @return none
+ */
+Usergrid.Collection.prototype.resetPaging = function() {
+  this._previous = [];
+  this._next = null;
+  this._cursor = null;
+};
+
+/*
+ *  Paging -  checks to see if there is a next page od data
+ *
+ *  @method hasNextPage
+ *  @return {boolean} returns true if there is a next page of data, false otherwise
+ */
+Usergrid.Collection.prototype.hasNextPage = function () {
+  return (this._next);
+};
+
+/*
+ *  Paging - advances the cursor and gets the next
+ *  page of data from the API.  Stores returned entities
+ *  in the Entity list.
+ *
+ *  @method getNextPage
+ *  @param {function} callback
+ *  @return {callback} callback(err, data)
+ */
+Usergrid.Collection.prototype.getNextPage = function (callback) {
+  if (this.hasNextPage()) {
+    //set the cursor to the next page of data
+    this._previous.push(this._cursor);
+    this._cursor = this._next;
+    //empty the list
+    this._list = [];
+    this.fetch(callback);
+  }
+}
+
+/*
+ *  Paging -  checks to see if there is a previous page od data
+ *
+ *  @method hasPreviousPage
+ *  @return {boolean} returns true if there is a previous page of data, false otherwise
+ */
+Usergrid.Collection.prototype.hasPreviousPage = function () {
+  return (this._previous.length > 0);
+};
+
+/*
+ *  Paging - reverts the cursor and gets the previous
+ *  page of data from the API.  Stores returned entities
+ *  in the Entity list.
+ *
+ *  @method getPreviousPage
+ *  @param {function} callback
+ *  @return {callback} callback(err, data)
+ */
+Usergrid.Collection.prototype.getPreviousPage = function (callback) {
+  if (this.hasPreviousPage()) {
+    this._next=null; //clear out next so the comparison will find the next item
+    this._cursor = this._previous.pop();
+    //empty the list
+    this._list = [];
+    this.fetch(callback);
+  }
+};

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ef53b3b5/sdks/html5-javascript/lib/Entity.js
----------------------------------------------------------------------
diff --git a/sdks/html5-javascript/lib/Entity.js b/sdks/html5-javascript/lib/Entity.js
new file mode 100644
index 0000000..e070d8a
--- /dev/null
+++ b/sdks/html5-javascript/lib/Entity.js
@@ -0,0 +1,646 @@
+/*
+ *  A class to Model a Usergrid Entity.
+ *  Set the type and uuid of entity in the 'data' json object
+ *
+ *  @constructor
+ *  @param {object} options {client:client, data:{'type':'collection_type', uuid:'uuid', 'key':'value'}}
+ */
+Usergrid.Entity = function(options) {
+  if (options) {
+    this._data = options.data || {};
+    this._client = options.client || {};
+  }
+};
+
+/*
+ *  returns a serialized version of the entity object
+ *
+ *  Note: use the client.restoreEntity() function to restore
+ *
+ *  @method serialize
+ *  @return {string} data
+ */
+Usergrid.Entity.prototype.serialize = function () {
+  return JSON.stringify(this._data);
+};
+
+/*
+ *  gets a specific field or the entire data object. If null or no argument
+ *  passed, will return all data, else, will return a specific field
+ *
+ *  @method get
+ *  @param {string} field
+ *  @return {string} || {object} data
+ */
+Usergrid.Entity.prototype.get = function (field) {
+  if (field) {
+    return this._data[field];
+  } else {
+    return this._data;
+  }
+};
+
+/*
+ *  adds a specific key value pair or object to the Entity's data
+ *  is additive - will not overwrite existing values unless they
+ *  are explicitly specified
+ *
+ *  @method set
+ *  @param {string} key || {object}
+ *  @param {string} value
+ *  @return none
+ */
+Usergrid.Entity.prototype.set = function (key, value) {
+  if (typeof key === 'object') {
+    for(var field in key) {
+      this._data[field] = key[field];
+    }
+  } else if (typeof key === 'string') {
+    if (value === null) {
+      delete this._data[key];
+    } else {
+      this._data[key] = value;
+    }
+  } else {
+    this._data = {};
+  }
+};
+
+/*
+ *  Saves the entity back to the database
+ *
+ *  @method save
+ *  @public
+ *  @param {function} callback
+ *  @return {callback} callback(err, data)
+ */
+Usergrid.Entity.prototype.save = function (callback) {
+  var type = this.get('type');
+  var method = 'POST';
+  if (isUUID(this.get('uuid'))) {
+    method = 'PUT';
+    type += '/' + this.get('uuid');
+  }
+
+  //update the entity
+  var self = this;
+  var data = {};
+  var entityData = this.get();
+    var password = this.get('password');
+    var oldpassword = this.get('oldpassword');
+    var newpassword = this.get('newpassword');
+  //remove system specific properties
+  for (var item in entityData) {
+    if (item === 'metadata' || item === 'created' || item === 'modified' ||
+          item === 'oldpassword' || item === 'newpassword' || //old and new pw not added to data
+      item === 'type' || item === 'activated' || item === 'uuid') {
+      continue;
+    }
+    data[item] = entityData[item];
+  }
+  var options =  {
+    method:method,
+    endpoint:type,
+    body:data
+  };
+  //save the entity first
+  this._client.request(options, function (err, retdata) {
+      //clear out pw info if present
+      self.set('password', null);
+      self.set('oldpassword', null);
+      self.set('newpassword', null);
+    if (err && self._client.logging) {
+      console.log('could not save entity');
+      if (typeof(callback) === 'function') {
+        return callback(err, retdata, self);
+      }
+    } else {
+      if (retdata.entities) {
+        if (retdata.entities.length) {
+          var entity = retdata.entities[0];
+          self.set(entity);
+          var path = retdata.path;
+          //for connections, API returns type
+          while (path.substring(0, 1) === "/") {
+            path = path.substring(1);
+          }
+          self.set('type', path);
+        }
+      }
+      //if this is a user, update the password if it has been specified;
+        var needPasswordChange = ((self.get('type') === 'user' || self.get('type') === 'users') && oldpassword && newpassword);
+      if (needPasswordChange) {
+        //Note: we have a ticket in to change PUT calls to /users to accept the password change
+        //      once that is done, we will remove this call and merge it all into one
+        var pwdata = {};
+          pwdata.oldpassword = oldpassword;
+          pwdata.newpassword = newpassword;
+        var options = {
+          method:'PUT',
+          endpoint:type+'/password',
+          body:pwdata
+        }
+        self._client.request(options, function (err, data) {
+          if (err && self._client.logging) {
+            console.log('could not update user');
+          }
+          //remove old and new password fields so they don't end up as part of the entity object
+          self.set('oldpassword', null);
+          self.set('newpassword', null);
+          if (typeof(callback) === 'function') {
+            callback(err, data, self);
+          }
+        });
+      } else if (typeof(callback) === 'function') {
+        callback(err, retdata, self);
+      }
+    }
+  });
+};
+
+/*
+ *  refreshes the entity by making a GET call back to the database
+ *
+ *  @method fetch
+ *  @public
+ *  @param {function} callback
+ *  @return {callback} callback(err, data)
+ */
+Usergrid.Entity.prototype.fetch = function (callback) {
+  var type = this.get('type');
+  var self = this;
+
+  //Check for an entity type, then if a uuid is available, use that, otherwise, use the name
+  try {
+    if (type === undefined) {
+      throw 'cannot fetch entity, no entity type specified'
+    } else if (this.get('uuid')) {
+      type += '/' + this.get('uuid');
+    } else if (type === 'users' && this.get('username')) {
+      type += '/' + this.get('username');
+    } else if (this.get('name')) {
+      type += '/' + encodeURIComponent(this.get('name'));
+    } else if (typeof(callback) === 'function') {
+      throw 'no_name_specified';
+    }
+  } catch (e) {
+    if (self._client.logging) {
+      console.log(e);
+    }
+    return callback(true, {
+      error: e
+    }, self);
+  }
+  var options = {
+    method:'GET',
+    endpoint:type
+  };
+  this._client.request(options, function (err, data) {
+    if (err && self._client.logging) {
+      console.log('could not get entity');
+    } else {
+      if (data.user) {
+        self.set(data.user);
+        self._json = JSON.stringify(data.user, null, 2);
+      } else if (data.entities) {
+        if (data.entities.length) {
+          var entity = data.entities[0];
+          self.set(entity);
+        }
+      }
+    }
+    if (typeof(callback) === 'function') {
+      callback(err, data, self);
+    }
+  });
+};
+
+/*
+ *  deletes the entity from the database - will only delete
+ *  if the object has a valid uuid
+ *
+ *  @method destroy
+ *  @public
+ *  @param {function} callback
+ *  @return {callback} callback(err, data)
+ *
+ */
+Usergrid.Entity.prototype.destroy = function (callback) {
+  var self = this;
+  var type = this.get('type');
+  if (isUUID(this.get('uuid'))) {
+    type += '/' + this.get('uuid');
+  } else {
+    if (typeof(callback) === 'function') {
+      var error = 'Error trying to delete object - no uuid specified.';
+      if (self._client.logging) {
+        console.log(error);
+      }
+      callback(true, error);
+    }
+  }
+  var options = {
+    method:'DELETE',
+    endpoint:type
+  };
+  this._client.request(options, function (err, data) {
+    if (err && self._client.logging) {
+      console.log('entity could not be deleted');
+    } else {
+      self.set(null);
+    }
+    if (typeof(callback) === 'function') {
+      callback(err, data);
+    }
+  });
+};
+
+/*
+ *  connects one entity to another
+ *
+ *  @method connect
+ *  @public
+ *  @param {string} connection
+ *  @param {object} entity
+ *  @param {function} callback
+ *  @return {callback} callback(err, data)
+ *
+ */
+Usergrid.Entity.prototype.connect = function (connection, entity, callback) {
+
+  var self = this;
+
+  var error;
+  //connectee info
+  var connecteeType = entity.get('type');
+  var connectee = this.getEntityId(entity);
+  if (!connectee) {
+    if (typeof(callback) === 'function') {
+      error = 'Error trying to delete object - no uuid specified.';
+      if (self._client.logging) {
+        console.log(error);
+      }
+      callback(true, error);
+    }
+    return;
+  }
+
+  //connector info
+  var connectorType = this.get('type');
+  var connector = this.getEntityId(this);
+  if (!connector) {
+    if (typeof(callback) === 'function') {
+      error = 'Error in connect - no uuid specified.';
+      if (self._client.logging) {
+        console.log(error);
+      }
+      callback(true, error);
+    }
+    return;
+  }
+
+  var endpoint = connectorType + '/' + connector + '/' + connection + '/' + connecteeType + '/' + connectee;
+  var options = {
+    method:'POST',
+    endpoint:endpoint
+  };
+  this._client.request(options, function (err, data) {
+    if (err && self._client.logging) {
+      console.log('entity could not be connected');
+    }
+    if (typeof(callback) === 'function') {
+      callback(err, data);
+    }
+  });
+};
+
+/*
+ *  returns a unique identifier for an entity
+ *
+ *  @method connect
+ *  @public
+ *  @param {object} entity
+ *  @param {function} callback
+ *  @return {callback} callback(err, data)
+ *
+ */
+Usergrid.Entity.prototype.getEntityId = function (entity) {
+  var id = false;
+  if (isUUID(entity.get('uuid'))) {
+    id = entity.get('uuid');
+  } else {
+    if (type === 'users') {
+      id = entity.get('username');
+    } else if (entity.get('name')) {
+      id = entity.get('name');
+    }
+  }
+  return id;
+};
+
+/*
+ *  gets an entities connections
+ *
+ *  @method getConnections
+ *  @public
+ *  @param {string} connection
+ *  @param {object} entity
+ *  @param {function} callback
+ *  @return {callback} callback(err, data, connections)
+ *
+ */
+Usergrid.Entity.prototype.getConnections = function (connection, callback) {
+
+  var self = this;
+
+  //connector info
+  var connectorType = this.get('type');
+  var connector = this.getEntityId(this);
+  if (!connector) {
+    if (typeof(callback) === 'function') {
+      var error = 'Error in getConnections - no uuid specified.';
+      if (self._client.logging) {
+        console.log(error);
+      }
+      callback(true, error);
+    }
+    return;
+  }
+
+  var endpoint = connectorType + '/' + connector + '/' + connection + '/';
+  var options = {
+    method:'GET',
+    endpoint:endpoint
+  };
+  this._client.request(options, function (err, data) {
+    if (err && self._client.logging) {
+      console.log('entity could not be connected');
+    }
+
+    self[connection] = {};
+
+    var length = data.entities.length;
+    for (var i = 0; i < length; i++) {
+      if (data.entities[i].type === 'user'){
+        self[connection][data.entities[i].username] = data.entities[i];
+      } else {
+        self[connection][data.entities[i].name] = data.entities[i]
+      }
+    }
+
+    if (typeof(callback) === 'function') {
+      callback(err, data, data.entities);
+    }
+  });
+
+};
+
+Usergrid.Entity.prototype.getGroups = function (callback) {
+
+  var self = this;
+
+  var endpoint = 'users' + '/' + this.get('uuid') + '/groups' ;
+  var options = {
+    method:'GET',
+    endpoint:endpoint
+  };
+  this._client.request(options, function (err, data) {
+    if (err && self._client.logging) {
+      console.log('entity could not be connected');
+    }
+
+    self.groups = data.entities;
+
+    if (typeof(callback) === 'function') {
+      callback(err, data, data.entities);
+    }
+  });
+
+};
+
+Usergrid.Entity.prototype.getActivities = function (callback) {
+
+  var self = this;
+
+  var endpoint = this.get('type') + '/' + this.get('uuid') + '/activities' ;
+  var options = {
+    method:'GET',
+    endpoint:endpoint
+  };
+  this._client.request(options, function (err, data) {
+    if (err && self._client.logging) {
+      console.log('entity could not be connected');
+    }
+
+    for (var entity in data.entities) {
+      data.entities[entity].createdDate = (new Date(data.entities[entity].created)).toUTCString();
+    }
+
+    self.activities = data.entities;
+
+    if (typeof(callback) === 'function') {
+      callback(err, data, data.entities);
+    }
+  });
+
+};
+
+Usergrid.Entity.prototype.getFollowing = function (callback) {
+
+  var self = this;
+
+  var endpoint = 'users' + '/' + this.get('uuid') + '/following' ;
+  var options = {
+    method:'GET',
+    endpoint:endpoint
+  };
+  this._client.request(options, function (err, data) {
+    if (err && self._client.logging) {
+      console.log('could not get user following');
+    }
+
+    for (var entity in data.entities) {
+      data.entities[entity].createdDate = (new Date(data.entities[entity].created)).toUTCString();
+      var image = self._client.getDisplayImage(data.entities[entity].email, data.entities[entity].picture);
+      data.entities[entity]._portal_image_icon =  image;
+    }
+
+    self.following = data.entities;
+
+    if (typeof(callback) === 'function') {
+      callback(err, data, data.entities);
+    }
+  });
+
+};
+
+
+Usergrid.Entity.prototype.getFollowers = function (callback) {
+
+  var self = this;
+
+  var endpoint = 'users' + '/' + this.get('uuid') + '/followers' ;
+  var options = {
+    method:'GET',
+    endpoint:endpoint
+  };
+  this._client.request(options, function (err, data) {
+    if (err && self._client.logging) {
+      console.log('could not get user followers');
+    }
+
+    for (var entity in data.entities) {
+      data.entities[entity].createdDate = (new Date(data.entities[entity].created)).toUTCString();
+      var image = self._client.getDisplayImage(data.entities[entity].email, data.entities[entity].picture);
+      data.entities[entity]._portal_image_icon =  image;
+    }
+
+    self.followers = data.entities;
+
+    if (typeof(callback) === 'function') {
+      callback(err, data, data.entities);
+    }
+  });
+
+};
+
+Usergrid.Entity.prototype.getRoles = function (callback) {
+
+  var self = this;
+
+  var endpoint = this.get('type') + '/' + this.get('uuid') + '/roles' ;
+  var options = {
+    method:'GET',
+    endpoint:endpoint
+  };
+  this._client.request(options, function (err, data) {
+    if (err && self._client.logging) {
+      console.log('could not get user roles');
+    }
+
+    self.roles = data.entities;
+
+    if (typeof(callback) === 'function') {
+      callback(err, data, data.entities);
+    }
+  });
+
+};
+
+Usergrid.Entity.prototype.getPermissions = function (callback) {
+
+  var self = this;
+
+  var endpoint = this.get('type') + '/' + this.get('uuid') + '/permissions' ;
+  var options = {
+    method:'GET',
+    endpoint:endpoint
+  };
+  this._client.request(options, function (err, data) {
+    if (err && self._client.logging) {
+      console.log('could not get user permissions');
+    }
+
+    var permissions = [];
+    if (data.data) {
+      var perms = data.data;
+      var count = 0;
+
+      for (var i in perms) {
+        count++;
+        var perm = perms[i];
+        var parts = perm.split(':');
+        var ops_part = "";
+        var path_part = parts[0];
+
+        if (parts.length > 1) {
+          ops_part = parts[0];
+          path_part = parts[1];
+        }
+
+        ops_part.replace("*", "get,post,put,delete")
+        var ops = ops_part.split(',');
+        var ops_object = {}
+        ops_object.get = 'no';
+        ops_object.post = 'no';
+        ops_object.put = 'no';
+        ops_object.delete = 'no';
+        for (var j in ops) {
+          ops_object[ops[j]] = 'yes';
+        }
+
+        permissions.push({
+          operations: ops_object,
+          path: path_part,
+          perm: perm
+        });
+      }
+    }
+
+    self.permissions = permissions;
+
+    if (typeof(callback) === 'function') {
+      callback(err, data, data.entities);
+    }
+  });
+
+};
+
+/*
+ *  disconnects one entity from another
+ *
+ *  @method disconnect
+ *  @public
+ *  @param {string} connection
+ *  @param {object} entity
+ *  @param {function} callback
+ *  @return {callback} callback(err, data)
+ *
+ */
+Usergrid.Entity.prototype.disconnect = function (connection, entity, callback) {
+
+  var self = this;
+
+  var error;
+  //connectee info
+  var connecteeType = entity.get('type');
+  var connectee = this.getEntityId(entity);
+  if (!connectee) {
+    if (typeof(callback) === 'function') {
+      error = 'Error trying to delete object - no uuid specified.';
+      if (self._client.logging) {
+        console.log(error);
+      }
+      callback(true, error);
+    }
+    return;
+  }
+
+  //connector info
+  var connectorType = this.get('type');
+  var connector = this.getEntityId(this);
+  if (!connector) {
+    if (typeof(callback) === 'function') {
+      error = 'Error in connect - no uuid specified.';
+      if (self._client.logging) {
+        console.log(error);
+      }
+      callback(true, error);
+    }
+    return;
+  }
+
+  var endpoint = connectorType + '/' + connector + '/' + connection + '/' + connecteeType + '/' + connectee;
+  var options = {
+    method:'DELETE',
+    endpoint:endpoint
+  };
+  this._client.request(options, function (err, data) {
+    if (err && self._client.logging) {
+      console.log('entity could not be disconnected');
+    }
+    if (typeof(callback) === 'function') {
+      callback(err, data);
+    }
+  });
+};

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ef53b3b5/sdks/html5-javascript/lib/Event.js
----------------------------------------------------------------------
diff --git a/sdks/html5-javascript/lib/Event.js b/sdks/html5-javascript/lib/Event.js
new file mode 100644
index 0000000..789240f
--- /dev/null
+++ b/sdks/html5-javascript/lib/Event.js
@@ -0,0 +1,163 @@
+var COUNTER_RESOLUTIONS = {
+  'ALL': 'all',
+  'MINUTE': 'minute',
+  'FIVE_MINUTES': 'five_minutes',
+  'HALF_HOUR': 'half_hour',
+  'HOUR': 'hour',
+  'SIX_DAY': 'six_day',
+  'DAY': 'day',
+  'WEEK': 'week',
+  'MONTH': 'month'
+};
+COUNTER_RESOLUTIONS.valueOf=function(str){
+  Object.keys(COUNTER_RESOLUTIONS).forEach(function(res){
+    if(COUNTER_RESOLUTIONS[res]===str){
+      return COUNTER_RESOLUTIONS[res];
+    }
+  });
+  return COUNTER_RESOLUTIONS.ALL;
+};
+
+/*
+ *  A class to model a Usergrid event.
+ *
+ *  @constructor
+ *  @param {object} options {timestamp:0, category:'value', counters:{name : value}}
+ *  @returns {callback} callback(err, event)
+ */
+Usergrid.Event = function(options, callback) {
+  var self=this;
+  this._client = options.client;
+  this._data = options.data || {};
+  this._data.category = options.category||"UNKNOWN";
+  this._data.timestamp = options.timestamp||0;
+  this._data.type = "events";
+  this._data.counters=options.counters||{};
+  if(typeof(callback) === 'function') {
+    callback.call(self, false, self);
+  }
+  //this.save(callback);
+};
+
+/*
+ *  Inherit from Usergrid.Entity.
+ *  Note: This only accounts for data on the group object itself.
+ *  You need to use add and remove to manipulate group membership.
+ */
+Usergrid.Event.prototype = new Usergrid.Entity();
+
+Usergrid.Event.prototype.fetch=function(callback){
+  this.getData(null, null, null, null, callback);
+}
+/*
+ * increments the counter for a specific event
+ *
+ * options object: {name: counter_name}
+ *
+ * @public
+ * @method increment
+ * @params {object} options
+ * @param {function} callback
+ * @returns {callback} callback(err, event)
+ */
+Usergrid.Event.prototype.increment=function(name, value, callback){
+  var self=this;
+  if(isNaN(value)){
+    if(typeof(callback) === 'function') {
+      return callback.call(self, true, "'value' for increment, decrement must be a number");
+    }
+  }
+  self._data.counters[name]=parseInt(value);
+  return self.save(callback);
+};
+/*
+ * decrements the counter for a specific event
+ *
+ * options object: {name: counter_name}
+ *
+ * @public
+ * @method decrement
+ * @params {object} options
+ * @param {function} callback
+ * @returns {callback} callback(err, event)
+ */
+
+Usergrid.Event.prototype.decrement=function(name, value, callback){
+  this.increment(name, -(value), callback);
+};
+/*
+ * resets the counter for a specific event
+ *
+ * options object: {name: counter_name}
+ *
+ * @public
+ * @method reset
+ * @params {object} options
+ * @param {function} callback
+ * @returns {callback} callback(err, event)
+ */
+
+Usergrid.Event.prototype.reset=function(name, callback){
+  this.increment(name, 0, callback);
+};
+
+Usergrid.Event.prototype.getData=function(start, end, resolution, counters, callback){
+  var start_time, 
+      end_time,
+      res=COUNTER_RESOLUTIONS.valueOf(resolution);
+  if(start){
+    switch(typeof start){
+      case "undefined":
+        start_time=0;
+        break;
+      case "number":
+        start_time=start;
+        break;
+      case "string":
+        start_time=(isNaN(start))?Date.parse(start):parseInt(start);
+        break;
+      default:
+        start_time=Date.parse(start.toString());
+    }
+  }
+  if(end){
+    switch(typeof end){
+      case "undefined":
+        end_time=Date.now();
+        break;
+      case "number":
+        end_time=end;
+        break;
+      case "string":
+        end_time=(isNaN(end))?Date.parse(end):parseInt(end);
+        break;
+      default:
+        end_time=Date.parse(end.toString());
+    }
+  }
+  var self=this;
+  //https://api.usergrid.com/yourorgname/sandbox/counters?counter=test_counter
+  if(counters===null || "undefined"===typeof counters)
+  counters=Object.keys(this._data.counters);
+  var params=Object.keys(counters).map(function(counter){
+      return ["counter", encodeURIComponent(counters[counter])].join('=');
+    });
+  params.push('resolution='+res)
+  params.push('start_time='+String(start_time))
+  params.push('end_time='+String(end_time))
+    
+  var endpoint="counters?"+params.join('&');
+  var options= {
+    endpoint:endpoint
+  };
+  this._client.request(options, function(err, data){
+    if(data.counters && data.counters.length){
+      data.counters.forEach(function(counter){
+        self._data.counters[counter.name]=counter.value||counter.values;
+      })
+    }
+    if(typeof(callback) === 'function') {
+      callback.call(self, err, data);
+    }
+  })
+};

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ef53b3b5/sdks/html5-javascript/lib/Group.js
----------------------------------------------------------------------
diff --git a/sdks/html5-javascript/lib/Group.js b/sdks/html5-javascript/lib/Group.js
new file mode 100644
index 0000000..2a853be
--- /dev/null
+++ b/sdks/html5-javascript/lib/Group.js
@@ -0,0 +1,233 @@
+/*
+ *  A class to model a Usergrid group.
+ *  Set the path in the options object.
+ *
+ *  @constructor
+ *  @param {object} options {client:client, data: {'key': 'value'}, path:'path'}
+ */
+Usergrid.Group = function(options, callback) {
+  this._path = options.path;
+  this._list = [];
+  this._client = options.client;
+  this._data = options.data || {};
+  this._data.type = "groups";
+};
+
+/*
+ *  Inherit from Usergrid.Entity.
+ *  Note: This only accounts for data on the group object itself.
+ *  You need to use add and remove to manipulate group membership.
+ */
+Usergrid.Group.prototype = new Usergrid.Entity();
+
+/*
+ *  Fetches current group data, and members.
+ *
+ *  @method fetch
+ *  @public
+ *  @param {function} callback
+ *  @returns {function} callback(err, data)
+ */
+Usergrid.Group.prototype.fetch = function(callback) {
+  var self = this;
+  var groupEndpoint = 'groups/'+this._path;
+  var memberEndpoint = 'groups/'+this._path+'/users';
+
+  var groupOptions = {
+    method:'GET',
+    endpoint:groupEndpoint
+  }
+
+  var memberOptions = {
+    method:'GET',
+    endpoint:memberEndpoint
+  }
+
+  this._client.request(groupOptions, function(err, data){
+    if(err) {
+      if(self._client.logging) {
+        console.log('error getting group');
+      }
+      if(typeof(callback) === 'function') {
+        callback(err, data);
+      }
+    } else {
+      if(data.entities) {
+        var groupData = data.entities[0];
+        self._data = groupData || {};
+        self._client.request(memberOptions, function(err, data) {
+          if(err && self._client.logging) {
+            console.log('error getting group users');
+          } else {
+            if(data.entities) {
+              var count = data.entities.length;
+              self._list = [];
+              for (var i = 0; i < count; i++) {
+                var uuid = data.entities[i].uuid;
+                if(uuid) {
+                  var entityData = data.entities[i] || {};
+                  var entityOptions = {
+                    type: entityData.type,
+                    client: self._client,
+                    uuid:uuid,
+                    data:entityData
+                  };
+                  var entity = new Usergrid.Entity(entityOptions);
+                  self._list.push(entity);
+                }
+
+              }
+            }
+          }
+          if(typeof(callback) === 'function') {
+            callback(err, data, self._list);
+          }
+        });
+      }
+    }
+  });
+};
+
+/*
+ *  Retrieves the members of a group.
+ *
+ *  @method members
+ *  @public
+ *  @param {function} callback
+ *  @return {function} callback(err, data);
+ */
+Usergrid.Group.prototype.members = function(callback) {
+  if(typeof(callback) === 'function') {
+    callback(null, this._list);
+  }
+};
+
+/*
+ *  Adds a user to the group, and refreshes the group object.
+ *
+ *  Options object: {user: user_entity}
+ *
+ *  @method add
+ *  @public
+ *  @params {object} options
+ *  @param {function} callback
+ *  @return {function} callback(err, data)
+ */
+Usergrid.Group.prototype.add = function(options, callback) {
+  var self = this;
+  var options = {
+    method:"POST",
+    endpoint:"groups/"+this._path+"/users/"+options.user.get('username')
+  }
+
+  this._client.request(options, function(error, data){
+    if(error) {
+      if(typeof(callback) === 'function') {
+        callback(error, data, data.entities);
+      }
+    } else {
+      self.fetch(callback);
+    }
+  });
+}
+
+/*
+ *  Removes a user from a group, and refreshes the group object.
+ *
+ *  Options object: {user: user_entity}
+ *
+ *  @method remove
+ *  @public
+ *  @params {object} options
+ *  @param {function} callback
+ *  @return {function} callback(err, data)
+ */
+Usergrid.Group.prototype.remove = function(options, callback) {
+  var self = this;
+
+  var options = {
+    method:"DELETE",
+    endpoint:"groups/"+this._path+"/users/"+options.user.get('username')
+  }
+
+  this._client.request(options, function(error, data){
+    if(error) {
+      if(typeof(callback) === 'function') {
+        callback(error, data);
+      }
+    } else {
+      self.fetch(callback);
+    }
+  });
+}
+
+/*
+ * Gets feed for a group.
+ *
+ * @public
+ * @method feed
+ * @param {function} callback
+ * @returns {callback} callback(err, data, activities)
+ */
+Usergrid.Group.prototype.feed = function(callback) {
+  var self = this;
+
+  var endpoint = "groups/"+this._path+"/feed";
+
+  var options = {
+    method:"GET",
+    endpoint:endpoint
+  }
+
+  this._client.request(options, function(err, data){
+    if (err && self.logging) {
+      console.log('error trying to log user in');
+    }
+    if(typeof(callback) === 'function') {
+      callback(err, data, data.entities);
+    }
+  });
+}
+
+/*
+ * Creates activity and posts to group feed.
+ *
+ * options object: {user: user_entity, content: "activity content"}
+ *
+ * @public
+ * @method createGroupActivity
+ * @params {object} options
+ * @param {function} callback
+ * @returns {callback} callback(err, entity)
+ */
+Usergrid.Group.prototype.createGroupActivity = function(options, callback){
+  var user = options.user;
+  options = {
+    client: this._client,
+    data: {
+      actor: {
+        "displayName": user.get("username"),
+        "uuid": user.get("uuid"),
+        "username": user.get("username"),
+        "email": user.get("email"),
+        "picture": user.get("picture"),
+        "image": {
+          "duration": 0,
+          "height": 80,
+          "url": user.get("picture"),
+          "width": 80
+        },
+      },
+      "verb": "post",
+      "content": options.content,
+      "type": 'groups/' + this._path + '/activities'
+    }
+  }
+
+  var entity = new Usergrid.Entity(options);
+  entity.save(function(err, data) {
+    if (typeof(callback) === 'function') {
+      callback(err, entity);
+    }
+  });
+};

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ef53b3b5/sdks/html5-javascript/lib/Usergrid.js
----------------------------------------------------------------------
diff --git a/sdks/html5-javascript/lib/Usergrid.js b/sdks/html5-javascript/lib/Usergrid.js
new file mode 100644
index 0000000..89507cf
--- /dev/null
+++ b/sdks/html5-javascript/lib/Usergrid.js
@@ -0,0 +1,87 @@
+/*
+ *  This module is a collection of classes designed to make working with
+ *  the Appigee App Services API as easy as possible.
+ *  Learn more at http://apigee.com/docs/usergrid
+ *
+ *   Copyright 2012 Apigee Corporation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ *  @author rod simpson (rod@apigee.com)
+ *  @author matt dobson (matt@apigee.com)
+ *  @author ryan bridges (rbridges@apigee.com)
+ */
+
+
+//Hack around IE console.log
+window.console = window.console || {};
+window.console.log = window.console.log || function() {};
+
+//Usergrid namespace encapsulates this SDK
+window.Usergrid = window.Usergrid || {};
+Usergrid = Usergrid || {};
+Usergrid.USERGRID_SDK_VERSION = '0.10.07';
+
+
+/*
+ * Tests if the string is a uuid
+ *
+ * @public
+ * @method isUUID
+ * @param {string} uuid The string to test
+ * @returns {Boolean} true if string is uuid
+ */
+function isUUID (uuid) {
+  var uuidValueRegex = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
+  if (!uuid) {
+    return false;
+  }
+  return uuidValueRegex.test(uuid);
+}
+
+/*
+ *  method to encode the query string parameters
+ *
+ *  @method encodeParams
+ *  @public
+ *  @params {object} params - an object of name value pairs that will be urlencoded
+ *  @return {string} Returns the encoded string
+ */
+function encodeParams (params) {
+  var tail = [];
+  var item = [];
+  var i;
+  if (params instanceof Array) {
+    for (i in params) {
+      item = params[i];
+      if ((item instanceof Array) && (item.length > 1)) {
+        tail.push(item[0] + "=" + encodeURIComponent(item[1]));
+      }
+    }
+  } else {
+    for (var key in params) {
+      if (params.hasOwnProperty(key)) {
+        var value = params[key];
+        if (value instanceof Array) {
+          for (i in value) {
+            item = value[i];
+            tail.push(key + "=" + encodeURIComponent(item));
+          }
+        } else {
+          tail.push(key + "=" + encodeURIComponent(value));
+        }
+      }
+    }
+  }
+  return tail.join("&");
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ef53b3b5/sdks/html5-javascript/package.json
----------------------------------------------------------------------
diff --git a/sdks/html5-javascript/package.json b/sdks/html5-javascript/package.json
new file mode 100644
index 0000000..a30f4e9
--- /dev/null
+++ b/sdks/html5-javascript/package.json
@@ -0,0 +1,22 @@
+{
+  "name": "usergrid",
+  "version": "0.0.0",
+  "description": "Detailed instructions follow but if you just want a quick example of how to get started with this SDK, here’s a minimal HTML5 file that shows you how to include & initialize the SDK, as well as how to read & write data from Usergrid with it.",
+  "main": "usergrid.js",
+  "directories": {
+    "example": "examples"
+  },
+  "scripts": {
+    "test": "echo \"Error: no test specified\" && exit 1"
+  },
+  "author": "",
+  "license": "Apache 2.0",
+  "devDependencies": {
+    "grunt": "~0.4.2",
+    "grunt-contrib-clean": "~0.5.0",
+    "grunt-contrib-watch": "~0.5.3",
+    "grunt-contrib-uglify": "~0.2.7",
+    "grunt-blanket-mocha": "~0.3.3",
+    "grunt-contrib-connect": "~0.6.0"
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ef53b3b5/sdks/html5-javascript/tests/mocha/index.html
----------------------------------------------------------------------
diff --git a/sdks/html5-javascript/tests/mocha/index.html b/sdks/html5-javascript/tests/mocha/index.html
new file mode 100644
index 0000000..f6d5c77
--- /dev/null
+++ b/sdks/html5-javascript/tests/mocha/index.html
@@ -0,0 +1,49 @@
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Mocha Tests</title>
+  <link rel="stylesheet" href="../resources/css/mocha.css" />
+
+
+
+
+
+
+
+  <script src="../resources/js/mocha.js"></script>
+  <script>mocha.setup('bdd')</script>
+  <script src="../resources/js/blanket_mocha.min.js"></script>
+  <script type="text/javascript" src="../../node_modules/grunt-blanket-mocha/support/mocha-blanket.js"></script>
+	<script>
+      function assert(expr, msg) {
+        if (!expr) throw new Error(msg || 'failed');
+      }
+    </script>
+
+  <script src="../../usergrid.js" data-cover></script>
+  <script src="test.js"></script>
+<!-- run mocha -->
+    <script type="text/javascript" charset="utf-8">
+    	var _onload=onload||function(){};
+        onload = function(){
+        	_onload.apply(this, arguments);
+	        mocha.checkLeaks();
+	        mocha.globals(['']);
+	        var runner = mocha.run();
+	        /*runner.on('test end', function(test){
+	          console.log(test.fullTitle());
+	        });*/
+	      };
+    </script>
+
+  <script>
+    if (window.PHANTOMJS) {
+        blanket.options("reporter", "../../node_modules/grunt-blanket-mocha/support/grunt-reporter.js");
+    }
+  </script>
+
+</head>
+<body>
+	<div id="mocha"></div>
+</body>
+</html>

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ef53b3b5/sdks/html5-javascript/tests/mocha/test.js
----------------------------------------------------------------------
diff --git a/sdks/html5-javascript/tests/mocha/test.js b/sdks/html5-javascript/tests/mocha/test.js
new file mode 100644
index 0000000..d74541c
--- /dev/null
+++ b/sdks/html5-javascript/tests/mocha/test.js
@@ -0,0 +1,348 @@
+
+/*
+	Creates a generic usergrid client with logging and buildCurl disabled
+
+ */
+function getClient(){
+	return new Usergrid.Client({
+		orgName:'yourorgname',
+		appName:'sandbox',
+		logging: false, //optional - turn on logging, off by default
+		buildCurl: true //optional - turn on curl commands, off by default
+	});
+}
+/*
+	A convenience function that will test for the presence of an API error
+	and run any number of additional tests
+ */
+function usergridTestHarness(err, data, done, tests, ignoreError){
+	if(!ignoreError) assert(!err, data.error_description);
+	if(tests){
+		if("function"===typeof tests){
+			tests(err, data);
+		}else if(tests.length){
+			tests.forEach(function(test){
+				if("function"===typeof test){
+					test(err, data);
+				}
+			})
+		}
+	}	
+	done();
+}
+describe('Usergrid', function(){
+	var client = getClient();
+
+	before(function(done){
+    	//Make sure our dog doesn't already exist
+		client.request({method:'DELETE',endpoint:'users/fred'}, function (err, data) {
+			done();
+	    });
+  	});
+	describe('Usergrid CRUD', function(){
+		var options = {
+			method:'GET',
+			endpoint:'users'
+		};
+		it('should Create a new user', function(done){
+			client.request({method:'POST',endpoint:'users', body:{ username:'fred', password:'secret' }}, function (err, data) {
+				usergridTestHarness(err, data, done, [
+					function(err, data){assert(true)}
+				]);
+		    });
+		});
+		it('should Retrieve an existing user', function(done){
+			client.request({method:'GET',endpoint:'users/fred', body:{}}, function (err, data) {
+				usergridTestHarness(err, data, done, [
+					function(err, data){assert(true)}
+				]);
+		    });
+		});
+		it('should Update an existing user', function(done){
+			client.request({method:'PUT',endpoint:'users/fred', body:{ newkey:'newvalue' }}, function (err, data) {
+				usergridTestHarness(err, data, done, [
+					function(err, data){assert(true)}
+				]);
+		    });
+		});
+		it('should Delete the user from the database', function(done){
+			client.request({method:'DELETE',endpoint:'users/fred'}, function (err, data) {
+				usergridTestHarness(err, data, done, [
+					function(err, data){assert(true)}
+				]);
+		    });
+		});
+	});
+	describe('Usergrid REST', function(){
+		it('should return a list of users', function(done){
+			client.request({method:'GET',endpoint:'users'}, function (err, data) {
+				usergridTestHarness(err, data, done, [
+					function(err, data){assert(data.entities.length >=0)}
+				]);
+		    });
+		});
+		it('should return no entities when an endpoint does not exist', function(done){
+			client.request({method:'GET',endpoint:'nonexistantendpoint'}, function (err, data) {
+				usergridTestHarness(err, data, done, [
+					function(err, data){assert(data.entities.length === 0)}
+				]);
+		    });
+		});
+	});
+	describe('Usergrid Entity', function(){
+		var dog;
+		before(function(done){
+	    	//Make sure our dog doesn't already exist
+	    	client.request({method:'DELETE',endpoint:'dogs/Rocky'}, function (err, data) {
+	    		assert(true);
+	    		done();
+		    });
+	  	});
+		it('should create a new dog', function(done){
+			var options = {
+				type:'dogs',
+				name:'Rocky'
+			}
+
+			client.createEntity(options, function (err, data) {
+				assert(!err, "dog not created");
+				dog=data;
+				done();
+			});
+		});
+		it('should retrieve the dog', function(done){
+			if(!dog){
+				assert(false, "dog not created");
+				done();
+				return;
+			}
+			//once the dog is created, you can set single properties:
+			dog.fetch(function(err){
+				assert(!err, "dog not fetched");
+				done();
+			});
+		});
+		it('should update the dog', function(done){
+			if(!dog){
+				assert(false, "dog not created");
+				done();
+				return;
+			}
+			//once the dog is created, you can set single properties:
+			dog.set('breed','Dinosaur');
+
+			//the set function can also take a JSON object:
+			var data = {
+				master:'Fred',
+				state:'hungry'
+			}
+			//set is additive, so previously set properties are not overwritten
+			dog.set(data);
+
+			//finally, call save on the object to save it back to the database
+			dog.save(function(err){
+				assert(!err, "dog not saved");
+				done();
+			});
+		});
+		it('should remove the dog', function(done){
+			if(!dog){
+				assert(false, "dog not created");
+				done();
+				return;
+			}
+			//once the dog is created, you can set single properties:
+			dog.destroy(function(err){
+				assert(!err, "dog not removed");
+				done();
+			});
+		});
+
+	});
+	describe('Usergrid Collections', function(){
+		var client = getClient();
+		var dog, dogs={};
+		function loop(done){
+			while(dogs.hasNextEntity()) {
+				//get a reference to the dog
+				dog = dogs.getNextEntity();
+				console.log(dog.get('name'));
+			}
+			if(done)done();
+		}
+		before(function(done){
+	    	//Make sure our dog doesn't already exist
+			var options = {
+				type:'dogs',
+				qs:{limit:50} //limit statement set to 50
+			}
+
+			client.createCollection(options, function (err, dogs) {
+				if (!err) {
+					assert(!err, "could not retrieve list of dogs: "+dogs.error_description);
+					//we got 50 dogs, now display the Entities:
+					//do doggy cleanup
+					if(dogs.hasNextEntity()){
+						while(dogs.hasNextEntity()) {
+							//get a reference to the dog
+							var dog = dogs.getNextEntity();
+							//notice('removing dog ' + dogname + ' from database');
+							dog.destroy(function(err, data) {
+								assert(!err, dog.get('name')+" not removed: "+data.error_description);
+								if(!dogs.hasNextEntity()){
+									done();
+								}
+							});
+						}
+					}else{
+						done();
+					}
+				}
+			});
+	  	});
+		before(function(done){
+			this.timeout(10000);
+			var totalDogs=30;
+			Array.apply(0, Array(totalDogs)).forEach(function (x, y) { 
+				var dogNum=y+1;
+				var options = {
+					type:'dogs',
+					name:'dog'+dogNum,
+					index:y
+				}
+				client.createEntity(options, function (err, dog) {
+					assert(!err, " not created: "+dog.error_description);
+					if(dogNum===totalDogs){
+						done();
+					}
+				});
+			})
+		});
+		it('should create a new dogs collection', function(done){
+			var options = {
+				type:'dogs',
+				qs:{ql:'order by index'}
+			}
+
+			client.createCollection(options, function (err, data) {
+				assert(!err, "could not create dogs collection: "+data.error_description);
+				dogs=data;
+				done();
+			});
+		});
+		it('should retrieve dogs from the collection', function(done){
+			loop(done);
+		});
+		it('should retrieve the next page of dogs from the collection', function(done){
+			if(dogs.hasNextPage()){
+				dogs.getNextPage(function(err){loop(done);});
+			}else{
+				done();
+			}
+		});
+		it('should retrieve the previous page of dogs from the collection', function(done){
+			if(dogs.hasPreviousPage()){
+				dogs.getPreviousPage(function(err){loop(done);});
+			}else{
+				done();
+			}
+		});
+		/*it('should retrieve the dog', function(done){
+			if(!dog){
+				assert(false, "dog not created");
+				done();
+			}
+			dog.fetch(function(err){
+				assert(!err, "dog not fetched");
+				done();
+			});
+		});*/
+	});
+	describe('Usergrid Events', function(){
+		var ev;
+		var MINUTE=1000*60;
+		var HOUR=MINUTE*60;
+		var time=Date.now()-HOUR;
+		it('should create an event', function(done){
+			ev = new Usergrid.Event({client:client, data:{category:'mocha_test', timestamp:time, name:"test", counters:{test:0,test_counter:0}}}, function(err, data){
+				assert(!err, data.error_description);
+				console.log(data);
+				done();
+			});
+		});
+		it('should save an event', function(done){
+			ev.save(function(err, data){
+				assert(!err, data.error_description);
+				console.log(data);
+				done();
+			});
+		});
+		/*it('should reset a counter', function(done){
+			time+=MINUTE*10
+			ev.set("timestamp", time);
+			ev.reset('test', function(err, data){
+				assert(!err, data.error_description);
+				console.log(data);
+				done();
+			});
+		});*/
+		it("should increment 'test' counter", function(done){
+			time+=MINUTE*10
+			ev.set("timestamp", time);
+			ev.increment('test', 1, function(err, data){
+				assert(!err, data.error_description);
+				console.log(data);
+				done();
+			});
+		});
+		it("should increment 'test_counter' counter by 4", function(done){
+			time+=MINUTE*10
+			ev.set("timestamp", time);
+			ev.increment('test_counter', 4, function(err, data){
+				assert(!err, data.error_description);
+				console.log(JSON.stringify(data,null,4));
+				done();
+			});
+		});
+		it("should decrement 'test' counter", function(done){
+			time+=MINUTE*10
+			ev.set("timestamp", time);
+			ev.decrement('test', 1, function(err, data){
+				assert(!err, data.error_description);
+				console.log(JSON.stringify(data,null,4));
+				done();
+			});
+		});
+		it('should fetch event', function(done){
+			ev.fetch(function(err, data){
+				assert(!err, data.error_description);
+				console.log(JSON.stringify(data,null,4));
+				console.log(time, Date.now());
+				done();
+			});
+		});
+		/*it('should fetch counter data', function(done){
+			ev.getData('all', null, null, ['test', 'test_counter'], function(err, data){
+				assert(!err, data.error_description);
+				console.log(data);
+				console.log(time, Date.now());
+				done();
+			});
+		});*/
+	});
+	/*describe('Usergrid Counters', function(){
+		it('should create a counter', function(done){
+			var counter = new Apigee.Event({client:client, data:{category:"mocha_test", timestamp:28, name:'test'}}, function(err, data){
+				assert(!err, data.error_description);
+				console.log(data);
+				done();
+			});
+		});
+	});*/
+	describe('Usergrid extra', function(){
+		it('should not be phonegap', function(done){
+			
+		});
+	});
+});
+

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ef53b3b5/sdks/html5-javascript/tests/qunit/apigee_test.html
----------------------------------------------------------------------
diff --git a/sdks/html5-javascript/tests/qunit/apigee_test.html b/sdks/html5-javascript/tests/qunit/apigee_test.html
new file mode 100644
index 0000000..0975bbd
--- /dev/null
+++ b/sdks/html5-javascript/tests/qunit/apigee_test.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>QUnit Example</title>
+  <link rel="stylesheet" href="http://code.jquery.com/qunit/qunit-1.12.0.css">
+</head>
+<body>
+  <div id="qunit"></div>
+  <div id="qunit-fixture"></div>
+  <script src="http://code.jquery.com/qunit/qunit-1.12.0.js"></script>
+  <script src="tests.js"></script>
+</body>
+</html>

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ef53b3b5/sdks/html5-javascript/tests/qunit/tests.js
----------------------------------------------------------------------
diff --git a/sdks/html5-javascript/tests/qunit/tests.js b/sdks/html5-javascript/tests/qunit/tests.js
new file mode 100644
index 0000000..67764a0
--- /dev/null
+++ b/sdks/html5-javascript/tests/qunit/tests.js
@@ -0,0 +1,3 @@
+test( "hello test", function() {
+  ok( 1 == "1", "Passed!" );
+});