You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@usergrid.apache.org by mr...@apache.org on 2016/09/02 16:27:14 UTC

[10/13] usergrid-javascript git commit: Initial commit of Usergrid Javascript SDK into its own repo

http://git-wip-us.apache.org/repos/asf/usergrid-javascript/blob/94020d26/examples/resources/css/styles.css
----------------------------------------------------------------------
diff --git a/examples/resources/css/styles.css b/examples/resources/css/styles.css
new file mode 100755
index 0000000..7492f93
--- /dev/null
+++ b/examples/resources/css/styles.css
@@ -0,0 +1,91 @@
+/**
+*  All Calls is a Node.js sample app that is powered by Usergrid
+*  This app shows how to make the 4 REST calls (GET, POST,
+*  PUT, DELETE) against the usergrid API.
+*
+*  Learn more at http://Usergrid.com/docs
+*
+*   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.
+*/
+
+/**
+*  @file styles.css
+*  @author Rod Simpson (rod@apigee.com)
+*
+*/
+
+body {
+  background-color: #fff;
+  min-height: 800px;
+}
+
+/* buttons ================================================================= */
+.btn-primary{border-color:#1455ab #1455ab #0c3367;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);background-color:#146cab;background-image:-moz-linear-gradient(top, #147bab, #1455ab);background-image:-ms-linear-gradient(top, #147bab, #1455ab);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#147bab), to(#1455ab));background-image:-webkit-linear-gradient(top, #147bab, #1455ab);background-image:-o-linear-gradient(top, #147bab, #1455ab);background-image:linear-gradient(top, #147bab, #1455ab);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#147bab', endColorstr='#1455ab', GradientType=0);filter:progid:dximagetransform.microsoft.gradient(enabled=false);}.btn-primary:hover,.btn-primary:active,.btn-primary.active,.btn-primary.disabled,.btn-primary[disabled]{background-color:#1455ab;}
+
+.header{
+   padding: 10px;
+   width: 100%;
+   height: 40px;
+   background-color: #ff4200;
+   color: #fff;
+   text-align: left;
+   font-size: 16px;
+   font-weight: 800;
+}
+.breadcrumb{
+  font-size: 16px;
+}
+.info{
+  padding: 0px 30px 30px 30px;
+  font-size: 16px;
+}
+h3{
+ padding-bottom: 20px;
+}
+.main{
+   display: block;
+   padding: 0 30px 30px 30px ;
+   background-color: #fff;
+}
+.form-block{
+  display: block;
+  display: none;
+  padding: 10px 0;
+  min-height: 210px;
+  background-color: #fff;
+}
+.section-header{
+  font-size: 20px;
+  font-weight: 200;
+  padding-bottom: 20px;
+}
+.note {
+   padding-bottom: 20px;
+}
+.response-box{
+   margin: 0 auto;
+   padding: 10px;
+   width: 640px;
+   border: 1px solid silver;
+   background-color: #ddd;
+   font-weight: bold;
+}
+pre{
+   border: none;
+   padding: 0;
+}
+.left{
+   float: left;
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/usergrid-javascript/blob/94020d26/examples/resources/images/apigee.png
----------------------------------------------------------------------
diff --git a/examples/resources/images/apigee.png b/examples/resources/images/apigee.png
new file mode 100755
index 0000000..c0d0f84
Binary files /dev/null and b/examples/resources/images/apigee.png differ

http://git-wip-us.apache.org/repos/asf/usergrid-javascript/blob/94020d26/examples/resources/js/json2.js
----------------------------------------------------------------------
diff --git a/examples/resources/js/json2.js b/examples/resources/js/json2.js
new file mode 100755
index 0000000..c7745df
--- /dev/null
+++ b/examples/resources/js/json2.js
@@ -0,0 +1,486 @@
+/*
+    json2.js
+    2012-10-08
+
+    Public Domain.
+
+    NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
+
+    See http://www.JSON.org/js.html
+
+
+    This code should be minified before deployment.
+    See http://javascript.crockford.com/jsmin.html
+
+    USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
+    NOT CONTROL.
+
+
+    This file creates a global JSON object containing two methods: stringify
+    and parse.
+
+        JSON.stringify(value, replacer, space)
+            value       any JavaScript value, usually an object or array.
+
+            replacer    an optional parameter that determines how object
+                        values are stringified for objects. It can be a
+                        function or an array of strings.
+
+            space       an optional parameter that specifies the indentation
+                        of nested structures. If it is omitted, the text will
+                        be packed without extra whitespace. If it is a number,
+                        it will specify the number of spaces to indent at each
+                        level. If it is a string (such as '\t' or ' '),
+                        it contains the characters used to indent at each level.
+
+            This method produces a JSON text from a JavaScript value.
+
+            When an object value is found, if the object contains a toJSON
+            method, its toJSON method will be called and the result will be
+            stringified. A toJSON method does not serialize: it returns the
+            value represented by the name/value pair that should be serialized,
+            or undefined if nothing should be serialized. The toJSON method
+            will be passed the key associated with the value, and this will be
+            bound to the value
+
+            For example, this would serialize Dates as ISO strings.
+
+                Date.prototype.toJSON = function (key) {
+                    function f(n) {
+                        // Format integers to have at least two digits.
+                        return n < 10 ? '0' + n : n;
+                    }
+
+                    return this.getUTCFullYear()   + '-' +
+                         f(this.getUTCMonth() + 1) + '-' +
+                         f(this.getUTCDate())      + 'T' +
+                         f(this.getUTCHours())     + ':' +
+                         f(this.getUTCMinutes())   + ':' +
+                         f(this.getUTCSeconds())   + 'Z';
+                };
+
+            You can provide an optional replacer method. It will be passed the
+            key and value of each member, with this bound to the containing
+            object. The value that is returned from your method will be
+            serialized. If your method returns undefined, then the member will
+            be excluded from the serialization.
+
+            If the replacer parameter is an array of strings, then it will be
+            used to select the members to be serialized. It filters the results
+            such that only members with keys listed in the replacer array are
+            stringified.
+
+            Values that do not have JSON representations, such as undefined or
+            functions, will not be serialized. Such values in objects will be
+            dropped; in arrays they will be replaced with null. You can use
+            a replacer function to replace those with JSON values.
+            JSON.stringify(undefined) returns undefined.
+
+            The optional space parameter produces a stringification of the
+            value that is filled with line breaks and indentation to make it
+            easier to read.
+
+            If the space parameter is a non-empty string, then that string will
+            be used for indentation. If the space parameter is a number, then
+            the indentation will be that many spaces.
+
+            Example:
+
+            text = JSON.stringify(['e', {pluribus: 'unum'}]);
+            // text is '["e",{"pluribus":"unum"}]'
+
+
+            text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
+            // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
+
+            text = JSON.stringify([new Date()], function (key, value) {
+                return this[key] instanceof Date ?
+                    'Date(' + this[key] + ')' : value;
+            });
+            // text is '["Date(---current time---)"]'
+
+
+        JSON.parse(text, reviver)
+            This method parses a JSON text to produce an object or array.
+            It can throw a SyntaxError exception.
+
+            The optional reviver parameter is a function that can filter and
+            transform the results. It receives each of the keys and values,
+            and its return value is used instead of the original value.
+            If it returns what it received, then the structure is not modified.
+            If it returns undefined then the member is deleted.
+
+            Example:
+
+            // Parse the text. Values that look like ISO date strings will
+            // be converted to Date objects.
+
+            myData = JSON.parse(text, function (key, value) {
+                var a;
+                if (typeof value === 'string') {
+                    a =
+/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
+                    if (a) {
+                        return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
+                            +a[5], +a[6]));
+                    }
+                }
+                return value;
+            });
+
+            myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
+                var d;
+                if (typeof value === 'string' &&
+                        value.slice(0, 5) === 'Date(' &&
+                        value.slice(-1) === ')') {
+                    d = new Date(value.slice(5, -1));
+                    if (d) {
+                        return d;
+                    }
+                }
+                return value;
+            });
+
+
+    This is a reference implementation. You are free to copy, modify, or
+    redistribute.
+*/
+
+/*jslint evil: true, regexp: true */
+
+/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply,
+    call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
+    getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
+    lastIndex, length, parse, prototype, push, replace, slice, stringify,
+    test, toJSON, toString, valueOf
+*/
+
+
+// Create a JSON object only if one does not already exist. We create the
+// methods in a closure to avoid creating global variables.
+
+if (typeof JSON !== 'object') {
+    JSON = {};
+}
+
+(function () {
+    'use strict';
+
+    function f(n) {
+        // Format integers to have at least two digits.
+        return n < 10 ? '0' + n : n;
+    }
+
+    if (typeof Date.prototype.toJSON !== 'function') {
+
+        Date.prototype.toJSON = function (key) {
+
+            return isFinite(this.valueOf())
+                ? this.getUTCFullYear()     + '-' +
+                    f(this.getUTCMonth() + 1) + '-' +
+                    f(this.getUTCDate())      + 'T' +
+                    f(this.getUTCHours())     + ':' +
+                    f(this.getUTCMinutes())   + ':' +
+                    f(this.getUTCSeconds())   + 'Z'
+                : null;
+        };
+
+        String.prototype.toJSON      =
+            Number.prototype.toJSON  =
+            Boolean.prototype.toJSON = function (key) {
+                return this.valueOf();
+            };
+    }
+
+    var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
+        escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
+        gap,
+        indent,
+        meta = {    // table of character substitutions
+            '\b': '\\b',
+            '\t': '\\t',
+            '\n': '\\n',
+            '\f': '\\f',
+            '\r': '\\r',
+            '"' : '\\"',
+            '\\': '\\\\'
+        },
+        rep;
+
+
+    function quote(string) {
+
+// If the string contains no control characters, no quote characters, and no
+// backslash characters, then we can safely slap some quotes around it.
+// Otherwise we must also replace the offending characters with safe escape
+// sequences.
+
+        escapable.lastIndex = 0;
+        return escapable.test(string) ? '"' + string.replace(escapable, function (a) {
+            var c = meta[a];
+            return typeof c === 'string'
+                ? c
+                : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
+        }) + '"' : '"' + string + '"';
+    }
+
+
+    function str(key, holder) {
+
+// Produce a string from holder[key].
+
+        var i,          // The loop counter.
+            k,          // The member key.
+            v,          // The member value.
+            length,
+            mind = gap,
+            partial,
+            value = holder[key];
+
+// If the value has a toJSON method, call it to obtain a replacement value.
+
+        if (value && typeof value === 'object' &&
+                typeof value.toJSON === 'function') {
+            value = value.toJSON(key);
+        }
+
+// If we were called with a replacer function, then call the replacer to
+// obtain a replacement value.
+
+        if (typeof rep === 'function') {
+            value = rep.call(holder, key, value);
+        }
+
+// What happens next depends on the value's type.
+
+        switch (typeof value) {
+        case 'string':
+            return quote(value);
+
+        case 'number':
+
+// JSON numbers must be finite. Encode non-finite numbers as null.
+
+            return isFinite(value) ? String(value) : 'null';
+
+        case 'boolean':
+        case 'null':
+
+// If the value is a boolean or null, convert it to a string. Note:
+// typeof null does not produce 'null'. The case is included here in
+// the remote chance that this gets fixed someday.
+
+            return String(value);
+
+// If the type is 'object', we might be dealing with an object or an array or
+// null.
+
+        case 'object':
+
+// Due to a specification blunder in ECMAScript, typeof null is 'object',
+// so watch out for that case.
+
+            if (!value) {
+                return 'null';
+            }
+
+// Make an array to hold the partial results of stringifying this object value.
+
+            gap += indent;
+            partial = [];
+
+// Is the value an array?
+
+            if (Object.prototype.toString.apply(value) === '[object Array]') {
+
+// The value is an array. Stringify every element. Use null as a placeholder
+// for non-JSON values.
+
+                length = value.length;
+                for (i = 0; i < length; i += 1) {
+                    partial[i] = str(i, value) || 'null';
+                }
+
+// Join all of the elements together, separated with commas, and wrap them in
+// brackets.
+
+                v = partial.length === 0
+                    ? '[]'
+                    : gap
+                    ? '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']'
+                    : '[' + partial.join(',') + ']';
+                gap = mind;
+                return v;
+            }
+
+// If the replacer is an array, use it to select the members to be stringified.
+
+            if (rep && typeof rep === 'object') {
+                length = rep.length;
+                for (i = 0; i < length; i += 1) {
+                    if (typeof rep[i] === 'string') {
+                        k = rep[i];
+                        v = str(k, value);
+                        if (v) {
+                            partial.push(quote(k) + (gap ? ': ' : ':') + v);
+                        }
+                    }
+                }
+            } else {
+
+// Otherwise, iterate through all of the keys in the object.
+
+                for (k in value) {
+                    if (Object.prototype.hasOwnProperty.call(value, k)) {
+                        v = str(k, value);
+                        if (v) {
+                            partial.push(quote(k) + (gap ? ': ' : ':') + v);
+                        }
+                    }
+                }
+            }
+
+// Join all of the member texts together, separated with commas,
+// and wrap them in braces.
+
+            v = partial.length === 0
+                ? '{}'
+                : gap
+                ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}'
+                : '{' + partial.join(',') + '}';
+            gap = mind;
+            return v;
+        }
+    }
+
+// If the JSON object does not yet have a stringify method, give it one.
+
+    if (typeof JSON.stringify !== 'function') {
+        JSON.stringify = function (value, replacer, space) {
+
+// The stringify method takes a value and an optional replacer, and an optional
+// space parameter, and returns a JSON text. The replacer can be a function
+// that can replace values, or an array of strings that will select the keys.
+// A default replacer method can be provided. Use of the space parameter can
+// produce text that is more easily readable.
+
+            var i;
+            gap = '';
+            indent = '';
+
+// If the space parameter is a number, make an indent string containing that
+// many spaces.
+
+            if (typeof space === 'number') {
+                for (i = 0; i < space; i += 1) {
+                    indent += ' ';
+                }
+
+// If the space parameter is a string, it will be used as the indent string.
+
+            } else if (typeof space === 'string') {
+                indent = space;
+            }
+
+// If there is a replacer, it must be a function or an array.
+// Otherwise, throw an error.
+
+            rep = replacer;
+            if (replacer && typeof replacer !== 'function' &&
+                    (typeof replacer !== 'object' ||
+                    typeof replacer.length !== 'number')) {
+                throw new Error('JSON.stringify');
+            }
+
+// Make a fake root object containing our value under the key of ''.
+// Return the result of stringifying the value.
+
+            return str('', {'': value});
+        };
+    }
+
+
+// If the JSON object does not yet have a parse method, give it one.
+
+    if (typeof JSON.parse !== 'function') {
+        JSON.parse = function (text, reviver) {
+
+// The parse method takes a text and an optional reviver function, and returns
+// a JavaScript value if the text is a valid JSON text.
+
+            var j;
+
+            function walk(holder, key) {
+
+// The walk method is used to recursively walk the resulting structure so
+// that modifications can be made.
+
+                var k, v, value = holder[key];
+                if (value && typeof value === 'object') {
+                    for (k in value) {
+                        if (Object.prototype.hasOwnProperty.call(value, k)) {
+                            v = walk(value, k);
+                            if (v !== undefined) {
+                                value[k] = v;
+                            } else {
+                                delete value[k];
+                            }
+                        }
+                    }
+                }
+                return reviver.call(holder, key, value);
+            }
+
+
+// Parsing happens in four stages. In the first stage, we replace certain
+// Unicode characters with escape sequences. JavaScript handles many characters
+// incorrectly, either silently deleting them, or treating them as line endings.
+
+            text = String(text);
+            cx.lastIndex = 0;
+            if (cx.test(text)) {
+                text = text.replace(cx, function (a) {
+                    return '\\u' +
+                        ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
+                });
+            }
+
+// In the second stage, we run the text against regular expressions that look
+// for non-JSON patterns. We are especially concerned with '()' and 'new'
+// because they can cause invocation, and '=' because it can cause mutation.
+// But just to be safe, we want to reject all unexpected forms.
+
+// We split the second stage into 4 regexp operations in order to work around
+// crippling inefficiencies in IE's and Safari's regexp engines. First we
+// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
+// replace all simple value tokens with ']' characters. Third, we delete all
+// open brackets that follow a colon or comma or that begin the text. Finally,
+// we look to see that the remaining characters are only whitespace or ']' or
+// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
+
+            if (/^[\],:{}\s]*$/
+                    .test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@')
+                        .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']')
+                        .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
+
+// In the third stage we use the eval function to compile the text into a
+// JavaScript structure. The '{' operator is subject to a syntactic ambiguity
+// in JavaScript: it can begin a block or an object literal. We wrap the text
+// in parens to eliminate the ambiguity.
+
+                j = eval('(' + text + ')');
+
+// In the optional fourth stage, we recursively walk the new structure, passing
+// each name/value pair to a reviver function for possible transformation.
+
+                return typeof reviver === 'function'
+                    ? walk({'': j}, '')
+                    : j;
+            }
+
+// If the text is not JSON parseable, then a SyntaxError is thrown.
+
+            throw new SyntaxError('JSON.parse');
+        };
+    }
+}());

