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;"> </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