http://git-wip-us.apache.org/repos/asf/usergrid-javascript/blob/94020d26/examples/test/test.html
----------------------------------------------------------------------
diff --git a/examples/test/test.html b/examples/test/test.html
new file mode 100755
index 0000000..e66fcb3
--- /dev/null
+++ b/examples/test/test.html
@@ -0,0 +1,54 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You 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.
+-->
+
+<!DOCTYPE html>
+<html>
+   <head>
+      <title>Readme File Tests</title>
+      <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+      <link rel="stylesheet" href="../resources/css/bootstrap-combined.min.css" />
+      <link rel="stylesheet" href="../resources/css/styles.css" />
+      <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js" type="text/javascript"></script>
+      <script src="../../usergrid.js" type="text/javascript"></script>
+      <script src="test.js" type="text/javascript"></script>
+      <script type="text/javascript">
+
+      </script>
+   </head>
+   <body>
+      <div class="header">
+         <img src="../resources/images/apigee.png"> App Services (Usergrid) Javascript SDK
+      </div>
+      <div class="info">
+      This sample application runs tests on the sample code examples in the readme file.  Tests are run against App Services (Usergrid) using the Usergrid Javascript SDK.
+      </div>
+    <div id="main" class="main">
+      <div class="section-header">README sample code tests</div>
+      <div class="well">
+        <div id="name-control" class="control-group">
+          <div class="controls">
+            <button class="btn btn-primary" id="start-button" style="width: 90px;">Start</button>
+            <span style="clear: both;">&nbsp;</span>
+          </div>
+        </div>
+      </div>
+      <div class="section-header"><b>Test Output</b></div>
+      <div class="well">
+        <pre id="test-output">// Press Start button to begin</pre>
+      </div>
+    </body>
+</html>

http://git-wip-us.apache.org/repos/asf/usergrid-javascript/blob/94020d26/examples/test/test.js
----------------------------------------------------------------------
diff --git a/examples/test/test.js b/examples/test/test.js
new file mode 100755
index 0000000..fb65d98
--- /dev/null
+++ b/examples/test/test.js
@@ -0,0 +1,978 @@
+/**
+* Test suite for all the examples in the readme
+*
+* NOTE: No, this test suite doesn't use the traditional format for
+* a test suite.  This is because the goal is to require as little
+* alteration as possible during the copy / paste operation from this test
+* suite to the readme file.
+*
+* @author rod simpson (rod@apigee.com)
+*/
+
+$(document).ready(function () {
+
+//call the runner function to start the process
+$('#start-button').bind('click', function() {
+	$('#start-button').attr("disabled", "disabled");
+	$('#test-output').html('');
+	runner(0);
+});
+
+var logSuccess = true;
+var successCount = 0;
+var logError = true;
+var errorCount = 0;
+var logNotice = true;
+var _unique = new Date().getTime();
+var _username = 'marty'+_unique;
+var _email = 'marty'+_unique+'@timetravel.com';
+var _password = 'password2';
+var _newpassword = 'password3';
+
+var client = new Usergrid.Client({
+	orgName:'yourorgname',
+	appName:'sandbox',
+	logging: true, //optional - turn on logging, off by default
+	buildCurl: true //optional - turn on curl commands, off by default
+});
+
+client.logout();
+
+function runner(step, arg, arg2){
+	step++;
+	switch(step)
+	{
+		case 1:
+			notice('-----running step '+step+': DELETE user from DB to prep test');
+			clearUser(step);
+			break;
+		case 2:
+			notice('-----running step '+step+': GET test');
+			testGET(step);
+			break;
+		case 3:
+			notice('-----running step '+step+': POST test');
+			testPOST(step);
+			break;
+		case 4:
+			notice('-----running step '+step+': PUT test');
+			testPUT(step);
+			break;
+		case 5:
+			notice('-----running step '+step+': DELETE test');
+			testDELETE(step);
+			break;
+		case 6:
+			notice('-----running step '+step+': prepare database - remove all dogs (no real dogs harmed here!!)');
+			cleanupAllDogs(step);
+			break;
+		case 7:
+			notice('-----running step '+step+': make a new dog');
+			makeNewDog(step);
+			break;
+		case 8:
+			notice('-----running step '+step+': update our dog');
+			updateDog(step, arg);
+			break;
+		case 9:
+			notice('-----running step '+step+': refresh our dog');
+			refreshDog(step, arg);
+			break;
+		case 10:
+			notice('-----running step '+step+': remove our dog from database (no real dogs harmed here!!)');
+			removeDogFromDatabase(step, arg);
+			break;
+		case 11:
+			notice('-----running step '+step+': make lots of dogs!');
+			makeSampleData(step, arg);
+			break;
+		case 12:
+			notice('-----running step '+step+': make a dogs collection and show each dog');
+			testDogsCollection(step);
+			break;
+		case 13:
+			notice('-----running step '+step+': get the next page of the dogs collection and show each dog');
+			getNextDogsPage(step, arg);
+			break;
+		case 14:
+			notice('-----running step '+step+': get the previous page of the dogs collection and show each dog');
+			getPreviousDogsPage(step, arg);
+			break;
+		case 15:
+			notice('-----running step '+step+': remove all dogs from the database (no real dogs harmed here!!)');
+			cleanupAllDogs(step);
+			break;
+		case 16:
+			notice('-----running step '+step+': prepare database (remove existing user if present)');
+			prepareDatabaseForNewUser(step);
+			break;
+		case 17:
+			notice('-----running step '+step+': create a new user');
+			createUser(step);
+			break;
+		case 18:
+			notice('-----running step '+step+': update the user');
+			updateUser(step, arg);
+			break;
+		case 19:
+			notice('-----running step '+step+': get the existing user');
+			getExistingUser(step, arg);
+			break;
+		case 20:
+			notice('-----running step '+step+': refresh the user from the database');
+			refreshUser(step, arg);
+			break;
+		case 21:
+			notice('-----running step '+step+': log user in');
+			loginUser(step, arg);
+			break;
+		case 22:
+			notice('-----running step '+step+': change users password');
+			changeUsersPassword(step, arg);
+			break;
+		case 23:
+			notice('-----running step '+step+': log user out');
+			logoutUser(step, arg);
+			break;
+		case 24:
+			notice('-----running step '+step+': relogin user');
+			reloginUser(step, arg);
+			break;
+		case 25:
+			notice('-----running step '+step+': logged in user creates dog');
+			createDog(step, arg);
+			break;
+		case 26:
+			notice('-----running step '+step+': logged in user likes dog');
+			userLikesDog(step, arg, arg2);
+			break;
+		case 27:
+			notice('-----running step '+step+': logged in user removes likes connection to dog');
+			removeUserLikesDog(step, arg, arg2);
+			break;
+		case 28:
+			notice('-----running step '+step+': user removes dog');
+			removeDog(step, arg, arg2);
+			break;
+		case 29:
+			notice('-----running step '+step+': log the user out');
+			logoutUser(step, arg);
+			break;
+		case 30:
+			notice('-----running step '+step+': remove the user from the database');
+			destroyUser(step, arg);
+			break;
+		case 31:
+			notice('-----running step '+step+': try to create existing entity');
+			createExistingEntity(step, arg);
+			break;
+		case 32:
+			notice('-----running step '+step+': try to create new entity with no name');
+			createNewEntityNoName(step, arg);
+			break;
+		case 33:
+			notice('-----running step '+step+': clean up users');
+      cleanUpUsers(step, arg);
+			break;
+		case 34:
+			notice('-----running step '+step+': clean up dogs');
+      cleanUpDogs(step, arg);
+			break;
+		default:
+			notice('-----test complete!-----');
+			notice('Success count= ' + successCount);
+			notice('Error count= ' + errorCount);
+			notice('-----thank you for playing!-----');
+			$('#start-button').removeAttr("disabled");
+	}
+}
+
+//logging functions
+function success(message){
+	successCount++;
+	if (logSuccess) {
+		console.log('SUCCESS: ' + message);
+		var html = $('#test-output').html();
+		html += ('SUCCESS: ' + message + '\r\n');
+		$('#test-output').html(html);
+	}
+}
+
+function error(message){
+	errorCount++
+	if (logError) {
+		console.log('ERROR: ' + message);
+		var html = $('#test-output').html();
+		html += ('ERROR: ' + message + '\r\n');
+		$('#test-output').html(html);
+	}
+}
+
+function notice(message){
+	if (logNotice) {
+		console.log('NOTICE: ' + message);
+		var html = $('#test-output').html();
+		html += (message + '\r\n');
+		$('#test-output').html(html);
+	}
+}
+
+//tests
+function clearUser(step) {
+  var options = {
+    method:'DELETE',
+    endpoint:'users/fred'
+  };
+  client.request(options, function (err, data) {
+    //data will contain raw results from API call
+    success('User cleared from DB');
+    runner(step);
+  });
+}
+
+function testGET(step) {
+	var options = {
+		method:'GET',
+		endpoint:'users'
+	};
+	client.request(options, function (err, data) {
+		if (err) {
+			error('GET failed');
+		} else {
+			//data will contain raw results from API call
+			success('GET worked');
+			runner(step);
+		}
+	});
+}
+
+function testPOST(step) {
+	var options = {
+		method:'POST',
+		endpoint:'users',
+		body:{ username:'fred', password:'secret' }
+	};
+	client.request(options, function (err, data) {
+		if (err) {
+			error('POST failed');
+		} else {
+			//data will contain raw results from API call
+			success('POST worked');
+			runner(step);
+		}
+	});
+}
+
+function testPUT(step) {
+	var options = {
+		method:'PUT',
+		endpoint:'users/fred',
+		body:{ newkey:'newvalue' }
+	};
+	client.request(options, function (err, data) {
+		if (err) {
+			error('PUT failed');
+		} else {
+			//data will contain raw results from API call
+			success('PUT worked');
+			runner(step);
+		}
+	});
+}
+
+function testDELETE(step) {
+	var options = {
+		method:'DELETE',
+		endpoint:'users/fred'
+	};
+	client.request(options, function (err, data) {
+		if (err) {
+			error('DELETE failed');
+		} else {
+			//data will contain raw results from API call
+			success('DELETE worked');
+			runner(step);
+		}
+	});
+}
+
+function makeNewDog(step) {
+
+	var options = {
+		type:'dogs',
+		name:'Ralph'+_unique
+	}
+
+	client.createEntity(options, function (err, response, dog) {
+		if (err) {
+			error('dog not created');
+		} else {
+			success('dog is created');
+
+			//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){
+				if (err){
+					error('dog not saved');
+				} else {
+					success('new dog is saved');
+					runner(step, dog);
+				}
+			});
+		}
+	});
+
+}
+
+function updateDog(step, dog) {
+
+	//change a property in the object
+	dog.set("state", "fed");
+	//and save back to the database
+	dog.save(function(err){
+		if (err){
+			error('dog not saved');
+		} else {
+			success('dog is saved');
+			runner(step, dog);
+		}
+	});
+
+}
+
+function refreshDog(step, dog){
+
+	//call fetch to refresh the data from the server
+	dog.fetch(function(err){
+		if (err){
+			error('dog not refreshed from database');
+		} else {
+			//dog has been refreshed from the database
+			//will only work if the UUID for the entity is in the dog object
+			success('dog entity refreshed from database');
+			runner(step, dog);
+		}
+	});
+
+}
+
+function removeDogFromDatabase(step, dog){
+
+	//the destroy method will delete the entity from the database
+	dog.destroy(function(err){
+		if (err){
+			error('dog not removed from database');
+		} else {
+			success('dog removed from database'); // no real dogs were harmed!
+			dog = null; //no real dogs were harmed!
+			runner(step, 1);
+		}
+	});
+
+}
+
+function makeSampleData(step, i) {
+	notice('making dog '+i);
+
+	var options = {
+		type:'dogs',
+		name:'dog'+_unique+i,
+		index:i
+	}
+
+	client.createEntity(options, function (err, dog) {
+		if (err) {
+			error('dog ' + i + ' not created');
+		} else {
+			if (i >= 30) {
+				//data made, ready to go
+				success('all dogs made');
+				runner(step);
+			} else {
+				success('dog ' + i + ' made');
+				//keep making dogs
+				makeSampleData(step, ++i);
+			}
+		}
+	});
+}
+
+function testDogsCollection(step) {
+
+	var options = {
+		type:'dogs',
+		qs:{ql:'order by index'}
+	}
+
+	client.createCollection(options, function (err, response, dogs) {
+		if (err) {
+			error('could not make collection');
+		} else {
+
+			success('new Collection created');
+
+			//we got the dogs, now display the Entities:
+			while(dogs.hasNextEntity()) {
+				//get a reference to the dog
+				dog = dogs.getNextEntity();
+				var name = dog.get('name');
+				notice('dog is called ' + name);
+			}
+
+			success('looped through dogs');
+
+			//create a new dog and add it to the collection
+			var options = {
+				name:'extra-dog',
+				fur:'shedding'
+			}
+			//just pass the options to the addEntity method
+			//to the collection and it is saved automatically
+			dogs.addEntity(options, function(err, dog, data) {
+				if (err) {
+					error('extra dog not saved or added to collection');
+				} else {
+					success('extra dog saved and added to collection');
+					runner(step, dogs);
+				}
+			});
+		}
+	});
+
+}
+
+function getNextDogsPage(step, dogs) {
+
+	if (dogs.hasNextPage()) {
+		//there is a next page, so get it from the server
+		dogs.getNextPage(function(err){
+			if (err) {
+				error('could not get next page of dogs');
+			} else {
+				success('got next page of dogs');
+				//we got the next page of data, so do something with it:
+				var i = 11;
+				while(dogs.hasNextEntity()) {
+					//get a reference to the dog
+					var dog = dogs.getNextEntity();
+					var index = dog.get('index');
+					if(i !== index) {
+						error('wrong dog loaded: wanted' + i + ', got ' + index);
+					}
+					notice('got dog ' + i);
+					i++
+				}
+				success('looped through dogs')
+				runner(step, dogs);
+			}
+		});
+	} else {
+		getPreviousDogsPage(dogs);
+	}
+
+}
+
+function getPreviousDogsPage(step, dogs) {
+
+	if (dogs.hasPreviousPage()) {
+		//there is a previous page, so get it from the server
+		dogs.getPreviousPage(function(err){
+			if(err) {
+				error('could not get previous page of dogs');
+			} else {
+				success('got next page of dogs');
+				//we got the previous page of data, so do something with it:
+				var i = 1;
+				while(dogs.hasNextEntity()) {
+					//get a reference to the dog
+					var dog = dogs.getNextEntity();
+					var index = dog.get('index');
+					if(i !== index) {
+						error('wrong dog loaded: wanted' + i + ', got ' + index);
+					}
+					notice('got dog ' + i);
+					i++
+				}
+				success('looped through dogs');
+				runner(step);
+			}
+		});
+	} else {
+		getAllDogs();
+	}
+}
+
+function cleanupAllDogs(step){
+
+	var options = {
+		type:'dogs',
+		qs:{limit:50} //limit statement set to 50
+	}
+
+	client.createCollection(options, function (err, response, dogs) {
+		if (err) {
+			error('could not get all dogs');
+		} else {
+			success('got at most 50 dogs');
+			//we got 50 dogs, now display the Entities:
+			while(dogs.hasNextEntity()) {
+				//get a reference to the dog
+				var dog = dogs.getNextEntity();
+				var name = dog.get('name');
+				notice('dog is called ' + name);
+			}
+			dogs.resetEntityPointer();
+			//do doggy cleanup
+			while(dogs.hasNextEntity()) {
+				//get a reference to the dog
+				var dog = dogs.getNextEntity();
+				var dogname = dog.get('name');
+				notice('removing dog ' + dogname + ' from database');
+				dog.destroy(function(err, data) {
+					if (err) {
+						error('dog not removed');
+					} else {
+						success('dog removed');
+					}
+				});
+			}
+
+			//no need to wait around for dogs to be removed, so go on to next test
+			runner(step);
+		}
+	});
+}
+
+
+function prepareDatabaseForNewUser(step) {
+	var options = {
+		method:'DELETE',
+		endpoint:'users/',
+    qs:{ql:"select * where username ='marty*'"}
+	};
+	client.request(options, function (err, data) {
+		if (err) {
+			notice('database ready - no user to delete');
+		runner(step);
+		} else {
+			//data will contain raw results from API call
+			success('database ready - user deleted worked');
+			runner(step);
+		}
+	});
+}
+
+function createUser(step) {
+	client.signup(_username, _password, _email, 'Marty McFly',
+		function (err, response, marty) {
+			if (err){
+				error('user not created');
+				runner(step, marty);
+			} else {
+				success('user created');
+				runner(step, marty);
+			}
+		}
+	);
+}
+
+function updateUser(step, marty) {
+
+	//add properties cumulatively
+	marty.set('state', 'California');
+	marty.set("girlfriend","Jennifer");
+	marty.save(function(err){
+		if (err){
+			error('user not updated');
+		} else {
+			success('user updated');
+			runner(step, marty);
+		}
+	});
+
+}
+
+function getExistingUser(step, marty) {
+
+	var options = {
+		type:'users',
+		username:_username
+	}
+	client.getEntity(options, function(err, response, existingUser){
+		if (err){
+			error('existing user not retrieved');
+		} else {
+			success('existing user was retrieved');
+
+			var username = existingUser.get('username');
+			if (username === _username){
+				success('got existing user username');
+			} else {
+				error('could not get existing user username');
+			}
+			runner(step, marty);
+		}
+	});
+
+}
+
+
+function refreshUser(step, marty) {
+
+	marty.fetch(function(err){
+		if (err){
+			error('not refreshed');
+		} else {
+			success('user refreshed');
+			runner(step, marty);
+		}
+	});
+
+}
+
+function loginUser(step, marty) {
+	username = _username;
+	password = _password;
+	client.login(username, password,
+		function (err, response, user) {
+			if (err) {
+				error('could not log user in');
+			} else {
+				success('user has been logged in');
+
+				//the login call will return an OAuth token, which is saved
+				//in the client. Any calls made now will use the token.
+				//once a user has logged in, their user object is stored
+				//in the client and you can access it this way:
+				var token = client.token;
+
+				//Then make calls against the API.  For example, you can
+				//get the user entity this way:
+				client.getLoggedInUser(function(err, data, user) {
+					console.error(err, data, user);
+					if(err) {
+						error('could not get logged in user');
+					} else {
+						success('got logged in user');
+
+						//you can then get info from the user entity object:
+						var username = user.get('username');
+						notice('logged in user was: ' + username);
+
+						runner(step, user);
+					}
+				});
+
+			}
+		}
+	);
+}
+
+function changeUsersPassword(step, marty) {
+
+	marty.set('oldpassword', _password);
+	marty.set('password', _newpassword);
+	marty.set('newpassword', _newpassword);
+	marty.changePassword(function(err, response){
+		if (err){
+			error('user password not updated');
+		} else {
+			success('user password updated');
+			runner(step, marty);
+		}
+	});
+
+}
+
+function logoutUser(step, marty) {
+
+	//to log the user out, call the logout() method
+	client.logout();
+
+	//verify the logout worked
+	if (client.isLoggedIn()) {
+		error('logout failed');
+	} else {
+		success('user has been logged out');
+	}
+
+	runner(step, marty);
+}
+
+function reloginUser(step, marty) {
+
+	username = _username
+	password = _newpassword;
+	client.login(username, password,
+		function (err, response, marty) {
+			if (err) {
+				error('could not relog user in');
+			} else {
+				success('user has been re-logged in');
+				runner(step, marty);
+			}
+		}
+	);
+}
+
+
+
+//TODO: currently, this code assumes permissions have been set to support user actions.  need to add code to show how to add new role and permission programatically
+//
+//first create a new permission on the default role:
+//POST "https://api.usergrid.com/yourorgname/yourappname/roles/default/permissions" -d '{"permission":"get,post,put,delete:/dogs/**"}'
+//then after user actions, delete the permission on the default role:
+//DELETE "https://api.usergrid.com/yourorgname/yourappname/roles/default/permissions?permission=get%2Cpost%2Cput%2Cdelete%3A%2Fdogs%2F**"
+
+
+function createDog(step, marty) {
+
+	var options = {
+		type:'dogs',
+		name:'einstein',
+		breed:'mutt'
+	}
+
+	client.createEntity(options, function (err, response, dog) {
+		if (err) {
+			error('POST new dog by logged in user failed');
+		} else {
+			success('POST new dog by logged in user succeeded');
+			runner(step, marty, dog);
+		}
+	});
+
+}
+
+function userLikesDog(step, marty, dog) {
+
+	marty.connect('likes', dog, function (err, response, data) {
+		if (err) {
+			error('connection not created');
+			runner(step, marty);
+		} else {
+
+			//call succeeded, so pull the connections back down
+			marty.getConnections('likes', function (err, response, data) {
+				if (err) {
+						error('could not get connections');
+				} else {
+					//verify that connection exists
+					if (marty.likes.einstein) {
+						success('connection exists');
+					} else {
+						error('connection does not exist');
+					}
+
+					runner(step, marty, dog);
+				}
+			});
+		}
+	});
+
+}
+
+function removeUserLikesDog(step, marty, dog) {
+
+	marty.disconnect('likes', dog, function (err, data) {
+		if (err) {
+			error('connection not deleted');
+			runner(step, marty);
+		} else {
+
+			//call succeeded, so pull the connections back down
+			marty.getConnections('likes', function (err, data) {
+				if (err) {
+					error('error getting connections');
+				} else {
+					//verify that connection exists
+					if (marty.likes.einstein) {
+						error('connection still exists');
+					} else {
+						success('connection deleted');
+					}
+
+					runner(step, marty, dog);
+				}
+			});
+		}
+	});
+
+}
+
+function removeDog(step, marty, dog) {
+
+	//now delete the dog from the database
+	dog.destroy(function(err, data) {
+		if (err) {
+			error('dog not removed');
+		} else {
+			success('dog removed');
+		}
+	});
+	runner(step, marty);
+}
+
+function destroyUser(step, marty) {
+
+	marty.destroy(function(err){
+		if (err){
+			error('user not deleted from database');
+		} else {
+			success('user deleted from database');
+			marty = null; //blow away the local object
+			runner(step);
+		}
+	});
+
+}
+
+function createExistingEntity(step, marty) {
+
+	var options = {
+		type:'dogs',
+		name:'einstein'
+	}
+
+	client.createEntity(options, function (err, response, dog) {
+		if (err) {
+			error('Create new entity to use for existing entity failed');
+		} else {
+			success('Create new entity to use for existing entity succeeded');
+
+			var uuid = dog.get('uuid');
+			//now create new entity, but use same entity name of einstein.  This means that
+			//the original einstein entity now exists.  Thus, the new einstein entity should
+			//be the same as the original + any data differences from the options var:
+
+			options = {
+				type:'dogs',
+				name:'einstein',
+				breed:'mutt'
+			}
+			client.createEntity(options, function (err, response, newdog) {
+				if (err) {
+					success('Create new entity to use for existing entity failed');
+				} else {
+					error('Create new entity to use for existing entity succeeded');
+				}
+				dog.destroy(function(err){
+					if (err){
+						error('existing entity not deleted from database');
+					} else {
+						success('existing entity deleted from database');
+						dog = null; //blow away the local object
+						newdog = null; //blow away the local object
+						runner(step);
+					}
+				});
+			});
+		}
+	});
+
+}
+
+function createNewEntityNoName(step, marty) {
+
+	var options = {
+   type:"something",
+   othervalue:"something else"
+	}
+
+	client.createEntity(options, function (err, response, entity) {
+		if (err) {
+			error('Create new entity with no name failed');
+		} else {
+			success('Create new entity with no name succeeded');
+
+      entity.destroy();
+      runner(step);
+		}
+	});
+
+}
+
+function cleanUpUsers(step){
+
+  var options = {
+    type:'users',
+    qs:{limit:50} //limit statement set to 50
+  }
+
+  client.createCollection(options, function (err, response, users) {
+    if (err) {
+      error('could not get all users');
+    } else {
+      success('got users');
+      //do doggy cleanup
+      while(users.hasNextEntity()) {
+        //get a reference to the dog
+        var user = users.getNextEntity();
+        var username = user.get('name');
+        notice('removing dog ' + username + ' from database');
+        user.destroy(function(err, data) {
+          if (err) {
+            error('user not removed');
+          } else {
+            success('user removed');
+          }
+        });
+      }
+
+      runner(step);
+    }
+  });
+
+}
+
+function cleanUpDogs(step){
+
+    var options = {
+      type:'dogs',
+      qs:{limit:50} //limit statement set to 50
+    }
+
+    client.createCollection(options, function (err, response, dogs) {
+      if (err) {
+        error('could not get all dogs');
+      } else {
+        success('got at most 50 dogs');
+        //we got 50 dogs, now display the Entities:
+        while(dogs.hasNextEntity()) {
+          //get a reference to the dog
+          var dog = dogs.getNextEntity();
+          var name = dog.get('name');
+          notice('dog is called ' + name);
+        }
+        dogs.resetEntityPointer();
+        //do doggy cleanup
+        while(dogs.hasNextEntity()) {
+          //get a reference to the dog
+          var dog = dogs.getNextEntity();
+          var dogname = dog.get('name');
+          notice('removing dog ' + dogname + ' from database');
+          dog.destroy(function(err, data) {
+            if (err) {
+              error('dog not removed');
+            } else {
+              success('dog removed');
+            }
+          });
+        }
+
+        //no need to wait around for dogs to be removed, so go on to next test
+        runner(step);
+      }
+    });
+  }
+});

http://git-wip-us.apache.org/repos/asf/usergrid-javascript/blob/94020d26/extensions/usergrid.validation.js
----------------------------------------------------------------------
diff --git a/extensions/usergrid.validation.js b/extensions/usergrid.validation.js
new file mode 100755
index 0000000..42c1564
--- /dev/null
+++ b/extensions/usergrid.validation.js
@@ -0,0 +1,267 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+/**
+ * validation is a Singleton that provides methods for validating common field types
+ *
+ * @class Usergrid.validation
+ * @author Rod Simpson (rod@apigee.com)
+**/
+Usergrid.validation = (function () {
+
+  var usernameRegex = new RegExp("^([0-9a-zA-Z\.\-])+$");
+  var nameRegex     = new RegExp("^([0-9a-zA-Z@#$%^&!?;:.,'\"~*-=+_\[\\](){}/\\ |])+$");
+  var emailRegex    = new RegExp("^(([0-9a-zA-Z]+[_\+.-]?)+@[0-9a-zA-Z]+[0-9,a-z,A-Z,.,-]*(.){1}[a-zA-Z]{2,4})+$");
+  var passwordRegex = new RegExp("^([0-9a-zA-Z@#$%^&!?<>;:.,'\"~*-=+_\[\\](){}/\\ |])+$");
+  var pathRegex     = new RegExp("^([0-9a-z./-])+$");
+  var titleRegex    = new RegExp("^([0-9a-zA-Z.!-?/])+$");
+
+  /**
+    * Tests the string against the allowed chars regex
+    *
+    * @public
+    * @method validateUsername
+    * @param {string} username - The string to test
+    * @param {function} failureCallback - (optional), the function to call on a failure
+    * @return {boolean} Returns true if string passes regex, false if not
+    */
+  function validateUsername(username, failureCallback) {
+    if (usernameRegex.test(username) && checkLength(username, 4, 80)) {
+      return true;
+    } else {
+      if (failureCallback && typeof(failureCallback) === "function") {
+        failureCallback(this.getUsernameAllowedChars());
+      }
+      return false;
+    }
+  }
+
+  /**
+    * Returns the regex of allowed chars
+    *
+    * @public
+    * @method getUsernameAllowedChars
+    * @return {string} Returns a string with the allowed chars
+    */
+  function getUsernameAllowedChars(){
+    return 'Length: min 4, max 80. Allowed: A-Z, a-z, 0-9, dot, and dash';
+  }
+
+  /**
+    * Tests the string against the allowed chars regex
+    *
+    * @public
+    * @method validateName
+    * @param {string} name - The string to test
+    * @param {function} failureCallback - (optional), the function to call on a failure
+    * @return {boolean} Returns true if string passes regex, false if not
+    */
+  function validateName(name, failureCallback) {
+    if (nameRegex.test(name) && checkLength(name, 4, 80)) {
+      return true;
+    } else {
+      if (failureCallback && typeof(failureCallback) === "function") {
+        failureCallback(this.getNameAllowedChars());
+      }
+      return false;
+    }
+  }
+
+  /**
+    * Returns the regex of allowed chars
+    *
+    * @public
+    * @method getNameAllowedChars
+    * @return {string} Returns a string with the allowed chars
+    */
+  function getNameAllowedChars(){
+    return 'Length: min 4, max 80. Allowed: A-Z, a-z, 0-9, ~ @ # % ^ & * ( ) - _ = + [ ] { } \\ | ; : \' " , . / ? !';
+  }
+
+  /**
+    * Tests the string against the allowed chars regex
+    *
+    * @public
+    * @method validatePassword
+    * @param {string} password - The string to test
+    * @param {function} failureCallback - (optional), the function to call on a failure
+    * @return {boolean} Returns true if string passes regex, false if not
+    */
+  function validatePassword(password, failureCallback) {
+    if (passwordRegex.test(password) && checkLength(password, 5, 16)) {
+      return true;
+    } else {
+      if (failureCallback && typeof(failureCallback) === "function") {
+        failureCallback(this.getPasswordAllowedChars());
+      }
+      return false;
+    }
+  }
+
+  /**
+    * Returns the regex of allowed chars
+    *
+    * @public
+    * @method getPasswordAllowedChars
+    * @return {string} Returns a string with the allowed chars
+    */
+  function getPasswordAllowedChars(){
+    return 'Length: min 5, max 16. Allowed: A-Z, a-z, 0-9, ~ @ # % ^ & * ( ) - _ = + [ ] { } \\ | ; : \' " , . < > / ? !';
+  }
+
+  /**
+    * Tests the string against the allowed chars regex
+    *
+    * @public
+    * @method validateEmail
+    * @param {string} email - The string to test
+    * @param {function} failureCallback - (optional), the function to call on a failure
+    * @return {boolean} Returns true if string passes regex, false if not
+    */
+  function validateEmail(email, failureCallback) {
+    if (emailRegex.test(email) && checkLength(email, 4, 80)) {
+      return true;
+    } else {
+      if (failureCallback && typeof(failureCallback) === "function") {
+        failureCallback(this.getEmailAllowedChars());
+      }
+      return false;
+    }
+  }
+
+  /**
+    * Returns the regex of allowed chars
+    *
+    * @public
+    * @method getEmailAllowedChars
+    * @return {string} Returns a string with the allowed chars
+    */
+  function getEmailAllowedChars(){
+    return 'Email must be in standard form: e.g. example@Usergrid.com';
+  }
+
+  /**
+    * Tests the string against the allowed chars regex
+    *
+    * @public
+    * @method validatePath
+    * @param {string} path - The string to test
+    * @param {function} failureCallback - (optional), the function to call on a failure
+    * @return {boolean} Returns true if string passes regex, false if not
+    */
+  function validatePath(path, failureCallback) {
+    if (pathRegex.test(path) && checkLength(path, 4, 80)) {
+      return true;
+    } else {
+      if (failureCallback && typeof(failureCallback) === "function") {
+        failureCallback(this.getPathAllowedChars());
+      }
+      return false;
+    }
+  }
+
+  /**
+    * Returns the regex of allowed chars
+    *
+    * @public
+    * @method getPathAllowedChars
+    * @return {string} Returns a string with the allowed chars
+    */
+  function getPathAllowedChars(){
+    return 'Length: min 4, max 80. Allowed: /, a-z, 0-9, dot, and dash';
+  }
+
+  /**
+    * Tests the string against the allowed chars regex
+    *
+    * @public
+    * @method validateTitle
+    * @param {string} title - The string to test
+    * @param {function} failureCallback - (optional), the function to call on a failure
+    * @return {boolean} Returns true if string passes regex, false if not
+    */
+  function validateTitle(title, failureCallback) {
+    if (titleRegex.test(title) && checkLength(title, 4, 80)) {
+      return true;
+    } else {
+      if (failureCallback && typeof(failureCallback) === "function") {
+        failureCallback(this.getTitleAllowedChars());
+      }
+      return false;
+    }
+  }
+
+  /**
+    * Returns the regex of allowed chars
+    *
+    * @public
+    * @method getTitleAllowedChars
+    * @return {string} Returns a string with the allowed chars
+    */
+  function getTitleAllowedChars(){
+    return 'Length: min 4, max 80. Allowed: space, A-Z, a-z, 0-9, dot, dash, /, !, and ?';
+  }
+
+  /**
+    * Tests if the string is the correct length
+    *
+    * @public
+    * @method checkLength
+    * @param {string} string - The string to test
+    * @param {integer} min - the lower bound
+    * @param {integer} max - the upper bound
+    * @return {boolean} Returns true if string is correct length, false if not
+    */
+  function checkLength(string, min, max) {
+    if (string.length > max || string.length < min) {
+      return false;
+    }
+    return true;
+  }
+
+  /**
+    * 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);
+  }
+
+  return {
+    validateUsername:validateUsername,
+    getUsernameAllowedChars:getUsernameAllowedChars,
+    validateName:validateName,
+    getNameAllowedChars:getNameAllowedChars,
+    validatePassword:validatePassword,
+    getPasswordAllowedChars:getPasswordAllowedChars,
+    validateEmail:validateEmail,
+    getEmailAllowedChars:getEmailAllowedChars,
+    validatePath:validatePath,
+    getPathAllowedChars:getPathAllowedChars,
+    validateTitle:validateTitle,
+    getTitleAllowedChars:getTitleAllowedChars,
+    isUUID:isUUID
+  }
+})();

http://git-wip-us.apache.org/repos/asf/usergrid-javascript/blob/94020d26/index.html
----------------------------------------------------------------------
diff --git a/index.html b/index.html
new file mode 100755
index 0000000..309d90b
--- /dev/null
+++ b/index.html
@@ -0,0 +1,70 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You 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.
+-->
+
+<!DOCTYPE html>
+<html>
+   <head>
+      <title></title>
+      <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+      <link rel="stylesheet" href="examples/resources/css/bootstrap-combined.min.css" />
+      <link rel="stylesheet" href="examples/resources/css/styles.css" />
+   </head>
+   <body>
+      <div class="header">
+         <img src="examples/resources/images/apigee.png"> Usergrid Javascript SDK
+      </div>
+      <div id="main" class="main">
+         <br/>
+         <h2>Javascript SDK</h2>
+         This SDK is designed to enable you to connect your apps to the Usergrid API.
+         <br/>
+         <br/>
+         <h2>Read me</h2>
+         View the read me file for the SDK:
+         <a href="https://github.com/usergrid/usergrid/blob/master/sdks/html5-javascript/README.md">https://github.com/usergrid/usergrid/blob/master/sdks/html5-javascript/README.md</a>.
+         <br/>
+         <br/>
+         <h2>API documentation</h2>
+         For more information on Usergrid, see the Apache docs:
+         <a href="http://usergrid.apache.org/">http://usergrid.apache.org/</a>
+         <br/>
+         <br/>
+
+         <h2>Example Apps:</h2>
+         <br/>
+         <a href="examples/all-calls/all-calls.html" style="font-size: 20px;">All Calls example app</a>
+         <br/>
+         This app shows the basic method for making all call types against the API.  It also shows how to log in an app user.
+         <br/>
+         <br/>
+         <a href="examples/dogs/dogs.html" style="font-size: 20px;">Dogs example app</a>
+         <br/>
+         This app shows you how to use the Entity and Collection objects
+         <br/>
+         <br/>
+         <a href="examples/facebook/facebook.html" style="font-size: 20px;">Facebook Login Example</a>
+         <br/>
+         This app shows you how to use Facebook to log into Usergrid.
+         <br/>
+         <br/>
+         <a href="examples/test/test.html" style="font-size: 20px;">Tests for examples in readme file</a>
+         <br/>
+         This is a test script that runs all the sample code shown in the readme file.  See the samples in action!
+      </div>
+
+    </body>
+</html>

http://git-wip-us.apache.org/repos/asf/usergrid-javascript/blob/94020d26/lib/Module.js
----------------------------------------------------------------------
diff --git a/lib/Module.js b/lib/Module.js
new file mode 100644
index 0000000..38f5047
--- /dev/null
+++ b/lib/Module.js
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+    //noinspection ThisExpressionReferencesGlobalObjectJS
+(function (global) {
+    var name = 'Module',
+        short = '_m',
+        _name = global[name],
+        _short = (short !== undefined) ? global[short] : undefined;
+
+    function Module() {
+        /* put code in here */
+
+    }
+
+    global[name] = Module;
+    if (short !== undefined) {
+        global[short] = Module;
+    }
+    global[name].global[name].noConflict = function () {
+        if (_name) {
+            global[name] = _name;
+        }
+        if (short !== undefined) {
+            global[short] = _short;
+        }
+        return Module;
+    };
+    if( typeof module !== "undefined" && ('exports' in module)){
+        module.exports	= global[name]
+    }
+    return global[name];
+}(this));

http://git-wip-us.apache.org/repos/asf/usergrid-javascript/blob/94020d26/lib/Usergrid.js
----------------------------------------------------------------------
diff --git a/lib/Usergrid.js b/lib/Usergrid.js
new file mode 100644
index 0000000..f1bb218
--- /dev/null
+++ b/lib/Usergrid.js
@@ -0,0 +1,323 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+//Hack around IE console.log
+window.console = window.console || {};
+window.console.log = window.console.log || function() {};
+
+
+function extend(subClass, superClass) {
+    var F = function() {};
+    F.prototype = superClass.prototype;
+    subClass.prototype = new F();
+    subClass.prototype.constructor = subClass;
+
+    subClass.superclass = superClass.prototype;
+    if (superClass.prototype.constructor == Object.prototype.constructor) {
+        superClass.prototype.constructor = superClass;
+    }
+    return subClass;
+}
+
+function propCopy(from, to) {
+    for (var prop in from) {
+        if (from.hasOwnProperty(prop)) {
+            if ("object" === typeof from[prop] && "object" === typeof to[prop]) {
+                to[prop] = propCopy(from[prop], to[prop]);
+            } else {
+                to[prop] = from[prop];
+            }
+        }
+    }
+    return to;
+}
+
+function NOOP() {}
+
+function isValidUrl(url) {
+    if (!url) return false;
+    var doc, base, anchor, isValid = false;
+    try {
+        doc = document.implementation.createHTMLDocument('');
+        base = doc.createElement('base');
+        base.href = base || window.lo;
+        doc.head.appendChild(base);
+        anchor = doc.createElement('a');
+        anchor.href = url;
+        doc.body.appendChild(anchor);
+        isValid = !(anchor.href === '')
+    } catch (e) {
+        console.error(e);
+    } finally {
+        doc.head.removeChild(base);
+        doc.body.removeChild(anchor);
+        base = null;
+        anchor = null;
+        doc = null;
+        return isValid;
+    }
+}
+
+/*
+ * Tests if the string is a uuid
+ *
+ * @public
+ * @method isUUID
+ * @param {string} uuid The string to test
+ * @returns {Boolean} true if string is 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}$/;
+
+function isUUID(uuid) {
+    return (!uuid) ? false : 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 queryString;
+    if (params && Object.keys(params)) {
+        queryString = [].slice.call(arguments)
+            .reduce(function(a, b) {
+                return a.concat((b instanceof Array) ? b : [b]);
+            }, [])
+            .filter(function(c) {
+                return "object" === typeof c
+            })
+            .reduce(function(p, c) {
+                (!(c instanceof Array)) ? p = p.concat(Object.keys(c).map(function(key) {
+                    return [key, c[key]]
+                })) : p.push(c);
+                return p;
+            }, [])
+            .reduce(function(p, c) {
+                ((c.length === 2) ? p.push(c) : p = p.concat(c));
+                return p;
+            }, [])
+            .reduce(function(p, c) {
+                (c[1] instanceof Array) ? c[1].forEach(function(v) {
+                    p.push([c[0], v])
+                }) : p.push(c);
+                return p;
+            }, [])
+            .map(function(c) {
+                c[1] = encodeURIComponent(c[1]);
+                return c.join('=')
+            })
+            .join('&');
+    }
+    return queryString;
+}
+
+
+/*
+ *  method to determine whether or not the passed variable is a function
+ *
+ *  @method isFunction
+ *  @public
+ *  @params {any} f - any variable
+ *  @return {boolean} Returns true or false
+ */
+function isFunction(f) {
+    return (f && f !== null && typeof(f) === 'function');
+}
+
+/*
+ *  a safe wrapper for executing a callback
+ *
+ *  @method doCallback
+ *  @public
+ *  @params {Function} callback - the passed-in callback method
+ *  @params {Array} params - an array of arguments to pass to the callback
+ *  @params {Object} context - an optional calling context for the callback
+ *  @return Returns whatever would be returned by the callback. or false.
+ */
+function doCallback(callback, params, context) {
+    var returnValue;
+    if (isFunction(callback)) {
+        if (!params) params = [];
+        if (!context) context = this;
+        params.push(context);
+        //try {
+        returnValue = callback.apply(context, params);
+        /*} catch (ex) {
+			if (console && console.error) {
+				console.error("Callback error:", ex);
+			}
+		}*/
+    }
+    return returnValue;
+}
+
+//noinspection ThisExpressionReferencesGlobalObjectJS
+(function(global) {
+    var name = 'Usergrid',
+        overwrittenName = global[name];
+    var VALID_REQUEST_METHODS = ['GET', 'POST', 'PUT', 'DELETE'];
+
+    function Usergrid() {
+        this.logger = new Logger(name);
+    }
+
+    Usergrid.isValidEndpoint = function(endpoint) {
+        //TODO actually implement this
+        return true;
+    };
+
+    Usergrid.Request = function(method, endpoint, query_params, data, callback) {
+        var p = new Promise();
+        /*
+         Create a logger
+         */
+        this.logger = new global.Logger("Usergrid.Request");
+        this.logger.time("process request " + method + " " + endpoint);
+        /*
+         Validate our input
+         */
+        this.endpoint = endpoint + '?' + encodeParams(query_params);
+        this.method = method.toUpperCase();
+        //this.query_params = query_params;
+        this.data = ("object" === typeof data) ? JSON.stringify(data) : data;
+
+        if (VALID_REQUEST_METHODS.indexOf(this.method) === -1) {
+            throw new UsergridInvalidHTTPMethodError("invalid request method '" + this.method + "'");
+        }
+
+        /*
+         Prepare our request
+         */
+        if (!isValidUrl(this.endpoint)) {
+            this.logger.error(endpoint, this.endpoint, /^https:\/\//.test(endpoint));
+            throw new UsergridInvalidURIError("The provided endpoint is not valid: " + this.endpoint);
+        }
+        /* a callback to make the request */
+        var request = function() {
+            return Ajax.request(this.method, this.endpoint, this.data)
+        }.bind(this);
+        /* a callback to process the response */
+        var response = function(err, request) {
+            return new Usergrid.Response(err, request)
+        }.bind(this);
+        /* a callback to clean up and return data to the client */
+        var oncomplete = function(err, response) {
+            p.done(err, response);
+            this.logger.info("REQUEST", err, response);
+            doCallback(callback, [err, response]);
+            this.logger.timeEnd("process request " + method + " " + endpoint);
+        }.bind(this);
+        /* and a promise to chain them all together */
+        Promise.chain([request, response]).then(oncomplete);
+
+        return p;
+    };
+    //TODO more granular handling of statusCodes
+    Usergrid.Response = function(err, response) {
+        var p = new Promise();
+        var data = null;
+        try {
+            data = JSON.parse(response.responseText);
+        } catch (e) {
+            //this.logger.error("Error parsing response text: ",this.text);
+            //this.logger.error("Caught error ", e.message);
+            data = {}
+        }
+        Object.keys(data).forEach(function(key) {
+            Object.defineProperty(this, key, {
+                value: data[key],
+                enumerable: true
+            });
+        }.bind(this));
+        Object.defineProperty(this, "logger", {
+            enumerable: false,
+            configurable: false,
+            writable: false,
+            value: new global.Logger(name)
+        });
+        Object.defineProperty(this, "success", {
+            enumerable: false,
+            configurable: false,
+            writable: true,
+            value: true
+        });
+        Object.defineProperty(this, "err", {
+            enumerable: false,
+            configurable: false,
+            writable: true,
+            value: err
+        });
+        Object.defineProperty(this, "status", {
+            enumerable: false,
+            configurable: false,
+            writable: true,
+            value: parseInt(response.status)
+        });
+        Object.defineProperty(this, "statusGroup", {
+            enumerable: false,
+            configurable: false,
+            writable: true,
+            value: (this.status - this.status % 100)
+        });
+        switch (this.statusGroup) {
+            case 200: //success
+                this.success = true;
+                break;
+            case 400: //user error
+            case 500: //server error
+            case 300: //cache and redirects
+            case 100: //upgrade
+            default:
+                //server error
+                this.success = false;
+                break;
+        }
+        if (this.success) {
+            p.done(null, this);
+        } else {
+            p.done(UsergridError.fromResponse(data), this);
+        }
+        return p;
+    };
+    Usergrid.Response.prototype.getEntities = function() {
+        var entities;
+        if (this.success) {
+            entities = (this.data) ? this.data.entities : this.entities;
+        }
+        return entities || [];
+    }
+    Usergrid.Response.prototype.getEntity = function() {
+        var entities = this.getEntities();
+        return entities[0];
+    }
+    Usergrid.VERSION = Usergrid.USERGRID_SDK_VERSION = '0.11.0';
+
+    global[name] = Usergrid;
+    global[name].noConflict = function() {
+        if (overwrittenName) {
+            global[name] = overwrittenName;
+        }
+        return Usergrid;
+    };
+    return global[name];
+}(this));

http://git-wip-us.apache.org/repos/asf/usergrid-javascript/blob/94020d26/lib/modules/Asset.js
----------------------------------------------------------------------
diff --git a/lib/modules/Asset.js b/lib/modules/Asset.js
new file mode 100644
index 0000000..862313a
--- /dev/null
+++ b/lib/modules/Asset.js
@@ -0,0 +1,248 @@
+/*
+ *Licensed to the Apache Software Foundation (ASF) under one
+ *or more contributor license agreements.  See the NOTICE file
+ *distributed with this work for additional information
+ *regarding copyright ownership.  The ASF licenses this file
+ *to you 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.
+ */
+
+
+
+/*
+ *  XMLHttpRequest.prototype.sendAsBinary polyfill
+ *  from: https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest#sendAsBinary()
+ *
+ *  @method sendAsBinary
+ *  @param {string} sData
+ */
+if (!XMLHttpRequest.prototype.sendAsBinary) {
+	XMLHttpRequest.prototype.sendAsBinary = function(sData) {
+		var nBytes = sData.length,
+			ui8Data = new Uint8Array(nBytes);
+		for (var nIdx = 0; nIdx < nBytes; nIdx++) {
+			ui8Data[nIdx] = sData.charCodeAt(nIdx) & 0xff;
+		}
+		this.send(ui8Data);
+	};
+}
+
+
+/*
+ *  A class to model a Usergrid asset.
+ *
+ *  @constructor
+ *  @param {object} options {name:"photo.jpg", path:"/user/uploads", "content-type":"image/jpeg", owner:"F01DE600-0000-0000-0000-000000000000" }
+ *  @returns {callback} callback(err, asset)
+ */
+Usergrid.Asset = function(options, callback) {
+	var self = this,
+		messages = [];
+	self._client = options.client;
+	self._data = options.data || {};
+	self._data.type = "assets";
+	var missingData = ["name", "owner", "path"].some(function(required) {
+		return !(required in self._data);
+	});
+	if (missingData) {
+		doCallback(callback, [new UsergridError("Invalid asset data: 'name', 'owner', and 'path' are required properties."), null, self], self);
+	} else {
+		self.save(function(err, data) {
+			if (err) {
+				doCallback(callback, [new UsergridError(data), data, self], self);
+			} else {
+				if (data && data.entities && data.entities.length) {
+					self.set(data.entities[0]);
+				}
+				doCallback(callback, [null, data, self], self);
+			}
+		});
+	}
+};
+
+/*
+ *  Inherit from Usergrid.Entity.
+ */
+Usergrid.Asset.prototype = new Usergrid.Entity();
+
+/*
+ *  Add an asset to a folder.
+ *
+ *  @method connect
+ *  @public
+ *  @param {object} options {folder:"F01DE600-0000-0000-0000-000000000000"}
+ *  @returns {callback} callback(err, asset)
+ */
+
+Usergrid.Asset.prototype.addToFolder = function(options, callback) {
+	var self = this,
+		error = null;
+	if (('folder' in options) && isUUID(options.folder)) {
+		//we got a valid UUID
+		var folder = Usergrid.Folder({
+			uuid: options.folder
+		}, function(err, folder) {
+			if (err) {
+				doCallback(callback, [UsergridError.fromResponse(folder), folder, self], self);
+			} else {
+				var endpoint = ["folders", folder.get("uuid"), "assets", self.get("uuid")].join('/');
+				var options = {
+					method: 'POST',
+					endpoint: endpoint
+				};
+				this._client.request(options, function(err, response) {
+					if (err) {
+						doCallback(callback, [UsergridError.fromResponse(folder), response, self], self);
+					} else {
+						doCallback(callback, [null, folder, self], self);
+					}
+
+
+				});
+			}
+		});
+	} else {
+		doCallback(callback, [new UsergridError('folder not specified'), null, self], self);
+	}
+};
+
+Usergrid.Entity.prototype.attachAsset = function (file, callback) { 
+    
+    if (!(window.File && window.FileReader && window.FileList && window.Blob)) {
+        doCallback(callback, [ new UsergridError("The File APIs are not fully supported by your browser."), null, this ], this);
+        return;
+    }
+    var self = this;
+    var args = arguments;
+    var type = this._data.type;
+    var attempts = self.get("attempts");
+    if (isNaN(attempts)) {
+        attempts = 3;
+    }
+
+    if(type != 'assets' && type != 'asset') {        
+        var endpoint = [ this._client.URI, this._client.orgName, this._client.appName, type, self.get("uuid") ].join("/");            
+    } else {
+        self.set("content-type", file.type);
+        self.set("size", file.size);
+        var endpoint = [ this._client.URI, this._client.orgName, this._client.appName, "assets", self.get("uuid"), "data" ].join("/");    
+    }
+
+    var xhr = new XMLHttpRequest();
+    xhr.open("POST", endpoint, true);
+    xhr.onerror = function(err) {
+        doCallback(callback, [ new UsergridError("The File APIs are not fully supported by your browser.") ], xhr, self);
+    };
+    xhr.onload = function(ev) {
+        if (xhr.status >= 500 && attempts > 0) {
+            self.set("attempts", --attempts);
+            setTimeout(function() {
+                self.attachAsset.apply(self, args);
+            }, 100);
+        } else if (xhr.status >= 300) {
+            self.set("attempts");
+            doCallback(callback, [ new UsergridError(JSON.parse(xhr.responseText)), xhr, self ], self);
+        } else {
+            self.set("attempts");
+            self.fetch();
+            doCallback(callback, [ null, xhr, self ], self);
+        }
+    };
+    var fr = new FileReader();
+    fr.onload = function() {
+        var binary = fr.result;        
+        if (type === 'assets' || type === 'asset') {
+            xhr.overrideMimeType("application/octet-stream");
+            xhr.setRequestHeader("Content-Type", "application/octet-stream");
+        }
+        xhr.sendAsBinary(binary);
+    };
+    fr.readAsBinaryString(file);
+
+};
+
+/*
+ *  Upload Asset data
+ *
+ *  @method upload
+ *  @public
+ *  @param {object} data Can be a javascript Blob or File object
+ *  @returns {callback} callback(err, asset)
+ */
+Usergrid.Asset.prototype.upload = function(data, callback) {
+   this.attachAsset(data, function(err, response) {
+        if(!err){
+            doCallback(callback, [ null, response, self ], self);
+        } else {
+            doCallback(callback, [ new UsergridError(err), response, self ], self);
+        }
+    }); 
+};
+
+/*
+ *  Download Asset data
+ *
+ *  @method download
+ *  @public
+ *  @returns {callback} callback(err, blob) blob is a javascript Blob object.
+ */
+Usergrid.Entity.prototype.downloadAsset = function(callback) {
+    var self = this;    
+    var endpoint;
+    var type = this._data.type;
+
+    var xhr = new XMLHttpRequest();
+    if(type != "assets" && type != 'asset') {
+        endpoint = [ this._client.URI, this._client.orgName, this._client.appName, type, self.get("uuid") ].join("/");     
+    } else {        
+        endpoint = [ this._client.URI, this._client.orgName, this._client.appName, "assets", self.get("uuid"), "data" ].join("/");        
+    }
+    xhr.open("GET", endpoint, true);
+    xhr.responseType = "blob";
+    xhr.onload = function(ev) {
+        var blob = xhr.response;
+        if(type != "assets" && type != 'asset') {
+            doCallback(callback, [ null, blob, xhr ], self);
+        } else {
+            doCallback(callback, [ null, xhr, self ], self);
+        }
+    };
+    xhr.onerror = function(err) {
+        callback(true, err);
+        doCallback(callback, [ new UsergridError(err), xhr, self ], self);
+    };
+
+    if(type != "assets" && type != 'asset') {            
+        xhr.setRequestHeader("Accept", self._data["file-metadata"]["content-type"]);
+    } else {        
+        xhr.overrideMimeType(self.get("content-type"));
+    }
+    xhr.send();
+};
+
+/*
+ *  Download Asset data
+ *
+ *  @method download
+ *  @public
+ *  @returns {callback} callback(err, blob) blob is a javascript Blob object.
+ */
+Usergrid.Asset.prototype.download = function(callback) {
+    this.downloadAsset(function(err, response) {
+        if(!err){
+            doCallback(callback, [ null, response, self ], self);
+        } else {
+            doCallback(callback, [ new UsergridError(err), response, self ], self);
+        }
+    });
+};
\ No newline at end of file